Add boundsread annotations throughout, enable +bounds checking.
[platform/upstream/rpm.git] / lib / fsm.c
1 /** \ingroup payload
2  * \file lib/fsm.c
3  * File state machine to handle a payload from a package.
4  */
5
6 #include "system.h"
7
8 #include <rpmio_internal.h>
9 #include <rpmlib.h>
10
11 #include "rpmps.h"
12
13 #include "cpio.h"
14 #include "fsm.h"
15 #include "psm.h"        /* XXX fiTypeString */
16 #include "rpmerr.h"
17
18 #define _RPMFI_INTERNAL
19 #include "rpmfi.h"
20 #include "rpmte.h"
21 #include "rpmts.h"
22
23 #include "debug.h"
24
25 /*@access FD_t @*/
26 /*@access FSMI_t @*/
27 /*@access FSM_t @*/
28
29 /*@access rpmfi @*/
30 /*@access rpmte @*/
31 /*@access rpmts @*/
32
33 #define alloca_strdup(_s)       strcpy(alloca(strlen(_s)+1), (_s))
34
35 /*@unchecked@*/
36 int _fsm_debug = 0;
37
38 /* XXX Failure to remove is not (yet) cause for failure. */
39 /*@-exportlocal -exportheadervar@*/
40 /*@unchecked@*/
41 int strict_erasures = 0;
42 /*@=exportlocal =exportheadervar@*/
43
44 rpmts fsmGetTs(const FSM_t fsm) {
45     const FSMI_t iter = fsm->iter;
46     /*@-compdef -refcounttrans -retexpose -usereleased @*/
47     return (iter ? iter->ts : NULL);
48     /*@=compdef =refcounttrans =retexpose =usereleased @*/
49 }
50
51 rpmfi fsmGetFi(const FSM_t fsm)
52 {
53     const FSMI_t iter = fsm->iter;
54     /*@-compdef -refcounttrans -retexpose -usereleased @*/
55     return (iter ? iter->fi : NULL);
56     /*@=compdef =refcounttrans =retexpose =usereleased @*/
57 }
58
59 #define SUFFIX_RPMORIG  ".rpmorig"
60 #define SUFFIX_RPMSAVE  ".rpmsave"
61 #define SUFFIX_RPMNEW   ".rpmnew"
62
63 /** \ingroup payload
64  * Build path to file from file info, ornamented with subdir and suffix.
65  * @param fsm           file state machine data
66  * @param st            file stat info
67  * @param subdir        subdir to use (NULL disables)
68  * @param suffix        suffix to use (NULL disables)
69  * @retval              path to file
70  */
71 static /*@only@*//*@null@*/
72 const char * fsmFsPath(/*@special@*/ /*@null@*/ const FSM_t fsm,
73                 /*@null@*/ const struct stat * st,
74                 /*@null@*/ const char * subdir,
75                 /*@null@*/ const char * suffix)
76         /*@uses fsm->dirName, fsm->baseName */
77         /*@*/
78 {
79     const char * s = NULL;
80
81     if (fsm) {
82         int nb;
83         char * t;
84         nb = strlen(fsm->dirName) +
85             (st && !S_ISDIR(st->st_mode) ? (subdir ? strlen(subdir) : 0) : 0) +
86             (st && !S_ISDIR(st->st_mode) ? (suffix ? strlen(suffix) : 0) : 0) +
87             strlen(fsm->baseName) + 1;
88 /*@-boundswrite@*/
89         s = t = xmalloc(nb);
90         t = stpcpy(t, fsm->dirName);
91         if (st && !S_ISDIR(st->st_mode))
92             if (subdir) t = stpcpy(t, subdir);
93         t = stpcpy(t, fsm->baseName);
94         if (st && !S_ISDIR(st->st_mode))
95             if (suffix) t = stpcpy(t, suffix);
96 /*@=boundswrite@*/
97     }
98     return s;
99 }
100
101 /** \ingroup payload
102  * Destroy file info iterator.
103  * @param p             file info iterator
104  * @retval              NULL always
105  */
106 static /*@null@*/ void * mapFreeIterator(/*@only@*//*@null@*/ void * p)
107         /*@*/
108 {
109     FSMI_t iter = p;
110     if (iter) {
111         iter->ts = rpmtsUnlink(iter->ts, "mapIterator");
112         iter->fi = rpmfiUnlink(iter->fi, "mapIterator");
113     }
114     return _free(p);
115 }
116
117 /** \ingroup payload
118  * Create file info iterator.
119  * @param ts            transaction set
120  * @param fi            transaction element file info
121  * @return              file info iterator
122  */
123 static void *
124 mapInitIterator(rpmts ts, rpmfi fi)
125         /*@modifies ts, fi @*/
126 {
127     FSMI_t iter = NULL;
128
129     iter = xcalloc(1, sizeof(*iter));
130     iter->ts = rpmtsLink(ts, "mapIterator");
131     iter->fi = rpmfiLink(fi, "mapIterator");
132     iter->reverse = (rpmteType(fi->te) == TR_REMOVED && fi->action != FA_COPYOUT);
133     iter->i = (iter->reverse ? (fi->fc - 1) : 0);
134     iter->isave = iter->i;
135     return iter;
136 }
137
138 /** \ingroup payload
139  * Return next index into file info.
140  * @param a             file info iterator
141  * @return              next index, -1 on termination
142  */
143 static int mapNextIterator(/*@null@*/ void * a)
144         /*@*/
145 {
146     FSMI_t iter = a;
147     int i = -1;
148
149     if (iter) {
150         const rpmfi fi = iter->fi;
151         if (iter->reverse) {
152             if (iter->i >= 0)   i = iter->i--;
153         } else {
154             if (iter->i < fi->fc)       i = iter->i++;
155         }
156         iter->isave = i;
157     }
158     return i;
159 }
160
161 /** \ingroup payload
162  */
163 /*@-boundsread@*/
164 static int cpioStrCmp(const void * a, const void * b)
165         /*@*/
166 {
167     const char * afn = *(const char **)a;
168     const char * bfn = *(const char **)b;
169
170     /* Match rpm-4.0 payloads with ./ prefixes. */
171     if (afn[0] == '.' && afn[1] == '/') afn += 2;
172     if (bfn[0] == '.' && bfn[1] == '/') bfn += 2;
173
174     /* If either path is absolute, make it relative. */
175     if (afn[0] == '/')  afn += 1;
176     if (bfn[0] == '/')  bfn += 1;
177
178     return strcmp(afn, bfn);
179 }
180 /*@=boundsread@*/
181
182 /** \ingroup payload
183  * Locate archive path in file info.
184  * @param iter          file info iterator
185  * @param fsmPath       archive path
186  * @return              index into file info, -1 if archive path was not found
187  */
188 /*@-boundsread@*/
189 static int mapFind(/*@null@*/ FSMI_t iter, const char * fsmPath)
190         /*@modifies iter @*/
191 {
192     int ix = -1;
193
194     if (iter) {
195         const rpmfi fi = iter->fi;
196         if (fi && fi->fc > 0 && fi->apath && fsmPath && *fsmPath) {
197             const char ** p = NULL;
198
199 /*@-boundswrite@*/
200             if (fi->apath != NULL)
201                 p = bsearch(&fsmPath, fi->apath, fi->fc, sizeof(fsmPath),
202                         cpioStrCmp);
203 /*@=boundswrite@*/
204             if (p) {
205                 iter->i = p - fi->apath;
206                 ix = mapNextIterator(iter);
207             }
208         }
209     }
210     return ix;
211 }
212 /*@=boundsread@*/
213
214 /** \ingroup payload
215  * Directory name iterator.
216  */
217 typedef struct dnli_s {
218     rpmfi fi;
219 /*@only@*/ /*@null@*/ char * active;
220     int reverse;
221     int isave;
222     int i;
223 } * DNLI_t;
224
225 /** \ingroup payload
226  * Destroy directory name iterator.
227  * @param a             directory name iterator
228  * @retval              NULL always
229  */
230 static /*@null@*/ void * dnlFreeIterator(/*@only@*//*@null@*/ const void * a)
231         /*@modifies a @*/
232 {
233     if (a) {
234         DNLI_t dnli = (void *)a;
235         if (dnli->active) free(dnli->active);
236     }
237     return _free(a);
238 }
239
240 /** \ingroup payload
241  */
242 static inline int dnlCount(const DNLI_t dnli)
243         /*@*/
244 {
245     return (dnli ? dnli->fi->dc : 0);
246 }
247
248 /** \ingroup payload
249  */
250 static inline int dnlIndex(const DNLI_t dnli)
251         /*@*/
252 {
253     return (dnli ? dnli->isave : -1);
254 }
255
256 /** \ingroup payload
257  * Create directory name iterator.
258  * @param fsm           file state machine data
259  * @param reverse       traverse directory names in reverse order?
260  * @return              directory name iterator
261  */
262 /*@-boundsread@*/
263 /*@-usereleased@*/
264 static /*@only@*/ void * dnlInitIterator(/*@special@*/ const FSM_t fsm,
265                 int reverse)
266         /*@uses fsm->iter @*/ 
267         /*@*/
268 {
269     rpmfi fi = fsmGetFi(fsm);
270     DNLI_t dnli;
271     int i, j;
272
273     if (fi == NULL)
274         return NULL;
275     dnli = xcalloc(1, sizeof(*dnli));
276     dnli->fi = fi;
277     dnli->reverse = reverse;
278     /*@-branchstate@*/
279     dnli->i = (reverse ? fi->dc : 0);
280     /*@=branchstate@*/
281
282     if (fi->dc) {
283         dnli->active = xcalloc(fi->dc, sizeof(*dnli->active));
284
285         /* Identify parent directories not skipped. */
286 /*@-boundswrite@*/
287         for (i = 0; i < fi->fc; i++)
288             if (!XFA_SKIPPING(fi->actions[i])) dnli->active[fi->dil[i]] = 1;
289 /*@=boundswrite@*/
290
291         /* Exclude parent directories that are explicitly included. */
292         for (i = 0; i < fi->fc; i++) {
293             int dil, dnlen, bnlen;
294
295             if (!S_ISDIR(fi->fmodes[i]))
296                 continue;
297
298             dil = fi->dil[i];
299             dnlen = strlen(fi->dnl[dil]);
300             bnlen = strlen(fi->bnl[i]);
301
302             for (j = 0; j < fi->dc; j++) {
303                 const char * dnl;
304                 int jlen;
305
306                 if (!dnli->active[j] || j == dil)
307                     /*@innercontinue@*/ continue;
308                 dnl = fi->dnl[j];
309                 jlen = strlen(dnl);
310                 if (jlen != (dnlen+bnlen+1))
311                     /*@innercontinue@*/ continue;
312                 if (strncmp(dnl, fi->dnl[dil], dnlen))
313                     /*@innercontinue@*/ continue;
314                 if (strncmp(dnl+dnlen, fi->bnl[i], bnlen))
315                     /*@innercontinue@*/ continue;
316                 if (dnl[dnlen+bnlen] != '/' || dnl[dnlen+bnlen+1] != '\0')
317                     /*@innercontinue@*/ continue;
318                 /* This directory is included in the package. */
319 /*@-boundswrite@*/
320                 dnli->active[j] = 0;
321 /*@=boundswrite@*/
322                 /*@innerbreak@*/ break;
323             }
324         }
325
326         /* Print only once per package. */
327         if (!reverse) {
328             j = 0;
329             for (i = 0; i < fi->dc; i++) {
330                 if (!dnli->active[i]) continue;
331                 if (j == 0) {
332                     j = 1;
333                     rpmMessage(RPMMESS_DEBUG,
334         _("========== Directories not explictly included in package:\n"));
335                 }
336                 rpmMessage(RPMMESS_DEBUG, _("%10d %s\n"), i, fi->dnl[i]);
337             }
338             if (j)
339                 rpmMessage(RPMMESS_DEBUG, "==========\n");
340         }
341     }
342     return dnli;
343 }
344 /*@=usereleased@*/
345 /*@=boundsread@*/
346
347 /** \ingroup payload
348  * Return next directory name (from file info).
349  * @param dnli          directory name iterator
350  * @return              next directory name
351  */
352 /*@-boundsread@*/
353 static /*@observer@*/ const char * dnlNextIterator(/*@null@*/ DNLI_t dnli)
354         /*@modifies dnli @*/
355 {
356     const char * dn = NULL;
357
358     if (dnli) {
359         rpmfi fi = dnli->fi;
360         int i = -1;
361
362         if (dnli->active)
363         do {
364             i = (!dnli->reverse ? dnli->i++ : --dnli->i);
365         } while (i >= 0 && i < fi->dc && !dnli->active[i]);
366
367         if (i >= 0 && i < fi->dc)
368             dn = fi->dnl[i];
369         else
370             i = -1;
371         dnli->isave = i;
372     }
373     return dn;
374 }
375 /*@=boundsread@*/
376
377 /** \ingroup payload
378  * Save hard link in chain.
379  * @param fsm           file state machine data
380  * @return              Is chain only partially filled?
381  */
382 /*@-boundsread@*/
383 static int saveHardLink(/*@special@*/ /*@partial@*/ FSM_t fsm)
384         /*@uses fsm->links, fsm->ix, fsm->sb, fsm->goal, fsm->nsuffix @*/
385         /*@defines fsm->li @*/
386         /*@releases fsm->path @*/
387         /*@globals fileSystem@*/
388         /*@modifies fsm, fileSystem @*/
389 {
390     struct stat * st = &fsm->sb;
391     int rc = 0;
392     int ix = -1;
393     int j;
394
395     /* Find hard link set. */
396     /*@-branchstate@*/
397     for (fsm->li = fsm->links; fsm->li; fsm->li = fsm->li->next) {
398         if (fsm->li->sb.st_ino == st->st_ino && fsm->li->sb.st_dev == st->st_dev)
399             break;
400     }
401     /*@=branchstate@*/
402
403     /* New hard link encountered, add new link to set. */
404 /*@-boundswrite@*/
405     /*@-branchstate@*/
406     if (fsm->li == NULL) {
407         fsm->li = xcalloc(1, sizeof(*fsm->li));
408         fsm->li->next = NULL;
409         fsm->li->sb = *st;      /* structure assignment */
410         fsm->li->nlink = st->st_nlink;
411         fsm->li->linkIndex = fsm->ix;
412         fsm->li->createdPath = -1;
413
414         fsm->li->filex = xcalloc(st->st_nlink, sizeof(fsm->li->filex[0]));
415         memset(fsm->li->filex, -1, (st->st_nlink * sizeof(fsm->li->filex[0])));
416         fsm->li->nsuffix = xcalloc(st->st_nlink, sizeof(*fsm->li->nsuffix));
417
418         if (fsm->goal == FSM_PKGBUILD)
419             fsm->li->linksLeft = st->st_nlink;
420         if (fsm->goal == FSM_PKGINSTALL)
421             fsm->li->linksLeft = 0;
422
423         /*@-kepttrans@*/
424         fsm->li->next = fsm->links;
425         /*@=kepttrans@*/
426         fsm->links = fsm->li;
427     }
428     /*@=branchstate@*/
429 /*@=boundswrite@*/
430
431     if (fsm->goal == FSM_PKGBUILD) --fsm->li->linksLeft;
432 /*@-boundswrite@*/
433     fsm->li->filex[fsm->li->linksLeft] = fsm->ix;
434     /*@-observertrans -dependenttrans@*/
435     fsm->li->nsuffix[fsm->li->linksLeft] = fsm->nsuffix;
436     /*@=observertrans =dependenttrans@*/
437 /*@=boundswrite@*/
438     if (fsm->goal == FSM_PKGINSTALL) fsm->li->linksLeft++;
439
440     if (fsm->goal == FSM_PKGBUILD)
441         return (fsm->li->linksLeft > 0);
442
443     if (fsm->goal != FSM_PKGINSTALL)
444         return 0;
445
446     if (!(st->st_size || fsm->li->linksLeft == st->st_nlink))
447         return 1;
448
449     /* Here come the bits, time to choose a non-skipped file name. */
450     {   rpmfi fi = fsmGetFi(fsm);
451
452         for (j = fsm->li->linksLeft - 1; j >= 0; j--) {
453             ix = fsm->li->filex[j];
454             if (ix < 0 || XFA_SKIPPING(fi->actions[ix]))
455                 continue;
456             break;
457         }
458     }
459
460     /* Are all links skipped or not encountered yet? */
461     if (ix < 0 || j < 0)
462         return 1;       /* XXX W2DO? */
463
464     /* Save the non-skipped file name and map index. */
465     fsm->li->linkIndex = j;
466     fsm->path = _free(fsm->path);
467     fsm->ix = ix;
468     rc = fsmStage(fsm, FSM_MAP);
469     return rc;
470 }
471 /*@=boundsread@*/
472
473 /** \ingroup payload
474  * Destroy set of hard links.
475  * @param li            set of hard links
476  * @return              NULL always
477  */
478 static /*@null@*/ void * freeHardLink(/*@only@*/ /*@null@*/ struct hardLink_s * li)
479         /*@modifies li @*/
480 {
481     if (li) {
482         li->nsuffix = _free(li->nsuffix);       /* XXX elements are shared */
483         li->filex = _free(li->filex);
484     }
485     return _free(li);
486 }
487
488 FSM_t newFSM(void)
489 {
490     FSM_t fsm = xcalloc(1, sizeof(*fsm));
491     return fsm;
492 }
493
494 FSM_t freeFSM(FSM_t fsm)
495 {
496     if (fsm) {
497         fsm->path = _free(fsm->path);
498         /*@-branchstate@*/
499         while ((fsm->li = fsm->links) != NULL) {
500             fsm->links = fsm->li->next;
501             fsm->li->next = NULL;
502             fsm->li = freeHardLink(fsm->li);
503         }
504         /*@=branchstate@*/
505         fsm->dnlx = _free(fsm->dnlx);
506         fsm->ldn = _free(fsm->ldn);
507         fsm->iter = mapFreeIterator(fsm->iter);
508     }
509     return _free(fsm);
510 }
511
512 int fsmSetup(FSM_t fsm, fileStage goal,
513                 const rpmts ts, const rpmfi fi, FD_t cfd,
514                 unsigned int * archiveSize, const char ** failedFile)
515 {
516     size_t pos = 0;
517     int rc, ec = 0;
518
519     fsm->goal = goal;
520     if (cfd) {
521         /*@-type@*/ /* FIX: cast? */
522         fsm->cfd = fdLink(cfd, "persist (fsm)");
523         /*@=type@*/
524         pos = fdGetCpioPos(fsm->cfd);
525         fdSetCpioPos(fsm->cfd, 0);
526     }
527     fsm->iter = mapInitIterator(ts, fi);
528
529     if (fsm->goal == FSM_PKGINSTALL) {
530         void * ptr;
531         ptr = rpmtsNotify(ts, fi->te,
532                         RPMCALLBACK_INST_START, 0, fi->archiveSize);
533     }
534
535 /*@-boundswrite@*/
536     /*@-assignexpose@*/
537     fsm->archiveSize = archiveSize;
538     if (fsm->archiveSize)
539         *fsm->archiveSize = 0;
540     fsm->failedFile = failedFile;
541     if (fsm->failedFile)
542         *fsm->failedFile = NULL;
543     /*@=assignexpose@*/
544 /*@=boundswrite@*/
545
546     memset(fsm->sufbuf, 0, sizeof(fsm->sufbuf));
547     if (fsm->goal == FSM_PKGINSTALL) {
548         if (ts && rpmtsGetTid(ts) > 0)
549             sprintf(fsm->sufbuf, ";%08x", (unsigned)rpmtsGetTid(ts));
550     }
551
552     ec = fsm->rc = 0;
553     rc = fsmStage(fsm, FSM_CREATE);
554     if (rc && !ec) ec = rc;
555
556     rc = fsmStage(fsm, fsm->goal);
557     if (rc && !ec) ec = rc;
558
559 /*@-boundswrite@*/
560     if (fsm->archiveSize && ec == 0)
561         *fsm->archiveSize = (fdGetCpioPos(fsm->cfd) - pos);
562 /*@=boundswrite@*/
563
564    return ec;
565 }
566
567 int fsmTeardown(FSM_t fsm)
568 {
569     int rc = fsm->rc;
570
571     if (!rc)
572         rc = fsmStage(fsm, FSM_DESTROY);
573
574     fsm->iter = mapFreeIterator(fsm->iter);
575     if (fsm->cfd) {
576         /*@-type@*/ /* FIX: cast? */
577         fsm->cfd = fdFree(fsm->cfd, "persist (fsm)");
578         /*@=type@*/
579         fsm->cfd = NULL;
580     }
581     fsm->failedFile = NULL;
582     return rc;
583 }
584
585 int fsmMapPath(FSM_t fsm)
586 {
587     rpmfi fi = fsmGetFi(fsm);   /* XXX const except for fstates */
588     int rc = 0;
589     int i;
590
591     fsm->osuffix = NULL;
592     fsm->nsuffix = NULL;
593     fsm->astriplen = 0;
594     fsm->action = FA_UNKNOWN;
595     fsm->mapFlags = 0;
596
597     i = fsm->ix;
598     if (fi && i >= 0 && i < fi->fc) {
599
600 /*@-boundsread@*/
601         fsm->astriplen = fi->astriplen;
602         fsm->action = (fi->actions ? fi->actions[i] : fi->action);
603         fsm->fflags = (fi->fflags ? fi->fflags[i] : fi->flags);
604         fsm->mapFlags = (fi->fmapflags ? fi->fmapflags[i] : fi->mapflags);
605
606         /* src rpms have simple base name in payload. */
607         fsm->dirName = fi->dnl[fi->dil[i]];
608         fsm->baseName = fi->bnl[i];
609 /*@=boundsread@*/
610
611 /*@-boundswrite@*/
612         switch (fsm->action) {
613         case FA_SKIP:
614             break;
615         case FA_SKIPMULTILIB:   /* XXX RPMFILE_STATE_MULTILIB? */
616             break;
617         case FA_UNKNOWN:
618             break;
619
620         case FA_COPYOUT:
621             break;
622         case FA_COPYIN:
623         case FA_CREATE:
624 assert(rpmteType(fi->te) == TR_ADDED);
625             break;
626
627         case FA_SKIPNSTATE:
628             if (fi->fstates && rpmteType(fi->te) == TR_ADDED)
629                 fi->fstates[i] = RPMFILE_STATE_NOTINSTALLED;
630             break;
631
632         case FA_SKIPNETSHARED:
633             if (fi->fstates && rpmteType(fi->te) == TR_ADDED)
634                 fi->fstates[i] = RPMFILE_STATE_NETSHARED;
635             break;
636
637         case FA_BACKUP:
638             if (!(fsm->fflags & RPMFILE_GHOST)) /* XXX Don't if %ghost file. */
639             switch (rpmteType(fi->te)) {
640             case TR_ADDED:
641                 fsm->osuffix = SUFFIX_RPMORIG;
642                 /*@innerbreak@*/ break;
643             case TR_REMOVED:
644                 fsm->osuffix = SUFFIX_RPMSAVE;
645                 /*@innerbreak@*/ break;
646             }
647             break;
648
649         case FA_ALTNAME:
650 assert(rpmteType(fi->te) == TR_ADDED);
651             if (!(fsm->fflags & RPMFILE_GHOST)) /* XXX Don't if %ghost file. */
652                 fsm->nsuffix = SUFFIX_RPMNEW;
653             break;
654
655         case FA_SAVE:
656 assert(rpmteType(fi->te) == TR_ADDED);
657             if (!(fsm->fflags & RPMFILE_GHOST)) /* XXX Don't if %ghost file. */
658                 fsm->osuffix = SUFFIX_RPMSAVE;
659             break;
660         case FA_ERASE:
661             assert(rpmteType(fi->te) == TR_REMOVED);
662             /*
663              * XXX TODO: %ghost probably shouldn't be removed, but that changes
664              * legacy rpm behavior.
665              */
666             break;
667         default:
668             break;
669         }
670 /*@=boundswrite@*/
671
672         if ((fsm->mapFlags & CPIO_MAP_PATH) || fsm->nsuffix) {
673             const struct stat * st = &fsm->sb;
674             fsm->path = _free(fsm->path);
675             fsm->path = fsmFsPath(fsm, st, fsm->subdir,
676                 (fsm->suffix ? fsm->suffix : fsm->nsuffix));
677         }
678     }
679     return rc;
680 }
681
682 int fsmMapAttrs(FSM_t fsm)
683 {
684     struct stat * st = &fsm->sb;
685     rpmfi fi = fsmGetFi(fsm);
686     int i = fsm->ix;
687
688     if (fi && i >= 0 && i < fi->fc) {
689         mode_t perms =
690                 (S_ISDIR(st->st_mode) ? fi->dperms : fi->fperms);
691         mode_t finalMode =
692                 (fi->fmodes ? fi->fmodes[i] : perms);
693         uid_t finalUid =
694                 (fi->fuids ? fi->fuids[i] : fi->uid); /* XXX chmod u-s */
695         gid_t finalGid =
696                 (fi->fgids ? fi->fgids[i] : fi->gid); /* XXX chmod g-s */
697         dev_t finalRdev =
698                 (fi->frdevs ? fi->frdevs[i] : 0);
699         int_32 finalMtime =
700                 (fi->fmtimes ? fi->fmtimes[i] : 0);
701
702         if (fsm->mapFlags & CPIO_MAP_MODE)
703             st->st_mode = (st->st_mode & S_IFMT) | (finalMode & ~S_IFMT);
704         if (fsm->mapFlags & CPIO_MAP_TYPE) {
705             st->st_mode = (st->st_mode & ~S_IFMT) | (finalMode & S_IFMT);
706             if ((S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
707             && st->st_nlink == 0)
708                 st->st_nlink = 1;
709             st->st_rdev = finalRdev;
710             st->st_mtime = finalMtime;
711         }
712         if (fsm->mapFlags & CPIO_MAP_UID)
713             st->st_uid = finalUid;
714         if (fsm->mapFlags & CPIO_MAP_GID)
715             st->st_gid = finalGid;
716
717         {   rpmts ts = fsmGetTs(fsm);
718
719             if (ts != NULL && !(rpmtsFlags(ts) & RPMTRANS_FLAG_NOMD5)) {
720                 fsm->fmd5sum = (fi->fmd5s ? fi->fmd5s[i] : NULL);
721                 fsm->md5sum = (fi->md5s ? (fi->md5s + (16 * i)) : NULL);
722             } else {
723                 fsm->fmd5sum = NULL;
724                 fsm->md5sum = NULL;
725             }
726         }
727
728     }
729     return 0;
730 }
731
732 /** \ingroup payload
733  * Create file from payload stream.
734  * @param fsm           file state machine data
735  * @return              0 on success
736  */
737 static int expandRegular(/*@special@*/ FSM_t fsm)
738         /*@uses fsm->sb @*/
739         /*@globals fileSystem@*/
740         /*@modifies fsm, fileSystem @*/
741 {
742     const struct stat * st = &fsm->sb;
743     int left = st->st_size;
744     int rc = 0;
745
746     rc = fsmStage(fsm, FSM_WOPEN);
747     if (rc)
748         goto exit;
749
750     if (st->st_size > 0 && (fsm->fmd5sum || fsm->md5sum))
751         fdInitDigest(fsm->wfd, PGPHASHALGO_MD5, 0);
752
753     while (left) {
754
755         fsm->wrlen = (left > fsm->wrsize ? fsm->wrsize : left);
756         rc = fsmStage(fsm, FSM_DREAD);
757         if (rc)
758             goto exit;
759
760         rc = fsmStage(fsm, FSM_WRITE);
761         if (rc)
762             goto exit;
763
764         left -= fsm->wrnb;
765
766         /* don't call this with fileSize == fileComplete */
767         if (!rc && left)
768             (void) fsmStage(fsm, FSM_NOTIFY);
769     }
770
771     if (st->st_size > 0 && (fsm->fmd5sum || fsm->md5sum)) {
772         void * md5sum = NULL;
773         int asAscii = (fsm->md5sum == NULL ? 1 : 0);
774
775         (void) Fflush(fsm->wfd);
776         fdFiniDigest(fsm->wfd, PGPHASHALGO_MD5, &md5sum, NULL, asAscii);
777
778         if (md5sum == NULL) {
779             rc = CPIOERR_MD5SUM_MISMATCH;
780             goto exit;
781         }
782
783         if (fsm->md5sum != NULL) {
784             if (memcmp(md5sum, fsm->md5sum, 16))
785                 rc = CPIOERR_MD5SUM_MISMATCH;
786         } else {
787             if (strcmp(md5sum, fsm->fmd5sum))
788                 rc = CPIOERR_MD5SUM_MISMATCH;
789         }
790         md5sum = _free(md5sum);
791     }
792
793 exit:
794     (void) fsmStage(fsm, FSM_WCLOSE);
795     return rc;
796 }
797
798 /** \ingroup payload
799  * Write next item to payload stream.
800  * @param fsm           file state machine data
801  * @param writeData     should data be written?
802  * @return              0 on success
803  */
804 static int writeFile(/*@special@*/ FSM_t fsm, int writeData)
805         /*@uses fsm->path, fsm->opath, fsm->sb, fsm->osb, fsm->cfd @*/
806         /*@globals fileSystem@*/
807         /*@modifies fsm, fileSystem @*/
808 {
809     const char * path = fsm->path;
810     const char * opath = fsm->opath;
811     struct stat * st = &fsm->sb;
812     struct stat * ost = &fsm->osb;
813     size_t pos = fdGetCpioPos(fsm->cfd);
814     char * symbuf = NULL;
815     int left;
816     int xx;
817     int rc;
818
819     st->st_size = (writeData ? ost->st_size : 0);
820
821     /*@-branchstate@*/
822     if (S_ISDIR(st->st_mode)) {
823         st->st_size = 0;
824     } else if (S_ISLNK(st->st_mode)) {
825         /*
826          * While linux puts the size of a symlink in the st_size field,
827          * I don't think that's a specified standard.
828          */
829         /* XXX NUL terminated result in fsm->rdbuf, len in fsm->rdnb. */
830         rc = fsmStage(fsm, FSM_READLINK);
831         if (rc) goto exit;
832         st->st_size = fsm->rdnb;
833         symbuf = alloca_strdup(fsm->rdbuf);     /* XXX save readlink return. */
834     }
835     /*@=branchstate@*/
836
837     if (fsm->mapFlags & CPIO_MAP_ABSOLUTE) {
838 /*@-compdef@*/ /* FIX: dirName/baseName annotations ? */
839 /*@-boundswrite@*/
840         int nb = strlen(fsm->dirName) + strlen(fsm->baseName) + sizeof(".");
841         char * t = alloca(nb);
842         *t = '\0';
843         fsm->path = t;
844         if (fsm->mapFlags & CPIO_MAP_ADDDOT)
845             *t++ = '.';
846         t = stpcpy( stpcpy(t, fsm->dirName), fsm->baseName);
847 /*@=boundswrite@*/
848 /*@=compdef@*/
849     } else if (fsm->mapFlags & CPIO_MAP_PATH) {
850         rpmfi fi = fsmGetFi(fsm);
851         fsm->path =
852             (fi->apath ? fi->apath[fsm->ix] + fi->striplen : fi->bnl[fsm->ix]);
853     }
854
855     rc = fsmStage(fsm, FSM_HWRITE);
856     fsm->path = path;
857     if (rc) goto exit;
858
859     if (writeData && S_ISREG(st->st_mode)) {
860 #if HAVE_MMAP
861         char * rdbuf = NULL;
862         void * mapped = (void *)-1;
863         size_t nmapped;
864 #endif
865
866         rc = fsmStage(fsm, FSM_ROPEN);
867         if (rc) goto exit;
868
869         /* XXX unbuffered mmap generates *lots* of fdio debugging */
870 #if HAVE_MMAP
871         nmapped = 0;
872         mapped = mmap(NULL, st->st_size, PROT_READ, MAP_SHARED, Fileno(fsm->rfd), 0);
873         if (mapped != (void *)-1) {
874             rdbuf = fsm->rdbuf;
875             fsm->rdbuf = (char *) mapped;
876             fsm->rdlen = nmapped = st->st_size;
877 #if defined(MADV_DONTNEED)
878             xx = madvise(mapped, nmapped, MADV_DONTNEED);
879 #endif
880         }
881 #endif
882
883         left = st->st_size;
884
885         while (left) {
886 #if HAVE_MMAP
887           if (mapped != (void *)-1) {
888             fsm->rdnb = nmapped;
889           } else
890 #endif
891           {
892             fsm->rdlen = (left > fsm->rdsize ? fsm->rdsize : left),
893             rc = fsmStage(fsm, FSM_READ);
894             if (rc) goto exit;
895           }
896
897             /* XXX DWRITE uses rdnb for I/O length. */
898             rc = fsmStage(fsm, FSM_DWRITE);
899             if (rc) goto exit;
900
901             left -= fsm->wrnb;
902         }
903
904 #if HAVE_MMAP
905         if (mapped != (void *)-1) {
906             xx = msync(mapped, nmapped, MS_ASYNC);
907 #if defined(MADV_DONTNEED)
908             xx = madvise(mapped, nmapped, MADV_DONTNEED);
909 #endif
910             /*@-noeffect@*/ xx = munmap(mapped, nmapped) /*@=noeffect@*/;
911             fsm->rdbuf = rdbuf;
912         }
913 #endif
914
915     } else if (writeData && S_ISLNK(st->st_mode)) {
916         /* XXX DWRITE uses rdnb for I/O length. */
917 /*@-boundswrite@*/
918         strcpy(fsm->rdbuf, symbuf);     /* XXX restore readlink buffer. */
919 /*@=boundswrite@*/
920         fsm->rdnb = strlen(symbuf);
921         rc = fsmStage(fsm, FSM_DWRITE);
922         if (rc) goto exit;
923     }
924
925     rc = fsmStage(fsm, FSM_PAD);
926     if (rc) goto exit;
927
928     {   const rpmts ts = fsmGetTs(fsm);
929         rpmfi fi = fsmGetFi(fsm);
930         size_t size = (fdGetCpioPos(fsm->cfd) - pos);
931         void * ptr;
932         ptr = rpmtsNotify(ts, fi->te, RPMCALLBACK_INST_PROGRESS, size, size);
933     }
934
935     rc = 0;
936
937 exit:
938     if (fsm->rfd)
939         (void) fsmStage(fsm, FSM_RCLOSE);
940     /*@-dependenttrans@*/
941     fsm->opath = opath;
942     fsm->path = path;
943     /*@=dependenttrans@*/
944     return rc;
945 }
946
947 /** \ingroup payload
948  * Write set of linked files to payload stream.
949  * @param fsm           file state machine data
950  * @return              0 on success
951  */
952 static int writeLinkedFile(/*@special@*/ FSM_t fsm)
953         /*@uses fsm->path, fsm->nsuffix, fsm->ix, fsm->li, fsm->failedFile @*/
954         /*@globals fileSystem@*/
955         /*@modifies fsm, fileSystem @*/
956 {
957     const char * path = fsm->path;
958     const char * nsuffix = fsm->nsuffix;
959     int iterIndex = fsm->ix;
960     int ec = 0;
961     int rc;
962     int i;
963
964     fsm->path = NULL;
965     fsm->nsuffix = NULL;
966     fsm->ix = -1;
967
968 /*@-boundswrite@*/
969     for (i = fsm->li->nlink - 1; i >= 0; i--) {
970
971         if (fsm->li->filex[i] < 0) continue;
972
973         fsm->ix = fsm->li->filex[i];
974         rc = fsmStage(fsm, FSM_MAP);
975
976         /* Write data after last link. */
977         rc = writeFile(fsm, (i == 0));
978         if (fsm->failedFile && rc != 0 && *fsm->failedFile == NULL) {
979             ec = rc;
980             *fsm->failedFile = xstrdup(fsm->path);
981         }
982
983         fsm->path = _free(fsm->path);
984         fsm->li->filex[i] = -1;
985     }
986 /*@=boundswrite@*/
987
988     fsm->ix = iterIndex;
989     fsm->nsuffix = nsuffix;
990     fsm->path = path;
991     return ec;
992 }
993
994 /** \ingroup payload
995  * Create pending hard links to existing file.
996  * @param fsm           file state machine data
997  * @return              0 on success
998  */
999 /*@-boundsread@*/
1000 static int fsmMakeLinks(/*@special@*/ FSM_t fsm)
1001         /*@uses fsm->path, fsm->opath, fsm->nsuffix, fsm->ix, fsm->li @*/
1002         /*@globals fileSystem@*/
1003         /*@modifies fsm, fileSystem @*/
1004 {
1005     const char * path = fsm->path;
1006     const char * opath = fsm->opath;
1007     const char * nsuffix = fsm->nsuffix;
1008     int iterIndex = fsm->ix;
1009     int ec = 0;
1010     int rc;
1011     int i;
1012
1013     fsm->path = NULL;
1014     fsm->opath = NULL;
1015     fsm->nsuffix = NULL;
1016     fsm->ix = -1;
1017
1018     fsm->ix = fsm->li->filex[fsm->li->createdPath];
1019     rc = fsmStage(fsm, FSM_MAP);
1020     fsm->opath = fsm->path;
1021     fsm->path = NULL;
1022     /*@-branchstate@*/
1023     for (i = 0; i < fsm->li->nlink; i++) {
1024         if (fsm->li->filex[i] < 0) continue;
1025         if (fsm->li->createdPath == i) continue;
1026
1027         fsm->ix = fsm->li->filex[i];
1028         fsm->path = _free(fsm->path);
1029         rc = fsmStage(fsm, FSM_MAP);
1030         if (XFA_SKIPPING(fsm->action)) continue;
1031
1032         rc = fsmStage(fsm, FSM_VERIFY);
1033         if (!rc) continue;
1034         if (rc != CPIOERR_LSTAT_FAILED) break;
1035
1036         /* XXX link(fsm->opath, fsm->path) */
1037         rc = fsmStage(fsm, FSM_LINK);
1038         if (fsm->failedFile && rc != 0 && *fsm->failedFile == NULL) {
1039             ec = rc;
1040 /*@-boundswrite@*/
1041             *fsm->failedFile = xstrdup(fsm->path);
1042 /*@=boundswrite@*/
1043         }
1044
1045         fsm->li->linksLeft--;
1046     }
1047     /*@=branchstate@*/
1048     fsm->path = _free(fsm->path);
1049     fsm->opath = _free(fsm->opath);
1050
1051     fsm->ix = iterIndex;
1052     fsm->nsuffix = nsuffix;
1053     fsm->path = path;
1054     fsm->opath = opath;
1055     return ec;
1056 }
1057 /*@=boundsread@*/
1058
1059 /** \ingroup payload
1060  * Commit hard linked file set atomically.
1061  * @param fsm           file state machine data
1062  * @return              0 on success
1063  */
1064 static int fsmCommitLinks(/*@special@*/ FSM_t fsm)
1065         /*@uses fsm->path, fsm->nsuffix, fsm->ix, fsm->sb,
1066                 fsm->li, fsm->links @*/
1067         /*@globals fileSystem@*/
1068         /*@modifies fsm, fileSystem @*/
1069 {
1070     const char * path = fsm->path;
1071     const char * nsuffix = fsm->nsuffix;
1072     int iterIndex = fsm->ix;
1073     struct stat * st = &fsm->sb;
1074     int rc = 0;
1075     int i;
1076
1077     fsm->path = NULL;
1078     fsm->nsuffix = NULL;
1079     fsm->ix = -1;
1080
1081     /*@-branchstate@*/
1082     for (fsm->li = fsm->links; fsm->li; fsm->li = fsm->li->next) {
1083         if (fsm->li->sb.st_ino == st->st_ino && fsm->li->sb.st_dev == st->st_dev)
1084             break;
1085     }
1086     /*@=branchstate@*/
1087
1088 /*@-boundswrite@*/
1089     for (i = 0; i < fsm->li->nlink; i++) {
1090         if (fsm->li->filex[i] < 0) continue;
1091         fsm->ix = fsm->li->filex[i];
1092         rc = fsmStage(fsm, FSM_MAP);
1093         if (!XFA_SKIPPING(fsm->action))
1094             rc = fsmStage(fsm, FSM_COMMIT);
1095         fsm->path = _free(fsm->path);
1096         fsm->li->filex[i] = -1;
1097     }
1098 /*@=boundswrite@*/
1099
1100     fsm->ix = iterIndex;
1101     fsm->nsuffix = nsuffix;
1102     fsm->path = path;
1103     return rc;
1104 }
1105
1106 /**
1107  * Remove (if created) directories not explicitly included in package.
1108  * @param fsm           file state machine data
1109  * @return              0 on success
1110  */
1111 static int fsmRmdirs(/*@special@*/ FSM_t fsm)
1112         /*@uses fsm->path, fsm->dnlx, fsm->ldn, fsm->rdbuf, fsm->iter @*/
1113         /*@globals fileSystem@*/
1114         /*@modifies fsm, fileSystem @*/
1115 {
1116     const char * path = fsm->path;
1117     void * dnli = dnlInitIterator(fsm, 1);
1118     char * dn = fsm->rdbuf;
1119     int dc = dnlCount(dnli);
1120     int rc = 0;
1121
1122     fsm->path = NULL;
1123 /*@-boundswrite@*/
1124     dn[0] = '\0';
1125     /*@-observertrans -dependenttrans@*/
1126     if (fsm->ldn != NULL && fsm->dnlx != NULL)
1127     while ((fsm->path = dnlNextIterator(dnli)) != NULL) {
1128         int dnlen = strlen(fsm->path);
1129         char * te;
1130
1131         dc = dnlIndex(dnli);
1132         if (fsm->dnlx[dc] < 1 || fsm->dnlx[dc] >= dnlen)
1133             continue;
1134
1135         /* Copy to avoid const on fsm->path. */
1136         te = stpcpy(dn, fsm->path) - 1;
1137         fsm->path = dn;
1138
1139         /* Remove generated directories. */
1140         /*@-usereleased@*/ /* LCL: te used after release? */
1141         do {
1142             if (*te == '/') {
1143                 *te = '\0';
1144                 rc = fsmStage(fsm, FSM_RMDIR);
1145                 *te = '/';
1146             }
1147             if (rc)
1148                 /*@innerbreak@*/ break;
1149             te--;
1150         } while ((te - fsm->path) > fsm->dnlx[dc]);
1151         /*@=usereleased@*/
1152     }
1153 /*@=boundswrite@*/
1154     dnli = dnlFreeIterator(dnli);
1155     /*@=observertrans =dependenttrans@*/
1156
1157     fsm->path = path;
1158     return rc;
1159 }
1160
1161 /**
1162  * Create (if necessary) directories not explicitly included in package.
1163  * @param fsm           file state machine data
1164  * @return              0 on success
1165  */
1166 static int fsmMkdirs(/*@special@*/ FSM_t fsm)
1167         /*@uses fsm->path, fsm->sb, fsm->osb, fsm->rdbuf, fsm->iter,
1168                 fsm->ldn, fsm->ldnlen, fsm->ldnalloc @*/
1169         /*@defines fsm->dnlx, fsm->ldn @*/
1170         /*@globals fileSystem@*/
1171         /*@modifies fsm, fileSystem @*/
1172 {
1173     struct stat * st = &fsm->sb;
1174     struct stat * ost = &fsm->osb;
1175     const char * path = fsm->path;
1176     mode_t st_mode = st->st_mode;
1177     void * dnli = dnlInitIterator(fsm, 0);
1178     char * dn = fsm->rdbuf;
1179     int dc = dnlCount(dnli);
1180     int rc = 0;
1181     int i;
1182
1183     fsm->path = NULL;
1184
1185 /*@-boundswrite@*/
1186     dn[0] = '\0';
1187     fsm->dnlx = (dc ? xcalloc(dc, sizeof(*fsm->dnlx)) : NULL);
1188     /*@-observertrans -dependenttrans@*/
1189     if (fsm->dnlx != NULL)
1190     while ((fsm->path = dnlNextIterator(dnli)) != NULL) {
1191         int dnlen = strlen(fsm->path);
1192         char * te;
1193
1194         dc = dnlIndex(dnli);
1195         if (dc < 0) continue;
1196         fsm->dnlx[dc] = dnlen;
1197         if (dnlen <= 1)
1198             continue;
1199
1200         /*@-compdef -nullpass@*/        /* FIX: fsm->ldn not defined ??? */
1201         if (dnlen <= fsm->ldnlen && !strcmp(fsm->path, fsm->ldn))
1202             continue;
1203         /*@=compdef =nullpass@*/
1204
1205         /* Copy to avoid const on fsm->path. */
1206         (void) stpcpy(dn, fsm->path);
1207         fsm->path = dn;
1208
1209         /* Assume '/' directory exists, "mkdir -p" for others if non-existent */
1210         for (i = 1, te = dn + 1; *te != '\0'; te++, i++) {
1211             if (*te != '/')
1212                 /*@innercontinue@*/ continue;
1213
1214             *te = '\0';
1215
1216             /* Already validated? */
1217             /*@-usedef -compdef -nullpass -nullderef@*/
1218             if (i < fsm->ldnlen &&
1219                 (fsm->ldn[i] == '/' || fsm->ldn[i] == '\0') &&
1220                 !strncmp(fsm->path, fsm->ldn, i))
1221             {
1222                 *te = '/';
1223                 /* Move pre-existing path marker forward. */
1224                 fsm->dnlx[dc] = (te - dn);
1225                 /*@innercontinue@*/ continue;
1226             }
1227             /*@=usedef =compdef =nullpass =nullderef@*/
1228
1229             /* Validate next component of path. */
1230             rc = fsmStage(fsm, FSM_LSTAT);
1231             *te = '/';
1232
1233             /* Directory already exists? */
1234             if (rc == 0 && S_ISDIR(ost->st_mode)) {
1235                 /* Move pre-existing path marker forward. */
1236                 fsm->dnlx[dc] = (te - dn);
1237             } else if (rc == CPIOERR_LSTAT_FAILED) {
1238                 rpmfi fi = fsmGetFi(fsm);
1239                 *te = '\0';
1240                 st->st_mode = S_IFDIR | (fi->dperms & 07777);
1241                 rc = fsmStage(fsm, FSM_MKDIR);
1242                 if (!rc)
1243                     rpmMessage(RPMMESS_DEBUG,
1244                         _("%s directory created with perms %04o.\n"),
1245                         fsm->path, (unsigned)(st->st_mode & 07777));
1246                 *te = '/';
1247             }
1248             if (rc)
1249                 /*@innerbreak@*/ break;
1250         }
1251         if (rc) break;
1252
1253         /* Save last validated path. */
1254 /*@-compdef@*/ /* FIX: ldn/path annotations ? */
1255         if (fsm->ldnalloc < (dnlen + 1)) {
1256             fsm->ldnalloc = dnlen + 100;
1257             fsm->ldn = xrealloc(fsm->ldn, fsm->ldnalloc);
1258         }
1259         if (fsm->ldn != NULL) { /* XXX can't happen */
1260             strcpy(fsm->ldn, fsm->path);
1261             fsm->ldnlen = dnlen;
1262         }
1263 /*@=compdef@*/
1264     }
1265 /*@=boundswrite@*/
1266     dnli = dnlFreeIterator(dnli);
1267     /*@=observertrans =dependenttrans@*/
1268
1269     fsm->path = path;
1270     st->st_mode = st_mode;              /* XXX restore st->st_mode */
1271 /*@-compdef@*/ /* FIX: ldn/path annotations ? */
1272     return rc;
1273 /*@=compdef@*/
1274 }
1275
1276 #ifdef  NOTYET
1277 /**
1278  * Check for file on disk.
1279  * @param fsm           file state machine data
1280  * @return              0 on success
1281  */
1282 static int fsmStat(FSM_t fsm)
1283 {
1284     int saveerrno = errno;
1285     int rc = 0;
1286
1287     if (fsm->path != NULL) {
1288         int saveernno = errno;
1289         rc = fsmStage(fsm, (!(fsm->mapFlags & CPIO_FOLLOW_SYMLINKS)
1290                         ? FSM_LSTAT : FSM_STAT));
1291         if (rc == CPIOERR_LSTAT_FAILED && errno == ENOENT) {
1292             errno = saveerrno;
1293             rc = 0;
1294             fsm->exists = 0;
1295         } else if (rc == 0) {
1296             fsm->exists = 1;
1297         }
1298     } else {
1299         /* Skip %ghost files on build. */
1300         fsm->exists = 0;
1301     }
1302     return rc;
1303 }
1304 #endif
1305
1306 #define IS_DEV_LOG(_x)  \
1307         ((_x) != NULL && strlen(_x) >= (sizeof("/dev/log")-1) && \
1308         !strncmp((_x), "/dev/log", sizeof("/dev/log")-1) && \
1309         ((_x)[sizeof("/dev/log")-1] == '\0' || \
1310          (_x)[sizeof("/dev/log")-1] == ';'))
1311
1312 /*@-boundsread@*/
1313 /*@-compmempass@*/
1314 int fsmStage(FSM_t fsm, fileStage stage)
1315 {
1316 #ifdef  UNUSED
1317     fileStage prevStage = fsm->stage;
1318     const char * const prev = fileStageString(prevStage);
1319 #endif
1320     static int modulo = 4;
1321     const char * const cur = fileStageString(stage);
1322     struct stat * st = &fsm->sb;
1323     struct stat * ost = &fsm->osb;
1324     int saveerrno = errno;
1325     int rc = fsm->rc;
1326     size_t left;
1327     int i;
1328
1329 #define _fafilter(_a)   \
1330     (!((_a) == FA_CREATE || (_a) == FA_ERASE || (_a) == FA_COPYIN || (_a) == FA_COPYOUT) \
1331         ? fileActionString(_a) : "")
1332
1333     if (stage & FSM_DEAD) {
1334         /* do nothing */
1335     } else if (stage & FSM_INTERNAL) {
1336         if (_fsm_debug && !(stage & FSM_SYSCALL))
1337             rpmMessage(RPMMESS_DEBUG, " %8s %06o%3d (%4d,%4d)%10d %s %s\n",
1338                 cur,
1339                 (unsigned)st->st_mode, (int)st->st_nlink,
1340                 (int)st->st_uid, (int)st->st_gid, (int)st->st_size,
1341                 (fsm->path ? fsm->path : ""),
1342                 _fafilter(fsm->action));
1343     } else {
1344         fsm->stage = stage;
1345         if (_fsm_debug || !(stage & FSM_VERBOSE))
1346             rpmMessage(RPMMESS_DEBUG, "%-8s  %06o%3d (%4d,%4d)%10d %s %s\n",
1347                 cur,
1348                 (unsigned)st->st_mode, (int)st->st_nlink,
1349                 (int)st->st_uid, (int)st->st_gid, (int)st->st_size,
1350                 (fsm->path ? fsm->path + fsm->astriplen : ""),
1351                 _fafilter(fsm->action));
1352     }
1353 #undef  _fafilter
1354
1355     /*@-branchstate@*/
1356     switch (stage) {
1357     case FSM_UNKNOWN:
1358         break;
1359     case FSM_PKGINSTALL:
1360         while (1) {
1361             /* Clean fsm, free'ing memory. Read next archive header. */
1362             rc = fsmStage(fsm, FSM_INIT);
1363
1364             /* Exit on end-of-payload. */
1365             if (rc == CPIOERR_HDR_TRAILER) {
1366                 rc = 0;
1367                 /*@loopbreak@*/ break;
1368             }
1369
1370             /* Exit on error. */
1371             if (rc) {
1372                 fsm->postpone = 1;
1373                 (void) fsmStage(fsm, FSM_UNDO);
1374                 /*@loopbreak@*/ break;
1375             }
1376
1377             /* Extract file from archive. */
1378             rc = fsmStage(fsm, FSM_PROCESS);
1379             if (rc) {
1380                 (void) fsmStage(fsm, FSM_UNDO);
1381                 /*@loopbreak@*/ break;
1382             }
1383
1384             /* Notify on success. */
1385             (void) fsmStage(fsm, FSM_NOTIFY);
1386
1387             rc = fsmStage(fsm, FSM_FINI);
1388             if (rc) {
1389                 /*@loopbreak@*/ break;
1390             }
1391         }
1392         break;
1393     case FSM_PKGERASE:
1394     case FSM_PKGCOMMIT:
1395         while (1) {
1396             /* Clean fsm, free'ing memory. */
1397             rc = fsmStage(fsm, FSM_INIT);
1398
1399             /* Exit on end-of-payload. */
1400             if (rc == CPIOERR_HDR_TRAILER) {
1401                 rc = 0;
1402                 /*@loopbreak@*/ break;
1403             }
1404
1405             /* Rename/erase next item. */
1406             if (fsmStage(fsm, FSM_FINI))
1407                 /*@loopbreak@*/ break;
1408         }
1409         break;
1410     case FSM_PKGBUILD:
1411         while (1) {
1412
1413             rc = fsmStage(fsm, FSM_INIT);
1414
1415             /* Exit on end-of-payload. */
1416             if (rc == CPIOERR_HDR_TRAILER) {
1417                 rc = 0;
1418                 /*@loopbreak@*/ break;
1419             }
1420
1421             /* Exit on error. */
1422             if (rc) {
1423                 fsm->postpone = 1;
1424                 (void) fsmStage(fsm, FSM_UNDO);
1425                 /*@loopbreak@*/ break;
1426             }
1427
1428             /* Copy file into archive. */
1429             rc = fsmStage(fsm, FSM_PROCESS);
1430             if (rc) {
1431                 (void) fsmStage(fsm, FSM_UNDO);
1432                 /*@loopbreak@*/ break;
1433             }
1434
1435             if (fsmStage(fsm, FSM_FINI))
1436                 /*@loopbreak@*/ break;
1437         }
1438
1439         /* Flush partial sets of hard linked files. */
1440         if (!(fsm->mapFlags & CPIO_ALL_HARDLINKS)) {
1441             int nlink, j;
1442             while ((fsm->li = fsm->links) != NULL) {
1443                 fsm->links = fsm->li->next;
1444                 fsm->li->next = NULL;
1445
1446                 /* Re-calculate link count for archive header. */
1447                 for (j = -1, nlink = 0, i = 0; i < fsm->li->nlink; i++) {
1448                     if (fsm->li->filex[i] < 0)
1449                         /*@innercontinue@*/ continue;
1450                     nlink++;
1451                     if (j == -1) j = i;
1452                 }
1453                 /* XXX force the contents out as well. */
1454 /*@-boundswrite@*/
1455                 if (j != 0) {
1456                     fsm->li->filex[0] = fsm->li->filex[j];
1457                     fsm->li->filex[j] = -1;
1458                 }
1459 /*@=boundswrite@*/
1460                 fsm->li->sb.st_nlink = nlink;
1461
1462                 fsm->sb = fsm->li->sb;  /* structure assignment */
1463                 fsm->osb = fsm->sb;     /* structure assignment */
1464
1465                 if (!rc) rc = writeLinkedFile(fsm);
1466
1467                 fsm->li = freeHardLink(fsm->li);
1468             }
1469         }
1470
1471         if (!rc)
1472             rc = fsmStage(fsm, FSM_TRAILER);
1473
1474         break;
1475     case FSM_CREATE:
1476         {   rpmts ts = fsmGetTs(fsm);
1477 #define _tsmask (RPMTRANS_FLAG_PKGCOMMIT | RPMTRANS_FLAG_COMMIT)
1478             fsm->commit = ((ts && (rpmtsFlags(ts) & _tsmask) &&
1479                         fsm->goal != FSM_PKGCOMMIT) ? 0 : 1);
1480 #undef _tsmask
1481         }
1482         fsm->path = _free(fsm->path);
1483         fsm->opath = _free(fsm->opath);
1484         fsm->dnlx = _free(fsm->dnlx);
1485
1486         fsm->ldn = _free(fsm->ldn);
1487         fsm->ldnalloc = fsm->ldnlen = 0;
1488
1489         fsm->rdsize = fsm->wrsize = 0;
1490         fsm->rdbuf = fsm->rdb = _free(fsm->rdb);
1491         fsm->wrbuf = fsm->wrb = _free(fsm->wrb);
1492         if (fsm->goal == FSM_PKGINSTALL || fsm->goal == FSM_PKGBUILD) {
1493             fsm->rdsize = 8 * BUFSIZ;
1494             fsm->rdbuf = fsm->rdb = xmalloc(fsm->rdsize);
1495             fsm->wrsize = 8 * BUFSIZ;
1496             fsm->wrbuf = fsm->wrb = xmalloc(fsm->wrsize);
1497         }
1498
1499         fsm->mkdirsdone = 0;
1500         fsm->ix = -1;
1501         fsm->links = NULL;
1502         fsm->li = NULL;
1503         /*@-mods@*/
1504         errno = 0;      /* XXX get rid of EBADF */
1505         /*@=mods@*/
1506
1507         /* Detect and create directories not explicitly in package. */
1508         if (fsm->goal == FSM_PKGINSTALL) {
1509             rc = fsmStage(fsm, FSM_MKDIRS);
1510             if (!rc) fsm->mkdirsdone = 1;
1511         }
1512
1513         break;
1514     case FSM_INIT:
1515         fsm->path = _free(fsm->path);
1516         fsm->postpone = 0;
1517         fsm->diskchecked = fsm->exists = 0;
1518         fsm->subdir = NULL;
1519         fsm->suffix = (fsm->sufbuf[0] != '\0' ? fsm->sufbuf : NULL);
1520         fsm->action = FA_UNKNOWN;
1521         fsm->osuffix = NULL;
1522         fsm->nsuffix = NULL;
1523
1524         if (fsm->goal == FSM_PKGINSTALL) {
1525             /* Read next header from payload, checking for end-of-payload. */
1526             rc = fsmStage(fsm, FSM_NEXT);
1527         }
1528         if (rc) break;
1529
1530         /* Identify mapping index. */
1531         fsm->ix = ((fsm->goal == FSM_PKGINSTALL)
1532                 ? mapFind(fsm->iter, fsm->path) : mapNextIterator(fsm->iter));
1533
1534         /* Detect end-of-loop and/or mapping error. */
1535         if (fsm->ix < 0) {
1536             if (fsm->goal == FSM_PKGINSTALL) {
1537 #if 0
1538                 rpmMessage(RPMMESS_WARNING,
1539                     _("archive file %s was not found in header file list\n"),
1540                         fsm->path);
1541 #endif
1542 /*@-boundswrite@*/
1543                 if (fsm->failedFile && *fsm->failedFile == NULL)
1544                     *fsm->failedFile = xstrdup(fsm->path);
1545 /*@=boundswrite@*/
1546                 rc = CPIOERR_UNMAPPED_FILE;
1547             } else {
1548                 rc = CPIOERR_HDR_TRAILER;
1549             }
1550             break;
1551         }
1552
1553         /* On non-install, mode must be known so that dirs don't get suffix. */
1554         if (fsm->goal != FSM_PKGINSTALL) {
1555             rpmfi fi = fsmGetFi(fsm);
1556             st->st_mode = fi->fmodes[fsm->ix];
1557         }
1558
1559         /* Generate file path. */
1560         rc = fsmStage(fsm, FSM_MAP);
1561         if (rc) break;
1562
1563         /* Perform lstat/stat for disk file. */
1564 #ifdef  NOTYET
1565         rc = fsmStat(fsm);
1566 #else
1567         if (fsm->path != NULL &&
1568             !(fsm->goal == FSM_PKGINSTALL && S_ISREG(st->st_mode)))
1569         {
1570             rc = fsmStage(fsm, (!(fsm->mapFlags & CPIO_FOLLOW_SYMLINKS)
1571                         ? FSM_LSTAT : FSM_STAT));
1572             if (rc == CPIOERR_LSTAT_FAILED && errno == ENOENT) {
1573                 /*@-mods@*/
1574                 errno = saveerrno;
1575                 /*@=mods@*/
1576                 rc = 0;
1577                 fsm->exists = 0;
1578             } else if (rc == 0) {
1579                 fsm->exists = 1;
1580             }
1581         } else {
1582             /* Skip %ghost files on build. */
1583             fsm->exists = 0;
1584         }
1585 #endif
1586         fsm->diskchecked = 1;
1587         if (rc) break;
1588
1589         /* On non-install, the disk file stat is what's remapped. */
1590 /*@-boundswrite@*/
1591         if (fsm->goal != FSM_PKGINSTALL)
1592             *st = *ost;                 /* structure assignment */
1593 /*@=boundswrite@*/
1594
1595         /* Remap file perms, owner, and group. */
1596         rc = fsmMapAttrs(fsm);
1597         if (rc) break;
1598
1599         fsm->postpone = XFA_SKIPPING(fsm->action);
1600         if (fsm->goal == FSM_PKGINSTALL || fsm->goal == FSM_PKGBUILD) {
1601             /*@-evalorder@*/ /* FIX: saveHardLink can modify fsm */
1602             if (!S_ISDIR(st->st_mode) && st->st_nlink > 1)
1603                 fsm->postpone = saveHardLink(fsm);
1604             /*@=evalorder@*/
1605         }
1606         break;
1607     case FSM_PRE:
1608         break;
1609     case FSM_MAP:
1610         rc = fsmMapPath(fsm);
1611         break;
1612     case FSM_MKDIRS:
1613         rc = fsmMkdirs(fsm);
1614         break;
1615     case FSM_RMDIRS:
1616         if (fsm->dnlx)
1617             rc = fsmRmdirs(fsm);
1618         break;
1619     case FSM_PROCESS:
1620         if (fsm->postpone) {
1621             if (fsm->goal == FSM_PKGINSTALL)
1622                 rc = fsmStage(fsm, FSM_EAT);
1623             break;
1624         }
1625
1626         if (fsm->goal == FSM_PKGBUILD) {
1627             if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
1628                 struct hardLink_s * li, * prev;
1629
1630 if (!(fsm->mapFlags & CPIO_ALL_HARDLINKS)) break;
1631                 rc = writeLinkedFile(fsm);
1632                 if (rc) break;  /* W2DO? */
1633
1634                 for (li = fsm->links, prev = NULL; li; prev = li, li = li->next)
1635                      if (li == fsm->li)
1636                         /*@loopbreak@*/ break;
1637
1638                 if (prev == NULL)
1639                     fsm->links = fsm->li->next;
1640                 else
1641                     prev->next = fsm->li->next;
1642                 fsm->li->next = NULL;
1643                 fsm->li = freeHardLink(fsm->li);
1644             } else {
1645                 rc = writeFile(fsm, 1);
1646             }
1647             break;
1648         }
1649
1650         if (fsm->goal != FSM_PKGINSTALL)
1651             break;
1652
1653         if (S_ISREG(st->st_mode)) {
1654             const char * path = fsm->path;
1655             if (fsm->osuffix)
1656                 fsm->path = fsmFsPath(fsm, st, NULL, NULL);
1657             rc = fsmStage(fsm, FSM_VERIFY);
1658
1659             if (rc == 0 && fsm->osuffix) {
1660                 const char * opath = fsm->opath;
1661                 fsm->opath = fsm->path;
1662                 fsm->path = fsmFsPath(fsm, st, NULL, fsm->osuffix);
1663                 rc = fsmStage(fsm, FSM_RENAME);
1664                 if (!rc)
1665                     rpmMessage(RPMMESS_WARNING,
1666                         _("%s saved as %s\n"), fsm->opath, fsm->path);
1667                 fsm->path = _free(fsm->path);
1668                 fsm->opath = opath;
1669             }
1670
1671             /*@-dependenttrans@*/
1672             fsm->path = path;
1673             /*@=dependenttrans@*/
1674             if (rc != CPIOERR_LSTAT_FAILED) return rc;
1675             rc = expandRegular(fsm);
1676         } else if (S_ISDIR(st->st_mode)) {
1677             mode_t st_mode = st->st_mode;
1678             rc = fsmStage(fsm, FSM_VERIFY);
1679             if (rc == CPIOERR_LSTAT_FAILED) {
1680                 st->st_mode &= ~07777;          /* XXX abuse st->st_mode */
1681                 st->st_mode |=  00700;
1682                 rc = fsmStage(fsm, FSM_MKDIR);
1683                 st->st_mode = st_mode;          /* XXX restore st->st_mode */
1684             }
1685         } else if (S_ISLNK(st->st_mode)) {
1686             const char * opath = fsm->opath;
1687
1688             if ((st->st_size + 1) > fsm->rdsize) {
1689                 rc = CPIOERR_HDR_SIZE;
1690                 break;
1691             }
1692
1693             fsm->wrlen = st->st_size;
1694             rc = fsmStage(fsm, FSM_DREAD);
1695             if (!rc && fsm->rdnb != fsm->wrlen)
1696                 rc = CPIOERR_READ_FAILED;
1697             if (rc) break;
1698
1699 /*@-boundswrite@*/
1700             fsm->wrbuf[st->st_size] = '\0';
1701 /*@=boundswrite@*/
1702             /* XXX symlink(fsm->opath, fsm->path) */
1703             /*@-dependenttrans@*/
1704             fsm->opath = fsm->wrbuf;
1705             /*@=dependenttrans@*/
1706             rc = fsmStage(fsm, FSM_VERIFY);
1707             if (rc == CPIOERR_LSTAT_FAILED)
1708                 rc = fsmStage(fsm, FSM_SYMLINK);
1709             fsm->opath = opath;         /* XXX restore fsm->path */
1710         } else if (S_ISFIFO(st->st_mode)) {
1711             mode_t st_mode = st->st_mode;
1712             /* This mimics cpio S_ISSOCK() behavior but probably isnt' right */
1713             rc = fsmStage(fsm, FSM_VERIFY);
1714             if (rc == CPIOERR_LSTAT_FAILED) {
1715                 st->st_mode = 0000;             /* XXX abuse st->st_mode */
1716                 rc = fsmStage(fsm, FSM_MKFIFO);
1717                 st->st_mode = st_mode;  /* XXX restore st->st_mode */
1718             }
1719         } else if (S_ISCHR(st->st_mode) ||
1720                    S_ISBLK(st->st_mode) ||
1721     /*@-unrecog@*/ S_ISSOCK(st->st_mode) /*@=unrecog@*/)
1722         {
1723             rc = fsmStage(fsm, FSM_VERIFY);
1724             if (rc == CPIOERR_LSTAT_FAILED)
1725                 rc = fsmStage(fsm, FSM_MKNOD);
1726         } else {
1727             /* XXX Special case /dev/log, which shouldn't be packaged anyways */
1728             if (!IS_DEV_LOG(fsm->path))
1729                 rc = CPIOERR_UNKNOWN_FILETYPE;
1730         }
1731         if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) {
1732             fsm->li->createdPath = fsm->li->linkIndex;
1733             rc = fsmMakeLinks(fsm);
1734         }
1735         break;
1736     case FSM_POST:
1737         break;
1738     case FSM_MKLINKS:
1739         rc = fsmMakeLinks(fsm);
1740         break;
1741     case FSM_NOTIFY:            /* XXX move from fsm to psm -> tsm */
1742         if (fsm->goal == FSM_PKGINSTALL || fsm->goal == FSM_PKGBUILD) {
1743             rpmts ts = fsmGetTs(fsm);
1744             rpmfi fi = fsmGetFi(fsm);
1745             void * ptr;
1746             ptr = rpmtsNotify(ts, fi->te, RPMCALLBACK_INST_PROGRESS,
1747                         fdGetCpioPos(fsm->cfd), fi->archiveSize);
1748         }
1749         break;
1750     case FSM_UNDO:
1751         if (fsm->postpone)
1752             break;
1753         if (fsm->goal == FSM_PKGINSTALL) {
1754             (void) fsmStage(fsm,
1755                 (S_ISDIR(st->st_mode) ? FSM_RMDIR : FSM_UNLINK));
1756
1757 #ifdef  NOTYET  /* XXX remove only dirs just created, not all. */
1758             if (fsm->dnlx)
1759                 (void) fsmStage(fsm, FSM_RMDIRS);
1760 #endif
1761             /*@-mods@*/
1762             errno = saveerrno;
1763             /*@=mods@*/
1764         }
1765 /*@-boundswrite@*/
1766         if (fsm->failedFile && *fsm->failedFile == NULL)
1767             *fsm->failedFile = xstrdup(fsm->path);
1768 /*@=boundswrite@*/
1769         break;
1770     case FSM_FINI:
1771         if (!fsm->postpone && fsm->commit) {
1772             if (fsm->goal == FSM_PKGINSTALL)
1773                 rc = ((!S_ISDIR(st->st_mode) && st->st_nlink > 1)
1774                         ? fsmCommitLinks(fsm) : fsmStage(fsm, FSM_COMMIT));
1775             if (fsm->goal == FSM_PKGCOMMIT)
1776                 rc = fsmStage(fsm, FSM_COMMIT);
1777             if (fsm->goal == FSM_PKGERASE)
1778                 rc = fsmStage(fsm, FSM_COMMIT);
1779         }
1780         fsm->path = _free(fsm->path);
1781         fsm->opath = _free(fsm->opath);
1782 /*@-boundswrite@*/
1783         memset(st, 0, sizeof(*st));
1784         memset(ost, 0, sizeof(*ost));
1785 /*@=boundswrite@*/
1786         break;
1787     case FSM_COMMIT:
1788         /* Rename pre-existing modified or unmanaged file. */
1789         if (fsm->osuffix && fsm->diskchecked &&
1790           (fsm->exists || (fsm->goal == FSM_PKGINSTALL && S_ISREG(st->st_mode))))
1791         {
1792             const char * opath = fsm->opath;
1793             const char * path = fsm->path;
1794             fsm->opath = fsmFsPath(fsm, st, NULL, NULL);
1795             fsm->path = fsmFsPath(fsm, st, NULL, fsm->osuffix);
1796             rc = fsmStage(fsm, FSM_RENAME);
1797             if (!rc) {
1798                 rpmMessage(RPMMESS_WARNING, _("%s saved as %s\n"),
1799                                 fsm->opath, fsm->path);
1800             }
1801             fsm->path = _free(fsm->path);
1802             fsm->path = path;
1803             fsm->opath = _free(fsm->opath);
1804             fsm->opath = opath;
1805         }
1806
1807         /* Remove erased files. */
1808         if (fsm->goal == FSM_PKGERASE) {
1809             if (fsm->action == FA_ERASE) {
1810                 rpmfi fi = fsmGetFi(fsm);
1811                 if (S_ISDIR(st->st_mode)) {
1812                     rc = fsmStage(fsm, FSM_RMDIR);
1813                     if (!rc) break;
1814                     switch (errno) {
1815                     case ENOENT: /* XXX rmdir("/") linux 2.2.x kernel hack */
1816                     case ENOTEMPTY:
1817         /* XXX make sure that build side permits %missingok on directories. */
1818                         if (fsm->fflags & RPMFILE_MISSINGOK)
1819                             /*@innerbreak@*/ break;
1820
1821                         /* XXX common error message. */
1822                         rpmError(
1823                             (strict_erasures ? RPMERR_RMDIR : RPMDEBUG_RMDIR),
1824                             _("%s rmdir of %s failed: Directory not empty\n"), 
1825                                 fiTypeString(fi), fsm->path);
1826                         /*@innerbreak@*/ break;
1827                     default:
1828                         rpmError(
1829                             (strict_erasures ? RPMERR_RMDIR : RPMDEBUG_RMDIR),
1830                                 _("%s rmdir of %s failed: %s\n"),
1831                                 fiTypeString(fi), fsm->path, strerror(errno));
1832                         /*@innerbreak@*/ break;
1833                     }
1834                 } else {
1835                     rc = fsmStage(fsm, FSM_UNLINK);
1836                     if (!rc) break;
1837                     if (!(errno == ENOENT && (fsm->fflags & RPMFILE_MISSINGOK)))
1838                         rpmError(
1839                             (strict_erasures ? RPMERR_UNLINK : RPMDEBUG_UNLINK),
1840                                 _("%s unlink of %s failed: %s\n"),
1841                                 fiTypeString(fi), fsm->path, strerror(errno));
1842                 }
1843             }
1844             /* XXX Failure to remove is not (yet) cause for failure. */
1845             if (!strict_erasures) rc = 0;
1846             break;
1847         }
1848
1849         /* XXX Special case /dev/log, which shouldn't be packaged anyways */
1850         if (!S_ISSOCK(st->st_mode) && !IS_DEV_LOG(fsm->path)) {
1851             /* Rename temporary to final file name. */
1852             if (!S_ISDIR(st->st_mode) &&
1853                 (fsm->subdir || fsm->suffix || fsm->nsuffix))
1854             {
1855                 fsm->opath = fsm->path;
1856                 fsm->path = fsmFsPath(fsm, st, NULL, fsm->nsuffix);
1857                 rc = fsmStage(fsm, FSM_RENAME);
1858                 if (!rc && fsm->nsuffix) {
1859                     const char * opath = fsmFsPath(fsm, st, NULL, NULL);
1860                     rpmMessage(RPMMESS_WARNING, _("%s created as %s\n"),
1861                                 (opath ? opath : ""), fsm->path);
1862                     opath = _free(opath);
1863                 }
1864                 fsm->opath = _free(fsm->opath);
1865             }
1866             if (S_ISLNK(st->st_mode)) {
1867                 if (!rc && !getuid())
1868                     rc = fsmStage(fsm, FSM_LCHOWN);
1869             } else {
1870                 if (!rc && !getuid())
1871                     rc = fsmStage(fsm, FSM_CHOWN);
1872                 if (!rc)
1873                     rc = fsmStage(fsm, FSM_CHMOD);
1874                 if (!rc) {
1875                     time_t mtime = st->st_mtime;
1876                     rpmfi fi = fsmGetFi(fsm);
1877                     if (fi->fmtimes)
1878                         st->st_mtime = fi->fmtimes[fsm->ix];
1879                     rc = fsmStage(fsm, FSM_UTIME);
1880                     st->st_mtime = mtime;
1881                 }
1882             }
1883         }
1884
1885         /* Notify on success. */
1886         if (!rc)                rc = fsmStage(fsm, FSM_NOTIFY);
1887         else if (fsm->failedFile && *fsm->failedFile == NULL) {
1888 /*@-boundswrite@*/
1889             *fsm->failedFile = fsm->path;
1890 /*@=boundswrite@*/
1891             fsm->path = NULL;
1892         }
1893         break;
1894     case FSM_DESTROY:
1895         fsm->path = _free(fsm->path);
1896
1897         /* Check for hard links missing from payload. */
1898         while ((fsm->li = fsm->links) != NULL) {
1899             fsm->links = fsm->li->next;
1900             fsm->li->next = NULL;
1901             if (fsm->goal == FSM_PKGINSTALL &&
1902                         fsm->commit && fsm->li->linksLeft)
1903             {
1904                 for (i = 0 ; i < fsm->li->linksLeft; i++) {
1905                     if (fsm->li->filex[i] < 0)
1906                         /*@innercontinue@*/ continue;
1907                     rc = CPIOERR_MISSING_HARDLINK;
1908                     if (fsm->failedFile && *fsm->failedFile == NULL) {
1909                         fsm->ix = fsm->li->filex[i];
1910                         if (!fsmStage(fsm, FSM_MAP)) {
1911 /*@-boundswrite@*/
1912                             *fsm->failedFile = fsm->path;
1913 /*@=boundswrite@*/
1914                             fsm->path = NULL;
1915                         }
1916                     }
1917                     /*@loopbreak@*/ break;
1918                 }
1919             }
1920             if (fsm->goal == FSM_PKGBUILD &&
1921                 (fsm->mapFlags & CPIO_ALL_HARDLINKS))
1922             {
1923                 rc = CPIOERR_MISSING_HARDLINK;
1924             }
1925             fsm->li = freeHardLink(fsm->li);
1926         }
1927         fsm->ldn = _free(fsm->ldn);
1928         fsm->ldnalloc = fsm->ldnlen = 0;
1929         fsm->rdbuf = fsm->rdb = _free(fsm->rdb);
1930         fsm->wrbuf = fsm->wrb = _free(fsm->wrb);
1931         break;
1932     case FSM_VERIFY:
1933         if (fsm->diskchecked && !fsm->exists) {
1934             rc = CPIOERR_LSTAT_FAILED;
1935             break;
1936         }
1937         if (S_ISREG(st->st_mode)) {
1938             char * path = alloca(strlen(fsm->path) + sizeof("-RPMDELETE"));
1939 /*@-boundswrite@*/
1940             (void) stpcpy( stpcpy(path, fsm->path), "-RPMDELETE");
1941 /*@=boundswrite@*/
1942             /*
1943              * XXX HP-UX (and other os'es) don't permit unlink on busy
1944              * XXX files.
1945              */
1946             fsm->opath = fsm->path;
1947             fsm->path = path;
1948             rc = fsmStage(fsm, FSM_RENAME);
1949             if (!rc)
1950                     (void) fsmStage(fsm, FSM_UNLINK);
1951             else
1952                     rc = CPIOERR_UNLINK_FAILED;
1953             fsm->path = fsm->opath;
1954             fsm->opath = NULL;
1955             return (rc ? rc : CPIOERR_LSTAT_FAILED);    /* XXX HACK */
1956             /*@notreached@*/ break;
1957         } else if (S_ISDIR(st->st_mode)) {
1958             if (S_ISDIR(ost->st_mode))          return 0;
1959             if (S_ISLNK(ost->st_mode)) {
1960                 rc = fsmStage(fsm, FSM_STAT);
1961                 if (rc == CPIOERR_STAT_FAILED && errno == ENOENT) rc = 0;
1962                 if (rc) break;
1963                 /*@-mods@*/
1964                 errno = saveerrno;
1965                 /*@=mods@*/
1966                 if (S_ISDIR(ost->st_mode))      return 0;
1967             }
1968         } else if (S_ISLNK(st->st_mode)) {
1969             if (S_ISLNK(ost->st_mode)) {
1970         /* XXX NUL terminated result in fsm->rdbuf, len in fsm->rdnb. */
1971                 rc = fsmStage(fsm, FSM_READLINK);
1972                 /*@-mods@*/
1973                 errno = saveerrno;
1974                 /*@=mods@*/
1975                 if (rc) break;
1976                 if (!strcmp(fsm->opath, fsm->rdbuf))    return 0;
1977             }
1978         } else if (S_ISFIFO(st->st_mode)) {
1979             if (S_ISFIFO(ost->st_mode))         return 0;
1980         } else if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
1981             if ((S_ISCHR(ost->st_mode) || S_ISBLK(ost->st_mode)) &&
1982                 (ost->st_rdev == st->st_rdev))  return 0;
1983         } else if (S_ISSOCK(st->st_mode)) {
1984             if (S_ISSOCK(ost->st_mode))         return 0;
1985         }
1986             /* XXX shouldn't do this with commit/undo. */
1987         rc = 0;
1988         if (fsm->stage == FSM_PROCESS) rc = fsmStage(fsm, FSM_UNLINK);
1989         if (rc == 0)    rc = CPIOERR_LSTAT_FAILED;
1990         return (rc ? rc : CPIOERR_LSTAT_FAILED);        /* XXX HACK */
1991         /*@notreached@*/ break;
1992
1993     case FSM_UNLINK:
1994         rc = Unlink(fsm->path);
1995         if (_fsm_debug && (stage & FSM_SYSCALL))
1996             rpmMessage(RPMMESS_DEBUG, " %8s (%s) %s\n", cur,
1997                 fsm->path, (rc < 0 ? strerror(errno) : ""));
1998         if (rc < 0)     rc = CPIOERR_UNLINK_FAILED;
1999         break;
2000     case FSM_RENAME:
2001         rc = Rename(fsm->opath, fsm->path);
2002         if (_fsm_debug && (stage & FSM_SYSCALL))
2003             rpmMessage(RPMMESS_DEBUG, " %8s (%s, %s) %s\n", cur,
2004                 fsm->opath, fsm->path, (rc < 0 ? strerror(errno) : ""));
2005         if (rc < 0)     rc = CPIOERR_RENAME_FAILED;
2006         break;
2007     case FSM_MKDIR:
2008         rc = Mkdir(fsm->path, (st->st_mode & 07777));
2009         if (_fsm_debug && (stage & FSM_SYSCALL))
2010             rpmMessage(RPMMESS_DEBUG, " %8s (%s, 0%04o) %s\n", cur,
2011                 fsm->path, (unsigned)(st->st_mode & 07777),
2012                 (rc < 0 ? strerror(errno) : ""));
2013         if (rc < 0)     rc = CPIOERR_MKDIR_FAILED;
2014         break;
2015     case FSM_RMDIR:
2016         rc = Rmdir(fsm->path);
2017         if (_fsm_debug && (stage & FSM_SYSCALL))
2018             rpmMessage(RPMMESS_DEBUG, " %8s (%s) %s\n", cur,
2019                 fsm->path, (rc < 0 ? strerror(errno) : ""));
2020         if (rc < 0)     rc = CPIOERR_RMDIR_FAILED;
2021         break;
2022     case FSM_CHOWN:
2023         rc = chown(fsm->path, st->st_uid, st->st_gid);
2024         if (_fsm_debug && (stage & FSM_SYSCALL))
2025             rpmMessage(RPMMESS_DEBUG, " %8s (%s, %d, %d) %s\n", cur,
2026                 fsm->path, (int)st->st_uid, (int)st->st_gid,
2027                 (rc < 0 ? strerror(errno) : ""));
2028         if (rc < 0)     rc = CPIOERR_CHOWN_FAILED;
2029         break;
2030     case FSM_LCHOWN:
2031 #if ! CHOWN_FOLLOWS_SYMLINK
2032         rc = lchown(fsm->path, st->st_uid, st->st_gid);
2033         if (_fsm_debug && (stage & FSM_SYSCALL))
2034             rpmMessage(RPMMESS_DEBUG, " %8s (%s, %d, %d) %s\n", cur,
2035                 fsm->path, (int)st->st_uid, (int)st->st_gid,
2036                 (rc < 0 ? strerror(errno) : ""));
2037         if (rc < 0)     rc = CPIOERR_CHOWN_FAILED;
2038 #endif
2039         break;
2040     case FSM_CHMOD:
2041         rc = chmod(fsm->path, (st->st_mode & 07777));
2042         if (_fsm_debug && (stage & FSM_SYSCALL))
2043             rpmMessage(RPMMESS_DEBUG, " %8s (%s, 0%04o) %s\n", cur,
2044                 fsm->path, (unsigned)(st->st_mode & 07777),
2045                 (rc < 0 ? strerror(errno) : ""));
2046         if (rc < 0)     rc = CPIOERR_CHMOD_FAILED;
2047         break;
2048     case FSM_UTIME:
2049         {   struct utimbuf stamp;
2050             stamp.actime = st->st_mtime;
2051             stamp.modtime = st->st_mtime;
2052             rc = utime(fsm->path, &stamp);
2053             if (_fsm_debug && (stage & FSM_SYSCALL))
2054                 rpmMessage(RPMMESS_DEBUG, " %8s (%s, 0x%x) %s\n", cur,
2055                         fsm->path, (unsigned)st->st_mtime,
2056                         (rc < 0 ? strerror(errno) : ""));
2057             if (rc < 0) rc = CPIOERR_UTIME_FAILED;
2058         }
2059         break;
2060     case FSM_SYMLINK:
2061         rc = symlink(fsm->opath, fsm->path);
2062         if (_fsm_debug && (stage & FSM_SYSCALL))
2063             rpmMessage(RPMMESS_DEBUG, " %8s (%s, %s) %s\n", cur,
2064                 fsm->opath, fsm->path, (rc < 0 ? strerror(errno) : ""));
2065         if (rc < 0)     rc = CPIOERR_SYMLINK_FAILED;
2066         break;
2067     case FSM_LINK:
2068         rc = Link(fsm->opath, fsm->path);
2069         if (_fsm_debug && (stage & FSM_SYSCALL))
2070             rpmMessage(RPMMESS_DEBUG, " %8s (%s, %s) %s\n", cur,
2071                 fsm->opath, fsm->path, (rc < 0 ? strerror(errno) : ""));
2072         if (rc < 0)     rc = CPIOERR_LINK_FAILED;
2073         break;
2074     case FSM_MKFIFO:
2075         rc = mkfifo(fsm->path, (st->st_mode & 07777));
2076         if (_fsm_debug && (stage & FSM_SYSCALL))
2077             rpmMessage(RPMMESS_DEBUG, " %8s (%s, 0%04o) %s\n", cur,
2078                 fsm->path, (unsigned)(st->st_mode & 07777),
2079                 (rc < 0 ? strerror(errno) : ""));
2080         if (rc < 0)     rc = CPIOERR_MKFIFO_FAILED;
2081         break;
2082     case FSM_MKNOD:
2083         /*@-unrecog -portability @*/ /* FIX: check S_IFIFO or dev != 0 */
2084         rc = mknod(fsm->path, (st->st_mode & ~07777), st->st_rdev);
2085         /*@=unrecog =portability @*/
2086         if (_fsm_debug && (stage & FSM_SYSCALL))
2087             rpmMessage(RPMMESS_DEBUG, " %8s (%s, 0%o, 0x%x) %s\n", cur,
2088                 fsm->path, (unsigned)(st->st_mode & ~07777),
2089                 (unsigned)st->st_rdev,
2090                 (rc < 0 ? strerror(errno) : ""));
2091         if (rc < 0)     rc = CPIOERR_MKNOD_FAILED;
2092         break;
2093     case FSM_LSTAT:
2094         rc = Lstat(fsm->path, ost);
2095         if (_fsm_debug && (stage & FSM_SYSCALL) && rc && errno != ENOENT)
2096             rpmMessage(RPMMESS_DEBUG, " %8s (%s, ost) %s\n", cur,
2097                 fsm->path, (rc < 0 ? strerror(errno) : ""));
2098         if (rc < 0)     rc = CPIOERR_LSTAT_FAILED;
2099         break;
2100     case FSM_STAT:
2101         rc = Stat(fsm->path, ost);
2102         if (_fsm_debug && (stage & FSM_SYSCALL) && rc && errno != ENOENT)
2103             rpmMessage(RPMMESS_DEBUG, " %8s (%s, ost) %s\n", cur,
2104                 fsm->path, (rc < 0 ? strerror(errno) : ""));
2105         if (rc < 0)     rc = CPIOERR_STAT_FAILED;
2106         break;
2107     case FSM_READLINK:
2108         /* XXX NUL terminated result in fsm->rdbuf, len in fsm->rdnb. */
2109 /*@-boundswrite@*/
2110         rc = Readlink(fsm->path, fsm->rdbuf, fsm->rdsize - 1);
2111 /*@=boundswrite@*/
2112         if (_fsm_debug && (stage & FSM_SYSCALL))
2113             rpmMessage(RPMMESS_DEBUG, " %8s (%s, rdbuf, %d) %s\n", cur,
2114                 fsm->path, (int)(fsm->rdsize -1), (rc < 0 ? strerror(errno) : ""));
2115         if (rc < 0)     rc = CPIOERR_READLINK_FAILED;
2116         else {
2117             fsm->rdnb = rc;
2118 /*@-boundswrite@*/
2119             fsm->rdbuf[fsm->rdnb] = '\0';
2120 /*@=boundswrite@*/
2121             rc = 0;
2122         }
2123         break;
2124     case FSM_CHROOT:
2125         break;
2126
2127     case FSM_NEXT:
2128         rc = fsmStage(fsm, FSM_HREAD);
2129         if (rc) break;
2130         if (!strcmp(fsm->path, CPIO_TRAILER)) { /* Detect end-of-payload. */
2131             fsm->path = _free(fsm->path);
2132             rc = CPIOERR_HDR_TRAILER;
2133         }
2134         if (!rc)
2135             rc = fsmStage(fsm, FSM_POS);
2136         break;
2137     case FSM_EAT:
2138         for (left = st->st_size; left > 0; left -= fsm->rdnb) {
2139             fsm->wrlen = (left > fsm->wrsize ? fsm->wrsize : left);
2140             rc = fsmStage(fsm, FSM_DREAD);
2141             if (rc)
2142                 /*@loopbreak@*/ break;
2143         }
2144         break;
2145     case FSM_POS:
2146         left = (modulo - (fdGetCpioPos(fsm->cfd) % modulo)) % modulo;
2147         if (left) {
2148             fsm->wrlen = left;
2149             (void) fsmStage(fsm, FSM_DREAD);
2150         }
2151         break;
2152     case FSM_PAD:
2153         left = (modulo - (fdGetCpioPos(fsm->cfd) % modulo)) % modulo;
2154         if (left) {
2155 /*@-boundswrite@*/
2156             memset(fsm->rdbuf, 0, left);
2157 /*@=boundswrite@*/
2158             /* XXX DWRITE uses rdnb for I/O length. */
2159             fsm->rdnb = left;
2160             (void) fsmStage(fsm, FSM_DWRITE);
2161         }
2162         break;
2163     case FSM_TRAILER:
2164         rc = cpioTrailerWrite(fsm);
2165         break;
2166     case FSM_HREAD:
2167         rc = fsmStage(fsm, FSM_POS);
2168         if (!rc)
2169             rc = cpioHeaderRead(fsm, st);       /* Read next payload header. */
2170         break;
2171     case FSM_HWRITE:
2172         rc = cpioHeaderWrite(fsm, st);          /* Write next payload header. */
2173         break;
2174     case FSM_DREAD:
2175         fsm->rdnb = Fread(fsm->wrbuf, sizeof(*fsm->wrbuf), fsm->wrlen, fsm->cfd);
2176         if (_fsm_debug && (stage & FSM_SYSCALL))
2177             rpmMessage(RPMMESS_DEBUG, " %8s (%s, %d, cfd)\trdnb %d\n",
2178                 cur, (fsm->wrbuf == fsm->wrb ? "wrbuf" : "mmap"),
2179                 (int)fsm->wrlen, (int)fsm->rdnb);
2180         if (fsm->rdnb != fsm->wrlen || Ferror(fsm->cfd))
2181             rc = CPIOERR_READ_FAILED;
2182         if (fsm->rdnb > 0)
2183             fdSetCpioPos(fsm->cfd, fdGetCpioPos(fsm->cfd) + fsm->rdnb);
2184         break;
2185     case FSM_DWRITE:
2186         fsm->wrnb = Fwrite(fsm->rdbuf, sizeof(*fsm->rdbuf), fsm->rdnb, fsm->cfd);
2187         if (_fsm_debug && (stage & FSM_SYSCALL))
2188             rpmMessage(RPMMESS_DEBUG, " %8s (%s, %d, cfd)\twrnb %d\n",
2189                 cur, (fsm->rdbuf == fsm->rdb ? "rdbuf" : "mmap"),
2190                 (int)fsm->rdnb, (int)fsm->wrnb);
2191         if (fsm->rdnb != fsm->wrnb || Ferror(fsm->cfd))
2192             rc = CPIOERR_WRITE_FAILED;
2193         if (fsm->wrnb > 0)
2194             fdSetCpioPos(fsm->cfd, fdGetCpioPos(fsm->cfd) + fsm->wrnb);
2195         break;
2196
2197     case FSM_ROPEN:
2198         fsm->rfd = Fopen(fsm->path, "r.ufdio");
2199         if (fsm->rfd == NULL || Ferror(fsm->rfd)) {
2200             if (fsm->rfd)       (void) fsmStage(fsm, FSM_RCLOSE);
2201             fsm->rfd = NULL;
2202             rc = CPIOERR_OPEN_FAILED;
2203             break;
2204         }
2205         if (_fsm_debug && (stage & FSM_SYSCALL))
2206             rpmMessage(RPMMESS_DEBUG, " %8s (%s, \"r\") rfd %p rdbuf %p\n", cur,
2207                 fsm->path, fsm->rfd, fsm->rdbuf);
2208         break;
2209     case FSM_READ:
2210         fsm->rdnb = Fread(fsm->rdbuf, sizeof(*fsm->rdbuf), fsm->rdlen, fsm->rfd);
2211         if (_fsm_debug && (stage & FSM_SYSCALL))
2212             rpmMessage(RPMMESS_DEBUG, " %8s (rdbuf, %d, rfd)\trdnb %d\n",
2213                 cur, (int)fsm->rdlen, (int)fsm->rdnb);
2214         if (fsm->rdnb != fsm->rdlen || Ferror(fsm->rfd))
2215             rc = CPIOERR_READ_FAILED;
2216         break;
2217     case FSM_RCLOSE:
2218         if (fsm->rfd) {
2219             if (_fsm_debug && (stage & FSM_SYSCALL))
2220                 rpmMessage(RPMMESS_DEBUG, " %8s (%p)\n", cur, fsm->rfd);
2221             (void) Fclose(fsm->rfd);
2222             /*@-mods@*/
2223             errno = saveerrno;
2224             /*@=mods@*/
2225         }
2226         fsm->rfd = NULL;
2227         break;
2228     case FSM_WOPEN:
2229         fsm->wfd = Fopen(fsm->path, "w.ufdio");
2230         if (fsm->wfd == NULL || Ferror(fsm->wfd)) {
2231             if (fsm->wfd)       (void) fsmStage(fsm, FSM_WCLOSE);
2232             fsm->wfd = NULL;
2233             rc = CPIOERR_OPEN_FAILED;
2234         }
2235         if (_fsm_debug && (stage & FSM_SYSCALL))
2236             rpmMessage(RPMMESS_DEBUG, " %8s (%s, \"w\") wfd %p wrbuf %p\n", cur,
2237                 fsm->path, fsm->wfd, fsm->wrbuf);
2238         break;
2239     case FSM_WRITE:
2240         fsm->wrnb = Fwrite(fsm->wrbuf, sizeof(*fsm->wrbuf), fsm->rdnb, fsm->wfd);
2241         if (_fsm_debug && (stage & FSM_SYSCALL))
2242             rpmMessage(RPMMESS_DEBUG, " %8s (wrbuf, %d, wfd)\twrnb %d\n",
2243                 cur, (int)fsm->rdnb, (int)fsm->wrnb);
2244         if (fsm->rdnb != fsm->wrnb || Ferror(fsm->wfd))
2245             rc = CPIOERR_WRITE_FAILED;
2246         break;
2247     case FSM_WCLOSE:
2248         if (fsm->wfd) {
2249             if (_fsm_debug && (stage & FSM_SYSCALL))
2250                 rpmMessage(RPMMESS_DEBUG, " %8s (%p)\n", cur, fsm->wfd);
2251             (void) Fclose(fsm->wfd);
2252             /*@-mods@*/
2253             errno = saveerrno;
2254             /*@=mods@*/
2255         }
2256         fsm->wfd = NULL;
2257         break;
2258
2259     default:
2260         break;
2261     }
2262     /*@=branchstate@*/
2263
2264     if (!(stage & FSM_INTERNAL)) {
2265         fsm->rc = (rc == CPIOERR_HDR_TRAILER ? 0 : rc);
2266     }
2267     return rc;
2268 }
2269 /*@=compmempass@*/
2270 /*@=boundsread@*/
2271
2272 /*@obserever@*/ const char *const fileActionString(fileAction a)
2273 {
2274     switch (a) {
2275     case FA_UNKNOWN:    return "unknown";
2276     case FA_CREATE:     return "create";
2277     case FA_COPYOUT:    return "copyout";
2278     case FA_COPYIN:     return "copyin";
2279     case FA_BACKUP:     return "backup";
2280     case FA_SAVE:       return "save";
2281     case FA_SKIP:       return "skip";
2282     case FA_ALTNAME:    return "altname";
2283     case FA_ERASE:      return "erase";
2284     case FA_SKIPNSTATE: return "skipnstate";
2285     case FA_SKIPNETSHARED: return "skipnetshared";
2286     case FA_SKIPMULTILIB: return "skipmultilib";
2287     default:            return "???";
2288     }
2289     /*@notreached@*/
2290 }
2291
2292 /*@observer@*/ const char *const fileStageString(fileStage a) {
2293     switch(a) {
2294     case FSM_UNKNOWN:   return "unknown";
2295
2296     case FSM_PKGINSTALL:return "INSTALL";
2297     case FSM_PKGERASE:  return "ERASE";
2298     case FSM_PKGBUILD:  return "BUILD";
2299     case FSM_PKGCOMMIT: return "COMMIT";
2300     case FSM_PKGUNDO:   return "UNDO";
2301
2302     case FSM_CREATE:    return "create";
2303     case FSM_INIT:      return "init";
2304     case FSM_MAP:       return "map";
2305     case FSM_MKDIRS:    return "mkdirs";
2306     case FSM_RMDIRS:    return "rmdirs";
2307     case FSM_PRE:       return "pre";
2308     case FSM_PROCESS:   return "process";
2309     case FSM_POST:      return "post";
2310     case FSM_MKLINKS:   return "mklinks";
2311     case FSM_NOTIFY:    return "notify";
2312     case FSM_UNDO:      return "undo";
2313     case FSM_FINI:      return "fini";
2314     case FSM_COMMIT:    return "commit";
2315     case FSM_DESTROY:   return "destroy";
2316     case FSM_VERIFY:    return "verify";
2317
2318     case FSM_UNLINK:    return "Unlink";
2319     case FSM_RENAME:    return "Rename";
2320     case FSM_MKDIR:     return "Mkdir";
2321     case FSM_RMDIR:     return "rmdir";
2322     case FSM_CHOWN:     return "chown";
2323     case FSM_LCHOWN:    return "lchown";
2324     case FSM_CHMOD:     return "chmod";
2325     case FSM_UTIME:     return "utime";
2326     case FSM_SYMLINK:   return "symlink";
2327     case FSM_LINK:      return "Link";
2328     case FSM_MKFIFO:    return "mkfifo";
2329     case FSM_MKNOD:     return "mknod";
2330     case FSM_LSTAT:     return "Lstat";
2331     case FSM_STAT:      return "Stat";
2332     case FSM_READLINK:  return "Readlink";
2333     case FSM_CHROOT:    return "chroot";
2334
2335     case FSM_NEXT:      return "next";
2336     case FSM_EAT:       return "eat";
2337     case FSM_POS:       return "pos";
2338     case FSM_PAD:       return "pad";
2339     case FSM_TRAILER:   return "trailer";
2340     case FSM_HREAD:     return "hread";
2341     case FSM_HWRITE:    return "hwrite";
2342     case FSM_DREAD:     return "Fread";
2343     case FSM_DWRITE:    return "Fwrite";
2344
2345     case FSM_ROPEN:     return "Fopen";
2346     case FSM_READ:      return "Fread";
2347     case FSM_RCLOSE:    return "Fclose";
2348     case FSM_WOPEN:     return "Fopen";
2349     case FSM_WRITE:     return "Fwrite";
2350     case FSM_WCLOSE:    return "Fclose";
2351
2352     default:            return "???";
2353     }
2354     /*@noteached@*/
2355 }