LCOV - code coverage report
Current view: top level - basic - tmpfile-util.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 109 154 70.8 %
Date: 2019-08-22 15:41:25 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <stdio.h>
       4             : #include <sys/mman.h>
       5             : 
       6             : #include "alloc-util.h"
       7             : #include "fd-util.h"
       8             : #include "fileio.h"
       9             : #include "fs-util.h"
      10             : #include "hexdecoct.h"
      11             : #include "macro.h"
      12             : #include "memfd-util.h"
      13             : #include "missing_fcntl.h"
      14             : #include "missing_syscall.h"
      15             : #include "path-util.h"
      16             : #include "process-util.h"
      17             : #include "random-util.h"
      18             : #include "stdio-util.h"
      19             : #include "string-util.h"
      20             : #include "tmpfile-util.h"
      21             : #include "umask-util.h"
      22             : 
      23           5 : int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
      24             :         FILE *f;
      25             :         char *t;
      26             :         int r, fd;
      27             : 
      28           5 :         assert(path);
      29           5 :         assert(_f);
      30           5 :         assert(_temp_path);
      31             : 
      32           5 :         r = tempfn_xxxxxx(path, NULL, &t);
      33           5 :         if (r < 0)
      34           0 :                 return r;
      35             : 
      36           5 :         fd = mkostemp_safe(t);
      37           5 :         if (fd < 0) {
      38           0 :                 free(t);
      39           0 :                 return -errno;
      40             :         }
      41             : 
      42             :         /* This assumes that returned FILE object is short-lived and used within the same single-threaded
      43             :          * context and never shared externally, hence locking is not necessary. */
      44             : 
      45           5 :         r = fdopen_unlocked(fd, "w", &f);
      46           5 :         if (r < 0) {
      47           0 :                 unlink(t);
      48           0 :                 free(t);
      49           0 :                 safe_close(fd);
      50           0 :                 return r;
      51             :         }
      52             : 
      53           5 :         *_f = f;
      54           5 :         *_temp_path = t;
      55             : 
      56           5 :         return 0;
      57             : }
      58             : 
      59             : /* This is much like mkostemp() but is subject to umask(). */
      60         123 : int mkostemp_safe(char *pattern) {
      61         246 :         _unused_ _cleanup_umask_ mode_t u = umask(0077);
      62             :         int fd;
      63             : 
      64         123 :         assert(pattern);
      65             : 
      66         123 :         fd = mkostemp(pattern, O_CLOEXEC);
      67         123 :         if (fd < 0)
      68           0 :                 return -errno;
      69             : 
      70         123 :         return fd;
      71             : }
      72             : 
      73          29 : int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) {
      74             :         int fd;
      75             :         FILE *f;
      76             : 
      77          29 :         fd = mkostemp_safe(pattern);
      78          29 :         if (fd < 0)
      79           0 :                 return fd;
      80             : 
      81          29 :         f = fdopen(fd, mode);
      82          29 :         if (!f) {
      83           0 :                 safe_close(fd);
      84           0 :                 return -errno;
      85             :         }
      86             : 
      87          29 :         *ret_f = f;
      88          29 :         return 0;
      89             : }
      90             : 
      91           7 : int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
      92             :         const char *fn;
      93             :         char *t;
      94             : 
      95           7 :         assert(ret);
      96             : 
      97           7 :         if (isempty(p))
      98           0 :                 return -EINVAL;
      99           7 :         if (path_equal(p, "/"))
     100           0 :                 return -EINVAL;
     101             : 
     102             :         /*
     103             :          * Turns this:
     104             :          *         /foo/bar/waldo
     105             :          *
     106             :          * Into this:
     107             :          *         /foo/bar/.#<extra>waldoXXXXXX
     108             :          */
     109             : 
     110           7 :         fn = basename(p);
     111           7 :         if (!filename_is_valid(fn))
     112           0 :                 return -EINVAL;
     113             : 
     114           7 :         extra = strempty(extra);
     115             : 
     116           7 :         t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
     117           7 :         if (!t)
     118           0 :                 return -ENOMEM;
     119             : 
     120           7 :         strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX");
     121             : 
     122           7 :         *ret = path_simplify(t, false);
     123           7 :         return 0;
     124             : }
     125             : 
     126           4 : int tempfn_random(const char *p, const char *extra, char **ret) {
     127             :         const char *fn;
     128             :         char *t, *x;
     129             :         uint64_t u;
     130             :         unsigned i;
     131             : 
     132           4 :         assert(ret);
     133             : 
     134           4 :         if (isempty(p))
     135           0 :                 return -EINVAL;
     136           4 :         if (path_equal(p, "/"))
     137           0 :                 return -EINVAL;
     138             : 
     139             :         /*
     140             :          * Turns this:
     141             :          *         /foo/bar/waldo
     142             :          *
     143             :          * Into this:
     144             :          *         /foo/bar/.#<extra>waldobaa2a261115984a9
     145             :          */
     146             : 
     147           4 :         fn = basename(p);
     148           4 :         if (!filename_is_valid(fn))
     149           0 :                 return -EINVAL;
     150             : 
     151           4 :         extra = strempty(extra);
     152             : 
     153           4 :         t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
     154           4 :         if (!t)
     155           0 :                 return -ENOMEM;
     156             : 
     157           4 :         x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn);
     158             : 
     159           4 :         u = random_u64();
     160          68 :         for (i = 0; i < 16; i++) {
     161          64 :                 *(x++) = hexchar(u & 0xF);
     162          64 :                 u >>= 4;
     163             :         }
     164             : 
     165           4 :         *x = 0;
     166             : 
     167           4 :         *ret = path_simplify(t, false);
     168           4 :         return 0;
     169             : }
     170             : 
     171           3 : int tempfn_random_child(const char *p, const char *extra, char **ret) {
     172             :         char *t, *x;
     173             :         uint64_t u;
     174             :         unsigned i;
     175             :         int r;
     176             : 
     177           3 :         assert(ret);
     178             : 
     179             :         /* Turns this:
     180             :          *         /foo/bar/waldo
     181             :          * Into this:
     182             :          *         /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
     183             :          */
     184             : 
     185           3 :         if (!p) {
     186           1 :                 r = tmp_dir(&p);
     187           1 :                 if (r < 0)
     188           0 :                         return r;
     189             :         }
     190             : 
     191           3 :         extra = strempty(extra);
     192             : 
     193           3 :         t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
     194           3 :         if (!t)
     195           0 :                 return -ENOMEM;
     196             : 
     197           3 :         if (isempty(p))
     198           0 :                 x = stpcpy(stpcpy(t, ".#"), extra);
     199             :         else
     200           3 :                 x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
     201             : 
     202           3 :         u = random_u64();
     203          51 :         for (i = 0; i < 16; i++) {
     204          48 :                 *(x++) = hexchar(u & 0xF);
     205          48 :                 u >>= 4;
     206             :         }
     207             : 
     208           3 :         *x = 0;
     209             : 
     210           3 :         *ret = path_simplify(t, false);
     211           3 :         return 0;
     212             : }
     213             : 
     214           9 : int open_tmpfile_unlinkable(const char *directory, int flags) {
     215             :         char *p;
     216             :         int fd, r;
     217             : 
     218           9 :         if (!directory) {
     219           3 :                 r = tmp_dir(&directory);
     220           3 :                 if (r < 0)
     221           0 :                         return r;
     222           6 :         } else if (isempty(directory))
     223           0 :                 return -EINVAL;
     224             : 
     225             :         /* Returns an unlinked temporary file that cannot be linked into the file system anymore */
     226             : 
     227             :         /* Try O_TMPFILE first, if it is supported */
     228           9 :         fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
     229           9 :         if (fd >= 0)
     230           9 :                 return fd;
     231             : 
     232             :         /* Fall back to unguessable name + unlinking */
     233           0 :         p = strjoina(directory, "/systemd-tmp-XXXXXX");
     234             : 
     235           0 :         fd = mkostemp_safe(p);
     236           0 :         if (fd < 0)
     237           0 :                 return fd;
     238             : 
     239           0 :         (void) unlink(p);
     240             : 
     241           0 :         return fd;
     242             : }
     243             : 
     244           3 : int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
     245           3 :         _cleanup_free_ char *tmp = NULL;
     246             :         int r, fd;
     247             : 
     248           3 :         assert(target);
     249           3 :         assert(ret_path);
     250             : 
     251             :         /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
     252           3 :         assert((flags & O_EXCL) == 0);
     253             : 
     254             :         /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in
     255             :          * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
     256             :          * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
     257             : 
     258           3 :         fd = open_parent(target, O_TMPFILE|flags, 0640);
     259           3 :         if (fd >= 0) {
     260           3 :                 *ret_path = NULL;
     261           3 :                 return fd;
     262             :         }
     263             : 
     264           0 :         log_debug_errno(fd, "Failed to use O_TMPFILE for %s: %m", target);
     265             : 
     266           0 :         r = tempfn_random(target, NULL, &tmp);
     267           0 :         if (r < 0)
     268           0 :                 return r;
     269             : 
     270           0 :         fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640);
     271           0 :         if (fd < 0)
     272           0 :                 return -errno;
     273             : 
     274           0 :         *ret_path = TAKE_PTR(tmp);
     275             : 
     276           0 :         return fd;
     277             : }
     278             : 
     279           4 : int link_tmpfile(int fd, const char *path, const char *target) {
     280             :         int r;
     281             : 
     282           4 :         assert(fd >= 0);
     283           4 :         assert(target);
     284             : 
     285             :         /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd
     286             :          * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported
     287             :          * on the directory, and renameat2() is used instead.
     288             :          *
     289             :          * Note that in both cases we will not replace existing files. This is because linkat() does not support this
     290             :          * operation currently (renameat2() does), and there is no nice way to emulate this. */
     291             : 
     292           4 :         if (path) {
     293           0 :                 r = rename_noreplace(AT_FDCWD, path, AT_FDCWD, target);
     294           0 :                 if (r < 0)
     295           0 :                         return r;
     296             :         } else {
     297             :                 char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
     298             : 
     299           4 :                 xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
     300             : 
     301           4 :                 if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
     302           2 :                         return -errno;
     303             :         }
     304             : 
     305           2 :         return 0;
     306             : }
     307             : 
     308          21 : int mkdtemp_malloc(const char *template, char **ret) {
     309          21 :         _cleanup_free_ char *p = NULL;
     310             :         int r;
     311             : 
     312          21 :         assert(ret);
     313             : 
     314          21 :         if (template)
     315          19 :                 p = strdup(template);
     316             :         else {
     317             :                 const char *tmp;
     318             : 
     319           2 :                 r = tmp_dir(&tmp);
     320           2 :                 if (r < 0)
     321           0 :                         return r;
     322             : 
     323           2 :                 p = path_join(tmp, "XXXXXX");
     324             :         }
     325          21 :         if (!p)
     326           0 :                 return -ENOMEM;
     327             : 
     328          21 :         if (!mkdtemp(p))
     329           0 :                 return -errno;
     330             : 
     331          21 :         *ret = TAKE_PTR(p);
     332          21 :         return 0;
     333             : }

Generated by: LCOV version 1.14