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