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