3 * File state machine to handle a payload from a package.
11 #include <sys/capability.h>
14 #include <rpm/rpmte.h>
15 #include <rpm/rpmts.h>
16 #include <rpm/rpmlog.h>
17 #include <rpm/rpmmacro.h>
19 #include "rpmio/rpmio_internal.h" /* fdInit/FiniDigest */
21 #include "lib/rpmte_internal.h" /* XXX rpmfs */
22 #include "lib/rpmplugins.h" /* rpm plugins hooks */
23 #include "lib/rpmug.h"
28 int _fsm_debug = _FSM_DEBUG;
30 /* XXX Failure to remove is not (yet) cause for failure. */
31 static int strict_erasures = 0;
33 #define SUFFIX_RPMORIG ".rpmorig"
34 #define SUFFIX_RPMSAVE ".rpmsave"
35 #define SUFFIX_RPMNEW ".rpmnew"
37 /* Default directory and file permissions if not mapped */
38 #define _dirPerms 0755
39 #define _filePerms 0644
42 * XXX Forward declarations for previously exported functions to avoid moving
43 * things around needlessly
45 static const char * fileActionString(rpmFileAction a);
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)
53 static char * fsmFsPath(rpmfi fi, const char * suffix)
55 return rstrscat(NULL, rpmfiDN(fi), rpmfiBN(fi), suffix ? suffix : "", NULL);
59 * Directory name iterator.
61 typedef struct dnli_s {
70 * Destroy directory name iterator.
71 * @param dnli directory name iterator
74 static DNLI_t dnlFreeIterator(DNLI_t dnli)
77 if (dnli->active) free(dnli->active);
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
90 static DNLI_t dnlInitIterator(rpmfiles fi, rpmfs fs, int reverse)
99 dnli = xcalloc(1, sizeof(*dnli));
101 dnli->reverse = reverse;
102 dnli->i = (reverse ? dc : 0);
105 dnli->active = xcalloc(dc, sizeof(*dnli->active));
106 int fc = rpmfilesFC(fi);
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;
113 /* Exclude parent directories that are explicitly included. */
114 for (i = 0; i < fc; i++) {
118 if (!S_ISDIR(rpmfilesFMode(fi, i)))
121 dil = rpmfilesDI(fi, i);
122 dnlen = strlen(rpmfilesDN(fi, dil));
123 bnlen = strlen(rpmfilesBN(fi, i));
125 for (j = 0; j < dc; j++) {
129 if (!dnli->active[j] || j == dil)
131 dnl = rpmfilesDN(fi, j);
133 if (jlen != (dnlen+bnlen+1))
135 if (!rstreqn(dnl, rpmfilesDN(fi, dil), dnlen))
137 if (!rstreqn(dnl+dnlen, rpmfilesBN(fi, i), bnlen))
139 if (dnl[dnlen+bnlen] != '/' || dnl[dnlen+bnlen+1] != '\0')
141 /* This directory is included in the package. */
147 /* Print only once per package. */
150 for (i = 0; i < dc; i++) {
151 if (!dnli->active[i]) continue;
155 "========== Directories not explicitly included in package:\n");
157 rpmlog(RPMLOG_DEBUG, "%10d %s\n", i, rpmfilesDN(fi, i));
160 rpmlog(RPMLOG_DEBUG, "==========\n");
167 * Return next directory name (from file info).
168 * @param dnli directory name iterator
169 * @return next directory name
172 const char * dnlNextIterator(DNLI_t dnli)
174 const char * dn = NULL;
177 rpmfiles fi = dnli->fi;
178 int dc = rpmfilesDC(fi);
183 i = (!dnli->reverse ? dnli->i++ : --dnli->i);
184 } while (i >= 0 && i < dc && !dnli->active[i]);
186 if (i >= 0 && i < dc)
187 dn = rpmfilesDN(fi, i);
195 static int fsmSetFCaps(const char *path, const char *captxt)
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;
210 static void wfd_close(FD_t *wfdp)
214 static int oneshot = 0;
215 static int flush_io = 0;
217 flush_io = rpmExpandNumeric("%{?_flush_io}");
221 int fdno = Fileno(*wfdp);
230 static int wfd_open(FD_t *wfdp, const char *dest)
233 /* Create the file with 0200 permissions (write by owner). */
235 mode_t old_umask = umask(0577);
236 *wfdp = Fopen(dest, "wx.ufdio");
240 rc = RPMERR_OPEN_FAILED;
252 * Create file from payload stream.
253 * @return 0 on success
255 static int expandRegular(rpmfi fi, const char *dest, rpmpsm psm, int nodigest)
260 rc = wfd_open(&wfd, dest);
264 rc = rpmfiArchiveReadToFilePsm(fi, wfd, nodigest, psm);
270 static int fsmMkfile(rpmfi fi, const char *dest, rpmfiles files,
271 rpmpsm psm, int nodigest, int *setmeta,
272 int * firsthardlink, FD_t *firstlinkfile)
275 int numHardlinks = rpmfiFNlink(fi);
277 if (numHardlinks > 1) {
278 /* Create first hardlinked file empty */
279 if (*firsthardlink < 0) {
280 *firsthardlink = rpmfiFX(fi);
281 rc = wfd_open(firstlinkfile, dest);
283 /* Create hard links for others */
284 char *fn = rpmfilesFN(files, *firsthardlink);
287 rc = RPMERR_LINK_FAILED;
292 /* Write normal files or fill the last hardlinked (already
293 existing) file with content */
294 if (numHardlinks<=1) {
296 rc = expandRegular(fi, dest, psm, nodigest);
297 } else if (rpmfiArchiveHasContent(fi)) {
299 rc = rpmfiArchiveReadToFilePsm(fi, *firstlinkfile, nodigest, psm);
300 wfd_close(firstlinkfile);
309 static int fsmReadLink(const char *path,
310 char *buf, size_t bufsize, size_t *linklen)
312 ssize_t llen = readlink(path, buf, bufsize - 1);
313 int rc = RPMERR_READLINK_FAILED;
316 rpmlog(RPMLOG_DEBUG, " %8s (%s, buf, %d) %s\n",
318 path, (int)(bufsize -1), (llen < 0 ? strerror(errno) : ""));
329 static int fsmStat(const char *path, int dolstat, struct stat *sb)
333 rc = lstat(path, sb);
337 if (_fsm_debug && rc && errno != ENOENT)
338 rpmlog(RPMLOG_DEBUG, " %8s (%s, ost) %s\n",
340 path, (rc < 0 ? strerror(errno) : ""));
342 rc = (errno == ENOENT ? RPMERR_ENOENT : RPMERR_LSTAT_FAILED);
343 /* Ensure consistent struct content on failure */
344 memset(sb, 0, sizeof(*sb));
349 static int fsmRmdir(const char *path)
351 int rc = rmdir(path);
353 rpmlog(RPMLOG_DEBUG, " %8s (%s) %s\n", __func__,
354 path, (rc < 0 ? strerror(errno) : ""));
357 case ENOENT: rc = RPMERR_ENOENT; break;
358 case ENOTEMPTY: rc = RPMERR_ENOTEMPTY; break;
359 default: rc = RPMERR_RMDIR_FAILED; break;
364 static int fsmMkdir(const char *path, mode_t mode)
366 int rc = mkdir(path, (mode & 07777));
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;
375 static int fsmMkfifo(const char *path, mode_t mode)
377 int rc = mkfifo(path, (mode & 07777));
380 rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%04o) %s\n",
381 __func__, path, (unsigned)(mode & 07777),
382 (rc < 0 ? strerror(errno) : ""));
386 rc = RPMERR_MKFIFO_FAILED;
391 static int fsmMknod(const char *path, mode_t mode, dev_t dev)
393 /* FIX: check S_IFIFO or dev != 0 */
394 int rc = mknod(path, (mode & ~07777), dev);
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) : ""));
403 rc = RPMERR_MKNOD_FAILED;
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
415 static int fsmMkdirs(rpmfiles files, rpmfs fs, rpmPlugins plugins)
417 DNLI_t dnli = dnlInitIterator(files, fs, 0);
420 int dc = rpmfilesDC(files);
428 dnlx = (dc ? xcalloc(dc, sizeof(*dnlx)) : NULL);
431 while ((dpath = dnlNextIterator(dnli)) != NULL) {
432 size_t dnlen = strlen(dpath);
433 char * te, dn[dnlen+1];
436 if (dc < 0) continue;
441 if (dnlen <= ldnlen && rstreq(dpath, ldn))
444 /* Copy as we need to modify the string */
445 (void) stpcpy(dn, dpath);
447 /* Assume '/' directory exists, "mkdir -p" for others if non-existent */
448 for (i = 1, te = dn + 1; *te != '\0'; te++, i++) {
454 /* Already validated? */
456 (ldn[i] == '/' || ldn[i] == '\0') && rstreqn(dn, ldn, i))
459 /* Move pre-existing path marker forward. */
460 dnlx[dc] = (te - dn);
464 /* Validate next component of path. */
465 rc = fsmStat(dn, 1, &sb); /* lstat */
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) {
474 mode_t mode = S_IFDIR | (_dirPerms & 07777);
475 rpmFsmOp op = (FA_CREATE|FAF_UNOWNED);
477 /* Run fsm file pre hook for all plugins */
478 rc = rpmpluginsCallFsmFilePre(plugins, NULL, dn, mode, op);
481 rc = fsmMkdir(dn, mode);
484 rc = rpmpluginsCallFsmFilePrepare(plugins, NULL, dn, dn,
488 /* Run fsm file post hook for all plugins */
489 rpmpluginsCallFsmFilePost(plugins, NULL, dn, mode, op, rc);
493 "%s directory created with perms %04o\n",
494 dn, (unsigned)(mode & 07777));
498 /* Run file closed hook for all plugins */
499 rc = rpmpluginsCallFsmCommit(plugins, dn, mode, DIR_TYPE_UNOWNED);
508 /* Save last validated path. */
509 if (ldnalloc < (dnlen + 1)) {
510 ldnalloc = dnlen + 100;
511 ldn = xrealloc(ldn, ldnalloc);
513 if (ldn != NULL) { /* XXX can't happen */
520 dnlFreeIterator(dnli);
525 static void removeSBITS(const char *path)
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);
533 if (stb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) {
534 (void) cap_set_file(path, NULL);
540 static void fsmDebug(const char *fpath, rpmFileAction action,
541 const struct stat *st)
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 : ""));
550 static int fsmSymlink(const char *opath, const char *path)
552 int rc = symlink(opath, path);
555 rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", __func__,
556 opath, path, (rc < 0 ? strerror(errno) : ""));
560 rc = RPMERR_SYMLINK_FAILED;
564 static int fsmUnlink(const char *path)
570 rpmlog(RPMLOG_DEBUG, " %8s (%s) %s\n", __func__,
571 path, (rc < 0 ? strerror(errno) : ""));
573 rc = (errno == ENOENT ? RPMERR_ENOENT : RPMERR_UNLINK_FAILED);
577 static int fsmRename(const char *opath, const char *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) {
585 rstrscat(&rmpath, path, "-RPMDELETE", NULL);
586 rc = rename(path, rmpath);
587 if (!rc) rc = rename(opath, path);
592 rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", __func__,
593 opath, path, (rc < 0 ? strerror(errno) : ""));
595 rc = (errno == EISDIR ? RPMERR_EXIST_AS_DIR : RPMERR_RENAME_FAILED);
599 static int fsmRemove(const char *path, mode_t mode)
601 return S_ISDIR(mode) ? fsmRmdir(path) : fsmUnlink(path);
604 static int fsmChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
606 int rc = S_ISLNK(mode) ? lchown(path, uid, gid) : chown(path, uid, gid);
609 if (lstat(path, &st) == 0 && st.st_uid == uid && st.st_gid == gid)
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;
620 static int fsmChmod(const char *path, mode_t mode)
622 int rc = chmod(path, (mode & 07777));
625 if (lstat(path, &st) == 0 && (st.st_mode & 07777) == (mode & 07777))
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;
636 static int fsmUtime(const char *path, mode_t mode, time_t mtime)
639 struct timeval stamps[2] = {
640 { .tv_sec = mtime, .tv_usec = 0 },
641 { .tv_sec = mtime, .tv_usec = 0 },
645 rc = lutimes(path, stamps);
648 rc = utimes(path, stamps);
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))
661 static int fsmVerify(const char *path, rpmfi fi, const struct stat *fsb)
664 int saveerrno = errno;
666 mode_t mode = rpmfiFMode(fi);
668 rc = fsmStat(path, 1, &dsb);
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? */
678 (void) fsmUnlink(rmpath);
680 rc = RPMERR_UNLINK_FAILED;
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;
691 /* Only permit directory symlinks by target owner and root */
692 if (S_ISDIR(dsb.st_mode) && (luid == 0 || luid == fsb->st_uid))
695 } else if (S_ISLNK(mode)) {
696 if (S_ISLNK(dsb.st_mode)) {
697 char buf[8 * BUFSIZ];
699 rc = fsmReadLink(path, buf, 8 * BUFSIZ, &len);
702 if (rstreq(rpmfiFLink(fi), buf)) return 0;
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;
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 */
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] == ';'))
726 /* Rename pre-existing modified or unmanaged file. */
727 static int fsmBackup(rpmfi fi, rpmFileAction action)
730 const char *suffix = NULL;
732 if (!(rpmfiFFlags(fi) & RPMFILE_GHOST)) {
735 suffix = SUFFIX_RPMSAVE;
738 suffix = SUFFIX_RPMORIG;
746 char * opath = fsmFsPath(fi, NULL);
747 char * path = fsmFsPath(fi, suffix);
748 rc = fsmRename(opath, path);
750 rpmlog(RPMLOG_WARNING, _("%s saved as %s\n"), opath, path);
758 static int fsmSetmeta(const char *path, rpmfi fi, rpmPlugins plugins,
759 rpmFileAction action, const struct stat * st,
763 const char *dest = rpmfiFN(fi);
765 if (!rc && !getuid()) {
766 rc = fsmChown(path, st->st_mode, st->st_uid, st->st_gid);
768 if (!rc && !S_ISLNK(st->st_mode)) {
769 rc = fsmChmod(path, st->st_mode);
771 /* Set file capabilities (if enabled) */
772 if (!rc && !nofcaps && S_ISREG(st->st_mode) && !getuid()) {
773 rc = fsmSetFCaps(path, rpmfiFCaps(fi));
776 rc = fsmUtime(path, st->st_mode, rpmfiFMtime(fi));
779 rc = rpmpluginsCallFsmFilePrepare(plugins, fi,
780 path, dest, st->st_mode, action);
786 static int fsmCommit(char **path, rpmfi fi, rpmFileAction action, const char *suffix, rpmPlugins plugins)
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;
794 /* Construct final destination path (nsuffix is usually NULL) */
796 dest = fsmFsPath(fi, nsuffix);
798 /* Rename temporary to final file name if needed. */
800 rc = fsmRename(*path, dest);
801 if (!rc && nsuffix) {
802 char * opath = fsmFsPath(fi, NULL);
803 rpmlog(RPMLOG_WARNING, _("%s created as %s\n"),
810 /* Call fsm commit hook for all plugins */
812 rc = rpmpluginsCallFsmCommit(plugins, *path, rpmfiFMode(fi), DIR_TYPE_NORMAL);
820 * Return formatted string representation of file disposition.
821 * @param a file disposition
822 * @return formatted string
824 static const char * fileActionString(rpmFileAction 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 "???";
842 /* Remember any non-regular file state for recording in the rpmdb */
843 static void setFileState(rpmfs fs, int i)
845 switch (rpmfsGetAction(fs, i)) {
847 rpmfsSetState(fs, i, RPMFILE_STATE_NOTINSTALLED);
849 case FA_SKIPNETSHARED:
850 rpmfsSetState(fs, i, RPMFILE_STATE_NETSHARED);
853 rpmfsSetState(fs, i, RPMFILE_STATE_WRONGCOLOR);
856 rpmfsSetState(fs, i, RPMFILE_STATE_NORMAL);
863 int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
864 rpmpsm psm, char ** failedFile)
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);
871 int saveerrno = errno;
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;
878 rpmFileAction action;
884 rc = RPMERR_BAD_MAGIC;
888 /* transaction id used for temporary path suffix while installing */
889 rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts));
891 /* Detect and create directories not explicitly in package. */
892 rc = fsmMkdirs(files, fs, plugins);
895 /* Read next payload header. */
899 if (rc == RPMERR_ITER_END)
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);
910 fpath = fsmFsPath(fi, "");
913 /* Remap file perms, owner, and group. */
914 rc = rpmfiStat(fi, 1, &sb);
916 fsmDebug(fpath, action, &sb);
922 /* Run fsm init hook for all plugins */
923 //FIXME. functions related with msm.
924 rc = rpmpluginsCallFsmInit(plugins, fpath, sb.st_mode);
926 /* Run fsm file pre hook for all plugins */
927 rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath,
932 setFileState(fs, rpmfiFX(fi));
938 /* Directories replacing something need early backup */
940 rc = fsmBackup(fi, action);
942 /* Assume file does't exist when tmp suffix is in use */
944 rc = fsmVerify(fpath, fi, &sb);
946 rc = (action == FA_TOUCH) ? 0 : RPMERR_ENOENT;
949 if (S_ISREG(sb.st_mode)) {
950 if (rc == RPMERR_ENOENT) {
951 rc = fsmMkfile(fi, fpath, files, psm, nodigest,
952 &setmeta, &firsthardlink, &firstlinkfile);
954 } else if (S_ISDIR(sb.st_mode)) {
955 if (rc == RPMERR_ENOENT) {
956 mode_t mode = sb.st_mode;
959 rc = fsmMkdir(fpath, mode);
961 } else if (S_ISLNK(sb.st_mode)) {
962 if (rc == RPMERR_ENOENT) {
963 rc = fsmSymlink(rpmfiFLink(fi), fpath);
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);
970 } else if (S_ISCHR(sb.st_mode) ||
971 S_ISBLK(sb.st_mode) ||
972 S_ISSOCK(sb.st_mode))
974 if (rc == RPMERR_ENOENT) {
975 rc = fsmMknod(fpath, sb.st_mode, sb.st_rdev);
978 /* XXX Special case /dev/log, which shouldn't be packaged anyways */
979 if (!IS_DEV_LOG(fpath))
980 rc = RPMERR_UNKNOWN_FILETYPE;
982 /* Set permissions, timestamps etc for non-hardlink entries */
983 if (!rc && setmeta) {
984 rc = fsmSetmeta(fpath, fi, plugins, action, &sb, nofcaps);
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);
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);
1005 /* Notify on success. */
1006 rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmfiArchiveTell(fi));
1009 /* Backup file if needed. Directories are handled earlier */
1011 rc = fsmBackup(fi, action);
1014 rc = fsmCommit(&fpath, fi, action, suffix, plugins);
1019 *failedFile = xstrdup(fpath);
1021 /* Run fsm file post hook for all plugins */
1022 rpmpluginsCallFsmFilePost(plugins, fi, fpath,
1023 sb.st_mode, action, rc);
1024 fpath = _free(fpath);
1027 rpmswAdd(rpmtsOp(ts, RPMTS_OP_UNCOMPRESS), fdOp(payload, FDSTAT_READ));
1028 rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST), fdOp(payload, FDSTAT_DIGEST));
1032 /* No need to bother with close errors on read */
1033 rpmfiArchiveClose(fi);
1043 int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfiles files,
1044 rpmpsm psm, char ** failedFile)
1046 rpmfi fi = rpmfilesIter(files, RPMFI_ITER_BACK);
1047 rpmfs fs = rpmteGetFileStates(te);
1048 rpmPlugins plugins = rpmtsPlugins(ts);
1053 while (!rc && rpmfiNext(fi) >= 0) {
1054 rpmFileAction action = rpmfsGetAction(fs, rpmfiFX(fi));
1055 fpath = fsmFsPath(fi, NULL);
1056 rc = fsmStat(fpath, 1, &sb);
1058 fsmDebug(fpath, action, &sb);
1060 /* Run fsm file pre hook for all plugins */
1061 rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath,
1062 sb.st_mode, action);
1064 if (!XFA_SKIPPING(action))
1065 rc = fsmBackup(fi, action);
1067 /* Remove erased files. */
1068 if (action == FA_ERASE) {
1069 int missingok = (rpmfiFFlags(fi) & (RPMFILE_MISSINGOK | RPMFILE_GHOST));
1071 rc = fsmRemove(fpath, sb.st_mode);
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...
1080 if (rc == RPMERR_ENOENT && missingok) {
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.
1089 if (rc == RPMERR_ENOTEMPTY) {
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));
1101 /* Run fsm file post hook for all plugins */
1102 rpmpluginsCallFsmFilePost(plugins, fi, fpath,
1103 sb.st_mode, action, rc);
1105 /* XXX Failure to remove is not (yet) cause for failure. */
1106 if (!strict_erasures) rc = 0;
1109 *failedFile = xstrdup(fpath);
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);
1117 fpath = _free(fpath);