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