LCOV - code coverage report
Current view: top level - basic - xattr-util.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 49 100 49.0 %
Date: 2019-08-22 15:41:25 Functions: 5 8 62.5 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : #include <fcntl.h>
       5             : #include <stdint.h>
       6             : #include <stdlib.h>
       7             : #include <string.h>
       8             : #include <sys/time.h>
       9             : #include <sys/xattr.h>
      10             : 
      11             : #include "alloc-util.h"
      12             : #include "fd-util.h"
      13             : #include "macro.h"
      14             : #include "missing.h"
      15             : #include "sparse-endian.h"
      16             : #include "stdio-util.h"
      17             : #include "string-util.h"
      18             : #include "time-util.h"
      19             : #include "xattr-util.h"
      20             : 
      21           0 : int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink) {
      22             :         char *v;
      23             :         size_t l;
      24             :         ssize_t n;
      25             : 
      26           0 :         assert(path);
      27           0 :         assert(name);
      28           0 :         assert(value);
      29             : 
      30           0 :         for (l = 100; ; l = (size_t) n + 1 /* extra byte to make sure this remains NUL suffixed */) {
      31           0 :                 v = new0(char, l);
      32           0 :                 if (!v)
      33           0 :                         return -ENOMEM;
      34             : 
      35           0 :                 if (allow_symlink)
      36           0 :                         n = lgetxattr(path, name, v, l);
      37             :                 else
      38           0 :                         n = getxattr(path, name, v, l);
      39           0 :                 if (n >= 0 && (size_t) n < l) {
      40           0 :                         *value = v;
      41           0 :                         return n;
      42             :                 }
      43             : 
      44           0 :                 free(v);
      45             : 
      46           0 :                 if (n < 0 && errno != ERANGE)
      47           0 :                         return -errno;
      48             : 
      49           0 :                 if (allow_symlink)
      50           0 :                         n = lgetxattr(path, name, NULL, 0);
      51             :                 else
      52           0 :                         n = getxattr(path, name, NULL, 0);
      53           0 :                 if (n < 0)
      54           0 :                         return -errno;
      55             :         }
      56             : }
      57             : 
      58           0 : int fgetxattr_malloc(int fd, const char *name, char **value) {
      59             :         char *v;
      60             :         size_t l;
      61             :         ssize_t n;
      62             : 
      63           0 :         assert(fd >= 0);
      64           0 :         assert(name);
      65           0 :         assert(value);
      66             : 
      67           0 :         for (l = 100;; l = (size_t) n + 1 /* extra byte to make sure this remains NUL suffixed */) {
      68           0 :                 v = new0(char, l);
      69           0 :                 if (!v)
      70           0 :                         return -ENOMEM;
      71             : 
      72           0 :                 n = fgetxattr(fd, name, v, l);
      73           0 :                 if (n >= 0 && (size_t) n < l) {
      74           0 :                         *value = v;
      75           0 :                         return n;
      76             :                 }
      77             : 
      78           0 :                 free(v);
      79             : 
      80           0 :                 if (n < 0 && errno != ERANGE)
      81           0 :                         return -errno;
      82             : 
      83           0 :                 n = fgetxattr(fd, name, NULL, 0);
      84           0 :                 if (n < 0)
      85           0 :                         return -errno;
      86             :         }
      87             : }
      88             : 
      89           4 : int fgetxattrat_fake(
      90             :                 int dirfd,
      91             :                 const char *filename,
      92             :                 const char *attribute,
      93             :                 void *value, size_t size,
      94             :                 int flags,
      95             :                 size_t *ret_size) {
      96             : 
      97             :         char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
      98           4 :         _cleanup_close_ int fd = -1;
      99             :         ssize_t l;
     100             : 
     101             :         /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */
     102             : 
     103           4 :         if (flags & ~(AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH))
     104           0 :                 return -EINVAL;
     105             : 
     106           4 :         if (isempty(filename)) {
     107           1 :                 if (!(flags & AT_EMPTY_PATH))
     108           0 :                         return -EINVAL;
     109             : 
     110           1 :                 xsprintf(fn, "/proc/self/fd/%i", dirfd);
     111             :         } else {
     112           3 :                 fd = openat(dirfd, filename, O_CLOEXEC|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0));
     113           3 :                 if (fd < 0)
     114           0 :                         return -errno;
     115             : 
     116           3 :                 xsprintf(fn, "/proc/self/fd/%i", fd);
     117             :         }
     118             : 
     119           4 :         l = getxattr(fn, attribute, value, size);
     120           4 :         if (l < 0)
     121           2 :                 return -errno;
     122             : 
     123           2 :         *ret_size = l;
     124           2 :         return 0;
     125             : }
     126             : 
     127           1 : static int parse_crtime(le64_t le, usec_t *usec) {
     128             :         uint64_t u;
     129             : 
     130           1 :         assert(usec);
     131             : 
     132           1 :         u = le64toh(le);
     133           1 :         if (IN_SET(u, 0, (uint64_t) -1))
     134           0 :                 return -EIO;
     135             : 
     136           1 :         *usec = (usec_t) u;
     137           1 :         return 0;
     138             : }
     139             : 
     140           2 : int fd_getcrtime_at(int dirfd, const char *name, usec_t *ret, int flags) {
     141             :         struct_statx sx
     142             : #if HAS_FEATURE_MEMORY_SANITIZER
     143             :                 = {}
     144             : #  warning "Explicitly initializing struct statx, to work around msan limitation. Please remove as soon as msan has been updated to not require this."
     145             : #endif
     146             :                 ;
     147             :         usec_t a, b;
     148             :         le64_t le;
     149             :         size_t n;
     150             :         int r;
     151             : 
     152           2 :         assert(ret);
     153             : 
     154           2 :         if (flags & ~(AT_EMPTY_PATH|AT_SYMLINK_NOFOLLOW))
     155           0 :                 return -EINVAL;
     156             : 
     157             :         /* So here's the deal: the creation/birth time (crtime/btime) of a file is a relatively newly supported concept
     158             :          * on Linux (or more strictly speaking: a concept that only recently got supported in the API, it was
     159             :          * implemented on various file systems on the lower level since a while, but never was accessible). However, we
     160             :          * needed a concept like that for vaccuuming algorithms and such, hence we emulated it via a user xattr for a
     161             :          * long time. Starting with Linux 4.11 there's statx() which exposes the timestamp to userspace for the first
     162             :          * time, where it is available. Thius function will read it, but it tries to keep some compatibility with older
     163             :          * systems: we try to read both the crtime/btime and the xattr, and then use whatever is older. After all the
     164             :          * concept is useful for determining how "old" a file really is, and hence using the older of the two makes
     165             :          * most sense. */
     166             : 
     167           2 :         if (statx(dirfd, strempty(name), flags|AT_STATX_DONT_SYNC, STATX_BTIME, &sx) >= 0 &&
     168           2 :             (sx.stx_mask & STATX_BTIME) &&
     169           1 :             sx.stx_btime.tv_sec != 0)
     170           2 :                 a = (usec_t) sx.stx_btime.tv_sec * USEC_PER_SEC +
     171           1 :                         (usec_t) sx.stx_btime.tv_nsec / NSEC_PER_USEC;
     172             :         else
     173           1 :                 a = USEC_INFINITY;
     174             : 
     175           2 :         r = fgetxattrat_fake(dirfd, name, "user.crtime_usec", &le, sizeof(le), flags, &n);
     176           2 :         if (r >= 0) {
     177           1 :                 if (n != sizeof(le))
     178           0 :                         r = -EIO;
     179             :                 else
     180           1 :                         r = parse_crtime(le, &b);
     181             :         }
     182           2 :         if (r < 0) {
     183           1 :                 if (a != USEC_INFINITY) {
     184           0 :                         *ret = a;
     185           0 :                         return 0;
     186             :                 }
     187             : 
     188           1 :                 return r;
     189             :         }
     190             : 
     191           1 :         if (a != USEC_INFINITY)
     192           1 :                 *ret = MIN(a, b);
     193             :         else
     194           0 :                 *ret = b;
     195             : 
     196           1 :         return 0;
     197             : }
     198             : 
     199           1 : int fd_getcrtime(int fd, usec_t *ret) {
     200           1 :         return fd_getcrtime_at(fd, NULL, ret, AT_EMPTY_PATH);
     201             : }
     202             : 
     203           0 : int path_getcrtime(const char *p, usec_t *ret) {
     204           0 :         return fd_getcrtime_at(AT_FDCWD, p, ret, 0);
     205             : }
     206             : 
     207          26 : int fd_setcrtime(int fd, usec_t usec) {
     208             :         le64_t le;
     209             : 
     210          26 :         assert(fd >= 0);
     211             : 
     212          26 :         if (IN_SET(usec, 0, USEC_INFINITY))
     213          25 :                 usec = now(CLOCK_REALTIME);
     214             : 
     215          26 :         le = htole64((uint64_t) usec);
     216          26 :         if (fsetxattr(fd, "user.crtime_usec", &le, sizeof(le), 0) < 0)
     217           1 :                 return -errno;
     218             : 
     219          25 :         return 0;
     220             : }

Generated by: LCOV version 1.14