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