upgrade rpm version to 4.14.1
[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/rpmlog.h>
17 #include <rpm/rpmmacro.h>
18
19 #include "rpmio/rpmio_internal.h"       /* fdInit/FiniDigest */
20 #include "lib/fsm.h"
21 #include "lib/rpmte_internal.h" /* XXX rpmfs */
22 #include "lib/rpmplugins.h"     /* rpm plugins hooks */
23 #include "lib/rpmug.h"
24
25 #include "debug.h"
26
27 #define _FSM_DEBUG      0
28 int _fsm_debug = _FSM_DEBUG;
29
30 /* XXX Failure to remove is not (yet) cause for failure. */
31 static int strict_erasures = 0;
32
33 #define SUFFIX_RPMORIG  ".rpmorig"
34 #define SUFFIX_RPMSAVE  ".rpmsave"
35 #define SUFFIX_RPMNEW   ".rpmnew"
36
37 /* Default directory and file permissions if not mapped */
38 #define _dirPerms 0755
39 #define _filePerms 0644
40
41 /* 
42  * XXX Forward declarations for previously exported functions to avoid moving 
43  * things around needlessly 
44  */ 
45 static const char * fileActionString(rpmFileAction a);
46
47 /** \ingroup payload
48  * Build path to file from file info, optionally ornamented with suffix.
49  * @param fi            file info iterator
50  * @param suffix        suffix to use (NULL disables)
51  * @retval              path to file (malloced)
52  */
53 static char * fsmFsPath(rpmfi fi, const char * suffix)
54 {
55     return rstrscat(NULL, rpmfiDN(fi), rpmfiBN(fi), suffix ? suffix : "", NULL);
56 }
57
58 /** \ingroup payload
59  * Directory name iterator.
60  */
61 typedef struct dnli_s {
62     rpmfiles fi;
63     char * active;
64     int reverse;
65     int isave;
66     int i;
67 } * DNLI_t;
68
69 /** \ingroup payload
70  * Destroy directory name iterator.
71  * @param dnli          directory name iterator
72  * @retval              NULL always
73  */
74 static DNLI_t dnlFreeIterator(DNLI_t dnli)
75 {
76     if (dnli) {
77         if (dnli->active) free(dnli->active);
78         free(dnli);
79     }
80     return NULL;
81 }
82
83 /** \ingroup payload
84  * Create directory name iterator.
85  * @param fi            file info set
86  * @param fs            file state set
87  * @param reverse       traverse directory names in reverse order?
88  * @return              directory name iterator
89  */
90 static DNLI_t dnlInitIterator(rpmfiles fi, rpmfs fs, int reverse)
91 {
92     DNLI_t dnli;
93     int i, j;
94     int dc;
95
96     if (fi == NULL)
97         return NULL;
98     dc = rpmfilesDC(fi);
99     dnli = xcalloc(1, sizeof(*dnli));
100     dnli->fi = fi;
101     dnli->reverse = reverse;
102     dnli->i = (reverse ? dc : 0);
103
104     if (dc) {
105         dnli->active = xcalloc(dc, sizeof(*dnli->active));
106         int fc = rpmfilesFC(fi);
107
108         /* Identify parent directories not skipped. */
109         for (i = 0; i < fc; i++)
110             if (!XFA_SKIPPING(rpmfsGetAction(fs, i)))
111                 dnli->active[rpmfilesDI(fi, i)] = 1;
112
113         /* Exclude parent directories that are explicitly included. */
114         for (i = 0; i < fc; i++) {
115             int dil;
116             size_t dnlen, bnlen;
117
118             if (!S_ISDIR(rpmfilesFMode(fi, i)))
119                 continue;
120
121             dil = rpmfilesDI(fi, i);
122             dnlen = strlen(rpmfilesDN(fi, dil));
123             bnlen = strlen(rpmfilesBN(fi, i));
124
125             for (j = 0; j < dc; j++) {
126                 const char * dnl;
127                 size_t jlen;
128
129                 if (!dnli->active[j] || j == dil)
130                     continue;
131                 dnl = rpmfilesDN(fi, j);
132                 jlen = strlen(dnl);
133                 if (jlen != (dnlen+bnlen+1))
134                     continue;
135                 if (!rstreqn(dnl, rpmfilesDN(fi, dil), dnlen))
136                     continue;
137                 if (!rstreqn(dnl+dnlen, rpmfilesBN(fi, i), bnlen))
138                     continue;
139                 if (dnl[dnlen+bnlen] != '/' || dnl[dnlen+bnlen+1] != '\0')
140                     continue;
141                 /* This directory is included in the package. */
142                 dnli->active[j] = 0;
143                 break;
144             }
145         }
146
147         /* Print only once per package. */
148         if (!reverse) {
149             j = 0;
150             for (i = 0; i < dc; i++) {
151                 if (!dnli->active[i]) continue;
152                 if (j == 0) {
153                     j = 1;
154                     rpmlog(RPMLOG_DEBUG,
155         "========== Directories not explicitly included in package:\n");
156                 }
157                 rpmlog(RPMLOG_DEBUG, "%10d %s\n", i, rpmfilesDN(fi, i));
158             }
159             if (j)
160                 rpmlog(RPMLOG_DEBUG, "==========\n");
161         }
162     }
163     return dnli;
164 }
165
166 /** \ingroup payload
167  * Return next directory name (from file info).
168  * @param dnli          directory name iterator
169  * @return              next directory name
170  */
171 static
172 const char * dnlNextIterator(DNLI_t dnli)
173 {
174     const char * dn = NULL;
175
176     if (dnli) {
177         rpmfiles fi = dnli->fi;
178         int dc = rpmfilesDC(fi);
179         int i = -1;
180
181         if (dnli->active)
182         do {
183             i = (!dnli->reverse ? dnli->i++ : --dnli->i);
184         } while (i >= 0 && i < dc && !dnli->active[i]);
185
186         if (i >= 0 && i < dc)
187             dn = rpmfilesDN(fi, i);
188         else
189             i = -1;
190         dnli->isave = i;
191     }
192     return dn;
193 }
194
195 static int fsmSetFCaps(const char *path, const char *captxt)
196 {
197     int rc = 0;
198 #if WITH_CAP
199     if (captxt && *captxt != '\0') {
200         cap_t fcaps = cap_from_text(captxt);
201         if (fcaps == NULL || cap_set_file(path, fcaps) != 0) {
202             rc = RPMERR_SETCAP_FAILED;
203         }
204         cap_free(fcaps);
205     } 
206 #endif
207     return rc;
208 }
209
210 static void wfd_close(FD_t *wfdp)
211 {
212     if (wfdp && *wfdp) {
213         int myerrno = errno;
214         static int oneshot = 0;
215         static int flush_io = 0;
216         if (!oneshot) {
217             flush_io = rpmExpandNumeric("%{?_flush_io}");
218             oneshot = 1;
219         }
220         if (flush_io) {
221             int fdno = Fileno(*wfdp);
222             fsync(fdno);
223         }
224         Fclose(*wfdp);
225         *wfdp = NULL;
226         errno = myerrno;
227     }
228 }
229
230 static int wfd_open(FD_t *wfdp, const char *dest)
231 {
232     int rc = 0;
233     /* Create the file with 0200 permissions (write by owner). */
234     {
235         mode_t old_umask = umask(0577);
236         *wfdp = Fopen(dest, "wx.ufdio");
237         umask(old_umask);
238     }
239     if (Ferror(*wfdp)) {
240         rc = RPMERR_OPEN_FAILED;
241         goto exit;
242     }
243
244     return 0;
245
246 exit:
247     wfd_close(wfdp);
248     return rc;
249 }
250
251 /** \ingroup payload
252  * Create file from payload stream.
253  * @return              0 on success
254  */
255 static int expandRegular(rpmfi fi, const char *dest, rpmpsm psm, int nodigest)
256 {
257     FD_t wfd = NULL;
258     int rc;
259
260     rc = wfd_open(&wfd, dest);
261     if (rc != 0)
262         goto exit;
263
264     rc = rpmfiArchiveReadToFilePsm(fi, wfd, nodigest, psm);
265     wfd_close(&wfd);
266 exit:
267     return rc;
268 }
269
270 static int fsmMkfile(rpmfi fi, const char *dest, rpmfiles files,
271                      rpmpsm psm, int nodigest, int *setmeta,
272                      int * firsthardlink, FD_t *firstlinkfile)
273 {
274     int rc = 0;
275     int numHardlinks = rpmfiFNlink(fi);
276
277     if (numHardlinks > 1) {
278         /* Create first hardlinked file empty */
279         if (*firsthardlink < 0) {
280             *firsthardlink = rpmfiFX(fi);
281             rc = wfd_open(firstlinkfile, dest);
282         } else {
283             /* Create hard links for others */
284             char *fn = rpmfilesFN(files, *firsthardlink);
285             rc = link(fn, dest);
286             if (rc < 0) {
287                 rc = RPMERR_LINK_FAILED;
288             }
289             free(fn);
290         }
291     }
292     /* Write normal files or fill the last hardlinked (already
293        existing) file with content */
294     if (numHardlinks<=1) {
295         if (!rc)
296             rc = expandRegular(fi, dest, psm, nodigest);
297     } else if (rpmfiArchiveHasContent(fi)) {
298         if (!rc)
299             rc = rpmfiArchiveReadToFilePsm(fi, *firstlinkfile, nodigest, psm);
300         wfd_close(firstlinkfile);
301         *firsthardlink = -1;
302     } else {
303         *setmeta = 0;
304     }
305
306     return rc;
307 }
308
309 static int fsmReadLink(const char *path,
310                        char *buf, size_t bufsize, size_t *linklen)
311 {
312     ssize_t llen = readlink(path, buf, bufsize - 1);
313     int rc = RPMERR_READLINK_FAILED;
314
315     if (_fsm_debug) {
316         rpmlog(RPMLOG_DEBUG, " %8s (%s, buf, %d) %s\n",
317                __func__,
318                path, (int)(bufsize -1), (llen < 0 ? strerror(errno) : ""));
319     }
320
321     if (llen >= 0) {
322         buf[llen] = '\0';
323         rc = 0;
324         *linklen = llen;
325     }
326     return rc;
327 }
328
329 static int fsmStat(const char *path, int dolstat, struct stat *sb)
330 {
331     int rc;
332     if (dolstat){
333         rc = lstat(path, sb);
334     } else {
335         rc = stat(path, sb);
336     }
337     if (_fsm_debug && rc && errno != ENOENT)
338         rpmlog(RPMLOG_DEBUG, " %8s (%s, ost) %s\n",
339                __func__,
340                path, (rc < 0 ? strerror(errno) : ""));
341     if (rc < 0) {
342         rc = (errno == ENOENT ? RPMERR_ENOENT : RPMERR_LSTAT_FAILED);
343         /* Ensure consistent struct content on failure */
344         memset(sb, 0, sizeof(*sb));
345     }
346     return rc;
347 }
348
349 static int fsmRmdir(const char *path)
350 {
351     int rc = rmdir(path);
352     if (_fsm_debug)
353         rpmlog(RPMLOG_DEBUG, " %8s (%s) %s\n", __func__,
354                path, (rc < 0 ? strerror(errno) : ""));
355     if (rc < 0)
356         switch (errno) {
357         case ENOENT:        rc = RPMERR_ENOENT;    break;
358         case ENOTEMPTY:     rc = RPMERR_ENOTEMPTY; break;
359         default:            rc = RPMERR_RMDIR_FAILED; break;
360         }
361     return rc;
362 }
363
364 static int fsmMkdir(const char *path, mode_t mode)
365 {
366     int rc = mkdir(path, (mode & 07777));
367     if (_fsm_debug)
368         rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%04o) %s\n", __func__,
369                path, (unsigned)(mode & 07777),
370                (rc < 0 ? strerror(errno) : ""));
371     if (rc < 0) rc = RPMERR_MKDIR_FAILED;
372     return rc;
373 }
374
375 static int fsmMkfifo(const char *path, mode_t mode)
376 {
377     int rc = mkfifo(path, (mode & 07777));
378
379     if (_fsm_debug) {
380         rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%04o) %s\n",
381                __func__, path, (unsigned)(mode & 07777),
382                (rc < 0 ? strerror(errno) : ""));
383     }
384
385     if (rc < 0)
386         rc = RPMERR_MKFIFO_FAILED;
387
388     return rc;
389 }
390
391 static int fsmMknod(const char *path, mode_t mode, dev_t dev)
392 {
393     /* FIX: check S_IFIFO or dev != 0 */
394     int rc = mknod(path, (mode & ~07777), dev);
395
396     if (_fsm_debug) {
397         rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%o, 0x%x) %s\n",
398                __func__, path, (unsigned)(mode & ~07777),
399                (unsigned)dev, (rc < 0 ? strerror(errno) : ""));
400     }
401
402     if (rc < 0)
403         rc = RPMERR_MKNOD_FAILED;
404
405     return rc;
406 }
407
408 /**
409  * Create (if necessary) directories not explicitly included in package.
410  * @param files         file data
411  * @param fs            file states
412  * @param plugins       rpm plugins handle
413  * @return              0 on success
414  */
415 static int fsmMkdirs(rpmfiles files, rpmfs fs, rpmPlugins plugins)
416 {
417     DNLI_t dnli = dnlInitIterator(files, fs, 0);
418     struct stat sb;
419     const char *dpath;
420     int dc = rpmfilesDC(files);
421     int rc = 0;
422     int i;
423     int ldnlen = 0;
424     int ldnalloc = 0;
425     char * ldn = NULL;
426     short * dnlx = NULL; 
427
428     dnlx = (dc ? xcalloc(dc, sizeof(*dnlx)) : NULL);
429
430     if (dnlx != NULL)
431     while ((dpath = dnlNextIterator(dnli)) != NULL) {
432         size_t dnlen = strlen(dpath);
433         char * te, dn[dnlen+1];
434
435         dc = dnli->isave;
436         if (dc < 0) continue;
437         dnlx[dc] = dnlen;
438         if (dnlen <= 1)
439             continue;
440
441         if (dnlen <= ldnlen && rstreq(dpath, ldn))
442             continue;
443
444         /* Copy as we need to modify the string */
445         (void) stpcpy(dn, dpath);
446
447         /* Assume '/' directory exists, "mkdir -p" for others if non-existent */
448         for (i = 1, te = dn + 1; *te != '\0'; te++, i++) {
449             if (*te != '/')
450                 continue;
451
452             *te = '\0';
453
454             /* Already validated? */
455             if (i < ldnlen &&
456                 (ldn[i] == '/' || ldn[i] == '\0') && rstreqn(dn, ldn, i))
457             {
458                 *te = '/';
459                 /* Move pre-existing path marker forward. */
460                 dnlx[dc] = (te - dn);
461                 continue;
462             }
463
464             /* Validate next component of path. */
465             rc = fsmStat(dn, 1, &sb); /* lstat */
466             *te = '/';
467
468             /* Directory already exists? */
469             if (rc == 0 && S_ISDIR(sb.st_mode)) {
470                 /* Move pre-existing path marker forward. */
471                 dnlx[dc] = (te - dn);
472             } else if (rc == RPMERR_ENOENT) {
473                 *te = '\0';
474                 mode_t mode = S_IFDIR | (_dirPerms & 07777);
475                 rpmFsmOp op = (FA_CREATE|FAF_UNOWNED);
476
477                 /* Run fsm file pre hook for all plugins */
478                 rc = rpmpluginsCallFsmFilePre(plugins, NULL, dn, mode, op);
479
480                 if (!rc)
481                     rc = fsmMkdir(dn, mode);
482
483                 if (!rc) {
484                     rc = rpmpluginsCallFsmFilePrepare(plugins, NULL, dn, dn,
485                                                       mode, op);
486                 }
487
488                 /* Run fsm file post hook for all plugins */
489                 rpmpluginsCallFsmFilePost(plugins, NULL, dn, mode, op, rc);
490
491                 if (!rc) {
492                     rpmlog(RPMLOG_DEBUG,
493                             "%s directory created with perms %04o\n",
494                             dn, (unsigned)(mode & 07777));
495                 }
496
497                 if (!rc) {
498                         /* Run file closed hook for all plugins */
499                         rc = rpmpluginsCallFsmCommit(plugins, dn, mode, DIR_TYPE_UNOWNED);
500                 }
501                 *te = '/';
502             }
503             if (rc)
504                 break;
505         }
506         if (rc) break;
507
508         /* Save last validated path. */
509         if (ldnalloc < (dnlen + 1)) {
510             ldnalloc = dnlen + 100;
511             ldn = xrealloc(ldn, ldnalloc);
512         }
513         if (ldn != NULL) { /* XXX can't happen */
514             strcpy(ldn, dn);
515             ldnlen = dnlen;
516         }
517     }
518     free(dnlx);
519     free(ldn);
520     dnlFreeIterator(dnli);
521
522     return rc;
523 }
524
525 static void removeSBITS(const char *path)
526 {
527     struct stat stb;
528     if (lstat(path, &stb) == 0 && S_ISREG(stb.st_mode)) {
529         if ((stb.st_mode & 06000) != 0) {
530             (void) chmod(path, stb.st_mode & 0777);
531         }
532 #if WITH_CAP
533         if (stb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) {
534             (void) cap_set_file(path, NULL);
535         }
536 #endif
537     }
538 }
539
540 static void fsmDebug(const char *fpath, rpmFileAction action,
541                      const struct stat *st)
542 {
543     rpmlog(RPMLOG_DEBUG, "%-10s %06o%3d (%4d,%4d)%6d %s\n",
544            fileActionString(action), (int)st->st_mode,
545            (int)st->st_nlink, (int)st->st_uid,
546            (int)st->st_gid, (int)st->st_size,
547             (fpath ? fpath : ""));
548 }
549
550 static int fsmSymlink(const char *opath, const char *path)
551 {
552     int rc = symlink(opath, path);
553
554     if (_fsm_debug) {
555         rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", __func__,
556                opath, path, (rc < 0 ? strerror(errno) : ""));
557     }
558
559     if (rc < 0)
560         rc = RPMERR_SYMLINK_FAILED;
561     return rc;
562 }
563
564 static int fsmUnlink(const char *path)
565 {
566     int rc = 0;
567     removeSBITS(path);
568     rc = unlink(path);
569     if (_fsm_debug)
570         rpmlog(RPMLOG_DEBUG, " %8s (%s) %s\n", __func__,
571                path, (rc < 0 ? strerror(errno) : ""));
572     if (rc < 0)
573         rc = (errno == ENOENT ? RPMERR_ENOENT : RPMERR_UNLINK_FAILED);
574     return rc;
575 }
576
577 static int fsmRename(const char *opath, const char *path)
578 {
579     removeSBITS(path);
580     int rc = rename(opath, path);
581 #if defined(ETXTBSY) && defined(__HPUX__)
582     /* XXX HP-UX (and other os'es) don't permit rename to busy files. */
583     if (rc && errno == ETXTBSY) {
584         char *rmpath = NULL;
585         rstrscat(&rmpath, path, "-RPMDELETE", NULL);
586         rc = rename(path, rmpath);
587         if (!rc) rc = rename(opath, path);
588         free(rmpath);
589     }
590 #endif
591     if (_fsm_debug)
592         rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", __func__,
593                opath, path, (rc < 0 ? strerror(errno) : ""));
594     if (rc < 0)
595         rc = (errno == EISDIR ? RPMERR_EXIST_AS_DIR : RPMERR_RENAME_FAILED);
596     return rc;
597 }
598
599 static int fsmRemove(const char *path, mode_t mode)
600 {
601     return S_ISDIR(mode) ? fsmRmdir(path) : fsmUnlink(path);
602 }
603
604 static int fsmChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
605 {
606     int rc = S_ISLNK(mode) ? lchown(path, uid, gid) : chown(path, uid, gid);
607     if (rc < 0) {
608         struct stat st;
609         if (lstat(path, &st) == 0 && st.st_uid == uid && st.st_gid == gid)
610             rc = 0;
611     }
612     if (_fsm_debug)
613         rpmlog(RPMLOG_DEBUG, " %8s (%s, %d, %d) %s\n", __func__,
614                path, (int)uid, (int)gid,
615                (rc < 0 ? strerror(errno) : ""));
616     if (rc < 0) rc = RPMERR_CHOWN_FAILED;
617     return rc;
618 }
619
620 static int fsmChmod(const char *path, mode_t mode)
621 {
622     int rc = chmod(path, (mode & 07777));
623     if (rc < 0) {
624         struct stat st;
625         if (lstat(path, &st) == 0 && (st.st_mode & 07777) == (mode & 07777))
626             rc = 0;
627     }
628     if (_fsm_debug)
629         rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%04o) %s\n", __func__,
630                path, (unsigned)(mode & 07777),
631                (rc < 0 ? strerror(errno) : ""));
632     if (rc < 0) rc = RPMERR_CHMOD_FAILED;
633     return rc;
634 }
635
636 static int fsmUtime(const char *path, mode_t mode, time_t mtime)
637 {
638     int rc = 0;
639     struct timeval stamps[2] = {
640         { .tv_sec = mtime, .tv_usec = 0 },
641         { .tv_sec = mtime, .tv_usec = 0 },
642     };
643
644 #if HAVE_LUTIMES
645     rc = lutimes(path, stamps);
646 #else
647     if (!S_ISLNK(mode))
648         rc = utimes(path, stamps);
649 #endif
650     
651     if (_fsm_debug)
652         rpmlog(RPMLOG_DEBUG, " %8s (%s, 0x%x) %s\n", __func__,
653                path, (unsigned)mtime, (rc < 0 ? strerror(errno) : ""));
654     if (rc < 0) rc = RPMERR_UTIME_FAILED;
655     /* ...but utime error is not critical for directories */
656     if (rc && S_ISDIR(mode))
657         rc = 0;
658     return rc;
659 }
660
661 static int fsmVerify(const char *path, rpmfi fi, const struct stat *fsb)
662 {
663     int rc;
664     int saveerrno = errno;
665     struct stat dsb;
666     mode_t mode = rpmfiFMode(fi);
667
668     rc = fsmStat(path, 1, &dsb);
669     if (rc)
670         return rc;
671
672     if (S_ISREG(mode)) {
673         /* HP-UX (and other os'es) don't permit unlink on busy files. */
674         char *rmpath = rstrscat(NULL, path, "-RPMDELETE", NULL);
675         rc = fsmRename(path, rmpath);
676         /* XXX shouldn't we take unlink return code here? */
677         if (!rc)
678             (void) fsmUnlink(rmpath);
679         else
680             rc = RPMERR_UNLINK_FAILED;
681         free(rmpath);
682         return (rc ? rc : RPMERR_ENOENT);       /* XXX HACK */
683     } else if (S_ISDIR(mode)) {
684         if (S_ISDIR(dsb.st_mode)) return 0;
685         if (S_ISLNK(dsb.st_mode)) {
686             uid_t luid = dsb.st_uid;
687             rc = fsmStat(path, 0, &dsb);
688             if (rc == RPMERR_ENOENT) rc = 0;
689             if (rc) return rc;
690             errno = saveerrno;
691             /* Only permit directory symlinks by target owner and root */
692             if (S_ISDIR(dsb.st_mode) && (luid == 0 || luid == fsb->st_uid))
693                     return 0;
694         }
695     } else if (S_ISLNK(mode)) {
696         if (S_ISLNK(dsb.st_mode)) {
697             char buf[8 * BUFSIZ];
698             size_t len;
699             rc = fsmReadLink(path, buf, 8 * BUFSIZ, &len);
700             errno = saveerrno;
701             if (rc) return rc;
702             if (rstreq(rpmfiFLink(fi), buf)) return 0;
703         }
704     } else if (S_ISFIFO(mode)) {
705         if (S_ISFIFO(dsb.st_mode)) return 0;
706     } else if (S_ISCHR(mode) || S_ISBLK(mode)) {
707         if ((S_ISCHR(dsb.st_mode) || S_ISBLK(dsb.st_mode)) &&
708             (dsb.st_rdev == rpmfiFRdev(fi))) return 0;
709     } else if (S_ISSOCK(mode)) {
710         if (S_ISSOCK(dsb.st_mode)) return 0;
711     }
712     /* XXX shouldn't do this with commit/undo. */
713     rc = fsmUnlink(path);
714     if (rc == 0)        rc = RPMERR_ENOENT;
715     return (rc ? rc : RPMERR_ENOENT);   /* XXX HACK */
716 }
717
718 #define IS_DEV_LOG(_x)  \
719         ((_x) != NULL && strlen(_x) >= (sizeof("/dev/log")-1) && \
720         rstreqn((_x), "/dev/log", sizeof("/dev/log")-1) && \
721         ((_x)[sizeof("/dev/log")-1] == '\0' || \
722          (_x)[sizeof("/dev/log")-1] == ';'))
723
724
725
726 /* Rename pre-existing modified or unmanaged file. */
727 static int fsmBackup(rpmfi fi, rpmFileAction action)
728 {
729     int rc = 0;
730     const char *suffix = NULL;
731
732     if (!(rpmfiFFlags(fi) & RPMFILE_GHOST)) {
733         switch (action) {
734         case FA_SAVE:
735             suffix = SUFFIX_RPMSAVE;
736             break;
737         case FA_BACKUP:
738             suffix = SUFFIX_RPMORIG;
739             break;
740         default:
741             break;
742         }
743     }
744
745     if (suffix) {
746         char * opath = fsmFsPath(fi, NULL);
747         char * path = fsmFsPath(fi, suffix);
748         rc = fsmRename(opath, path);
749         if (!rc) {
750             rpmlog(RPMLOG_WARNING, _("%s saved as %s\n"), opath, path);
751         }
752         free(path);
753         free(opath);
754     }
755     return rc;
756 }
757
758 static int fsmSetmeta(const char *path, rpmfi fi, rpmPlugins plugins,
759                       rpmFileAction action, const struct stat * st,
760                       int nofcaps)
761 {
762     int rc = 0;
763     const char *dest = rpmfiFN(fi);
764
765     if (!rc && !getuid()) {
766         rc = fsmChown(path, st->st_mode, st->st_uid, st->st_gid);
767     }
768     if (!rc && !S_ISLNK(st->st_mode)) {
769         rc = fsmChmod(path, st->st_mode);
770     }
771     /* Set file capabilities (if enabled) */
772     if (!rc && !nofcaps && S_ISREG(st->st_mode) && !getuid()) {
773         rc = fsmSetFCaps(path, rpmfiFCaps(fi));
774     }
775     if (!rc) {
776         rc = fsmUtime(path, st->st_mode, rpmfiFMtime(fi));
777     }
778     if (!rc) {
779         rc = rpmpluginsCallFsmFilePrepare(plugins, fi,
780                                           path, dest, st->st_mode, action);
781     }
782
783     return rc;
784 }
785
786 static int fsmCommit(char **path, rpmfi fi, rpmFileAction action, const char *suffix, rpmPlugins plugins)
787 {
788     int rc = 0;
789
790     /* XXX Special case /dev/log, which shouldn't be packaged anyways */
791     if (!(S_ISSOCK(rpmfiFMode(fi)) && IS_DEV_LOG(*path))) {
792                 const char *nsuffix = (action == FA_ALTNAME) ? SUFFIX_RPMNEW : NULL;
793                 char *dest = *path;
794                 /* Construct final destination path (nsuffix is usually NULL) */
795                 if (suffix)
796                     dest = fsmFsPath(fi, nsuffix);
797
798                 /* Rename temporary to final file name if needed. */
799                 if (dest != *path) {
800                     rc = fsmRename(*path, dest);
801                     if (!rc && nsuffix) {
802                         char * opath = fsmFsPath(fi, NULL);
803                         rpmlog(RPMLOG_WARNING, _("%s created as %s\n"),
804                                opath, dest);
805                         free(opath);
806                     }
807                     free(*path);
808                     *path = dest;
809                 }
810         /* Call fsm commit hook for all plugins */
811         if (!rc) {
812             rc = rpmpluginsCallFsmCommit(plugins, *path, rpmfiFMode(fi), DIR_TYPE_NORMAL);
813         }
814     }
815
816     return rc;
817 }
818
819 /**
820  * Return formatted string representation of file disposition.
821  * @param a             file disposition
822  * @return              formatted string
823  */
824 static const char * fileActionString(rpmFileAction a)
825 {
826     switch (a) {
827     case FA_UNKNOWN:    return "unknown";
828     case FA_CREATE:     return "create";
829     case FA_BACKUP:     return "backup";
830     case FA_SAVE:       return "save";
831     case FA_SKIP:       return "skip";
832     case FA_ALTNAME:    return "altname";
833     case FA_ERASE:      return "erase";
834     case FA_SKIPNSTATE: return "skipnstate";
835     case FA_SKIPNETSHARED: return "skipnetshared";
836     case FA_SKIPCOLOR:  return "skipcolor";
837     case FA_TOUCH:     return "touch";
838     default:            return "???";
839     }
840 }
841
842 /* Remember any non-regular file state for recording in the rpmdb */
843 static void setFileState(rpmfs fs, int i)
844 {
845     switch (rpmfsGetAction(fs, i)) {
846     case FA_SKIPNSTATE:
847         rpmfsSetState(fs, i, RPMFILE_STATE_NOTINSTALLED);
848         break;
849     case FA_SKIPNETSHARED:
850         rpmfsSetState(fs, i, RPMFILE_STATE_NETSHARED);
851         break;
852     case FA_SKIPCOLOR:
853         rpmfsSetState(fs, i, RPMFILE_STATE_WRONGCOLOR);
854         break;
855     case FA_TOUCH:
856         rpmfsSetState(fs, i, RPMFILE_STATE_NORMAL);
857         break;
858     default:
859         break;
860     }
861 }
862
863 int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
864               rpmpsm psm, char ** failedFile)
865 {
866     FD_t payload = rpmtePayload(te);
867     rpmfi fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);
868     rpmfs fs = rpmteGetFileStates(te);
869     rpmPlugins plugins = rpmtsPlugins(ts);
870     struct stat sb;
871     int saveerrno = errno;
872     int rc = 0;
873     int nodigest = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOFILEDIGEST) ? 1 : 0;
874     int nofcaps = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCAPS) ? 1 : 0;
875     int firsthardlink = -1;
876     FD_t firstlinkfile = NULL;
877     int skip;
878     rpmFileAction action;
879     char *tid = NULL;
880     const char *suffix;
881     char *fpath = NULL;
882
883     if (fi == NULL) {
884         rc = RPMERR_BAD_MAGIC;
885         goto exit;
886     }
887
888     /* transaction id used for temporary path suffix while installing */
889     rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts));
890
891     /* Detect and create directories not explicitly in package. */
892     rc = fsmMkdirs(files, fs, plugins);
893
894     while (!rc) {
895         /* Read next payload header. */
896         rc = rpmfiNext(fi);
897
898         if (rc < 0) {
899             if (rc == RPMERR_ITER_END)
900                 rc = 0;
901             break;
902         }
903
904         action = rpmfsGetAction(fs, rpmfiFX(fi));
905         skip = XFA_SKIPPING(action);
906         suffix = S_ISDIR(rpmfiFMode(fi)) ? NULL : tid;
907         if (action != FA_TOUCH) {
908             fpath = fsmFsPath(fi, suffix);
909         } else {
910             fpath = fsmFsPath(fi, "");
911         }
912
913         /* Remap file perms, owner, and group. */
914         rc = rpmfiStat(fi, 1, &sb);
915
916         fsmDebug(fpath, action, &sb);
917
918         /* Exit on error. */
919         if (rc)
920             break;
921                 
922         /* Run fsm init hook for all plugins */
923         //FIXME. functions related with msm.
924         rc = rpmpluginsCallFsmInit(plugins, fpath, sb.st_mode);
925
926         /* Run fsm file pre hook for all plugins */
927         rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath,
928                                       sb.st_mode, action);
929         if (rc) {
930             skip = 1;
931         } else {
932             setFileState(fs, rpmfiFX(fi));
933         }
934
935         if (!skip) {
936             int setmeta = 1;
937
938             /* Directories replacing something need early backup */
939             if (!suffix) {
940                 rc = fsmBackup(fi, action);
941             }
942             /* Assume file does't exist when tmp suffix is in use */
943             if (!suffix) {
944                 rc = fsmVerify(fpath, fi, &sb);
945             } else {
946                 rc = (action == FA_TOUCH) ? 0 : RPMERR_ENOENT;
947             }
948
949             if (S_ISREG(sb.st_mode)) {
950                 if (rc == RPMERR_ENOENT) {
951                     rc = fsmMkfile(fi, fpath, files, psm, nodigest,
952                                    &setmeta, &firsthardlink, &firstlinkfile);
953                 }
954             } else if (S_ISDIR(sb.st_mode)) {
955                 if (rc == RPMERR_ENOENT) {
956                     mode_t mode = sb.st_mode;
957                     mode &= ~07777;
958                     mode |=  00700;
959                     rc = fsmMkdir(fpath, mode);
960                 }
961             } else if (S_ISLNK(sb.st_mode)) {
962                 if (rc == RPMERR_ENOENT) {
963                     rc = fsmSymlink(rpmfiFLink(fi), fpath);
964                 }
965             } else if (S_ISFIFO(sb.st_mode)) {
966                 /* This mimics cpio S_ISSOCK() behavior but probably isn't right */
967                 if (rc == RPMERR_ENOENT) {
968                     rc = fsmMkfifo(fpath, 0000);
969                 }
970             } else if (S_ISCHR(sb.st_mode) ||
971                        S_ISBLK(sb.st_mode) ||
972                        S_ISSOCK(sb.st_mode))
973             {
974                 if (rc == RPMERR_ENOENT) {
975                     rc = fsmMknod(fpath, sb.st_mode, sb.st_rdev);
976                 }
977             } else {
978                 /* XXX Special case /dev/log, which shouldn't be packaged anyways */
979                 if (!IS_DEV_LOG(fpath))
980                     rc = RPMERR_UNKNOWN_FILETYPE;
981             }
982             /* Set permissions, timestamps etc for non-hardlink entries */
983             if (!rc && setmeta) {
984                 rc = fsmSetmeta(fpath, fi, plugins, action, &sb, nofcaps);
985             }
986         } else if (firsthardlink >= 0 && rpmfiArchiveHasContent(fi)) {
987             /* we skip the hard linked file containing the content */
988             /* write the content to the first used instead */
989             char *fn = rpmfilesFN(files, firsthardlink);
990             rc = rpmfiArchiveReadToFilePsm(fi, firstlinkfile, nodigest, psm);
991             wfd_close(&firstlinkfile);
992             firsthardlink = -1;
993             free(fn);
994         }
995
996         if (rc) {
997             if (!skip) {
998                 /* XXX only erase if temp fn w suffix is in use */
999                 if (suffix && (action != FA_TOUCH)) {
1000                     (void) fsmRemove(fpath, sb.st_mode);
1001                 }
1002                 errno = saveerrno;
1003             }
1004         } else {
1005             /* Notify on success. */
1006             rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmfiArchiveTell(fi));
1007
1008             if (!skip) {
1009                 /* Backup file if needed. Directories are handled earlier */
1010                 if (suffix)
1011                     rc = fsmBackup(fi, action);
1012
1013                 if (!rc)
1014                     rc = fsmCommit(&fpath, fi, action, suffix, plugins);
1015             }
1016         }
1017
1018         if (rc)
1019             *failedFile = xstrdup(fpath);
1020
1021         /* Run fsm file post hook for all plugins */
1022         rpmpluginsCallFsmFilePost(plugins, fi, fpath,
1023                                   sb.st_mode, action, rc);
1024         fpath = _free(fpath);
1025     }
1026
1027     rpmswAdd(rpmtsOp(ts, RPMTS_OP_UNCOMPRESS), fdOp(payload, FDSTAT_READ));
1028     rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST), fdOp(payload, FDSTAT_DIGEST));
1029
1030 exit:
1031
1032     /* No need to bother with close errors on read */
1033     rpmfiArchiveClose(fi);
1034     rpmfiFree(fi);
1035     Fclose(payload);
1036     free(tid);
1037     free(fpath);
1038
1039     return rc;
1040 }
1041
1042
1043 int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfiles files,
1044               rpmpsm psm, char ** failedFile)
1045 {
1046     rpmfi fi = rpmfilesIter(files, RPMFI_ITER_BACK);
1047     rpmfs fs = rpmteGetFileStates(te);
1048     rpmPlugins plugins = rpmtsPlugins(ts);
1049     struct stat sb;
1050     int rc = 0;
1051     char *fpath = NULL;
1052
1053     while (!rc && rpmfiNext(fi) >= 0) {
1054         rpmFileAction action = rpmfsGetAction(fs, rpmfiFX(fi));
1055         fpath = fsmFsPath(fi, NULL);
1056         rc = fsmStat(fpath, 1, &sb);
1057
1058         fsmDebug(fpath, action, &sb);
1059
1060         /* Run fsm file pre hook for all plugins */
1061         rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath,
1062                                       sb.st_mode, action);
1063
1064         if (!XFA_SKIPPING(action))
1065             rc = fsmBackup(fi, action);
1066
1067         /* Remove erased files. */
1068         if (action == FA_ERASE) {
1069             int missingok = (rpmfiFFlags(fi) & (RPMFILE_MISSINGOK | RPMFILE_GHOST));
1070
1071             rc = fsmRemove(fpath, sb.st_mode);
1072
1073             /*
1074              * Missing %ghost or %missingok entries are not errors.
1075              * XXX: Are non-existent files ever an actual error here? Afterall
1076              * that's exactly what we're trying to accomplish here,
1077              * and complaining about job already done seems like kinderkarten
1078              * level "But it was MY turn!" whining...
1079              */
1080             if (rc == RPMERR_ENOENT && missingok) {
1081                 rc = 0;
1082             }
1083
1084             /*
1085              * Dont whine on non-empty directories for now. We might be able
1086              * to track at least some of the expected failures though,
1087              * such as when we knowingly left config file backups etc behind.
1088              */
1089             if (rc == RPMERR_ENOTEMPTY) {
1090                 rc = 0;
1091             }
1092
1093             if (rc) {
1094                 int lvl = strict_erasures ? RPMLOG_ERR : RPMLOG_WARNING;
1095                 rpmlog(lvl, _("%s %s: remove failed: %s\n"),
1096                         S_ISDIR(sb.st_mode) ? _("directory") : _("file"),
1097                         fpath, strerror(errno));
1098             }
1099         }
1100
1101         /* Run fsm file post hook for all plugins */
1102         rpmpluginsCallFsmFilePost(plugins, fi, fpath,
1103                                   sb.st_mode, action, rc);
1104
1105         /* XXX Failure to remove is not (yet) cause for failure. */
1106         if (!strict_erasures) rc = 0;
1107
1108         if (rc)
1109             *failedFile = xstrdup(fpath);
1110
1111         if (rc == 0) {
1112             /* Notify on success. */
1113             /* On erase we're iterating backwards, fixup for progress */
1114             rpm_loff_t amount = rpmfiFC(fi) - rpmfiFX(fi);
1115             rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, amount);
1116         }
1117         fpath = _free(fpath);
1118     }
1119
1120     free(fpath);
1121     rpmfiFree(fi);
1122
1123     return rc;
1124 }
1125
1126