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