LCOV - code coverage report
Current view: top level - basic - tmpfile-util.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 109 154 70.8 %
Date: 2019-08-23 13:36:53 Functions: 10 10 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 51 112 45.5 %

           Branch data     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                 :         20 : int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
      24                 :            :         FILE *f;
      25                 :            :         char *t;
      26                 :            :         int r, fd;
      27                 :            : 
      28         [ -  + ]:         20 :         assert(path);
      29         [ -  + ]:         20 :         assert(_f);
      30         [ -  + ]:         20 :         assert(_temp_path);
      31                 :            : 
      32                 :         20 :         r = tempfn_xxxxxx(path, NULL, &t);
      33         [ -  + ]:         20 :         if (r < 0)
      34                 :          0 :                 return r;
      35                 :            : 
      36                 :         20 :         fd = mkostemp_safe(t);
      37         [ -  + ]:         20 :         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                 :         20 :         r = fdopen_unlocked(fd, "w", &f);
      46         [ -  + ]:         20 :         if (r < 0) {
      47                 :          0 :                 unlink(t);
      48                 :          0 :                 free(t);
      49                 :          0 :                 safe_close(fd);
      50                 :          0 :                 return r;
      51                 :            :         }
      52                 :            : 
      53                 :         20 :         *_f = f;
      54                 :         20 :         *_temp_path = t;
      55                 :            : 
      56                 :         20 :         return 0;
      57                 :            : }
      58                 :            : 
      59                 :            : /* This is much like mkostemp() but is subject to umask(). */
      60                 :        492 : int mkostemp_safe(char *pattern) {
      61                 :        984 :         _unused_ _cleanup_umask_ mode_t u = umask(0077);
      62                 :            :         int fd;
      63                 :            : 
      64         [ -  + ]:        492 :         assert(pattern);
      65                 :            : 
      66                 :        492 :         fd = mkostemp(pattern, O_CLOEXEC);
      67         [ -  + ]:        492 :         if (fd < 0)
      68                 :          0 :                 return -errno;
      69                 :            : 
      70                 :        492 :         return fd;
      71                 :            : }
      72                 :            : 
      73                 :        116 : int fmkostemp_safe(char *pattern, const char *mode, FILE **ret_f) {
      74                 :            :         int fd;
      75                 :            :         FILE *f;
      76                 :            : 
      77                 :        116 :         fd = mkostemp_safe(pattern);
      78         [ -  + ]:        116 :         if (fd < 0)
      79                 :          0 :                 return fd;
      80                 :            : 
      81                 :        116 :         f = fdopen(fd, mode);
      82         [ -  + ]:        116 :         if (!f) {
      83                 :          0 :                 safe_close(fd);
      84                 :          0 :                 return -errno;
      85                 :            :         }
      86                 :            : 
      87                 :        116 :         *ret_f = f;
      88                 :        116 :         return 0;
      89                 :            : }
      90                 :            : 
      91                 :         28 : int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
      92                 :            :         const char *fn;
      93                 :            :         char *t;
      94                 :            : 
      95         [ -  + ]:         28 :         assert(ret);
      96                 :            : 
      97         [ -  + ]:         28 :         if (isempty(p))
      98                 :          0 :                 return -EINVAL;
      99         [ -  + ]:         28 :         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                 :         28 :         fn = basename(p);
     111         [ -  + ]:         28 :         if (!filename_is_valid(fn))
     112                 :          0 :                 return -EINVAL;
     113                 :            : 
     114                 :         28 :         extra = strempty(extra);
     115                 :            : 
     116                 :         28 :         t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
     117         [ -  + ]:         28 :         if (!t)
     118                 :          0 :                 return -ENOMEM;
     119                 :            : 
     120                 :         28 :         strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX");
     121                 :            : 
     122                 :         28 :         *ret = path_simplify(t, false);
     123                 :         28 :         return 0;
     124                 :            : }
     125                 :            : 
     126                 :         16 : 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         [ -  + ]:         16 :         assert(ret);
     133                 :            : 
     134         [ -  + ]:         16 :         if (isempty(p))
     135                 :          0 :                 return -EINVAL;
     136         [ -  + ]:         16 :         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                 :         16 :         fn = basename(p);
     148         [ -  + ]:         16 :         if (!filename_is_valid(fn))
     149                 :          0 :                 return -EINVAL;
     150                 :            : 
     151                 :         16 :         extra = strempty(extra);
     152                 :            : 
     153                 :         16 :         t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
     154         [ -  + ]:         16 :         if (!t)
     155                 :          0 :                 return -ENOMEM;
     156                 :            : 
     157                 :         16 :         x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn);
     158                 :            : 
     159                 :         16 :         u = random_u64();
     160         [ +  + ]:        272 :         for (i = 0; i < 16; i++) {
     161                 :        256 :                 *(x++) = hexchar(u & 0xF);
     162                 :        256 :                 u >>= 4;
     163                 :            :         }
     164                 :            : 
     165                 :         16 :         *x = 0;
     166                 :            : 
     167                 :         16 :         *ret = path_simplify(t, false);
     168                 :         16 :         return 0;
     169                 :            : }
     170                 :            : 
     171                 :         12 : 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         [ -  + ]:         12 :         assert(ret);
     178                 :            : 
     179                 :            :         /* Turns this:
     180                 :            :          *         /foo/bar/waldo
     181                 :            :          * Into this:
     182                 :            :          *         /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
     183                 :            :          */
     184                 :            : 
     185         [ +  + ]:         12 :         if (!p) {
     186                 :          4 :                 r = tmp_dir(&p);
     187         [ -  + ]:          4 :                 if (r < 0)
     188                 :          0 :                         return r;
     189                 :            :         }
     190                 :            : 
     191                 :         12 :         extra = strempty(extra);
     192                 :            : 
     193                 :         12 :         t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
     194         [ -  + ]:         12 :         if (!t)
     195                 :          0 :                 return -ENOMEM;
     196                 :            : 
     197         [ -  + ]:         12 :         if (isempty(p))
     198                 :          0 :                 x = stpcpy(stpcpy(t, ".#"), extra);
     199                 :            :         else
     200                 :         12 :                 x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
     201                 :            : 
     202                 :         12 :         u = random_u64();
     203         [ +  + ]:        204 :         for (i = 0; i < 16; i++) {
     204                 :        192 :                 *(x++) = hexchar(u & 0xF);
     205                 :        192 :                 u >>= 4;
     206                 :            :         }
     207                 :            : 
     208                 :         12 :         *x = 0;
     209                 :            : 
     210                 :         12 :         *ret = path_simplify(t, false);
     211                 :         12 :         return 0;
     212                 :            : }
     213                 :            : 
     214                 :         36 : int open_tmpfile_unlinkable(const char *directory, int flags) {
     215                 :            :         char *p;
     216                 :            :         int fd, r;
     217                 :            : 
     218         [ +  + ]:         36 :         if (!directory) {
     219                 :         12 :                 r = tmp_dir(&directory);
     220         [ -  + ]:         12 :                 if (r < 0)
     221                 :          0 :                         return r;
     222         [ -  + ]:         24 :         } 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                 :         36 :         fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
     229         [ +  - ]:         36 :         if (fd >= 0)
     230                 :         36 :                 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                 :         12 : int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
     245                 :         12 :         _cleanup_free_ char *tmp = NULL;
     246                 :            :         int r, fd;
     247                 :            : 
     248         [ -  + ]:         12 :         assert(target);
     249         [ -  + ]:         12 :         assert(ret_path);
     250                 :            : 
     251                 :            :         /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
     252         [ -  + ]:         12 :         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                 :         12 :         fd = open_parent(target, O_TMPFILE|flags, 0640);
     259         [ +  - ]:         12 :         if (fd >= 0) {
     260                 :         12 :                 *ret_path = NULL;
     261                 :         12 :                 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                 :         16 : int link_tmpfile(int fd, const char *path, const char *target) {
     280                 :            :         int r;
     281                 :            : 
     282         [ -  + ]:         16 :         assert(fd >= 0);
     283         [ -  + ]:         16 :         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         [ -  + ]:         16 :         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         [ -  + ]:         16 :                 xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
     300                 :            : 
     301         [ +  + ]:         16 :                 if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
     302                 :          8 :                         return -errno;
     303                 :            :         }
     304                 :            : 
     305                 :          8 :         return 0;
     306                 :            : }
     307                 :            : 
     308                 :         84 : int mkdtemp_malloc(const char *template, char **ret) {
     309                 :         84 :         _cleanup_free_ char *p = NULL;
     310                 :            :         int r;
     311                 :            : 
     312         [ -  + ]:         84 :         assert(ret);
     313                 :            : 
     314         [ +  + ]:         84 :         if (template)
     315                 :         76 :                 p = strdup(template);
     316                 :            :         else {
     317                 :            :                 const char *tmp;
     318                 :            : 
     319                 :          8 :                 r = tmp_dir(&tmp);
     320         [ -  + ]:          8 :                 if (r < 0)
     321                 :          0 :                         return r;
     322                 :            : 
     323                 :          8 :                 p = path_join(tmp, "XXXXXX");
     324                 :            :         }
     325         [ -  + ]:         84 :         if (!p)
     326                 :          0 :                 return -ENOMEM;
     327                 :            : 
     328         [ -  + ]:         84 :         if (!mkdtemp(p))
     329                 :          0 :                 return -errno;
     330                 :            : 
     331                 :         84 :         *ret = TAKE_PTR(p);
     332                 :         84 :         return 0;
     333                 :            : }

Generated by: LCOV version 1.14