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