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