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