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