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