LCOV - code coverage report
Current view: top level - cmdline - unix.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 635 748 84.9 %
Date: 2026-04-29 15:04:44 Functions: 46 49 93.9 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-3.0-or-later
       2             : // Copyright (C) 2013 Andrea Mazzoleni
       3             : 
       4             : #include "portable.h"
       5             : 
       6             : #ifndef __MINGW32__ /* Only for Unix */
       7             : 
       8             : #include "support.h"
       9             : 
      10             : /**
      11             :  * Exit codes.
      12             :  */
      13             : int exit_success = 0;
      14             : int exit_failure = 1;
      15             : int exit_sync_needed = 2;
      16             : 
      17             : #define BTRFS_SUPER_MAGIC 0x9123683E
      18             : 
      19             : #if HAVE_POSIX_FADVISE
      20             : /**
      21             :  * Wrapper around posix_fadvise() that handles ENOENT by converting it to
      22             :  * ENOSYS.
      23             :  *
      24             :  * Why can posix_fadvise() return ENOENT?
      25             :  *
      26             :  * According to POSIX and the Linux kernel's standard fadvise64 implementation
      27             :  * (mm/fadvise.c), posix_fadvise() should only return EBADF (bad file
      28             :  * descriptor), ESPIPE (fd refers to a pipe or FIFO), or EINVAL (invalid
      29             :  * advice value or arguments). ENOENT is never generated by the kernel's
      30             :  * generic path.
      31             :  *
      32             :  * However, the VFS layer allows individual filesystems to provide their own
      33             :  * .fadvise() method, which is called instead of (or in addition to) the
      34             :  * generic implementation. Certain filesystem drivers can return ENOENT in
      35             :  * this method for reasons that are completely unrelated to the file not
      36             :  * existing (the file is clearly open, we have a valid fd):
      37             :  *
      38             :  *  - NFS: A stale NFS inode can be revalidated against the server while
      39             :  *    fadvise is being processed. If the server-side file has been removed or
      40             :  *    renamed, the revalidation discovers the inode is gone and returns
      41             :  *    -ENOENT, even though the client still holds an open file descriptor.
      42             :  *
      43             :  *  - FUSE-based filesystems (mergerfs, sshfs, rclone, etc.): The FUSE daemon
      44             :  *    receives the fadvise request and may forward it to an underlying backend.
      45             :  *    If that backend cannot find the file (e.g., the backing path has changed,
      46             :  *    the FUSE daemon is partially initialised, or the daemon translates the
      47             :  *    request and the target layer returns an error), the daemon may return
      48             :  *    ENOENT to the kernel.
      49             :  *
      50             :  *  - Overlayfs (used by Docker, Podman, LXC): The overlayfs .fadvise()
      51             :  *    implementation delegates to the lower or upper layer. If the lookup
      52             :  *    through the overlay stack fails to find the real inode (e.g., the upper
      53             :  *    directory entry is absent and the lower copy is in a weird state), the
      54             :  *    delegation can return ENOENT.
      55             :  *
      56             :  *  - Other network or virtual filesystems: Any out-of-tree or in-tree
      57             :  *    filesystem that implements .fadvise() and performs a path or inode
      58             :  *    lookup as part of processing the hint can propagate ENOENT if that
      59             :  *    internal lookup fails.
      60             :  *
      61             :  * Because posix_fadvise() is always an advisory hint, it never affects
      62             :  * correctness, only performance, it is safe to treat unexpected errors,
      63             :  * including ENOENT, as "not supported" (ENOSYS) and continue.
      64             :  */
      65      121094 : int posix_fadvise_wrapper(int fd, off_t offset, off_t len, int advice)
      66             : {
      67      121094 :         int ret = posix_fadvise(fd, offset, len, advice);
      68             : 
      69      121094 :         if (ret == ENOENT)
      70           0 :                 return ENOSYS;
      71             : 
      72      121094 :         return ret;
      73             : }
      74             : #endif
      75             : 
      76     1830392 : int open_noatime(const char* file, int flags)
      77             : {
      78             : #ifdef O_NOATIME
      79     1830392 :         int f = open(file, flags | O_NOATIME);
      80             : 
      81             :         /* only root is allowed to use O_NOATIME, in case retry without it */
      82     1830392 :         if (f == -1 && errno == EPERM)
      83           0 :                 f = open(file, flags);
      84     1830392 :         return f;
      85             : #else
      86             :         return open(file, flags);
      87             : #endif
      88             : }
      89             : 
      90           0 : int dirent_hidden(struct dirent* dd)
      91             : {
      92           0 :         return dd->d_name[0] == '.';
      93             : }
      94             : 
      95           0 : const char* stat_desc(struct stat* st)
      96             : {
      97           0 :         if (S_ISREG(st->st_mode))
      98           0 :                 return "regular";
      99           0 :         if (S_ISDIR(st->st_mode))
     100           0 :                 return "directory";
     101           0 :         if (S_ISCHR(st->st_mode))
     102           0 :                 return "character";
     103           0 :         if (S_ISBLK(st->st_mode))
     104           0 :                 return "block-device";
     105           0 :         if (S_ISFIFO(st->st_mode))
     106           0 :                 return "fifo";
     107           0 :         if (S_ISLNK(st->st_mode))
     108           0 :                 return "symlink";
     109           0 :         if (S_ISSOCK(st->st_mode))
     110           0 :                 return "socket";
     111           0 :         return "unknown";
     112             : }
     113             : 
     114             : static const char* smartctl_paths[] = {
     115             :         /* Linux & BSD */
     116             :         "/usr/sbin/smartctl",
     117             :         "/sbin/smartctl",
     118             :         "/usr/local/sbin/smartctl",
     119             :         "/usr/bin/smartctl",
     120             :         "/usr/local/bin/smartctl",
     121             :         /* macOS (Intel & Apple Silicon) */
     122             :         "/opt/homebrew/sbin/smartctl",
     123             :         0
     124             : };
     125             : 
     126          83 : const char* find_smartctl(void)
     127             : {
     128             :         int i;
     129             : 
     130          83 :         for (i = 0; smartctl_paths[i]; ++i) {
     131          83 :                 if (access(smartctl_paths[i], X_OK) == 0)
     132          83 :                         return smartctl_paths[i];
     133             :         }
     134             : 
     135           0 :         return 0;
     136             : }
     137             : 
     138             : /**
     139             :  * Read a file from sys
     140             :  *
     141             :  * Return -1 on error, otherwise the size of data read
     142             :  */
     143             : #if HAVE_LINUX_DEVICE
     144        2172 : static int sysread(const char* path, char* buf, size_t buf_size)
     145             : {
     146             :         int f;
     147             :         int ret;
     148             :         int len;
     149             : 
     150        2172 :         f = open(path, O_RDONLY);
     151        2172 :         if (f == -1) {
     152             :                 /* LCOV_EXCL_START */
     153             :                 return -1;
     154             :                 /* LCOV_EXCL_STOP */
     155             :         }
     156             : 
     157        1220 :         len = read(f, buf, buf_size);
     158        1220 :         if (len < 0) {
     159             :                 /* LCOV_EXCL_START */
     160             :                 close(f);
     161             :                 return -1;
     162             :                 /* LCOV_EXCL_STOP */
     163             :         }
     164             : 
     165        1220 :         ret = close(f);
     166        1220 :         if (ret != 0) {
     167             :                 /* LCOV_EXCL_START */
     168             :                 return -1;
     169             :                 /* LCOV_EXCL_STOP */
     170             :         }
     171             : 
     172        1220 :         return len;
     173             : }
     174             : #endif
     175             : 
     176             : #if HAVE_LINUX_DEVICE
     177             : /*
     178             :  * sysread_vpd_pg83 — parse raw binary VPD page 0x83 (Device Identification)
     179             :  * from a sysfs vpd_pg83 file and extract the first NAA (Network Address
     180             :  * Authority) designator for the Logical Unit (ASSOCIATION == 0x00).
     181             :  */
     182         200 : static int sysattr_vpd_pg80(const char* path, char* dst, size_t dst_size)
     183             : {
     184             :         unsigned char buf[512];
     185         200 :         int ret = sysread(path, (char*)buf, sizeof(buf));
     186             : 
     187             :         /* need at least the header */
     188         200 :         if (ret < 4) {
     189             :                 /* LCOV_EXCL_START */
     190             :                 return -1;
     191             :                 /* LCOV_EXCL_STOP */
     192             :         }
     193             : 
     194             :         /* validate page code */
     195         200 :         if (buf[1] != 0x80) {
     196             :                 /* LCOV_EXCL_START */
     197             :                 return -1;
     198             :                 /* LCOV_EXCL_STOP */
     199             :         }
     200             : 
     201             :         /* clamp to what was actually read */
     202         200 :         size_t page_len = buf[3];
     203         200 :         size_t available = (size_t)ret - 4;
     204         200 :         if (available < page_len)
     205           0 :                 page_len = available;
     206             : 
     207             :         /* if empty */
     208         200 :         if (page_len == 0) {
     209             :                 /* LCOV_EXCL_START */
     210             :                 return -1;
     211             :                 /* LCOV_EXCL_STOP */
     212             :         }
     213             : 
     214             :         /* if too bit to store the 0 termination */
     215         200 :         if (4 + page_len + 1 > sizeof(buf)) {
     216             :                 /* LCOV_EXCL_START */
     217             :                 return -1;
     218             :                 /* LCOV_EXCL_STOP */
     219             :         }
     220             : 
     221         200 :         char* attr = (char*)buf + 4;
     222         200 :         attr[page_len] = 0;
     223         200 :         strtrim(attr);
     224             : 
     225             :         /* if empty */
     226         200 :         if (!*attr) {
     227             :                 /* LCOV_EXCL_START */
     228             :                 return -1;
     229             :                 /* LCOV_EXCL_STOP */
     230             :         }
     231             : 
     232         200 :         pathcpy(dst, dst_size, attr);
     233         200 :         return 0;
     234             : }
     235             : #endif
     236             : 
     237             : /*
     238             :  * sysread_vpd_pg83 — parse raw binary VPD page 0x83 (Device Identification)
     239             :  * from a sysfs vpd_pg83 file and extract the first NAA (Network Address
     240             :  * Authority) designator for the Logical Unit (ASSOCIATION == 0x00).
     241             :  *
     242             :  * The result is written into dst as a lowercase hex string,
     243             :  * e.g.: "5000c500abcdef01"   (8-byte / NAA type 1,2,3,5)
     244             :  *       "6000c500abcdef010000000000000000"  (16-byte / NAA type 6)
     245             :  *
     246             :  * VPD page 0x83 wire format (SPC-4 §7.7.2)
     247             :  *
     248             :  *  Page header — 4 bytes:
     249             :  *    [0]  peripheral qualifier (7:5) | device type (4:0)
     250             :  *    [1]  page code  = 0x83
     251             :  *    [2]  PAGE LENGTH MSB  }  16-bit big-endian: number of bytes that follow
     252             :  *    [3]  PAGE LENGTH LSB  }  the header (i.e. total descriptor bytes)
     253             :  *
     254             :  *  Then a sequence of Designation Descriptors:
     255             :  *    [+0]  PROTOCOL ID (7:4) | CODE SET (3:0)
     256             :  *              0x1 = binary, 0x2 = ASCII, 0x3 = UTF-8
     257             :  *    [+1]  PIV (7) | reserved (6) | ASSOCIATION (5:4) | DESIGNATOR TYPE (3:0)
     258             :  *              ASSOCIATION:      0x0 = logical unit  ← we want only this
     259             :  *                                0x1 = target port
     260             :  *                                0x2 = target device
     261             :  *              DESIGNATOR TYPE:  0x3 = NAA  ← we want only this
     262             :  *    [+2]  reserved
     263             :  *    [+3]  DESIGNATOR LENGTH = N  (bytes that follow in this descriptor)
     264             :  *    [+4 … +4+N-1]  DESIGNATOR (binary for NAA)
     265             :  *
     266             :  * ── NAA subtypes (top nibble of designator byte 0) ───────────────────────
     267             :  *
     268             :  *    0x1  NAA IEEE Extended          →  8 bytes
     269             :  *    0x2  NAA Locally Assigned       →  8 bytes
     270             :  *    0x3  NAA IEEE Registered        →  8 bytes   (most common on SATA/SAS)
     271             :  *    0x5  NAA IEEE Registered        →  8 bytes
     272             :  *    0x6  NAA IEEE Registered Ext.   → 16 bytes
     273             :  *    others: reserved
     274             :  *
     275             :  * The DESIGNATOR LENGTH from the descriptor header must agree with the
     276             :  * expected length for the NAA subtype.  If they disagree, the descriptor
     277             :  * is malformed and must be skipped.
     278             :  *
     279             :  * Returns 0 and fills dst on success, -1 if no valid NAA LU descriptor found.
     280             :  */
     281             : #if HAVE_LINUX_DEVICE
     282         188 : static int sysattr_vpd_pg83(const char* path, char* dst, size_t dst_size)
     283             : {
     284             :         unsigned char buf[4096];
     285             : 
     286         188 :         int ret = sysread(path, (char*)buf, sizeof(buf));
     287         188 :         if (ret < 4) {
     288             :                 /* LCOV_EXCL_START */
     289             :                 return -1;
     290             :                 /* LCOV_EXCL_STOP */
     291             :         }
     292             : 
     293             :         /* validate page code */
     294         188 :         if (buf[1] != 0x83) {
     295             :                 /* LCOV_EXCL_START */
     296             :                 return -1;
     297             :                 /* LCOV_EXCL_STOP */
     298             :         }
     299             : 
     300         188 :         size_t page_len = ((size_t)buf[2] << 8) | buf[3];
     301             : 
     302             :         /* clamp to what was actually read */
     303         188 :         size_t available = (size_t)ret - 4;
     304         188 :         if (page_len > available)
     305           0 :                 page_len = available;
     306             : 
     307             :         /* if empty */
     308         188 :         if (page_len == 0) {
     309             :                 /* LCOV_EXCL_START */
     310             :                 return -1;
     311             :                 /* LCOV_EXCL_STOP */
     312             :         }
     313             : 
     314             :         /*
     315             :          * Walk every Designation Descriptor looking for the first NAA descriptor
     316             :          * that is associated with the Logical Unit (ASSOCIATION == 0x00).
     317             :          *
     318             :          * We take the first one found rather than scoring: all NAA subtypes are
     319             :          * globally unique by construction; the first LU-associated NAA is the
     320             :          * canonical device WWN.
     321             :          */
     322         188 :         size_t offset = 0;
     323         552 :         while (offset + 4 <= page_len) {
     324         552 :                 const unsigned char* desc = buf + 4 + offset;
     325             : 
     326         552 :                 unsigned code_set = desc[0] & 0x0f;
     327         552 :                 unsigned assoc = (desc[1] >> 4) & 0x03;
     328         552 :                 unsigned dtype = desc[1] & 0x0f;
     329         552 :                 size_t id_len = desc[3];
     330             : 
     331             :                 /* bounds check: the full descriptor must lie within page_len */
     332         552 :                 if (offset + 4 + id_len > page_len)
     333           0 :                         break; /* malformed page — stop iterating */
     334             : 
     335             :                 /* skip anything that is not an LU-associated NAA descriptor */
     336         552 :                 if (assoc != 0x00 || dtype != 0x03)
     337         364 :                         goto next;
     338             : 
     339             :                 /*
     340             :                  * NAA is always binary (CODE SET == 0x01).
     341             :                  * Reject if the device mis-advertises it as ASCII (defensive).
     342             :                  */
     343         188 :                 if (code_set != 0x01)
     344           0 :                         goto next;
     345             : 
     346             :                 /* if empty */
     347         188 :                 if (id_len == 0)
     348           0 :                         goto next;
     349             : 
     350             :                 /*
     351             :                  * Validate NAA subtype vs. expected byte length.
     352             :                  * The NAA value is the top nibble of the first byte of the
     353             :                  * designator (NOT the descriptor header byte).
     354             :                  */
     355         188 :                 unsigned naa = (desc[4] >> 4) & 0x0f;
     356         188 :                 size_t expected = 0;
     357             : 
     358         188 :                 switch (naa) {
     359         188 :                 case 0x1 : /* IEEE Extended */
     360             :                 case 0x2 : /* lLocally Assigned */
     361             :                 case 0x3 : /* IEEE Registered */
     362             :                 case 0x5 : /* IEEE Registered */
     363         188 :                         expected = 8;
     364         188 :                         break;
     365           0 :                 case 0x6 : /* IEEE Registered Extended */
     366           0 :                         expected = 16;
     367           0 :                         break;
     368           0 :                 default :
     369           0 :                         goto next;
     370             :                 }
     371             : 
     372             :                 /* DESIGNATOR LENGTH must match the NAA subtype exactly */
     373         188 :                 if (id_len != expected)
     374           0 :                         goto next;
     375             : 
     376             :                 /*
     377             :                  * Format as "naa:<hex>".
     378             :                  * Each byte → 2 hex digits; prefix "naa:" is 4 chars; +1 for NUL.
     379             :                  * Maximum: "naa:" + 32 hex digits (16 bytes) + NUL = 37 bytes.
     380             :                  */
     381         188 :                 if (dst_size < 4 + id_len * 2 + 1)
     382           0 :                         return -1;   /* caller's buffer too small */
     383             : 
     384        1692 :                 for (size_t i = 0; i < id_len; i++)
     385        1504 :                         snprintf(dst + i * 2, 3, "%02x", desc[4 + i]);
     386         188 :                 dst[4 + id_len * 2] = '\0';
     387             : 
     388         188 :                 return 0;
     389             : 
     390         364 : next:
     391         364 :                 offset += 4 + id_len;
     392             :         }
     393             : 
     394           0 :         return -1;
     395             : }
     396             : #endif
     397             : 
     398             : /**
     399             :  * Read a file from sys.
     400             :  * Trim spaces.
     401             :  * Always put an ending 0.
     402             :  * Do not report error on reading.
     403             :  * Return an error if truncated
     404             :  *
     405             :  * Return -1 on error, 0 on success
     406             :  */
     407             : #if HAVE_LINUX_DEVICE
     408        1188 : static int sysattr(const char* path, char* buf, size_t buf_size)
     409             : {
     410             :         int len;
     411             : 
     412        1188 :         len = sysread(path, buf, buf_size);
     413        1188 :         if (len < 0) {
     414             :                 /* LCOV_EXCL_START */
     415             :                 return -1;
     416             :                 /* LCOV_EXCL_STOP */
     417             :         }
     418             : 
     419         236 :         if ((size_t)len + 1 > buf_size) {
     420             :                 /* LCOV_EXCL_START */
     421             :                 return -1;
     422             :                 /* LCOV_EXCL_STOP */
     423             :         }
     424             : 
     425         236 :         buf[len] = 0;
     426             : 
     427         236 :         strtrim(buf);
     428             : 
     429         236 :         return 0;
     430             : }
     431             : #endif
     432             : 
     433             : struct dev_struct {
     434             :         uint64_t device; /**< Device ID. */
     435             :         tommy_node node;
     436             : };
     437             : 
     438             : /**
     439             :  * Get the devices of a virtual device.
     440             :  *
     441             :  * This is intended to resolve the case of Btrfs filesystems that
     442             :  * create a virtual superblock (major==0) not backed by any low
     443             :  * level device.
     444             :  *
     445             :  * See:
     446             :  * Bug 711881 - too funny btrfs st_dev numbers
     447             :  * https://bugzilla.redhat.com/show_bug.cgi?id=711881
     448             :  */
     449             : #if HAVE_LINUX_DEVICE
     450         157 : static int devdereference(uint64_t device, const char* dir, tommy_list* devlist)
     451             : {
     452         157 :         if (major(device) == 0) {
     453             :                 struct statfs sfs;
     454             : 
     455           0 :                 int fd = open(dir, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
     456           0 :                 if (fd < 0) {
     457             :                         /* LCOV_EXCL_START */
     458             :                         return -1;
     459             :                         /* LCOV_EXCL_STOP */
     460             :                 }
     461             : 
     462           0 :                 if (fstatfs(fd, &sfs) != 0) {
     463             :                         /* LCOV_EXCL_START */
     464             :                         close(fd);
     465             :                         return -1;
     466             :                         /* LCOV_EXCL_STOP */
     467             :                 }
     468             : 
     469           0 :                 if (sfs.f_type != BTRFS_SUPER_MAGIC) {
     470             :                         /* LCOV_EXCL_START */
     471             :                         close(fd);
     472             :                         return -1;
     473             :                         /* LCOV_EXCL_STOP */
     474             :                 }
     475             : 
     476             :                 struct btrfs_ioctl_fs_info_args fs_info;
     477           0 :                 memset(&fs_info, 0, sizeof(fs_info));
     478           0 :                 if (ioctl(fd, BTRFS_IOC_FS_INFO, &fs_info) < 0) {
     479             :                         /* LCOV_EXCL_START */
     480             :                         close(fd);
     481             :                         return -1;
     482             :                         /* LCOV_EXCL_STOP */
     483             :                 }
     484             : 
     485           0 :                 if (fs_info.max_id == 0) {
     486             :                         /* LCOV_EXCL_START */
     487             :                         close(fd);
     488             :                         return -1;
     489             :                         /* LCOV_EXCL_STOP */
     490             :                 }
     491             : 
     492           0 :                 for (__u64 i = 1; i <= fs_info.max_id; ++i) {
     493             :                         struct btrfs_ioctl_dev_info_args dev_info;
     494           0 :                         memset(&dev_info, 0, sizeof(dev_info));
     495           0 :                         dev_info.devid = i;
     496             : 
     497           0 :                         if (ioctl(fd, BTRFS_IOC_DEV_INFO, &dev_info) != 0) {
     498             :                                 /* LCOV_EXCL_START */
     499             :                                 close(fd);
     500             :                                 return -1;
     501             :                                 /* LCOV_EXCL_STOP */
     502             :                         }
     503             : 
     504             :                         /* get major:minor, use stat on the path returned */
     505             :                         struct stat st;
     506           0 :                         if (stat((char*)dev_info.path, &st) != 0) {
     507             :                                 /* LCOV_EXCL_START */
     508             :                                 close(fd);
     509             :                                 return -1;
     510             :                                 /* LCOV_EXCL_STOP */
     511             :                         }
     512             : 
     513           0 :                         struct dev_struct* dev = malloc_nofail(sizeof(struct dev_struct));
     514           0 :                         dev->device = st.st_rdev;
     515           0 :                         tommy_list_insert_tail(devlist, &dev->node, dev);
     516             :                 }
     517             : 
     518           0 :                 close(fd);
     519           0 :                 return 0;
     520             :         }
     521             : 
     522             :         /* insert the device itself in the list */
     523         157 :         struct dev_struct* dev = malloc_nofail(sizeof(struct dev_struct));
     524         157 :         dev->device = device;
     525         157 :         tommy_list_insert_tail(devlist, &dev->node, dev);
     526             : 
     527         157 :         return 0;
     528             : }
     529             : #endif
     530             : 
     531             : /**
     532             :  * Read a file extracting the specified tag TAG=VALUE format.
     533             :  * Return !=0 on error.
     534             :  */
     535             : #if HAVE_LINUX_DEVICE
     536         439 : static int tagread(const char* path, const char* tag, char* value, size_t value_size)
     537             : {
     538             :         int ret;
     539             :         char buf[512];
     540             :         size_t tag_len;
     541             :         char* i;
     542             :         char* e;
     543             : 
     544         439 :         ret = sysread(path, buf, sizeof(buf));
     545         439 :         if (ret < 0) {
     546             :                 /* LCOV_EXCL_START */
     547             :                 log_fatal(errno, "Failed to read '%s'.\n", path);
     548             :                 return -1;
     549             :                 /* LCOV_EXCL_STOP */
     550             :         }
     551         439 :         if ((size_t)ret + 1 > sizeof(buf)) {
     552             :                 /* LCOV_EXCL_START */
     553             :                 log_fatal(EEXTERNAL, "Too long read '%s'.\n", path);
     554             :                 return -1;
     555             :                 /* LCOV_EXCL_STOP */
     556             :         }
     557             : 
     558             :         /* ending 0 */
     559         439 :         buf[ret] = 0;
     560             : 
     561         439 :         tag_len = strlen(tag);
     562             : 
     563        7725 :         for (i = buf; *i; ++i) {
     564        7725 :                 char* p = i;
     565             : 
     566             :                 /* start with a space */
     567        7725 :                 if (p != buf) {
     568        7286 :                         if (!isspace(*p))
     569        6408 :                                 continue;
     570         878 :                         ++p;
     571             :                 }
     572             : 
     573        1317 :                 if (strncmp(p, tag, tag_len) != 0)
     574         878 :                         continue;
     575         439 :                 p += tag_len;
     576             : 
     577             :                 /* end with a = */
     578         439 :                 if (*p != '=')
     579           0 :                         continue;
     580         439 :                 ++p;
     581             : 
     582             :                 /* found */
     583         439 :                 i = p;
     584         439 :                 break;
     585             :         }
     586         439 :         if (!*i) {
     587             :                 /* LCOV_EXCL_START */
     588             :                 log_fatal(EEXTERNAL, "Missing tag '%s' for '%s'.\n", tag, path);
     589             :                 return -1;
     590             :                 /* LCOV_EXCL_STOP */
     591             :         }
     592             : 
     593             :         /* terminate at the first space */
     594         439 :         e = i;
     595        1941 :         while (*e != 0 && !isspace(*e))
     596        1502 :                 ++e;
     597         439 :         *e = 0;
     598             : 
     599         439 :         if (!*i) {
     600             :                 /* LCOV_EXCL_START */
     601             :                 log_fatal(EEXTERNAL, "Empty tag '%s' for '%s'.\n", tag, path);
     602             :                 return -1;
     603             :                 /* LCOV_EXCL_STOP */
     604             :         }
     605             : 
     606         439 :         pathprint(value, value_size, "%s", i);
     607             : 
     608         439 :         return 0;
     609             : }
     610             : #endif
     611             : 
     612             : /**
     613             :  * Get the device file from the device number.
     614             :  *
     615             :  * It uses /sys/dev/block/.../uevent.
     616             :  *
     617             :  * For null device (major==0) it fails.
     618             :  */
     619             : #if HAVE_LINUX_DEVICE
     620         439 : static int devresolve_sys(dev_t device, char* path, size_t path_size)
     621             : {
     622             :         struct stat st;
     623             :         char buf[PATH_MAX];
     624             : 
     625             :         /* default device path from device number */
     626         439 :         pathprint(path, path_size, "/sys/dev/block/%u:%u/uevent", major(device), minor(device));
     627             : 
     628         439 :         if (tagread(path, "DEVNAME", buf, sizeof(buf)) != 0) {
     629             :                 /* LCOV_EXCL_START */
     630             :                 log_tag("resolve:sys:%u:%u: failed to read DEVNAME tag '%s'\n", major(device), minor(device), path);
     631             :                 return -1;
     632             :                 /* LCOV_EXCL_STOP */
     633             :         }
     634             : 
     635             :         /* set the real device path */
     636         439 :         pathprint(path, path_size, "/dev/%s", buf);
     637             : 
     638             :         /* check the device */
     639         439 :         if (stat(path, &st) != 0) {
     640             :                 /* LCOV_EXCL_START */
     641             :                 log_tag("resolve:sys:%u:%u: failed to stat '%s'\n", major(device), minor(device), path);
     642             :                 return -1;
     643             :                 /* LCOV_EXCL_STOP */
     644             :         }
     645         439 :         if (st.st_rdev != device) {
     646             :                 /* LCOV_EXCL_START */
     647             :                 log_tag("resolve:sys:%u:%u: unexpected device '%u:%u' for '%s'.\n", major(device), minor(device), major(st.st_rdev), minor(st.st_rdev), path);
     648             :                 return -1;
     649             :                 /* LCOV_EXCL_STOP */
     650             :         }
     651             : 
     652         439 :         log_tag("resolve:sys:%u:%u:%s: found\n", major(device), minor(device), path);
     653             : 
     654         439 :         return 0;
     655             : }
     656             : #endif
     657             : 
     658             : /**
     659             :  * Get the device file from the device number.
     660             :  */
     661             : #if HAVE_LINUX_DEVICE
     662         439 : static int devresolve(uint64_t device, char* path, size_t path_size)
     663             : {
     664         439 :         if (devresolve_sys(device, path, path_size) == 0)
     665         439 :                 return 0;
     666             : 
     667           0 :         return -1;
     668             : }
     669             : #endif
     670             : 
     671             : /**
     672             :  * Cache used by blkid.
     673             :  */
     674             : #if HAVE_BLKID
     675             : static blkid_cache cache = 0;
     676             : #endif
     677             : 
     678             : /**
     679             :  * Get the UUID using the /dev/disk/by-uuid/ links.
     680             :  * It doesn't require root permission, and the uuid are always updated.
     681             :  * It doesn't work with Btrfs file-systems that don't export the main UUID
     682             :  * in /dev/disk/by-uuid/.
     683             :  */
     684             : #if HAVE_LINUX_DEVICE
     685       10968 : static int devuuid_dev(uint64_t device, char* uuid, size_t uuid_size)
     686             : {
     687             :         int ret;
     688             :         DIR* d;
     689             :         struct dirent* dd;
     690             :         struct stat st;
     691             : 
     692             :         /* scan the UUID directory searching for the device */
     693       10968 :         d = opendir("/dev/disk/by-uuid");
     694       10968 :         if (!d) {
     695             :                 /* LCOV_EXCL_START */
     696             :                 log_tag("uuid:by-uuid:%u:%u: opendir(/dev/disk/by-uuid) failed, %s\n", major(device), minor(device), strerror(errno));
     697             :                 /* directory missing?, likely we are not in Linux */
     698             :                 return -1;
     699             :                 /* LCOV_EXCL_STOP */
     700             :         }
     701             : 
     702       10968 :         int dir_fd = dirfd(d);
     703       10968 :         if (dir_fd == -1) {
     704             :                 /* LCOV_EXCL_START */
     705             :                 log_tag("uuid:by-uuid:%u:%u: dirfd(/dev/disk/by-uuid) failed, %s\n", major(device), minor(device), strerror(errno));
     706             :                 return -1;
     707             :                 /* LCOV_EXCL_STOP */
     708             :         }
     709             : 
     710      196772 :         while ((dd = readdir(d)) != 0) {
     711             :                 /* skip "." and ".." files, UUIDs never start with '.' */
     712      196768 :                 if (dd->d_name[0] == '.')
     713       21936 :                         continue;
     714             : 
     715      174832 :                 ret = fstatat(dir_fd, dd->d_name, &st, 0);
     716      174832 :                 if (ret != 0) {
     717             :                         /* LCOV_EXCL_START */
     718             :                         log_tag("uuid:by-uuid:%u:%u: fstatat(%s) failed, %s\n", major(device), minor(device), dd->d_name, strerror(errno));
     719             :                         /* generic error, ignore and continue the search */
     720             :                         continue;
     721             :                         /* LCOV_EXCL_STOP */
     722             :                 }
     723             : 
     724             :                 /* if it matches, we have the uuid */
     725      174832 :                 if (S_ISBLK(st.st_mode) && st.st_rdev == (dev_t)device) {
     726             :                         char buf[PATH_MAX];
     727             :                         char path[PATH_MAX];
     728             : 
     729             :                         /* resolve the link */
     730       10964 :                         pathprint(path, sizeof(path), "/dev/disk/by-uuid/%s", dd->d_name);
     731       10964 :                         ret = readlink(path, buf, sizeof(buf));
     732       10964 :                         if (ret < 0 || ret >= PATH_MAX) {
     733             :                                 /* LCOV_EXCL_START */
     734             :                                 log_tag("uuid:by-uuid:%u:%u: readlink(/dev/disk/by-uuid/%s) failed, %s\n", major(device), minor(device), dd->d_name, strerror(errno));
     735             :                                 /* generic error, ignore and continue the search */
     736             :                                 continue;
     737             :                                 /* LCOV_EXCL_STOP */
     738             :                         }
     739       10964 :                         buf[ret] = 0;
     740             : 
     741             :                         /* found */
     742       10964 :                         pathcpy(uuid, uuid_size, dd->d_name);
     743             : 
     744       10964 :                         log_tag("uuid:by-uuid:%u:%u:%s: found %s\n", major(device), minor(device), uuid, buf);
     745             : 
     746       10964 :                         closedir(d);
     747       10964 :                         return 0;
     748             :                 }
     749             :         }
     750             : 
     751           4 :         log_tag("uuid:by-uuid:%u:%u: /dev/disk/by-uuid doesn't contain a matching block device\n", major(device), minor(device));
     752             : 
     753             :         /* not found */
     754           4 :         closedir(d);
     755           4 :         return -1;
     756             : }
     757             : #endif
     758             : 
     759             : /**
     760             :  * Get the UUID using libblkid.
     761             :  * It uses a cache to work without root permission, resulting in UUID
     762             :  * not necessarily recent.
     763             :  * We could call blkid_probe_all() to refresh the UUID, but it would
     764             :  * require root permission to read the superblocks, and resulting in
     765             :  * all the disks spinning.
     766             :  */
     767             : #if HAVE_BLKID
     768           4 : static int devuuid_blkid(uint64_t device, char* uuid, size_t uuid_size)
     769             : {
     770             :         char* devname;
     771             :         char* uuidname;
     772             : 
     773           4 :         devname = blkid_devno_to_devname(device);
     774           4 :         if (!devname) {
     775             :                 /* LCOV_EXCL_START */
     776             :                 log_tag("uuid:blkid:%u:%u: blkid_devno_to_devname() failed, %s\n", major(device), minor(device), strerror(errno));
     777             :                 /* device mapping failed */
     778             :                 return -1;
     779             :                 /* LCOV_EXCL_STOP */
     780             :         }
     781             : 
     782           0 :         uuidname = blkid_get_tag_value(cache, "UUID", devname);
     783           0 :         if (!uuidname) {
     784             :                 /* LCOV_EXCL_START */
     785             :                 log_tag("uuid:blkid:%u:%u: blkid_get_tag_value(UUID,%s) failed, %s\n", major(device), minor(device), devname, strerror(errno));
     786             :                 /* uuid mapping failed */
     787             :                 free(devname);
     788             :                 return -1;
     789             :                 /* LCOV_EXCL_STOP */
     790             :         }
     791             : 
     792           0 :         pathcpy(uuid, uuid_size, uuidname);
     793             : 
     794           0 :         log_tag("uuid:blkid:%u:%u:%s: found %s\n", major(device), minor(device), uuid, devname);
     795             : 
     796           0 :         free(devname);
     797           0 :         free(uuidname);
     798           0 :         return 0;
     799             : }
     800             : #endif
     801             : 
     802             : 
     803             : /**
     804             :  * Get the LABEL using libblkid.
     805             :  * It uses a cache to work without root permission, resulting in LABEL
     806             :  * not necessarily recent.
     807             :  */
     808             : #if HAVE_BLKID
     809        6002 : static int devuuid_label(uint64_t device, char* label, size_t label_size)
     810             : {
     811             :         char* devname;
     812             :         char* labelname;
     813             : 
     814        6002 :         devname = blkid_devno_to_devname(device);
     815        6002 :         if (!devname) {
     816             :                 /* LCOV_EXCL_START */
     817             :                 log_tag("label:blkid:%u:%u: blkid_devno_to_devname() failed, %s\n", major(device), minor(device), strerror(errno));
     818             :                 /* device mapping failed */
     819             :                 return -1;
     820             :                 /* LCOV_EXCL_STOP */
     821             :         }
     822             : 
     823        5939 :         labelname = blkid_get_tag_value(cache, "LABEL", devname);
     824        5939 :         if (!labelname) {
     825             :                 /* LCOV_EXCL_START */
     826             :                 log_tag("label:blkid:%u:%u: blkid_get_tag_value(LABEL,%s) failed, %s\n", major(device), minor(device), devname, strerror(errno));
     827             :                 free(devname);
     828             :                 /* label mapping failed */
     829             :                 return -1;
     830             :                 /* LCOV_EXCL_STOP */
     831             :         }
     832             : 
     833        5939 :         pathcpy(label, label_size, labelname);
     834             : 
     835        5939 :         log_tag("label:blkid:%u:%u:%s: found %s\n", major(device), minor(device), label, devname);
     836             : 
     837        5939 :         free(devname);
     838        5939 :         free(labelname);
     839        5939 :         return 0;
     840             : }
     841             : #endif
     842             : 
     843             : 
     844             : #ifdef __APPLE__
     845             : static int devuuid_darwin(const char* path, char* uuid, size_t uuid_size)
     846             : {
     847             :         CFStringRef path_apple = 0;
     848             :         DASessionRef session = 0;
     849             :         CFURLRef path_appler = 0;
     850             :         int result = -1;
     851             : 
     852             :         *uuid = 0;
     853             : 
     854             :         path_apple = CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8);
     855             :         if (!path_apple)
     856             :                 goto bail;
     857             : 
     858             :         session = DASessionCreate(kCFAllocatorDefault);
     859             :         if (!session)
     860             :                 goto bail;
     861             : 
     862             :         path_appler = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_apple, kCFURLPOSIXPathStyle, false);
     863             :         if (!path_appler)
     864             :                 goto bail;
     865             : 
     866             :         /* find the mount point */
     867             :         DADiskRef disk = NULL;
     868             :         while (path_appler) {
     869             :                 disk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, path_appler);
     870             :                 if (disk)
     871             :                         break;
     872             : 
     873             :                 CFURLRef parent = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, path_appler);
     874             :                 if (!parent)
     875             :                         break;
     876             : 
     877             :                 /* check if we hit the root (parent == child) */
     878             :                 if (CFEqual(parent, path_appler)) {
     879             :                         CFRelease(parent);
     880             :                         break;
     881             :                 }
     882             : 
     883             :                 CFRelease(path_appler);
     884             :                 path_appler = parent;
     885             :         }
     886             : 
     887             :         /* safely extract the UUID */
     888             :         if (disk) {
     889             :                 CFDictionaryRef description = DADiskCopyDescription(disk);
     890             :                 if (description) {
     891             :                         /* key might not exist for NTFS/ExFAT on some drivers */
     892             :                         const void* value = (CFUUIDRef)CFDictionaryGetValue(description, kDADiskDescriptionVolumeUUIDKey);
     893             :                         CFStringRef uuid_string = NULL;
     894             : 
     895             :                         if (value) {
     896             :                                 if (CFGetTypeID(value) == CFUUIDGetTypeID()) {
     897             :                                         uuid_string = CFUUIDCreateString(kCFAllocatorDefault, (CFUUIDRef)value);
     898             :                                 } else if (CFGetTypeID(value) == CFStringGetTypeID()) {
     899             :                                         /* if it's already a string, retain it so we can release it later consistently */
     900             :                                         uuid_string = (CFStringRef)CFRetain(value);
     901             :                                 }
     902             :                         }
     903             : 
     904             :                         if (uuid_string) {
     905             :                                 if (CFStringGetCString(uuid_string, uuid, uuid_size, kCFStringEncodingUTF8)) {
     906             :                                         result = 0; /* success */
     907             :                                 }
     908             :                                 CFRelease(uuid_string);
     909             :                         }
     910             : 
     911             :                         CFRelease(description);
     912             :                 }
     913             :                 CFRelease(disk);
     914             :         }
     915             : 
     916             : bail:
     917             :         /* clean up */
     918             :         if (path_appler) CFRelease(path_appler);
     919             :         if (session) CFRelease(session);
     920             :         if (path_apple) CFRelease(path_apple);
     921             : 
     922             :         return result;
     923             : }
     924             : #endif
     925             : 
     926             : #if HAVE_LINUX_DEVICE
     927       11031 : static int devuuid_btrfs(uint64_t device, const char* dir, char* uuid, size_t uuid_size)
     928             : {
     929             :         struct statfs sfs;
     930             : 
     931       11031 :         int fd = open(dir, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
     932       11031 :         if (fd < 0) {
     933             :                 /* LCOV_EXCL_START */
     934             :                 return -1;
     935             :                 /* LCOV_EXCL_STOP*/
     936             :         }
     937             : 
     938        6462 :         if (fstatfs(fd, &sfs) != 0) {
     939             :                 /* LCOV_EXCL_START */
     940             :                 close(fd);
     941             :                 return -1;
     942             :                 /* LCOV_EXCL_STOP */
     943             :         }
     944             : 
     945        6462 :         if (sfs.f_type != BTRFS_SUPER_MAGIC) {
     946        6399 :                 close(fd);
     947        6399 :                 return -1;
     948             :         }
     949             : 
     950             :         struct btrfs_ioctl_fs_info_args info;
     951          63 :         memset(&info, 0, sizeof(info));
     952          63 :         if (ioctl(fd, BTRFS_IOC_FS_INFO, &info) < 0) {
     953             :                 /* LCOV_EXCL_START */
     954             :                 close(fd);
     955             :                 return -1;
     956             :                 /* LCOV_EXCL_STOP */
     957             :         }
     958             : 
     959          63 :         snprintf(uuid, uuid_size,
     960             :                 "%02x%02x%02x%02x-"
     961             :                 "%02x%02x-"
     962             :                 "%02x%02x-"
     963             :                 "%02x%02x-"
     964             :                 "%02x%02x%02x%02x%02x%02x",
     965          63 :                 info.fsid[0], info.fsid[1], info.fsid[2], info.fsid[3],
     966          63 :                 info.fsid[4], info.fsid[5],
     967          63 :                 info.fsid[6], info.fsid[7],
     968          63 :                 info.fsid[8], info.fsid[9],
     969          63 :                 info.fsid[10], info.fsid[11], info.fsid[12], info.fsid[13], info.fsid[14], info.fsid[15]
     970             :         );
     971             : 
     972          63 :         log_tag("uuid:by-btrfs:%u:%u:%s: found %s\n", major(device), minor(device), uuid, dir);
     973             : 
     974          63 :         close(fd);
     975          63 :         return 0;
     976             : }
     977             : #endif
     978             : 
     979       11031 : int devuuid(uint64_t device_id, const char* device_path, char* uuid, size_t uuid_size)
     980             : {
     981             :         (void)device_path;
     982             :         (void)device_id;
     983             : 
     984             : #ifdef __APPLE__
     985             :         if (devuuid_darwin(device_path, uuid, uuid_size) == 0)
     986             :                 return 0;
     987             : #endif
     988             : 
     989             : #if HAVE_LINUX_DEVICE
     990       11031 :         if (devuuid_btrfs(device_id, device_path, uuid, uuid_size) == 0)
     991          63 :                 return 0;
     992             : #endif
     993             : 
     994             : #if HAVE_LINUX_DEVICE
     995       10968 :         if (devuuid_dev(device_id, uuid, uuid_size) == 0)
     996       10964 :                 return 0;
     997             : #else
     998             :         log_tag("uuid:by-uuid:%u:%u: by-uuid not supported\n", major(device_id), minor(device_id));
     999             : #endif
    1000             : 
    1001             : #if HAVE_BLKID
    1002           4 :         if (devuuid_blkid(device_id, uuid, uuid_size) == 0)
    1003           0 :                 return 0;
    1004             : #else
    1005             :         log_tag("uuid:blkid:%u:%u: blkid support not compiled in\n", major(device_id), minor(device_id));
    1006             : #endif
    1007             : 
    1008           4 :         log_tag("uuid:notfound:%u:%u:\n", major(device_id), minor(device_id));
    1009             : 
    1010             :         /* not supported */
    1011             :         (void)uuid;
    1012             :         (void)uuid_size;
    1013           4 :         return -1;
    1014             : }
    1015             : 
    1016       11432 : int filephy(const char* path, uint64_t size, uint64_t* physical)
    1017             : {
    1018             : #if HAVE_LINUX_FIEMAP_H
    1019             :         /*
    1020             :          * In Linux get the real physical address of the file
    1021             :          * Note that FIEMAP doesn't require root permission
    1022             :          */
    1023             :         int f;
    1024             :         struct fiemap* fiemap;
    1025             :         size_t fiemap_size;
    1026             :         unsigned int blknum;
    1027             : 
    1028       11432 :         f = open(path, O_RDONLY);
    1029       11432 :         if (f == -1) {
    1030             :                 /* LCOV_EXCL_START */
    1031             :                 return -1;
    1032             :                 /* LCOV_EXCL_STOP */
    1033             :         }
    1034             : 
    1035             :         /*
    1036             :          * First try with FIEMAP
    1037             :          * if works for ext2, ext3, ext4, xfs, btrfs
    1038             :          */
    1039       11432 :         fiemap_size = sizeof(struct fiemap) + sizeof(struct fiemap_extent);
    1040       11432 :         fiemap = malloc_nofail(fiemap_size);
    1041       11432 :         memset(fiemap, 0, fiemap_size);
    1042       11432 :         fiemap->fm_start = 0;
    1043       11432 :         fiemap->fm_length = ~0ULL;
    1044       11432 :         fiemap->fm_flags = FIEMAP_FLAG_SYNC; /* required to ensure that just created files report a valid address and not 0 */
    1045       11432 :         fiemap->fm_extent_count = 1; /* we are interested only at the first block */
    1046             : 
    1047       11432 :         if (ioctl(f, FS_IOC_FIEMAP, fiemap) != -1) {
    1048       11432 :                 uint32_t flags = fiemap->fm_extents[0].fe_flags;
    1049       11432 :                 uint64_t offset = fiemap->fm_extents[0].fe_physical;
    1050             : 
    1051             :                 /* check some condition for validating the offset */
    1052       11432 :                 if (flags & FIEMAP_EXTENT_DATA_INLINE) {
    1053             :                         /* if the data is inline, we don't have an offset to report */
    1054           0 :                         *physical = FILEPHY_WITHOUT_OFFSET;
    1055       11432 :                 } else if (flags & FIEMAP_EXTENT_UNKNOWN) {
    1056             :                         /* if the offset is unknown, we don't have an offset to report */
    1057           0 :                         *physical = FILEPHY_WITHOUT_OFFSET;
    1058       11432 :                 } else if (offset == 0) {
    1059             :                         /*
    1060             :                          * 0 is the general fallback for file-systems when
    1061             :                          * they don't have an offset to report
    1062             :                          */
    1063          14 :                         *physical = FILEPHY_WITHOUT_OFFSET;
    1064             :                 } else {
    1065             :                         /* finally report the real offset */
    1066       11418 :                         *physical = offset + FILEPHY_REAL_OFFSET;
    1067             :                 }
    1068             : 
    1069       11432 :                 free(fiemap);
    1070             : 
    1071       11432 :                 if (close(f) == -1)
    1072           0 :                         return -1;
    1073       11432 :                 return 0;
    1074             :         }
    1075             : 
    1076           0 :         free(fiemap);
    1077             : 
    1078             :         /* if the file is empty, FIBMAP doesn't work, and we don't even try to use it */
    1079           0 :         if (size == 0) {
    1080           0 :                 *physical = FILEPHY_WITHOUT_OFFSET;
    1081           0 :                 if (close(f) == -1)
    1082           0 :                         return -1;
    1083           0 :                 return 0;
    1084             :         }
    1085             : 
    1086             :         /*
    1087             :          * Then try with FIBMAP
    1088             :          * it works for jfs, reiserfs, ntfs-3g
    1089             :          * in exfat it always returns 0, that it's anyway better than the fake inodes
    1090             :          */
    1091           0 :         blknum = 0; /* first block */
    1092           0 :         if (ioctl(f, FIBMAP, &blknum) != -1) {
    1093           0 :                 *physical = blknum + FILEPHY_REAL_OFFSET;
    1094           0 :                 if (close(f) == -1)
    1095           0 :                         return -1;
    1096           0 :                 return 0;
    1097             :         }
    1098             : 
    1099             :         /*
    1100             :          * Otherwise don't use anything, and keep the directory traversal order
    1101             :          * at now this should happen only for vfat
    1102             :          * and it's surely better than using fake inodes
    1103             :          */
    1104           0 :         *physical = FILEPHY_UNREPORTED_OFFSET;
    1105           0 :         if (close(f) == -1)
    1106           0 :                 return -1;
    1107             : #else
    1108             :         /*
    1109             :          * In a generic Unix use a dummy value for all the files
    1110             :          * We don't want to risk to use the inode without knowing
    1111             :          * if it really improves performance.
    1112             :          * In this way we keep them in the directory traversal order
    1113             :          * that at least keeps files in the same directory together.
    1114             :          * Note also that in newer file-system with snapshot, like ZFS,
    1115             :          * the inode doesn't represent even more the disk position, because files
    1116             :          * are not overwritten in place, but rewritten in another location
    1117             :          * of the disk.
    1118             :          */
    1119             :         *physical = FILEPHY_UNREPORTED_OFFSET;
    1120             : 
    1121             :         (void)path; /* not used here */
    1122             :         (void)size;
    1123             : #endif
    1124             : 
    1125           0 :         return 0;
    1126             : }
    1127             : 
    1128             : /* from man statfs */
    1129             : #define ADFS_SUPER_MAGIC 0xadf5
    1130             : #define AFFS_SUPER_MAGIC 0xADFF
    1131             : #define BDEVFS_MAGIC 0x62646576
    1132             : #define BEFS_SUPER_MAGIC 0x42465331
    1133             : #define BFS_MAGIC 0x1BADFACE
    1134             : #define BINFMTFS_MAGIC 0x42494e4d
    1135             : #define BTRFS_SUPER_MAGIC 0x9123683E
    1136             : #define CGROUP_SUPER_MAGIC 0x27e0eb
    1137             : #define CIFS_MAGIC_NUMBER 0xFF534D42
    1138             : #define CODA_SUPER_MAGIC 0x73757245
    1139             : #define COH_SUPER_MAGIC 0x012FF7B7
    1140             : #define CRAMFS_MAGIC 0x28cd3d45
    1141             : #define DEBUGFS_MAGIC 0x64626720
    1142             : #define DEVFS_SUPER_MAGIC 0x1373
    1143             : #define DEVPTS_SUPER_MAGIC 0x1cd1
    1144             : #define EFIVARFS_MAGIC 0xde5e81e4
    1145             : #define EFS_SUPER_MAGIC 0x00414A53
    1146             : #define EXT_SUPER_MAGIC 0x137D
    1147             : #define EXT2_OLD_SUPER_MAGIC 0xEF51
    1148             : #define EXT4_SUPER_MAGIC 0xEF53 /* also ext2/ext3 */
    1149             : #define FUSE_SUPER_MAGIC 0x65735546
    1150             : #define FUTEXFS_SUPER_MAGIC 0xBAD1DEA
    1151             : #define HFS_SUPER_MAGIC 0x4244
    1152             : #define HFSPLUS_SUPER_MAGIC 0x482b
    1153             : #define HOSTFS_SUPER_MAGIC 0x00c0ffee
    1154             : #define HPFS_SUPER_MAGIC 0xF995E849
    1155             : #define HUGETLBFS_MAGIC 0x958458f6
    1156             : #define ISOFS_SUPER_MAGIC 0x9660
    1157             : #define JFFS2_SUPER_MAGIC 0x72b6
    1158             : #define JFS_SUPER_MAGIC 0x3153464a
    1159             : #define MINIX_SUPER_MAGIC 0x137F
    1160             : #define MINIX_SUPER_MAGIC2 0x138F
    1161             : #define MINIX2_SUPER_MAGIC 0x2468
    1162             : #define MINIX2_SUPER_MAGIC2 0x2478
    1163             : #define MINIX3_SUPER_MAGIC 0x4d5a
    1164             : #define MQUEUE_MAGIC 0x19800202
    1165             : #define MSDOS_SUPER_MAGIC 0x4d44
    1166             : #define NCP_SUPER_MAGIC 0x564c
    1167             : #define NFS_SUPER_MAGIC 0x6969
    1168             : #define NILFS_SUPER_MAGIC 0x3434
    1169             : #define NTFS_SB_MAGIC 0x5346544e
    1170             : #define OCFS2_SUPER_MAGIC 0x7461636f
    1171             : #define OPENPROM_SUPER_MAGIC 0x9fa1
    1172             : #define PIPEFS_MAGIC 0x50495045
    1173             : #define PROC_SUPER_MAGIC 0x9fa0
    1174             : #define PSTOREFS_MAGIC 0x6165676C
    1175             : #define QNX4_SUPER_MAGIC 0x002f
    1176             : #define QNX6_SUPER_MAGIC 0x68191122
    1177             : #define RAMFS_MAGIC 0x858458f6
    1178             : #define REISERFS_SUPER_MAGIC 0x52654973
    1179             : #define ROMFS_MAGIC 0x7275
    1180             : #define SELINUX_MAGIC 0xf97cff8c
    1181             : #define SMACK_MAGIC 0x43415d53
    1182             : #define SMB_SUPER_MAGIC 0x517B
    1183             : #define SOCKFS_MAGIC 0x534F434B
    1184             : #define SQUASHFS_MAGIC 0x73717368
    1185             : #define SYSFS_MAGIC 0x62656572
    1186             : #define SYSV2_SUPER_MAGIC 0x012FF7B6
    1187             : #define SYSV4_SUPER_MAGIC 0x012FF7B5
    1188             : #define TMPFS_MAGIC 0x01021994
    1189             : #define UDF_SUPER_MAGIC 0x15013346
    1190             : #define UFS_MAGIC 0x00011954
    1191             : #define USBDEVICE_SUPER_MAGIC 0x9fa2
    1192             : #define V9FS_MAGIC 0x01021997
    1193             : #define VXFS_SUPER_MAGIC 0xa501FCF5
    1194             : #define XENFS_SUPER_MAGIC 0xabba1974
    1195             : #define XENIX_SUPER_MAGIC 0x012FF7B4
    1196             : #define XFS_SUPER_MAGIC 0x58465342
    1197             : #define _XIAFS_SUPER_MAGIC 0x012FD16D
    1198             : #define AFS_SUPER_MAGIC 0x5346414F
    1199             : #define AUFS_SUPER_MAGIC 0x61756673
    1200             : #define ANON_INODE_FS_SUPER_MAGIC 0x09041934
    1201             : #define CEPH_SUPER_MAGIC 0x00C36400
    1202             : #define ECRYPTFS_SUPER_MAGIC 0xF15F
    1203             : #define FAT_SUPER_MAGIC 0x4006
    1204             : #define FHGFS_SUPER_MAGIC 0x19830326
    1205             : #define FUSEBLK_SUPER_MAGIC 0x65735546
    1206             : #define FUSECTL_SUPER_MAGIC 0x65735543
    1207             : #define GFS_SUPER_MAGIC 0x1161970
    1208             : #define GPFS_SUPER_MAGIC 0x47504653
    1209             : #define MTD_INODE_FS_SUPER_MAGIC 0x11307854
    1210             : #define INOTIFYFS_SUPER_MAGIC 0x2BAD1DEA
    1211             : #define ISOFS_R_WIN_SUPER_MAGIC 0x4004
    1212             : #define ISOFS_WIN_SUPER_MAGIC 0x4000
    1213             : #define JFFS_SUPER_MAGIC 0x07C0
    1214             : #define KAFS_SUPER_MAGIC 0x6B414653
    1215             : #define LUSTRE_SUPER_MAGIC 0x0BD00BD0
    1216             : #define NFSD_SUPER_MAGIC 0x6E667364
    1217             : #define PANFS_SUPER_MAGIC 0xAAD7AAEA
    1218             : #define RPC_PIPEFS_SUPER_MAGIC 0x67596969
    1219             : #define SECURITYFS_SUPER_MAGIC 0x73636673
    1220             : #define UFS_BYTESWAPPED_SUPER_MAGIC 0x54190100
    1221             : #define VMHGFS_SUPER_MAGIC 0xBACBACBC
    1222             : #define VZFS_SUPER_MAGIC 0x565A4653
    1223             : #define ZFS_SUPER_MAGIC 0x2FC12FC1
    1224             : 
    1225             : struct filesystem_entry {
    1226             :         unsigned id;
    1227             :         const char* name;
    1228             :         int remote;
    1229             : } FILESYSTEMS[] = {
    1230             :         { ADFS_SUPER_MAGIC, "adfs", 0 },
    1231             :         { AFFS_SUPER_MAGIC, "affs", 0 },
    1232             :         { AFS_SUPER_MAGIC, "afs", 1 },
    1233             :         { AUFS_SUPER_MAGIC, "aufs", 1 },
    1234             :         { BEFS_SUPER_MAGIC, "befs", 0 },
    1235             :         { BDEVFS_MAGIC, "bdevfs", 0 },
    1236             :         { BFS_MAGIC, "bfs", 0 },
    1237             :         { BINFMTFS_MAGIC, "binfmt_misc", 0 },
    1238             :         { BTRFS_SUPER_MAGIC, "btrfs", 0 },
    1239             :         { CEPH_SUPER_MAGIC, "ceph", 1 },
    1240             :         { CGROUP_SUPER_MAGIC, "cgroupfs", 0 },
    1241             :         { CIFS_MAGIC_NUMBER, "cifs", 1 },
    1242             :         { CODA_SUPER_MAGIC, "coda", 1 },
    1243             :         { COH_SUPER_MAGIC, "coh", 0 },
    1244             :         { CRAMFS_MAGIC, "cramfs", 0 },
    1245             :         { DEBUGFS_MAGIC, "debugfs", 0 },
    1246             :         { DEVFS_SUPER_MAGIC, "devfs", 0 },
    1247             :         { DEVPTS_SUPER_MAGIC, "devpts", 0 },
    1248             :         { ECRYPTFS_SUPER_MAGIC, "ecryptfs", 0 },
    1249             :         { EFS_SUPER_MAGIC, "efs", 0 },
    1250             :         { EXT_SUPER_MAGIC, "ext", 0 },
    1251             :         { EXT4_SUPER_MAGIC, "ext4", 0 },
    1252             :         { EXT2_OLD_SUPER_MAGIC, "ext2", 0 },
    1253             :         { FAT_SUPER_MAGIC, "fat", 0 },
    1254             :         { FHGFS_SUPER_MAGIC, "fhgfs", 1 },
    1255             :         { FUSEBLK_SUPER_MAGIC, "fuseblk", 1 },
    1256             :         { FUSECTL_SUPER_MAGIC, "fusectl", 1 },
    1257             :         { FUTEXFS_SUPER_MAGIC, "futexfs", 0 },
    1258             :         { GFS_SUPER_MAGIC, "gfs/gfs2", 1 },
    1259             :         { GPFS_SUPER_MAGIC, "gpfs", 1 },
    1260             :         { HFS_SUPER_MAGIC, "hfs", 0 },
    1261             :         { HFSPLUS_SUPER_MAGIC, "hfsplus", 0 },
    1262             :         { HPFS_SUPER_MAGIC, "hpfs", 0 },
    1263             :         { HUGETLBFS_MAGIC, "hugetlbfs", 0 },
    1264             :         { MTD_INODE_FS_SUPER_MAGIC, "inodefs", 0 },
    1265             :         { INOTIFYFS_SUPER_MAGIC, "inotifyfs", 0 },
    1266             :         { ISOFS_SUPER_MAGIC, "isofs", 0 },
    1267             :         { ISOFS_R_WIN_SUPER_MAGIC, "isofs", 0 },
    1268             :         { ISOFS_WIN_SUPER_MAGIC, "isofs", 0 },
    1269             :         { JFFS_SUPER_MAGIC, "jffs", 0 },
    1270             :         { JFFS2_SUPER_MAGIC, "jffs2", 0 },
    1271             :         { JFS_SUPER_MAGIC, "jfs", 0 },
    1272             :         { KAFS_SUPER_MAGIC, "k-afs", 1 },
    1273             :         { LUSTRE_SUPER_MAGIC, "lustre", 1 },
    1274             :         { MINIX_SUPER_MAGIC, "minix", 0 },
    1275             :         { MINIX_SUPER_MAGIC2, "minix", 0 },
    1276             :         { MINIX2_SUPER_MAGIC, "minix2", 0 },
    1277             :         { MINIX2_SUPER_MAGIC2, "minix2", 0 },
    1278             :         { MINIX3_SUPER_MAGIC, "minix3", 0 },
    1279             :         { MQUEUE_MAGIC, "mqueue", 0 },
    1280             :         { MSDOS_SUPER_MAGIC, "msdos", 0 },
    1281             :         { NCP_SUPER_MAGIC, "novell", 1 },
    1282             :         { NFS_SUPER_MAGIC, "nfs", 1 },
    1283             :         { NFSD_SUPER_MAGIC, "nfsd", 1 },
    1284             :         { NILFS_SUPER_MAGIC, "nilfs", 0 },
    1285             :         { NTFS_SB_MAGIC, "ntfs", 0 },
    1286             :         { OPENPROM_SUPER_MAGIC, "openprom", 0 },
    1287             :         { OCFS2_SUPER_MAGIC, "ocfs2", 1 },
    1288             :         { PANFS_SUPER_MAGIC, "panfs", 1 },
    1289             :         { PIPEFS_MAGIC, "pipefs", 1 },
    1290             :         { PROC_SUPER_MAGIC, "proc", 0 },
    1291             :         { PSTOREFS_MAGIC, "pstorefs", 0 },
    1292             :         { QNX4_SUPER_MAGIC, "qnx4", 0 },
    1293             :         { QNX6_SUPER_MAGIC, "qnx6", 0 },
    1294             :         { RAMFS_MAGIC, "ramfs", 0 },
    1295             :         { REISERFS_SUPER_MAGIC, "reiserfs", 0 },
    1296             :         { ROMFS_MAGIC, "romfs", 0 },
    1297             :         { RPC_PIPEFS_SUPER_MAGIC, "rpc_pipefs", 0 },
    1298             :         { SECURITYFS_SUPER_MAGIC, "securityfs", 0 },
    1299             :         { SELINUX_MAGIC, "selinux", 0 },
    1300             :         { SMB_SUPER_MAGIC, "smb", 1 },
    1301             :         { SOCKFS_MAGIC, "sockfs", 0 },
    1302             :         { SQUASHFS_MAGIC, "squashfs", 0 },
    1303             :         { SYSFS_MAGIC, "sysfs", 0 },
    1304             :         { SYSV2_SUPER_MAGIC, "sysv2", 0 },
    1305             :         { SYSV4_SUPER_MAGIC, "sysv4", 0 },
    1306             :         { TMPFS_MAGIC, "tmpfs", 0 },
    1307             :         { UDF_SUPER_MAGIC, "udf", 0 },
    1308             :         { UFS_MAGIC, "ufs", 0 },
    1309             :         { UFS_BYTESWAPPED_SUPER_MAGIC, "ufs", 0 },
    1310             :         { USBDEVICE_SUPER_MAGIC, "usbdevfs", 0 },
    1311             :         { V9FS_MAGIC, "v9fs", 0 },
    1312             :         { VMHGFS_SUPER_MAGIC, "vmhgfs", 1 },
    1313             :         { VXFS_SUPER_MAGIC, "vxfs", 0 },
    1314             :         { VZFS_SUPER_MAGIC, "vzfs", 0 },
    1315             :         { XENFS_SUPER_MAGIC, "xenfs", 0 },
    1316             :         { XENIX_SUPER_MAGIC, "xenix", 0 },
    1317             :         { XFS_SUPER_MAGIC, "xfs", 0 },
    1318             :         { _XIAFS_SUPER_MAGIC, "xia", 0 },
    1319             :         { ZFS_SUPER_MAGIC, "zfs", 0 },
    1320             :         { 0 }
    1321             : };
    1322             : 
    1323        6710 : int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_hardlinks, uint64_t* total_space, uint64_t* free_space, char* fstype, size_t fstype_size, char* fslabel, size_t fslabel_size)
    1324             : {
    1325             : #if HAVE_STATFS
    1326             :         struct statfs st;
    1327             : 
    1328        6710 :         if (statfs(path, &st) != 0) {
    1329             :                 char dir[PATH_MAX];
    1330             :                 char* slash;
    1331             : 
    1332          49 :                 if (errno != ENOENT) {
    1333             :                         /* LCOV_EXCL_START */
    1334             :                         return -1;
    1335             :                         /* LCOV_EXCL_STOP */
    1336             :                 }
    1337             : 
    1338             :                 /*
    1339             :                  * If it doesn't exist, we assume a file
    1340             :                  * and we check for the containing dir
    1341             :                  */
    1342          49 :                 if (strlen(path) + 1 > sizeof(dir)) {
    1343             :                         /* LCOV_EXCL_START */
    1344             :                         errno = ENAMETOOLONG;
    1345             :                         return -1;
    1346             :                         /* LCOV_EXCL_STOP */
    1347             :                 }
    1348             : 
    1349          49 :                 strcpy(dir, path);
    1350             : 
    1351          49 :                 slash = strrchr(dir, '/');
    1352          49 :                 if (!slash) {
    1353             :                         /* LCOV_EXCL_START */
    1354             :                         return -1;
    1355             :                         /* LCOV_EXCL_STOP */
    1356             :                 }
    1357             : 
    1358          49 :                 *slash = 0;
    1359          49 :                 if (statfs(dir, &st) != 0) {
    1360             :                         /* LCOV_EXCL_START */
    1361             :                         return -1;
    1362             :                         /* LCOV_EXCL_STOP */
    1363             :                 }
    1364             :         }
    1365             : #endif
    1366             : 
    1367             :         /* to get the fs type check "man stat" or "stat -f -t FILE" */
    1368        6710 :         if (has_persistent_inode) {
    1369             : #if HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
    1370         659 :                 switch (st.f_type) {
    1371           0 :                 case FUSEBLK_SUPER_MAGIC : /* FUSE, "fuseblk" in the stat command */
    1372             :                 case MSDOS_SUPER_MAGIC : /* VFAT, "msdos" in the stat command */
    1373           0 :                         *has_persistent_inode = 0;
    1374           0 :                         break;
    1375         659 :                 default :
    1376             :                         /* by default assume yes */
    1377         659 :                         *has_persistent_inode = 1;
    1378         659 :                         break;
    1379             :                 }
    1380             : #else
    1381             :                 /* in Unix inodes are persistent by default */
    1382             :                 *has_persistent_inode = 1;
    1383             : #endif
    1384             :         }
    1385             : 
    1386        6710 :         if (has_syncronized_hardlinks) {
    1387             : #if HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
    1388         659 :                 switch (st.f_type) {
    1389           0 :                 case NTFS_SB_MAGIC : /* NTFS */
    1390             :                 case MSDOS_SUPER_MAGIC : /* VFAT, "msdos" in the stat command */
    1391           0 :                         *has_syncronized_hardlinks = 0;
    1392           0 :                         break;
    1393         659 :                 default :
    1394             :                         /* by default assume yes */
    1395         659 :                         *has_syncronized_hardlinks = 1;
    1396         659 :                         break;
    1397             :                 }
    1398             : #else
    1399             :                 /* in Unix hardlinks share the same metadata by default */
    1400             :                 *has_syncronized_hardlinks = 1;
    1401             : #endif
    1402             :         }
    1403             : 
    1404        6710 :         if (total_space) {
    1405             : #if HAVE_STATFS
    1406        6051 :                 *total_space = st.f_bsize * (uint64_t)st.f_blocks;
    1407             : #else
    1408             :                 *total_space = 0;
    1409             : #endif
    1410             :         }
    1411             : 
    1412        6710 :         if (free_space) {
    1413             : #if HAVE_STATFS
    1414        6051 :                 *free_space = st.f_bsize * (uint64_t)st.f_bfree;
    1415             : #else
    1416             :                 *free_space = 0;
    1417             : #endif
    1418             :         }
    1419             : 
    1420        6710 :         const char* ptype = 0;
    1421             : 
    1422             : #if HAVE_STATFS && HAVE_STRUCT_STATFS_F_FSTYPENAME
    1423             :         /* get the filesystem type directly from the struct (Mac OS X) */
    1424             :         ptype = st.f_fstypename;
    1425             : #elif HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
    1426             :         /*
    1427             :          * Get the filesystem type from f_type (Linux)
    1428             :          * from: https://github.com/influxdata/gopsutil/blob/master/disk/disk_linux.go
    1429             :          */
    1430      146411 :         for (int i = 0; FILESYSTEMS[i].id != 0; ++i) {
    1431      146411 :                 if (st.f_type == FILESYSTEMS[i].id) {
    1432        6710 :                         ptype = FILESYSTEMS[i].name;
    1433        6710 :                         break;
    1434             :                 }
    1435             :         }
    1436             : #endif
    1437             : 
    1438        6710 :         if (fstype) {
    1439        6051 :                 fstype[0] = 0;
    1440        6051 :                 if (ptype)
    1441        6051 :                         snprintf(fstype, fstype_size, "%s", ptype);
    1442             :         }
    1443             : 
    1444        6710 :         if (fslabel) {
    1445        6051 :                 fslabel[0] = 0;
    1446             : #if HAVE_BLKID
    1447             :                 struct stat fst;
    1448        6051 :                 if (stat(path, &fst) == 0)
    1449        6002 :                         devuuid_label(fst.st_dev, fslabel, fslabel_size);
    1450             : #else
    1451             :                 (void)fslabel_size;
    1452             : #endif
    1453             :         }
    1454             : 
    1455        6710 :         return 0;
    1456             : }
    1457             : 
    1458          48 : int fssnapshot(const char* path, char* root, size_t root_size)
    1459             : {
    1460             : #if HAVE_LINUX_DEVICE
    1461             :         struct statfs sfs;
    1462             :         struct stat st;
    1463             :         char current_path[PATH_MAX];
    1464             : 
    1465          48 :         pathcpy(current_path, sizeof(current_path), path);
    1466             : 
    1467          48 :         if (statfs(current_path, &sfs) != 0) {
    1468             :                 /* LCOV_EXCL_START */
    1469             :                 log_error(errno, "Error stating filesystem '%s'. %s.\n", current_path, strerror(errno));
    1470             :                 return -1;
    1471             :                 /* LCOV_EXCL_STOP */
    1472             :         }
    1473             : 
    1474             :         /* only btrfs */
    1475          48 :         if (sfs.f_type != BTRFS_SUPER_MAGIC)
    1476           0 :                 return -1;
    1477             : 
    1478             :         /* walk up the directory tree to find the subvolume root (Inode 256) */
    1479             :         while (1) {
    1480          48 :                 if (stat(current_path, &st) != 0) {
    1481             :                         /* LCOV_EXCL_START */
    1482             :                         log_error(errno, "Error stating '%s'. %s.\n", current_path, strerror(errno));
    1483             :                         return -1;
    1484             :                         /* LCOV_EXCL_STOP */
    1485             :                 }
    1486             : 
    1487             :                 /* btrfs reserved inode 256 for subvolume roots */
    1488          48 :                 if (st.st_ino == 256) {
    1489             :                         /* copy the subvol root */
    1490          48 :                         pathcpy(root, root_size, current_path);
    1491          48 :                         return 0;
    1492             :                 }
    1493             : 
    1494             :                 /* move to parent directory */
    1495           0 :                 pathup(current_path);
    1496             : 
    1497           0 :                 if (current_path[0] == 0) {
    1498             :                         /* LCOV_EXCL_START */
    1499             :                         log_error(ENOENT, "No subvolume root found for '%s'. %s.\n", path, strerror(errno));
    1500             :                         return -1;
    1501             :                         /* LCOV_EXCL_STOP */
    1502             :                 }
    1503             :         }
    1504             : #else
    1505             :         (void)path;
    1506             :         (void)root;
    1507             :         (void)root_size;
    1508             :         return -1;
    1509             : #endif
    1510             : }
    1511             : 
    1512          30 : int fssnapshot_create(const char* source, const char* parent_dir, const char* name)
    1513             : {
    1514             : #if HAVE_LINUX_DEVICE
    1515             :         struct btrfs_ioctl_vol_args_v2 args;
    1516             :         int fd_source;
    1517             :         int fd_dest_parent;
    1518             :         int ret;
    1519             : 
    1520             :         /* open the source subvolume to get a file descriptor */
    1521          30 :         fd_source = open(source, O_RDONLY | O_DIRECTORY);
    1522          30 :         if (fd_source < 0) {
    1523             :                 /* LCOV_EXCL_START */
    1524             :                 return -1;
    1525             :                 /* LCOV_EXCL_STOP */
    1526             :         }
    1527             : 
    1528          30 :         fd_dest_parent = open(parent_dir, O_RDONLY | O_DIRECTORY);
    1529          30 :         if (fd_dest_parent < 0) {
    1530             :                 /* LCOV_EXCL_START */
    1531             :                 close(fd_source);
    1532             :                 return -1;
    1533             :                 /* LCOV_EXCL_STOP */
    1534             :         }
    1535             : 
    1536          30 :         memset(&args, 0, sizeof(args));
    1537          30 :         args.fd = fd_source;
    1538          30 :         args.flags = BTRFS_SUBVOL_RDONLY;
    1539          30 :         pathcpy(args.name, BTRFS_SUBVOL_NAME_MAX, name);
    1540             : 
    1541             :         /* issue the snapshot command to the PARENT directory of the destination */
    1542          30 :         ret = ioctl(fd_dest_parent, BTRFS_IOC_SNAP_CREATE_V2, &args);
    1543          30 :         if (ret < 0) {
    1544             :                 /* LCOV_EXCL_START */
    1545             :                 close(fd_source);
    1546             :                 close(fd_dest_parent);
    1547             :                 return -1;
    1548             :                 /* LCOV_EXCL_STOP */
    1549             :         }
    1550             : 
    1551          30 :         close(fd_source);
    1552          30 :         close(fd_dest_parent);
    1553             : 
    1554          30 :         return 0;
    1555             : #else
    1556             :         (void)source;
    1557             :         (void)parent_dir;
    1558             :         (void)name;
    1559             :         return -1;
    1560             : #endif
    1561             : }
    1562             : 
    1563          48 : int fssnapshot_delete(const char* parent_dir, const char* name)
    1564             : {
    1565             : #if HAVE_LINUX_DEVICE
    1566             :         struct btrfs_ioctl_vol_args args;
    1567             :         int fd_parent;
    1568             :         int ret;
    1569             : 
    1570          48 :         fd_parent = open(parent_dir, O_RDONLY | O_DIRECTORY);
    1571          48 :         if (fd_parent < 0) {
    1572             :                 /* LCOV_EXCL_START */
    1573             :                 return -1;
    1574             :                 /* LCOV_EXCL_STOP */
    1575             :         }
    1576             : 
    1577          48 :         memset(&args, 0, sizeof(args));
    1578          48 :         pathcpy(args.name, BTRFS_PATH_NAME_MAX, name);
    1579             : 
    1580          48 :         ret = ioctl(fd_parent, BTRFS_IOC_SNAP_DESTROY, &args);
    1581             : 
    1582          48 :         if (ret < 0 && errno != ENOENT) {
    1583             :                 /* LCOV_EXCL_START */
    1584             :                 close(fd_parent);
    1585             :                 return -1;
    1586             :                 /* LCOV_EXCL_STOP */
    1587             :         }
    1588             : 
    1589          48 :         close(fd_parent);
    1590          48 :         return 0;
    1591             : #else
    1592             :         (void)parent_dir;
    1593             :         (void)name;
    1594             :         return -1;
    1595             : #endif
    1596             : }
    1597             : 
    1598          18 : int fssnapshot_rename(const char* parent_dir, const char* old_name, const char* new_name)
    1599             : {
    1600             : #if HAVE_LINUX_DEVICE
    1601             :         char old_path[PATH_MAX];
    1602             :         char new_path[PATH_MAX];
    1603             : 
    1604          18 :         pathcpy(old_path, sizeof(old_path), parent_dir);
    1605          18 :         pathcatc(old_path, sizeof(old_path), '/');
    1606          18 :         pathcat(old_path, sizeof(old_path), old_name);
    1607          18 :         pathcpy(new_path, sizeof(new_path), parent_dir);
    1608          18 :         pathcatc(new_path, sizeof(new_path), '/');
    1609          18 :         pathcat(new_path, sizeof(new_path), new_name);
    1610             : 
    1611          18 :         if (rename(old_path, new_path) < 0) {
    1612             :                 /* LCOV_EXCL_START */
    1613             :                 return -1;
    1614             :                 /* LCOV_EXCL_STOP */
    1615             :         }
    1616             : 
    1617          18 :         return 0;
    1618             : #else
    1619             :         (void)parent_dir;
    1620             :         (void)old_name;
    1621             :         (void)new_name;
    1622             :         return -1;
    1623             : #endif
    1624             : }
    1625             : 
    1626     4133031 : uint64_t os_tick(void)
    1627             : {
    1628             : #if HAVE_MACH_ABSOLUTE_TIME
    1629             :         /* for Mac OS X */
    1630             :         return mach_absolute_time();
    1631             : #elif HAVE_CLOCK_GETTIME && (defined(CLOCK_MONOTONIC) || defined(CLOCK_MONOTONIC_RAW))
    1632             :         /* for Linux */
    1633             :         struct timespec tv;
    1634             : 
    1635             :         /* nanosecond precision with clock_gettime() */
    1636             : #if defined(CLOCK_MONOTONIC_RAW)
    1637     4133031 :         if (clock_gettime(CLOCK_MONOTONIC_RAW, &tv) != 0) {
    1638             : #else
    1639             :         if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
    1640             : #endif
    1641             :                 /* LCOV_EXCL_START */
    1642             :                 return 0;
    1643             :                 /* LCOV_EXCL_STOP */
    1644             :         }
    1645             : 
    1646     4133031 :         return tv.tv_sec * 1000000000ULL + tv.tv_nsec;
    1647             : #else
    1648             :         /* other platforms */
    1649             :         struct timeval tv;
    1650             : 
    1651             :         /* microsecond precision with gettimeofday() */
    1652             :         if (gettimeofday(&tv, 0) != 0) {
    1653             :                 /* LCOV_EXCL_START */
    1654             :                 return 0;
    1655             :                 /* LCOV_EXCL_STOP */
    1656             :         }
    1657             : 
    1658             :         return tv.tv_sec * 1000000ULL + tv.tv_usec;
    1659             : #endif
    1660             : }
    1661             : 
    1662        3894 : uint64_t os_tick_ms(void)
    1663             : {
    1664             :         struct timeval tv;
    1665             : 
    1666        3894 :         if (gettimeofday(&tv, 0) != 0) {
    1667             :                 /* LCOV_EXCL_START */
    1668             :                 return 0;
    1669             :                 /* LCOV_EXCL_STOP */
    1670             :         }
    1671             : 
    1672        3894 :         return tv.tv_sec * 1000ULL + tv.tv_usec / 1000;
    1673             : }
    1674             : 
    1675         332 : int randomize(void* ptr, size_t size)
    1676             : {
    1677             :         int f;
    1678             :         ssize_t ret;
    1679             : 
    1680         332 :         f = open("/dev/urandom", O_RDONLY);
    1681         332 :         if (f == -1) {
    1682             :                 /* LCOV_EXCL_START */
    1683             :                 return -1;
    1684             :                 /* LCOV_EXCL_STOP */
    1685             :         }
    1686             : 
    1687         332 :         ret = read(f, ptr, size);
    1688         332 :         if (ret < 0 || (size_t)ret != size) {
    1689             :                 /* LCOV_EXCL_START */
    1690             :                 close(f);
    1691             :                 return -1;
    1692             :                 /* LCOV_EXCL_STOP */
    1693             :         }
    1694             : 
    1695         332 :         if (close(f) != 0) {
    1696             :                 /* LCOV_EXCL_START */
    1697             :                 return -1;
    1698             :                 /* LCOV_EXCL_STOP */
    1699             :         }
    1700             : 
    1701         332 :         return 0;
    1702             : }
    1703             : 
    1704             : /**
    1705             :  * Read a file extracting the contained device number in %u:%u format.
    1706             :  * Return 0 on error.
    1707             :  */
    1708             : #if HAVE_LINUX_DEVICE
    1709         310 : static dev_t devread(const char* path)
    1710             : {
    1711             :         int f;
    1712             :         int ret;
    1713             :         int len;
    1714             :         char buf[64];
    1715             :         char* e;
    1716             :         unsigned ma;
    1717             :         unsigned mi;
    1718             : 
    1719         310 :         f = open(path, O_RDONLY);
    1720         310 :         if (f == -1) {
    1721             :                 /* LCOV_EXCL_START */
    1722             :                 log_fatal(errno, "Failed to open '%s'.\n", path);
    1723             :                 return 0;
    1724             :                 /* LCOV_EXCL_STOP */
    1725             :         }
    1726             : 
    1727         310 :         len = read(f, buf, sizeof(buf));
    1728         310 :         if (len < 0) {
    1729             :                 /* LCOV_EXCL_START */
    1730             :                 close(f);
    1731             :                 log_fatal(errno, "Failed to read '%s'.\n", path);
    1732             :                 return 0;
    1733             :                 /* LCOV_EXCL_STOP */
    1734             :         }
    1735         310 :         if (len == sizeof(buf)) {
    1736             :                 /* LCOV_EXCL_START */
    1737             :                 close(f);
    1738             :                 log_fatal(EEXTERNAL, "Too long read '%s'.\n", path);
    1739             :                 return 0;
    1740             :                 /* LCOV_EXCL_STOP */
    1741             :         }
    1742             : 
    1743         310 :         ret = close(f);
    1744         310 :         if (ret != 0) {
    1745             :                 /* LCOV_EXCL_START */
    1746             :                 log_fatal(errno, "Failed to close '%s'.\n", path);
    1747             :                 return 0;
    1748             :                 /* LCOV_EXCL_STOP */
    1749             :         }
    1750             : 
    1751         310 :         buf[len] = 0;
    1752             : 
    1753         310 :         ma = strtoul(buf, &e, 10);
    1754         310 :         if (*e != ':') {
    1755             :                 /* LCOV_EXCL_START */
    1756             :                 log_fatal(EEXTERNAL, "Invalid format in '%s' for '%s'.\n", path, buf);
    1757             :                 return 0;
    1758             :                 /* LCOV_EXCL_STOP */
    1759             :         }
    1760             : 
    1761         310 :         mi = strtoul(e + 1, &e, 10);
    1762         310 :         if (*e != 0 && !isspace(*e)) {
    1763             :                 /* LCOV_EXCL_START */
    1764             :                 log_fatal(EEXTERNAL, "Invalid format in '%s' for '%s'.\n", path, buf);
    1765             :                 return 0;
    1766             :                 /* LCOV_EXCL_STOP */
    1767             :         }
    1768             : 
    1769         310 :         return makedev(ma, mi);
    1770             : }
    1771             : #endif
    1772             : 
    1773             : /**
    1774             :  * Read a device tree filling the specified list of disk_t entries.
    1775             :  */
    1776             : #if HAVE_LINUX_DEVICE
    1777         310 : static int devtree(devinfo_t* parent, dev_t device, tommy_list* list)
    1778             : {
    1779             :         char path[PATH_MAX];
    1780             :         DIR* d;
    1781         310 :         int slaves = 0;
    1782             : 
    1783         310 :         pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/slaves", major(device), minor(device));
    1784             : 
    1785             :         /* check if there is a slaves list */
    1786         310 :         d = opendir(path);
    1787         310 :         if (d != 0) {
    1788             :                 struct dirent* dd;
    1789             : 
    1790         612 :                 while ((dd = readdir(d)) != 0) {
    1791         459 :                         if (dd->d_name[0] != '.') {
    1792             :                                 dev_t subdev;
    1793             : 
    1794             :                                 /* for each slave, expand the full potential tree */
    1795         153 :                                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/slaves/%s/dev", major(device), minor(device), dd->d_name);
    1796             : 
    1797         153 :                                 subdev = devread(path);
    1798         153 :                                 if (!subdev) {
    1799             :                                         /* LCOV_EXCL_START */
    1800             :                                         closedir(d);
    1801             :                                         return -1;
    1802             :                                         /* LCOV_EXCL_STOP */
    1803             :                                 }
    1804             : 
    1805         153 :                                 if (devtree(parent, subdev, list) != 0) {
    1806             :                                         /* LCOV_EXCL_START */
    1807             :                                         closedir(d);
    1808             :                                         return -1;
    1809             :                                         /* LCOV_EXCL_STOP */
    1810             :                                 }
    1811             : 
    1812         153 :                                 ++slaves;
    1813             :                         }
    1814             :                 }
    1815             : 
    1816         153 :                 closedir(d);
    1817             :         }
    1818             : 
    1819             :         /* if no slaves found */
    1820         310 :         if (!slaves) {
    1821             :                 /* this is a raw device */
    1822             :                 devinfo_t* devinfo;
    1823             : 
    1824             :                 /* check if it's a real device */
    1825         157 :                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/device", major(device), minor(device));
    1826         157 :                 if (access(path, F_OK) != 0) {
    1827             :                         /* get the parent device */
    1828         157 :                         pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/../dev", major(device), minor(device));
    1829             : 
    1830         157 :                         device = devread(path);
    1831         157 :                         if (!device) {
    1832             :                                 /* LCOV_EXCL_START */
    1833             :                                 return -1;
    1834             :                                 /* LCOV_EXCL_STOP */
    1835             :                         }
    1836             :                 }
    1837             : 
    1838             :                 /* get the device file */
    1839         157 :                 if (devresolve(device, path, sizeof(path)) != 0) {
    1840             :                         /* LCOV_EXCL_START */
    1841             :                         log_fatal(EEXTERNAL, "Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    1842             :                         return -1;
    1843             :                         /* LCOV_EXCL_STOP */
    1844             :                 }
    1845             : 
    1846         157 :                 devinfo = calloc_nofail(1, sizeof(devinfo_t));
    1847             : 
    1848         157 :                 devinfo->device = device;
    1849         157 :                 pathcpy(devinfo->name, sizeof(devinfo->name), parent->name);
    1850         157 :                 pathcpy(devinfo->smartctl, sizeof(devinfo->smartctl), parent->smartctl);
    1851         157 :                 memcpy(devinfo->smartignore, parent->smartignore, sizeof(devinfo->smartignore));
    1852         157 :                 pathcpy(devinfo->file, sizeof(devinfo->file), path);
    1853         157 :                 devinfo->parent = parent;
    1854             : 
    1855             :                 /* insert in the list */
    1856         157 :                 tommy_list_insert_tail(list, &devinfo->node, devinfo);
    1857             :         }
    1858             : 
    1859         310 :         return 0;
    1860             : }
    1861             : #endif
    1862             : 
    1863             : /**
    1864             :  * Compute disk usage by aggregating access statistics.
    1865             :  */
    1866             : #if HAVE_LINUX_DEVICE
    1867         157 : static int devstat(dev_t device, uint64_t* count)
    1868             : {
    1869             :         char path[PATH_MAX];
    1870             :         char buf[512];
    1871             :         int token;
    1872             :         int ret;
    1873             :         char* i;
    1874             : 
    1875         157 :         *count = 0;
    1876             : 
    1877         157 :         pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/stat", major(device), minor(device));
    1878             : 
    1879         157 :         ret = sysread(path, buf, sizeof(buf));
    1880         157 :         if (ret < 0) {
    1881             :                 /* LCOV_EXCL_START */
    1882             :                 log_fatal(errno, "Failed to read '%s'.\n", path);
    1883             :                 return -1;
    1884             :                 /* LCOV_EXCL_STOP */
    1885             :         }
    1886         157 :         if ((size_t)ret + 1 > sizeof(buf)) {
    1887             :                 /* LCOV_EXCL_START */
    1888             :                 log_fatal(EEXTERNAL, "Too long read '%s'.\n", path);
    1889             :                 return -1;
    1890             :                 /* LCOV_EXCL_STOP */
    1891             :         }
    1892             : 
    1893             :         /* ending 0 */
    1894         157 :         buf[ret] = 0;
    1895             : 
    1896         157 :         i = buf;
    1897         157 :         token = 1; /* token number */
    1898         785 :         while (*i) {
    1899             :                 char* n;
    1900             :                 char* e;
    1901             :                 unsigned long long v;
    1902             : 
    1903             :                 /* skip spaces */
    1904        2490 :                 while (*i && isspace(*i))
    1905        1705 :                         ++i;
    1906             : 
    1907             :                 /* read digits */
    1908         785 :                 n = i;
    1909        5652 :                 while (*i && isdigit(*i))
    1910        4867 :                         ++i;
    1911             : 
    1912         785 :                 if (i == n) /* if no digit, abort */
    1913         157 :                         break;
    1914         785 :                 if (*i == 0 || !isspace(*i)) /* if no space, abort */
    1915             :                         break;
    1916         785 :                 *i++ = 0; /* put a terminator */
    1917             : 
    1918         785 :                 v = strtoull(n, &e, 10);
    1919         785 :                 if (*e != 0) {
    1920             :                         /* LCOV_EXCL_START */
    1921             :                         break;
    1922             :                         /* LCOV_EXCL_STOP */
    1923             :                 }
    1924             : 
    1925             :                 /* sum reads and writes completed */
    1926         785 :                 if (token == 1 || token == 5) {
    1927         314 :                         *count += v;
    1928         314 :                         if (token == 5)
    1929         157 :                                 break; /* stop here */
    1930             :                 }
    1931             : 
    1932         628 :                 ++token;
    1933             :         }
    1934             : 
    1935         157 :         return 0;
    1936             : }
    1937             : #endif
    1938             : 
    1939             : /**
    1940             :  * Get SMART attributes.
    1941             :  */
    1942             : #if HAVE_LINUX_DEVICE
    1943          26 : static int devsmart(dev_t device, const char* name, const char* smartctl, struct smart_attr* smart, uint64_t* info, char* serial, char* family, char* model, char* interface)
    1944             : {
    1945             :         char cmd[PATH_MAX + 64];
    1946             :         char file[PATH_MAX];
    1947             :         FILE* f;
    1948             :         int ret;
    1949             :         const char* x;
    1950             : 
    1951          26 :         x = find_smartctl();
    1952          26 :         if (!x) {
    1953             :                 /* LCOV_EXCL_START */
    1954             :                 log_fatal(EEXTERNAL, "Cannot find smartctl.\n");
    1955             :                 return -1;
    1956             :                 /* LCOV_EXCL_STOP */
    1957             :         }
    1958             : 
    1959          26 :         if (devresolve(device, file, sizeof(file)) != 0) {
    1960             :                 /* LCOV_EXCL_START */
    1961             :                 log_fatal(EEXTERNAL, "Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    1962             :                 return -1;
    1963             :                 /* LCOV_EXCL_STOP */
    1964             :         }
    1965             : 
    1966             :         /* if there is a custom smartctl command */
    1967          26 :         if (smartctl[0]) {
    1968             :                 char option[PATH_MAX];
    1969           0 :                 snprintf(option, sizeof(option), smartctl, file);
    1970           0 :                 snprintf(cmd, sizeof(cmd), "%s -a %s", x, option);
    1971             :         } else {
    1972          26 :                 snprintf(cmd, sizeof(cmd), "%s -a %s", x, file);
    1973             :         }
    1974             : 
    1975          26 :         log_tag("smartctl:%s:%s:run: %s\n", file, name, cmd);
    1976             : 
    1977          26 :         f = popen(cmd, "r");
    1978          26 :         if (!f) {
    1979             :                 /* LCOV_EXCL_START */
    1980             :                 log_tag("device:%s:%s:shell\n", file, name);
    1981             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (from popen).\n", cmd);
    1982             :                 return -1;
    1983             :                 /* LCOV_EXCL_STOP */
    1984             :         }
    1985             : 
    1986          26 :         if (smartctl_attribute(f, file, name, smart, info, serial, family, model, interface) != 0) {
    1987             :                 /* LCOV_EXCL_START */
    1988             :                 pclose(f);
    1989             :                 log_tag("device:%s:%s:shell\n", file, name);
    1990             :                 return -1;
    1991             :                 /* LCOV_EXCL_STOP */
    1992             :         }
    1993             : 
    1994          26 :         ret = pclose(f);
    1995             : 
    1996          26 :         log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
    1997             : 
    1998          26 :         if (!WIFEXITED(ret)) {
    1999             :                 /* LCOV_EXCL_START */
    2000             :                 log_tag("device:%s:%s:abort\n", file, name);
    2001             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (not exited).\n", cmd);
    2002             :                 return -1;
    2003             :                 /* LCOV_EXCL_STOP */
    2004             :         }
    2005          26 :         if (WEXITSTATUS(ret) == 127) {
    2006             :                 /* LCOV_EXCL_START */
    2007             :                 log_tag("device:%s:%s:shell\n", file, name);
    2008             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (from sh).\n", cmd);
    2009             :                 return -1;
    2010             :                 /* LCOV_EXCL_STOP */
    2011             :         }
    2012             : 
    2013             :         /* store the return smartctl return value */
    2014          26 :         smart[SMART_FLAGS].raw = WEXITSTATUS(ret);
    2015             : 
    2016          26 :         return 0;
    2017             : }
    2018             : #endif
    2019             : 
    2020             : /**
    2021             :  * Get device attributes.
    2022             :  */
    2023             : #if HAVE_LINUX_DEVICE
    2024          54 : static void devattr(dev_t device, uint64_t* info, char* serial, char* family, char* model, char* interface)
    2025             : {
    2026             :         char path[PATH_MAX];
    2027             :         char buf[512];
    2028             :         int ret;
    2029             : 
    2030             :         (void)family; /* not available, smartctl uses an internal database to get it */
    2031             : 
    2032          54 :         if (info[INFO_SIZE] == SMART_UNASSIGNED) {
    2033          12 :                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/size", major(device), minor(device));
    2034          12 :                 if (sysattr(path, buf, sizeof(buf)) == 0) {
    2035             :                         char* e;
    2036             :                         uint64_t v;
    2037          12 :                         v = strtoul(buf, &e, 10);
    2038          12 :                         if (*e == 0)
    2039          12 :                                 info[INFO_SIZE] = v * 512;
    2040             :                 }
    2041             :         }
    2042             : 
    2043          54 :         if (info[INFO_ROTATION_RATE] == SMART_UNASSIGNED) {
    2044          12 :                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/queue/rotational", major(device), minor(device));
    2045          12 :                 if (sysattr(path, buf, sizeof(buf)) == 0) {
    2046             :                         char* e;
    2047             :                         uint64_t v;
    2048          12 :                         v = strtoul(buf, &e, 10);
    2049          12 :                         if (*e == 0)
    2050          12 :                                 info[INFO_ROTATION_RATE] = v;
    2051             :                 }
    2052             :         }
    2053             : 
    2054          54 :         if (*model == 0) {
    2055          12 :                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/device/model", major(device), minor(device));
    2056          12 :                 if (sysattr(path, buf, sizeof(buf)) == 0) {
    2057          12 :                         if (buf[0] != 0)
    2058          12 :                                 pathcpy(model, SMART_MAX, buf);
    2059             :                 }
    2060             :         }
    2061             : 
    2062          54 :         if (*serial == 0) {
    2063          12 :                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/device/serial", major(device), minor(device));
    2064          12 :                 if (sysattr(path, buf, sizeof(buf)) == 0) {
    2065           0 :                         if (buf[0] != 0)
    2066           0 :                                 pathcpy(serial, SMART_MAX, buf);
    2067             :                 }
    2068             :         }
    2069             : 
    2070          54 :         if (*serial == 0) {
    2071          12 :                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/device/vpd_pg80", major(device), minor(device));
    2072          12 :                 if (sysattr_vpd_pg80(path, buf, sizeof(buf)) == 0) {
    2073          12 :                         if (buf[0] != 0)
    2074          12 :                                 pathcpy(serial, SMART_MAX, buf);
    2075             :                 }
    2076             :         }
    2077             : 
    2078             :         /* always override interface if it's usb */
    2079          54 :         pathprint(path, sizeof(path), "/sys/dev/block/%u:%u", major(device), minor(device));
    2080          54 :         ret = readlink(path, buf, sizeof(buf));
    2081          54 :         if (ret > 0 && (unsigned)ret < sizeof(buf)) {
    2082          54 :                 buf[ret] = 0;
    2083          54 :                 if (strstr(buf, "usb") != 0)
    2084           0 :                         strcpy(interface, "USB");
    2085             :         }
    2086          54 : }
    2087             : #endif
    2088             : 
    2089             : /**
    2090             :  * Get POWER state.
    2091             :  */
    2092             : #if HAVE_LINUX_DEVICE
    2093          40 : static int devprobe(dev_t device, const char* name, const char* smartctl, int* power, struct smart_attr* smart, uint64_t* info, char* serial, char* family, char* model, char* interface)
    2094             : {
    2095             :         char cmd[PATH_MAX + 64];
    2096             :         char file[PATH_MAX];
    2097             :         FILE* f;
    2098             :         int ret;
    2099             :         const char* x;
    2100             : 
    2101          40 :         x = find_smartctl();
    2102          40 :         if (!x) {
    2103             :                 /* LCOV_EXCL_START */
    2104             :                 log_fatal(EEXTERNAL, "Cannot find smartctl.\n");
    2105             :                 return -1;
    2106             :                 /* LCOV_EXCL_STOP */
    2107             :         }
    2108             : 
    2109          40 :         if (devresolve(device, file, sizeof(file)) != 0) {
    2110             :                 /* LCOV_EXCL_START */
    2111             :                 log_fatal(EEXTERNAL, "Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    2112             :                 return -1;
    2113             :                 /* LCOV_EXCL_STOP */
    2114             :         }
    2115             : 
    2116             :         /* if there is a custom smartctl command */
    2117          40 :         if (smartctl[0]) {
    2118             :                 char option[PATH_MAX];
    2119           0 :                 snprintf(option, sizeof(option), smartctl, file);
    2120           0 :                 snprintf(cmd, sizeof(cmd), "%s -n standby,3 -a %s", x, option);
    2121             :         } else {
    2122          40 :                 snprintf(cmd, sizeof(cmd), "%s -n standby,3 -a %s", x, file);
    2123             :         }
    2124             : 
    2125          40 :         log_tag("smartctl:%s:%s:run: %s\n", file, name, cmd);
    2126             : 
    2127          40 :         f = popen(cmd, "r");
    2128          40 :         if (!f) {
    2129             :                 /* LCOV_EXCL_START */
    2130             :                 log_tag("device:%s:%s:shell\n", file, name);
    2131             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (from popen).\n", cmd);
    2132             :                 return -1;
    2133             :                 /* LCOV_EXCL_STOP */
    2134             :         }
    2135             : 
    2136          40 :         if (smartctl_attribute(f, file, name, smart, info, serial, family, model, interface) != 0) {
    2137             :                 /* LCOV_EXCL_START */
    2138             :                 pclose(f);
    2139             :                 log_tag("device:%s:%s:shell\n", file, name);
    2140             :                 return -1;
    2141             :                 /* LCOV_EXCL_STOP */
    2142             :         }
    2143             : 
    2144          40 :         ret = pclose(f);
    2145             : 
    2146          40 :         log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
    2147             : 
    2148          40 :         if (!WIFEXITED(ret)) {
    2149             :                 /* LCOV_EXCL_START */
    2150             :                 log_tag("device:%s:%s:abort\n", file, name);
    2151             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (not exited).\n", cmd);
    2152             :                 return -1;
    2153             :                 /* LCOV_EXCL_STOP */
    2154             :         }
    2155          40 :         if (WEXITSTATUS(ret) == 127) {
    2156             :                 /* LCOV_EXCL_START */
    2157             :                 log_tag("device:%s:%s:shell\n", file, name);
    2158             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (from sh).\n", cmd);
    2159             :                 return -1;
    2160             :                 /* LCOV_EXCL_STOP */
    2161             :         }
    2162             : 
    2163          40 :         if (WEXITSTATUS(ret) == 3) {
    2164          24 :                 log_tag("attr:%s:%s:power:standby\n", file, name);
    2165          24 :                 *power = POWER_STANDBY;
    2166             :         } else {
    2167          16 :                 log_tag("attr:%s:%s:power:active\n", file, name);
    2168          16 :                 *power = POWER_ACTIVE;
    2169             : 
    2170             :                 /* store the smartctl return value */
    2171          16 :                 if (smart)
    2172          16 :                         smart[SMART_FLAGS].raw = WEXITSTATUS(ret);
    2173             :         }
    2174             : 
    2175          40 :         return 0;
    2176             : }
    2177             : #endif
    2178             : 
    2179             : /**
    2180             :  * Spin down a specific device.
    2181             :  */
    2182             : #if HAVE_LINUX_DEVICE
    2183          17 : static int devdown(dev_t device, const char* name, const char* smartctl)
    2184             : {
    2185             :         char cmd[PATH_MAX + 64];
    2186             :         char file[PATH_MAX];
    2187             :         FILE* f;
    2188             :         int ret;
    2189             :         const char* x;
    2190             : 
    2191          17 :         x = find_smartctl();
    2192          17 :         if (!x) {
    2193             :                 /* LCOV_EXCL_START */
    2194             :                 log_fatal(EEXTERNAL, "Cannot find smartctl.\n");
    2195             :                 return -1;
    2196             :                 /* LCOV_EXCL_STOP */
    2197             :         }
    2198             : 
    2199          17 :         if (devresolve(device, file, sizeof(file)) != 0) {
    2200             :                 /* LCOV_EXCL_START */
    2201             :                 log_fatal(EEXTERNAL, "Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    2202             :                 return -1;
    2203             :                 /* LCOV_EXCL_STOP */
    2204             :         }
    2205             : 
    2206             :         /* if there is a custom smartctl command */
    2207          17 :         if (smartctl[0]) {
    2208             :                 char option[PATH_MAX];
    2209           5 :                 snprintf(option, sizeof(option), smartctl, file);
    2210           5 :                 snprintf(cmd, sizeof(cmd), "%s -s standby,now %s", x, option);
    2211             :         } else {
    2212          12 :                 snprintf(cmd, sizeof(cmd), "%s -s standby,now %s", x, file);
    2213             :         }
    2214             : 
    2215          17 :         log_tag("smartctl:%s:%s:run: %s\n", file, name, cmd);
    2216             : 
    2217          17 :         f = popen(cmd, "r");
    2218          17 :         if (!f) {
    2219             :                 /* LCOV_EXCL_START */
    2220             :                 log_tag("device:%s:%s:shell\n", file, name);
    2221             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (from popen).\n", cmd);
    2222             :                 return -1;
    2223             :                 /* LCOV_EXCL_STOP */
    2224             :         }
    2225             : 
    2226          17 :         if (smartctl_flush(f, file, name) != 0) {
    2227             :                 /* LCOV_EXCL_START */
    2228             :                 pclose(f);
    2229             :                 log_tag("device:%s:%s:shell\n", file, name);
    2230             :                 return -1;
    2231             :                 /* LCOV_EXCL_STOP */
    2232             :         }
    2233             : 
    2234          17 :         ret = pclose(f);
    2235             : 
    2236          17 :         log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
    2237             : 
    2238          17 :         if (!WIFEXITED(ret)) {
    2239             :                 /* LCOV_EXCL_START */
    2240             :                 log_tag("device:%s:%s:abort\n", file, name);
    2241             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (not exited).\n", cmd);
    2242             :                 return -1;
    2243             :                 /* LCOV_EXCL_STOP */
    2244             :         }
    2245          17 :         if (WEXITSTATUS(ret) == 127) {
    2246             :                 /* LCOV_EXCL_START */
    2247             :                 log_tag("device:%s:%s:shell\n", file, name);
    2248             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (from sh).\n", cmd);
    2249             :                 return -1;
    2250             :                 /* LCOV_EXCL_STOP */
    2251             :         }
    2252          17 :         if (WEXITSTATUS(ret) != 0) {
    2253             :                 /* LCOV_EXCL_START */
    2254             :                 log_tag("device:%s:%s:exit:%d\n", file, name, WEXITSTATUS(ret));
    2255             :                 log_fatal(EEXTERNAL, "Failed to run '%s' with return code %xh.\n", cmd, WEXITSTATUS(ret));
    2256             :                 return -1;
    2257             :                 /* LCOV_EXCL_STOP */
    2258             :         }
    2259             : 
    2260          12 :         log_tag("attr:%s:%s:power:down\n", file, name);
    2261             : 
    2262          12 :         return 0;
    2263             : }
    2264             : #endif
    2265             : 
    2266             : /**
    2267             :  * Spin down a specific device if it's up.
    2268             :  */
    2269             : #if HAVE_LINUX_DEVICE
    2270          12 : static int devdownifup(dev_t device, const char* name, const char* smartctl, int* power)
    2271             : {
    2272          12 :         *power = POWER_UNKNOWN;
    2273             : 
    2274          12 :         if (devprobe(device, name, smartctl, power, 0, 0, 0, 0, 0, 0) != 0)
    2275           0 :                 return -1;
    2276             : 
    2277          12 :         if (*power == POWER_ACTIVE)
    2278           0 :                 return devdown(device, name, smartctl);
    2279             : 
    2280          12 :         return 0;
    2281             : }
    2282             : #endif
    2283             : 
    2284             : /**
    2285             :  * Spin up a specific device.
    2286             :  */
    2287             : #if HAVE_LINUX_DEVICE
    2288          42 : static int devup(dev_t device, const char* name)
    2289             : {
    2290             :         char file[PATH_MAX];
    2291             :         int ret;
    2292             :         int f;
    2293             :         void* buf;
    2294             :         uint64_t size;
    2295             :         uint64_t offset;
    2296             : 
    2297          42 :         if (devresolve(device, file, sizeof(file)) != 0) {
    2298             :                 /* LCOV_EXCL_START */
    2299             :                 log_fatal(EEXTERNAL, "Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    2300             :                 return -1;
    2301             :                 /* LCOV_EXCL_STOP */
    2302             :         }
    2303             : 
    2304             :         /* O_DIRECT requires memory aligned to the block size */
    2305          42 :         if (posix_memalign(&buf, 4096, 4096) != 0) {
    2306             :                 /* LCOV_EXCL_START */
    2307             :                 log_fatal(errno, "Failed to allocate aligned memory for device '%u:%u'.\n", major(device), minor(device));
    2308             :                 return -1;
    2309             :                 /* LCOV_EXCL_STOP */
    2310             :         }
    2311             : 
    2312          42 :         f = open(file, O_RDONLY | O_DIRECT);
    2313          42 :         if (f < 0) {
    2314             :                 /* LCOV_EXCL_START */
    2315             :                 free(buf);
    2316             :                 log_tag("device:%s:%s:error:%d\n", file, name, errno);
    2317             :                 log_fatal(errno, "Failed to open device '%u:%u'.\n", major(device), minor(device));
    2318             :                 return -1;
    2319             :                 /* LCOV_EXCL_STOP */
    2320             :         }
    2321             : 
    2322          12 :         if (ioctl(f, BLKGETSIZE64, &size) < 0) {
    2323             :                 /* LCOV_EXCL_START */
    2324             :                 close(f);
    2325             :                 free(buf);
    2326             :                 log_tag("device:%s:%s:error:%d\n", file, name, errno);
    2327             :                 log_fatal(errno, "Failed to get device size '%u:%u'.\n", major(device), minor(device));
    2328             :                 return -1;
    2329             :                 /* LCOV_EXCL_STOP */
    2330             :         }
    2331             : 
    2332             :         /* select a random offset */
    2333          12 :         offset = (random_u64() % (size / 4096)) * 4096;
    2334             : 
    2335             : #if HAVE_POSIX_FADVISE
    2336             :         /* clear cache */
    2337          12 :         ret = posix_fadvise_wrapper(f, offset, 4096, POSIX_FADV_DONTNEED);
    2338          12 :         if (ret == ENOSYS) {
    2339             :                 /* call is not supported */
    2340           0 :                 ret = 0;
    2341             :         }
    2342          12 :         if (ret != 0) {
    2343             :                 /* LCOV_EXCL_START */
    2344             :                 close(f);
    2345             :                 free(buf);
    2346             :                 log_tag("device:%s:%s:error:%d\n", file, name, errno);
    2347             :                 log_fatal(errno, "Failed to advise device '%u:%u'.\n", major(device), minor(device));
    2348             :                 return -1;
    2349             :                 /* LCOV_EXCL_STOP */
    2350             :         }
    2351             : #endif
    2352             : 
    2353          12 :         ret = pread(f, buf, 4096, offset);
    2354          12 :         if (ret < 0) {
    2355             :                 /* LCOV_EXCL_START */
    2356             :                 close(f);
    2357             :                 free(buf);
    2358             :                 log_tag("device:%s:%s:error:%d\n", file, name, errno);
    2359             :                 log_fatal(errno, "Failed to read device '%u:%u'.\n", major(device), minor(device));
    2360             :                 return -1;
    2361             :                 /* LCOV_EXCL_STOP */
    2362             :         }
    2363             : 
    2364          12 :         ret = close(f);
    2365          12 :         if (ret < 0) {
    2366             :                 /* LCOV_EXCL_START */
    2367             :                 free(buf);
    2368             :                 log_tag("device:%s:%s:error:%d\n", file, name, errno);
    2369             :                 log_fatal(errno, "Failed to close device '%u:%u'.\n", major(device), minor(device));
    2370             :                 return -1;
    2371             :                 /* LCOV_EXCL_STOP */
    2372             :         }
    2373             : 
    2374          12 :         log_tag("attr:%s:%s:power:up\n", file, name);
    2375             : 
    2376          12 :         free(buf);
    2377          12 :         return 0;
    2378             : }
    2379             : #endif
    2380             : 
    2381             : /**
    2382             :  * Thread for spinning up.
    2383             :  *
    2384             :  * Note that filling up the devinfo object is done inside this thread,
    2385             :  * to avoid to block the main thread if the device need to be spin up
    2386             :  * to handle stat/resolve requests.
    2387             :  */
    2388          42 : static void* thread_spinup(void* arg)
    2389             : {
    2390             : #if HAVE_LINUX_DEVICE
    2391          42 :         devinfo_t* devinfo = arg;
    2392             :         uint64_t start;
    2393             : 
    2394          42 :         start = os_tick_ms();
    2395             : 
    2396          42 :         if (devup(devinfo->device, devinfo->name) != 0) {
    2397             :                 /* LCOV_EXCL_START */
    2398             :                 return (void*)-1;
    2399             :                 /* LCOV_EXCL_STOP */
    2400             :         }
    2401             : 
    2402          12 :         msg_status("Spunup device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
    2403             : 
    2404             :         /* after the spin up, get SMART info */
    2405          12 :         if (devsmart(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smart, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf) != 0) {
    2406             :                 /* LCOV_EXCL_START */
    2407             :                 return (void*)-1;
    2408             :                 /* LCOV_EXCL_STOP */
    2409             :         }
    2410             : 
    2411             :         /*
    2412             :          * Retrieve some attributes directly from the system.
    2413             :          *
    2414             :          * smartctl intentionally skips queries on devices in standby mode
    2415             :          * to prevent accidentally spinning them up.
    2416             :          */
    2417          12 :         devattr(devinfo->device, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf);
    2418             : 
    2419          12 :         return 0;
    2420             : #else
    2421             :         (void)arg;
    2422             :         return (void*)-1;
    2423             : #endif
    2424             : }
    2425             : 
    2426             : /**
    2427             :  * Thread for spinning down.
    2428             :  */
    2429          17 : static void* thread_spindown(void* arg)
    2430             : {
    2431             : #if HAVE_LINUX_DEVICE
    2432          17 :         devinfo_t* devinfo = arg;
    2433             :         uint64_t start;
    2434             : 
    2435          17 :         start = os_tick_ms();
    2436             : 
    2437          17 :         if (devdown(devinfo->device, devinfo->name, devinfo->smartctl) != 0) {
    2438             :                 /* LCOV_EXCL_START */
    2439             :                 return (void*)-1;
    2440             :                 /* LCOV_EXCL_STOP */
    2441             :         }
    2442             : 
    2443          12 :         msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
    2444             : 
    2445          12 :         return 0;
    2446             : #else
    2447             :         (void)arg;
    2448             :         return (void*)-1;
    2449             : #endif
    2450             : }
    2451             : 
    2452             : /**
    2453             :  * Thread for spinning down.
    2454             :  */
    2455          12 : static void* thread_spindownifup(void* arg)
    2456             : {
    2457             : #if HAVE_LINUX_DEVICE
    2458          12 :         devinfo_t* devinfo = arg;
    2459             :         uint64_t start;
    2460             :         int power;
    2461             : 
    2462          12 :         start = os_tick_ms();
    2463             : 
    2464          12 :         if (devdownifup(devinfo->device, devinfo->name, devinfo->smartctl, &power) != 0) {
    2465             :                 /* LCOV_EXCL_START */
    2466             :                 return (void*)-1;
    2467             :                 /* LCOV_EXCL_STOP */
    2468             :         }
    2469             : 
    2470          12 :         if (power == POWER_ACTIVE)
    2471           0 :                 msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
    2472             : 
    2473          12 :         return 0;
    2474             : #else
    2475             :         (void)arg;
    2476             :         return (void*)-1;
    2477             : #endif
    2478             : }
    2479             : 
    2480             : /**
    2481             :  * Thread for getting smart info.
    2482             :  */
    2483          14 : static void* thread_smart(void* arg)
    2484             : {
    2485             : #if HAVE_LINUX_DEVICE
    2486          14 :         devinfo_t* devinfo = arg;
    2487             : 
    2488          14 :         if (devsmart(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smart, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf) != 0) {
    2489             :                 /* LCOV_EXCL_START */
    2490             :                 return (void*)-1;
    2491             :                 /* LCOV_EXCL_STOP */
    2492             :         }
    2493             : 
    2494             :         /*
    2495             :          * Retrieve some attributes directly from the system.
    2496             :          *
    2497             :          * smartctl intentionally skips queries on devices in standby mode
    2498             :          * to prevent accidentally spinning them up.
    2499             :          */
    2500          14 :         devattr(devinfo->device, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf);
    2501             : 
    2502          14 :         return 0;
    2503             : #else
    2504             :         (void)arg;
    2505             :         return (void*)-1;
    2506             : #endif
    2507             : }
    2508             : 
    2509             : /**
    2510             :  * Thread for getting power info.
    2511             :  */
    2512          28 : static void* thread_probe(void* arg)
    2513             : {
    2514             : #if HAVE_LINUX_DEVICE
    2515          28 :         devinfo_t* devinfo = arg;
    2516             : 
    2517          28 :         if (devprobe(devinfo->device, devinfo->name, devinfo->smartctl, &devinfo->power, devinfo->smart, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf) != 0) {
    2518             :                 /* LCOV_EXCL_START */
    2519             :                 return (void*)-1;
    2520             :                 /* LCOV_EXCL_STOP */
    2521             :         }
    2522             : 
    2523             :         /*
    2524             :          * Retrieve some attributes directly from the system.
    2525             :          *
    2526             :          * smartctl intentionally skips queries on devices in standby mode
    2527             :          * to prevent accidentally spinning them up.
    2528             :          */
    2529          28 :         devattr(devinfo->device, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf);
    2530             : 
    2531          28 :         return 0;
    2532             : #else
    2533             :         (void)arg;
    2534             :         return (void*)-1;
    2535             : #endif
    2536             : }
    2537             : 
    2538           8 : static int device_thread(tommy_list* list, void* (*func)(void* arg))
    2539             : {
    2540           8 :         int fail = 0;
    2541             :         tommy_node* i;
    2542             : 
    2543             : #if HAVE_THREAD
    2544             :         /* start all threads */
    2545         121 :         for (i = tommy_list_head(list); i != 0; i = i->next) {
    2546         113 :                 devinfo_t* devinfo = i->data;
    2547             : 
    2548         113 :                 thread_create(&devinfo->thread, func, devinfo);
    2549             :         }
    2550             : 
    2551             :         /* join all threads */
    2552         121 :         for (i = tommy_list_head(list); i != 0; i = i->next) {
    2553         113 :                 devinfo_t* devinfo = i->data;
    2554             :                 void* retval;
    2555             : 
    2556         113 :                 thread_join(devinfo->thread, &retval);
    2557             : 
    2558         113 :                 if (retval != 0)
    2559          35 :                         ++fail;
    2560             :         }
    2561             : #else
    2562             :         for (i = tommy_list_head(list); i != 0; i = i->next) {
    2563             :                 devinfo_t* devinfo = i->data;
    2564             : 
    2565             :                 if (func(devinfo) != 0)
    2566             :                         ++fail;
    2567             :         }
    2568             : #endif
    2569           8 :         if (fail != 0) {
    2570             :                 /* LCOV_EXCL_START */
    2571             :                 return -1;
    2572             :                 /* LCOV_EXCL_STOP */
    2573             :         }
    2574             : 
    2575           6 :         return 0;
    2576             : }
    2577             : 
    2578          10 : int devquery(tommy_list* high, tommy_list* low, int operation)
    2579             : {
    2580          10 :         void* (*func)(void* arg) = 0;
    2581             : 
    2582             : #if HAVE_LINUX_DEVICE
    2583             :         tommy_node* i;
    2584             :         struct stat st;
    2585             : 
    2586             :         /* sysfs interface is required */
    2587          10 :         if (stat("/sys/dev/block", &st) != 0) {
    2588             :                 /* LCOV_EXCL_START */
    2589             :                 log_fatal(EEXTERNAL, "Missing interface /sys/dev/block.\n");
    2590             :                 return -1;
    2591             :                 /* LCOV_EXCL_STOP */
    2592             :         }
    2593             : 
    2594             :         /* for each high device */
    2595         167 :         for (i = tommy_list_head(high); i != 0; i = i->next) {
    2596         157 :                 devinfo_t* devinfo = i->data;
    2597         157 :                 uint64_t device = devinfo->device;
    2598             : 
    2599             : #if HAVE_SYNCFS
    2600         157 :                 if (operation == DEVICE_DOWN) {
    2601             :                         /* flush the high level filesystem before spinning down */
    2602          17 :                         int f = open(devinfo->mount, O_RDONLY);
    2603          17 :                         if (f >= 0) {
    2604          17 :                                 syncfs(f);
    2605          17 :                                 close(f);
    2606             :                         }
    2607             :                 }
    2608             : #endif
    2609             : 
    2610             :                 tommy_list devlist;
    2611         157 :                 tommy_list_init(&devlist);
    2612             : 
    2613             :                 /* obtain the real devices */
    2614         157 :                 if (devdereference(device, devinfo->mount, &devlist) != 0) {
    2615             :                         /* LCOV_EXCL_START */
    2616             :                         log_fatal(EEXTERNAL, "Failed to dereference device '%u:%u' at '%s'.\n", major(device), minor(device), devinfo->mount);
    2617             :                         return -1;
    2618             :                         /* LCOV_EXCL_STOP */
    2619             :                 }
    2620             : 
    2621         157 :                 devinfo->file[0] = 0;
    2622         314 :                 for (tommy_node* j = tommy_list_head(&devlist); j != 0; j = j->next) {
    2623         157 :                         struct dev_struct* dev = j->data;
    2624             :                         uint64_t access_stat;
    2625             : 
    2626             :                         /* retrieve access stat for the high level device */
    2627         157 :                         if (devstat(dev->device, &access_stat) == 0) {
    2628             :                                 /* cumulate access stat in the first split */
    2629         157 :                                 if (devinfo->split)
    2630          39 :                                         devinfo->split->access_stat += access_stat;
    2631             :                                 else
    2632         118 :                                         devinfo->access_stat += access_stat;
    2633             :                         }
    2634             : 
    2635             :                         /* get the device file */
    2636             :                         char file[PATH_MAX];
    2637         157 :                         if (devresolve(dev->device, file, sizeof(file)) != 0) {
    2638             :                                 /* LCOV_EXCL_START */
    2639             :                                 log_fatal(EEXTERNAL, "Failed to resolve device '%u:%u'.\n", major(dev->device), minor(dev->device));
    2640             :                                 return -1;
    2641             :                                 /* LCOV_EXCL_STOP */
    2642             :                         }
    2643             : 
    2644             :                         /* add to the list of device files */
    2645         157 :                         if (devinfo->file[0] != 0)
    2646           0 :                                 pathcat(devinfo->file, sizeof(devinfo->file), ",");
    2647         157 :                         pathcat(devinfo->file, sizeof(devinfo->file), file);
    2648             : 
    2649             :                         /* expand the tree of devices */
    2650         157 :                         if (devtree(devinfo, dev->device, low) != 0) {
    2651             :                                 /* LCOV_EXCL_START */
    2652             :                                 log_fatal(EEXTERNAL, "Failed to expand device '%u:%u'.\n", major(dev->device), minor(dev->device));
    2653             :                                 return -1;
    2654             :                                 /* LCOV_EXCL_STOP */
    2655             :                         }
    2656             :                 }
    2657             : 
    2658         157 :                 tommy_list_foreach(&devlist, free);
    2659             :         }
    2660             : #else
    2661             :         (void)high;
    2662             : #endif
    2663             : 
    2664          10 :         switch (operation) {
    2665           2 :         case DEVICE_UP : func = thread_spinup; break;
    2666           2 :         case DEVICE_DOWN : func = thread_spindown; break;
    2667           1 :         case DEVICE_SMART : func = thread_smart; break;
    2668           2 :         case DEVICE_PROBE : func = thread_probe; break;
    2669           1 :         case DEVICE_DOWNIFUP : func = thread_spindownifup; break;
    2670             :         }
    2671             : 
    2672          10 :         if (!func)
    2673           2 :                 return 0;
    2674             : 
    2675           8 :         return device_thread(low, func);
    2676             : }
    2677             : 
    2678         322 : void os_init(int opt)
    2679             : {
    2680             : #if HAVE_BLKID
    2681             :         int ret;
    2682         322 :         ret = blkid_get_cache(&cache, NULL);
    2683         322 :         if (ret != 0) {
    2684             :                 /* LCOV_EXCL_START */
    2685             :                 log_fatal(EEXTERNAL, "WARNING Failed to get blkid cache\n");
    2686             :                 /* LCOV_EXCL_STOP */
    2687             :         }
    2688             : #endif
    2689             : 
    2690             :         /* set LC_ALL=C to make smartctl ignoring the locale when printing info */
    2691         322 :         setenv("LC_ALL", "C", 1);
    2692             : 
    2693             :         (void)opt;
    2694         322 : }
    2695             : 
    2696         297 : void os_done(void)
    2697             : {
    2698             : #if HAVE_BLKID
    2699         297 :         if (cache != 0)
    2700         297 :                 blkid_put_cache(cache);
    2701             : #endif
    2702         297 : }
    2703             : 
    2704             : /* LCOV_EXCL_START */
    2705             : void os_abort(void)
    2706             : {
    2707             : #if HAVE_BACKTRACE && HAVE_BACKTRACE_SYMBOLS
    2708             :         void* stack[32];
    2709             :         char** messages;
    2710             :         size_t size;
    2711             :         unsigned i;
    2712             : #endif
    2713             : 
    2714             :         printf("Stacktrace of " PACKAGE " v" VERSION);
    2715             : #ifdef _linux
    2716             :         printf(", linux");
    2717             : #endif
    2718             : #ifdef __GNUC__
    2719             :         printf(", gcc " __VERSION__);
    2720             : #endif
    2721             :         printf(", %d-bit", (int)sizeof(void*) * 8);
    2722             :         printf(", PATH_MAX=%d", PATH_MAX);
    2723             :         printf("\n");
    2724             : 
    2725             : #if HAVE_BACKTRACE && HAVE_BACKTRACE_SYMBOLS
    2726             :         size = backtrace(stack, 32);
    2727             : 
    2728             :         messages = backtrace_symbols(stack, size);
    2729             : 
    2730             :         for (i = 1; i < size; ++i) {
    2731             :                 const char* msg;
    2732             : 
    2733             :                 if (messages)
    2734             :                         msg = messages[i];
    2735             :                 else
    2736             :                         msg = "<unknown>";
    2737             : 
    2738             :                 printf("[bt] %02u: %s\n", i, msg);
    2739             : 
    2740             :                 if (messages) {
    2741             :                         int ret;
    2742             :                         char addr2line[1024];
    2743             :                         size_t j = 0;
    2744             :                         while (msg[j] != '(' && msg[j] != ' ' && msg[j] != 0)
    2745             :                                 ++j;
    2746             : 
    2747             :                         snprintf(addr2line, sizeof(addr2line), "addr2line %p -e %.*s", stack[i], (unsigned)j, msg);
    2748             : 
    2749             :                         ret = system(addr2line);
    2750             :                         if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0)
    2751             :                                 printf("exit:%d\n", WEXITSTATUS(ret));
    2752             :                         if (WIFSIGNALED(ret))
    2753             :                                 printf("signal:%d\n", WTERMSIG(ret));
    2754             :                 }
    2755             :         }
    2756             : #endif
    2757             : 
    2758             :         printf("Please report this error to the SnapRAID Forum:\n");
    2759             :         printf("https://sourceforge.net/p/snapraid/discussion/1677233/\n");
    2760             : 
    2761             :         abort();
    2762             : }
    2763             : /* LCOV_EXCL_STOP */
    2764             : 
    2765           0 : void os_clear(void)
    2766             : {
    2767             :         /* ANSI codes */
    2768           0 :         printf("\033[H"); /* cursor at topleft */
    2769           0 :         printf("\033[2J"); /* clear screen */
    2770           0 : }
    2771             : 
    2772         135 : size_t direct_size(void)
    2773             : {
    2774             :         long size;
    2775             : 
    2776         135 :         size = sysconf(_SC_PAGESIZE);
    2777             : 
    2778         135 :         if (size == -1) {
    2779             :                 /* LCOV_EXCL_START */
    2780             :                 log_fatal(EEXTERNAL, "No page size\n");
    2781             :                 exit(EXIT_FAILURE);
    2782             :                 /* LCOV_EXCL_STOP */
    2783             :         }
    2784             : 
    2785         135 :         return size;
    2786             : }
    2787             : 
    2788             : #if HAVE_LINUX_DEVICE
    2789             : 
    2790             : /* List of possible ambient temperature labels */
    2791             : const char* AMBIENT_LABEL[] = {
    2792             :         "systin",
    2793             :         "auxtin",
    2794             :         "mb",
    2795             :         "m/b",
    2796             :         "board",
    2797             :         "motherboard",
    2798             :         "system",
    2799             :         "chassis",
    2800             :         "case",
    2801             :         "room",
    2802             :         "ambient",
    2803             :         0
    2804             : };
    2805             : 
    2806           1 : int ambient_temperature(void)
    2807             : {
    2808             :         DIR* dir;
    2809             :         struct dirent* entry;
    2810           1 :         int lowest_temp = 0;
    2811             : 
    2812           1 :         dir = opendir("/sys/class/hwmon");
    2813           1 :         if (!dir) {
    2814             :                 /* LCOV_EXCL_START */
    2815             :                 return 0;
    2816             :                 /* LCOV_EXCL_STOP */
    2817             :         }
    2818             : 
    2819             :         /* iterate through hwmon devices */
    2820           5 :         while ((entry = readdir(dir)) != NULL) {
    2821             :                 char path[PATH_MAX];
    2822             :                 DIR* hwmon_dir;
    2823             :                 struct dirent* hwmon_entry;
    2824             : 
    2825           4 :                 if (strncmp(entry->d_name, "hwmon", 5) != 0)
    2826           2 :                         continue;
    2827             : 
    2828           2 :                 pathprint(path, sizeof(path), "/sys/class/hwmon/%s", entry->d_name);
    2829             : 
    2830             :                 /* iterate through temp*_input files */
    2831           2 :                 hwmon_dir = opendir(path);
    2832           2 :                 if (!hwmon_dir) {
    2833             :                         /* LCOV_EXCL_START */
    2834             :                         continue;
    2835             :                         /* LCOV_EXCL_STOP */
    2836             :                 }
    2837             : 
    2838          66 :                 while ((hwmon_entry = readdir(hwmon_dir)) != NULL) {
    2839             :                         char value[128];
    2840             :                         char name[128];
    2841             :                         char label[128];
    2842             :                         char* dash;
    2843             :                         char* e;
    2844             :                         long temp;
    2845             : 
    2846          64 :                         if (strncmp(hwmon_entry->d_name, "temp", 4) != 0)
    2847          63 :                                 continue;
    2848             : 
    2849          18 :                         dash = strrchr(hwmon_entry->d_name, '_');
    2850          18 :                         if (dash == 0) {
    2851             :                                 /* LCOV_EXCL_START */
    2852             :                                 continue;
    2853             :                                 /* LCOV_EXCL_STOP */
    2854             :                         }
    2855             : 
    2856          18 :                         if (strcmp(dash, "_input") != 0)
    2857          14 :                                 continue;
    2858             : 
    2859             :                         /* read the temperature */
    2860           4 :                         pathprint(path, sizeof(path), "/sys/class/hwmon/%s/%s", entry->d_name, hwmon_entry->d_name);
    2861             : 
    2862           4 :                         if (sysattr(path, value, sizeof(value)) != 0) {
    2863             :                                 /* LCOV_EXCL_START */
    2864             :                                 continue;
    2865             :                                 /* LCOV_EXCL_STOP */
    2866             :                         }
    2867             : 
    2868           4 :                         temp = strtol(value, &e, 10) / 1000;
    2869           4 :                         if (*e != 0 && !isspace(*e)) {
    2870             :                                 /* LCOV_EXCL_START */
    2871             :                                 continue;
    2872             :                                 /* LCOV_EXCL_STOP */
    2873             :                         }
    2874             : 
    2875             :                         /* cut the file name at "_input" */
    2876           4 :                         *dash = 0;
    2877             : 
    2878             :                         /* read the corresponding name */
    2879           4 :                         pathprint(path, sizeof(path), "/sys/class/hwmon/%s/name", entry->d_name);
    2880           4 :                         if (sysattr(path, name, sizeof(name)) != 0) {
    2881             :                                 /* LCOV_EXCL_START */
    2882             :                                 /* fallback to using the hwmon name */
    2883             :                                 pathcpy(name, sizeof(name), entry->d_name);
    2884             :                                 /* LCOV_EXCL_STOP */
    2885             :                         }
    2886             : 
    2887             :                         /* read the corresponding label file */
    2888           4 :                         pathprint(path, sizeof(path), "/sys/class/hwmon/%s/%s_label", entry->d_name, hwmon_entry->d_name);
    2889           4 :                         if (sysattr(path, label, sizeof(label)) != 0) {
    2890             :                                 /* LCOV_EXCL_START */
    2891             :                                 /* fallback to using the temp* name (e.g., temp1, temp2) */
    2892             :                                 pathcpy(label, sizeof(label), hwmon_entry->d_name);
    2893             :                                 /* LCOV_EXCL_STOP */
    2894             :                         }
    2895             : 
    2896           4 :                         log_tag("thermal:ambient:device:%s:%s:%s:%s:%ld\n", entry->d_name, name, hwmon_entry->d_name, label, temp);
    2897             : 
    2898             :                         /* check if temperature is in reasonable range */
    2899           4 :                         if (temp < 15 || temp > 40)
    2900           3 :                                 continue;
    2901             : 
    2902             :                         /* lower case */
    2903           1 :                         strlwr(label);
    2904             : 
    2905             :                         /* check if label matches possible ambient labels */
    2906           3 :                         for (int i = 0; AMBIENT_LABEL[i]; ++i) {
    2907           3 :                                 if (worddigitstr(label, AMBIENT_LABEL[i]) != 0) {
    2908           1 :                                         log_tag("thermal:ambient:candidate:%ld\n", temp);
    2909           1 :                                         if (lowest_temp == 0 || lowest_temp > temp)
    2910           1 :                                                 lowest_temp = temp;
    2911           1 :                                         break;
    2912             :                                 }
    2913             :                         }
    2914             : 
    2915             :                         /* accept also generic "temp1" */
    2916           1 :                         if (strcmp(label, "temp1") == 0) {
    2917           0 :                                 log_tag("thermal:ambient:candidate:%ld\n", temp);
    2918           0 :                                 if (lowest_temp == 0 || lowest_temp > temp)
    2919           0 :                                         lowest_temp = temp;
    2920             :                         }
    2921             :                 }
    2922             : 
    2923           2 :                 closedir(hwmon_dir);
    2924             :         }
    2925             : 
    2926           1 :         closedir(dir);
    2927             : 
    2928           1 :         return lowest_temp;
    2929             : }
    2930             : #else
    2931             : int ambient_temperature(void)
    2932             : {
    2933             :         return 0;
    2934             : }
    2935             : #endif
    2936             : 
    2937          13 : int devmap(void)
    2938             : {
    2939             : #if HAVE_LINUX_DEVICE
    2940             :         char esc_buffer[ESC_MAX];
    2941             :         char path[PATH_MAX];
    2942          13 :         DIR* d = opendir("/sys/block");
    2943          13 :         if (!d) {
    2944             :                 /* LCOV_EXCL_START */
    2945             :                 return -1;
    2946             :                 /* LCOV_EXCL_STOP */
    2947             :         }
    2948             : 
    2949             :         struct dirent* dd;
    2950         714 :         while (1) {
    2951             :                 char buf[256];
    2952             : 
    2953         727 :                 dd = readdir(d);
    2954         727 :                 if (dd == 0)
    2955          13 :                         break;
    2956             : 
    2957         714 :                 if (dd->d_name[0] == '.')
    2958         526 :                         continue;
    2959             : 
    2960             :                 /* verify it has a hardware device link (excludes virtual disks) */
    2961         688 :                 pathprint(path, sizeof(path), "/sys/block/%s/device", dd->d_name);
    2962         688 :                 if (access(path, F_OK) != 0)
    2963         500 :                         continue;
    2964             : 
    2965             :                 /* device/serial */
    2966         188 :                 pathprint(path, sizeof(path), "/sys/block/%s/device/serial", dd->d_name);
    2967         188 :                 if (sysattr(path, buf, sizeof(buf)) == 0 && buf[0] != 0) {
    2968           0 :                         log_tag("map:/dev/%s:%s\n", dd->d_name, esc_tag(buf, esc_buffer));
    2969             :                 }
    2970             : 
    2971             :                 /* device/vpd_pg80 */
    2972         188 :                 pathprint(path, sizeof(path), "/sys/block/%s/device/vpd_pg80", dd->d_name);
    2973         188 :                 if (sysattr_vpd_pg80(path, buf, sizeof(buf)) == 0 && buf[0] != 0) {
    2974         188 :                         log_tag("map:/dev/%s:%s\n", dd->d_name, esc_tag(buf, esc_buffer));
    2975             :                 }
    2976             : 
    2977             :                 /* device/vpd_pg83 */
    2978         188 :                 pathprint(path, sizeof(path), "/sys/block/%s/device/vpd_pg83", dd->d_name);
    2979         188 :                 if (sysattr_vpd_pg83(path, buf, sizeof(buf)) == 0 && buf[0] != 0) {
    2980         188 :                         log_tag("map:/dev/%s:naa.%s\n", dd->d_name, esc_tag(buf, esc_buffer));
    2981             :                 }
    2982             : 
    2983             :                 /* device/WWN */
    2984         188 :                 pathprint(path, sizeof(path), "/sys/block/%s/device/wwn", dd->d_name);
    2985         188 :                 if (sysattr(path, buf, sizeof(buf)) == 0 && buf[0] != 0) {
    2986           0 :                         log_tag("map:/dev/%s:%s\n", dd->d_name, esc_tag(buf, esc_buffer));
    2987             :                 }
    2988             : 
    2989             :                 /* device/WWID */
    2990         188 :                 pathprint(path, sizeof(path), "/sys/block/%s/device/wwid", dd->d_name);
    2991         188 :                 if (sysattr(path, buf, sizeof(buf)) == 0 && buf[0] != 0) {
    2992         188 :                         log_tag("map:/dev/%s:%s\n", dd->d_name, esc_tag(buf, esc_buffer));
    2993             :                 }
    2994             : 
    2995             :                 /* WWN */
    2996         188 :                 pathprint(path, sizeof(path), "/sys/block/%s/wwn", dd->d_name);
    2997         188 :                 if (sysattr(path, buf, sizeof(buf)) == 0 && buf[0] != 0) {
    2998           0 :                         log_tag("map:/dev/%s:%s\n", dd->d_name, esc_tag(buf, esc_buffer));
    2999             :                 }
    3000             : 
    3001             :                 /* WWID */
    3002         188 :                 pathprint(path, sizeof(path), "/sys/block/%s/wwid", dd->d_name);
    3003         188 :                 if (sysattr(path, buf, sizeof(buf)) == 0 && buf[0] != 0) {
    3004           0 :                         log_tag("map:/dev/%s:%s\n", dd->d_name, esc_tag(buf, esc_buffer));
    3005             :                 }
    3006             : 
    3007             :                 /* uuid */
    3008         188 :                 pathprint(path, sizeof(path), "/sys/block/%s/uuid", dd->d_name);
    3009         188 :                 if (sysattr(path, buf, sizeof(buf)) == 0 && buf[0] != 0) {
    3010           0 :                         log_tag("map:/dev/%s:%s\n", dd->d_name, esc_tag(buf, esc_buffer));
    3011             :                 }
    3012             :         }
    3013             : 
    3014          13 :         closedir(d);
    3015             : #endif
    3016          13 :         return 0;
    3017             : }
    3018             : 
    3019             : #endif
    3020             : 

Generated by: LCOV version 1.0