LCOV - code coverage report
Current view: top level - cmdline - unix.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 488 646 75.5 %
Date: 2026-03-15 15:58:19 Functions: 35 41 85.4 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2013 Andrea Mazzoleni
       3             :  *
       4             :  * This program is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 3 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * This program is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "portable.h"
      19             : 
      20             : #ifndef __MINGW32__ /* Only for Unix */
      21             : 
      22             : #include "support.h"
      23             : 
      24             : /**
      25             :  * Exit codes.
      26             :  */
      27             : int exit_success = 0;
      28             : int exit_failure = 1;
      29             : int exit_sync_needed = 2;
      30             : 
      31     1785762 : int open_noatime(const char* file, int flags)
      32             : {
      33             : #ifdef O_NOATIME
      34     1785762 :         int f = open(file, flags | O_NOATIME);
      35             : 
      36             :         /* only root is allowed to use O_NOATIME, in case retry without it */
      37     1785762 :         if (f == -1 && errno == EPERM)
      38           0 :                 f = open(file, flags);
      39     1785762 :         return f;
      40             : #else
      41             :         return open(file, flags);
      42             : #endif
      43             : }
      44             : 
      45           0 : int dirent_hidden(struct dirent* dd)
      46             : {
      47           0 :         return dd->d_name[0] == '.';
      48             : }
      49             : 
      50           0 : const char* stat_desc(struct stat* st)
      51             : {
      52           0 :         if (S_ISREG(st->st_mode))
      53           0 :                 return "regular";
      54           0 :         if (S_ISDIR(st->st_mode))
      55           0 :                 return "directory";
      56           0 :         if (S_ISCHR(st->st_mode))
      57           0 :                 return "character";
      58           0 :         if (S_ISBLK(st->st_mode))
      59           0 :                 return "block-device";
      60           0 :         if (S_ISFIFO(st->st_mode))
      61           0 :                 return "fifo";
      62           0 :         if (S_ISLNK(st->st_mode))
      63           0 :                 return "link";
      64           0 :         if (S_ISLNK(st->st_mode))
      65           0 :                 return "symbolic-link";
      66           0 :         if (S_ISSOCK(st->st_mode))
      67           0 :                 return "socket";
      68           0 :         return "unknown";
      69             : }
      70             : 
      71             : static const char* smartctl_paths[] = {
      72             :         /* Linux & BSD */
      73             :         "/usr/sbin/smartctl",
      74             :         "/sbin/smartctl",
      75             :         "/usr/local/sbin/smartctl",
      76             :         "/usr/bin/smartctl",
      77             :         "/usr/local/bin/smartctl",
      78             :         /* macOS (Intel & Apple Silicon) */
      79             :         "/opt/homebrew/sbin/smartctl",
      80             :         0
      81             : };
      82             : 
      83          71 : const char* find_smartctl(void)
      84             : {
      85             :         int i;
      86             : 
      87          71 :         for (i = 0; smartctl_paths[i]; ++i) {
      88          71 :                 if (access(smartctl_paths[i], X_OK) == 0)
      89          71 :                         return smartctl_paths[i];
      90             :         }
      91             : 
      92           0 :         return 0;
      93             : }
      94             : 
      95             : /**
      96             :  * Read a file from sys
      97             :  *
      98             :  * Return -1 on error, otherwise the size of data read
      99             :  */
     100             : #if HAVE_LINUX_DEVICE
     101         704 : static int sysread(const char* path, char* buf, size_t buf_size)
     102             : {
     103             :         int f;
     104             :         int ret;
     105             :         int len;
     106             : 
     107         704 :         f = open(path, O_RDONLY);
     108         704 :         if (f == -1) {
     109             :                 /* LCOV_EXCL_START */
     110             :                 return -1;
     111             :                 /* LCOV_EXCL_STOP */
     112             :         }
     113             : 
     114         650 :         len = read(f, buf, buf_size);
     115         650 :         if (len < 0) {
     116             :                 /* LCOV_EXCL_START */
     117             :                 close(f);
     118             :                 return -1;
     119             :                 /* LCOV_EXCL_STOP */
     120             :         }
     121             : 
     122         650 :         ret = close(f);
     123         650 :         if (ret != 0) {
     124             :                 /* LCOV_EXCL_START */
     125             :                 return -1;
     126             :                 /* LCOV_EXCL_STOP */
     127             :         }
     128             : 
     129         650 :         return len;
     130             : }
     131             : #endif
     132             : 
     133             : /**
     134             :  * Read a file from sys.
     135             :  * Trim spaces.
     136             :  * Always put an ending 0.
     137             :  * Do not report error on reading.
     138             :  * Return an error if truncated
     139             :  *
     140             :  * Return -1 on error, 0 on success
     141             :  */
     142             : #if HAVE_LINUX_DEVICE
     143         102 : static int sysattr(const char* path, char* buf, size_t buf_size)
     144             : {
     145             :         int len;
     146             : 
     147         102 :         len = sysread(path, buf, buf_size);
     148         102 :         if (len < 0) {
     149             :                 /* LCOV_EXCL_START */
     150             :                 return -1;
     151             :                 /* LCOV_EXCL_STOP */
     152             :         }
     153             : 
     154          48 :         if ((size_t)len + 1 > buf_size) {
     155             :                 /* LCOV_EXCL_START */
     156             :                 return -1;
     157             :                 /* LCOV_EXCL_STOP */
     158             :         }
     159             : 
     160          48 :         buf[len] = 0;
     161             : 
     162          48 :         strtrim(buf);
     163             : 
     164          48 :         return 0;
     165             : }
     166             : #endif
     167             : 
     168             : /**
     169             :  * Get the device file from the device number.
     170             :  *
     171             :  * It uses /proc/self/mountinfo.
     172             :  *
     173             :  * Null devices (major==0) are resolved to the device indicated in mountinfo.
     174             :  */
     175             : #if HAVE_LINUX_DEVICE
     176           4 : static int devresolve_proc(uint64_t device, char* path, size_t path_size)
     177             : {
     178             :         FILE* f;
     179             :         char match[32];
     180             : 
     181             :         /* generate the matching string */
     182           4 :         snprintf(match, sizeof(match), "%u:%u", major(device), minor(device));
     183             : 
     184           4 :         f = fopen("/proc/self/mountinfo", "r");
     185           4 :         if (!f) {
     186           0 :                 log_tag("resolve:proc:%u:%u: failed to open /proc/self/mountinfo\n", major(device), minor(device));
     187           0 :                 return -1;
     188             :         }
     189             : 
     190             :         /*
     191             :          * mountinfo format
     192             :          * 0 - mount ID
     193             :          * 1 - parent ID
     194             :          * 2 - major:minor
     195             :          * 3 - root
     196             :          * 4 - mount point
     197             :          * 5 - options
     198             :          * 6 - "-" (separator)
     199             :          * 7 - fs
     200             :          * 8 - mount source - /dev/device
     201             :          */
     202             : 
     203         192 :         while (1) {
     204             :                 char buf[256];
     205             :                 char* first_map[8];
     206             :                 unsigned first_mac;
     207             :                 char* second_map[8];
     208             :                 unsigned second_mac;
     209             :                 char* s;
     210             :                 struct stat st;
     211             :                 char* separator;
     212             :                 char* majorminor;
     213             :                 char* mountpoint;
     214             :                 char* fs;
     215             :                 char* mountsource;
     216             : 
     217         196 :                 s = fgets(buf, sizeof(buf), f);
     218         196 :                 if (s == 0)
     219           4 :                         break;
     220             : 
     221             :                 /* find the separator position */
     222         192 :                 separator = strstr(s, " - ");
     223         192 :                 if (!separator)
     224           0 :                         continue;
     225             : 
     226             :                 /* skip the separator */
     227         192 :                 *separator = 0;
     228         192 :                 separator += 3;
     229             : 
     230             :                 /* split the line */
     231         192 :                 first_mac = strsplit(first_map, 8, s, " \t\r\n");
     232         192 :                 second_mac = strsplit(second_map, 8, separator, " \t\r\n");
     233             : 
     234             :                 /* if too short, it's the wrong line */
     235         192 :                 if (first_mac < 5)
     236           0 :                         continue;
     237         192 :                 if (second_mac < 2)
     238           0 :                         continue;
     239             : 
     240         192 :                 majorminor = first_map[2];
     241         192 :                 mountpoint = first_map[4];
     242         192 :                 fs = second_map[0];
     243         192 :                 mountsource = second_map[1];
     244             : 
     245             :                 /* compare major:minor from mountinfo */
     246         192 :                 if (strcmp(majorminor, match) == 0) {
     247             :                         /*
     248             :                          * Accept only /dev/... mountsource
     249             :                          *
     250             :                          * This excludes ZFS that uses a bare label for mountsource, like "tank".
     251             :                          *
     252             :                          * 410 408 0:193 / /XXX rw,relatime shared:217 - zfs tank/system/data/var/lib/docker/XXX rw,xattr,noacl
     253             :                          *
     254             :                          * Also excludes AUTOFS unmounted devices that point to a fake filesystem
     255             :                          * used to remount them at the first use.
     256             :                          *
     257             :                          * 97 25 0:42 / /XXX rw,relatime shared:76 - autofs /etc/auto.seed rw,fd=6,pgrp=952,timeout=30,minproto=5,maxproto=5,indirect
     258             :                          */
     259           0 :                         if (strncmp(mountsource, "/dev/", 5) != 0) {
     260           0 :                                 log_tag("resolve:proc:%u:%u: match skipped for not /dev/ mountsource for %s %s\n", major(device), minor(device), fs, mountsource);
     261           0 :                                 continue;
     262             :                         }
     263             : 
     264           0 :                         pathcpy(path, path_size, mountsource);
     265             : 
     266           0 :                         log_tag("resolve:proc:%u:%u: found device %s matching device %s\n", major(device), minor(device), path, match);
     267             : 
     268           0 :                         fclose(f);
     269           0 :                         return 0;
     270             :                 }
     271             : 
     272             :                 /* get the device of the mount point */
     273             :                 /* in Btrfs it could be different than the one in mountinfo */
     274         192 :                 if (stat(mountpoint, &st) == 0 && st.st_dev == device) {
     275           0 :                         if (strncmp(mountsource, "/dev/", 5) != 0) {
     276           0 :                                 log_tag("resolve:proc:%u:%u: match skipped for not /dev/ mountsource for %s %s\n", major(device), minor(device), fs, mountsource);
     277           0 :                                 continue;
     278             :                         }
     279             : 
     280           0 :                         pathcpy(path, path_size, mountsource);
     281             : 
     282           0 :                         log_tag("resolve:proc:%u:%u: found device %s matching mountpoint %s\n", major(device), minor(device), path, mountpoint);
     283             : 
     284           0 :                         fclose(f);
     285           0 :                         return 0;
     286             :                 }
     287             :         }
     288             : 
     289           4 :         log_tag("resolve:proc:%u:%u: not found\n", major(device), minor(device));
     290             : 
     291           4 :         fclose(f);
     292           4 :         return -1;
     293             : }
     294             : #endif
     295             : 
     296             : /**
     297             :  * Get the device of a virtual superblock.
     298             :  *
     299             :  * This is intended to resolve the case of Btrfs filesystems that
     300             :  * create a virtual superblock (major==0) not backed by any low
     301             :  * level device.
     302             :  *
     303             :  * See:
     304             :  * Bug 711881 - too funny btrfs st_dev numbers
     305             :  * https://bugzilla.redhat.com/show_bug.cgi?id=711881
     306             :  */
     307             : #if HAVE_LINUX_DEVICE
     308           4 : static int devdereference(uint64_t device, uint64_t* new_device)
     309             : {
     310             :         char path[PATH_MAX];
     311             :         struct stat st;
     312             : 
     313             :         /* use the proc interface to get the device containing the filesystem */
     314           4 :         if (devresolve_proc(device, path, sizeof(path)) != 0) {
     315             :                 /* LCOV_EXCL_START */
     316             :                 return -1;
     317             :                 /* LCOV_EXCL_STOP */
     318             :         }
     319             : 
     320             :         /* check the device */
     321           0 :         if (stat(path, &st) != 0) {
     322             :                 /* LCOV_EXCL_START */
     323             :                 log_tag("dereference:%u:%u: failed to stat %s\n", major(device), minor(device), path);
     324             :                 return -1;
     325             :                 /* LCOV_EXCL_STOP */
     326             :         }
     327             : 
     328           0 :         if (major(st.st_rdev) == 0) {
     329             :                 /* LCOV_EXCL_START */
     330             :                 log_tag("dereference:%u:%u: still null device %s -> %u:%u\n", major(device), minor(device), path, major(st.st_rdev), minor(st.st_rdev));
     331             :                 return -1;
     332             :                 /* LCOV_EXCL_STOP */
     333             :         }
     334             : 
     335           0 :         *new_device = st.st_rdev;
     336           0 :         log_tag("dereference:%u:%u: found %u:%u\n", major(device), minor(device), major(st.st_rdev), minor(st.st_rdev));
     337           0 :         return 0;
     338             : }
     339             : #endif
     340             : 
     341             : /**
     342             :  * Read a file extracting the specified tag TAG=VALUE format.
     343             :  * Return !=0 on error.
     344             :  */
     345             : #if HAVE_LINUX_DEVICE
     346         403 : static int tagread(const char* path, const char* tag, char* value, size_t value_size)
     347             : {
     348             :         int ret;
     349             :         char buf[512];
     350             :         size_t tag_len;
     351             :         char* i;
     352             :         char* e;
     353             : 
     354         403 :         ret = sysread(path, buf, sizeof(buf));
     355         403 :         if (ret < 0) {
     356             :                 /* LCOV_EXCL_START */
     357             :                 log_fatal(errno, "Failed to read '%s'.\n", path);
     358             :                 return -1;
     359             :                 /* LCOV_EXCL_STOP */
     360             :         }
     361         403 :         if ((size_t)ret + 1 > sizeof(buf)) {
     362             :                 /* LCOV_EXCL_START */
     363             :                 log_fatal(EEXTERNAL, "Too long read '%s'.\n", path);
     364             :                 return -1;
     365             :                 /* LCOV_EXCL_STOP */
     366             :         }
     367             : 
     368             :         /* ending 0 */
     369         403 :         buf[ret] = 0;
     370             : 
     371         403 :         tag_len = strlen(tag);
     372             : 
     373        7087 :         for (i = buf; *i; ++i) {
     374        7087 :                 char* p = i;
     375             : 
     376             :                 /* start with a space */
     377        7087 :                 if (p != buf) {
     378        6684 :                         if (!isspace(*p))
     379        5878 :                                 continue;
     380         806 :                         ++p;
     381             :                 }
     382             : 
     383        1209 :                 if (strncmp(p, tag, tag_len) != 0)
     384         806 :                         continue;
     385         403 :                 p += tag_len;
     386             : 
     387             :                 /* end with a = */
     388         403 :                 if (*p != '=')
     389           0 :                         continue;
     390         403 :                 ++p;
     391             : 
     392             :                 /* found */
     393         403 :                 i = p;
     394         403 :                 break;
     395             :         }
     396         403 :         if (!*i) {
     397             :                 /* LCOV_EXCL_START */
     398             :                 log_fatal(EEXTERNAL, "Missing tag '%s' for '%s'.\n", tag, path);
     399             :                 return -1;
     400             :                 /* LCOV_EXCL_STOP */
     401             :         }
     402             : 
     403             :         /* terminate at the first space */
     404         403 :         e = i;
     405        1781 :         while (*e != 0 && !isspace(*e))
     406        1378 :                 ++e;
     407         403 :         *e = 0;
     408             : 
     409         403 :         if (!*i) {
     410             :                 /* LCOV_EXCL_START */
     411             :                 log_fatal(EEXTERNAL, "Empty tag '%s' for '%s'.\n", tag, path);
     412             :                 return -1;
     413             :                 /* LCOV_EXCL_STOP */
     414             :         }
     415             : 
     416         403 :         pathprint(value, value_size, "%s", i);
     417             : 
     418         403 :         return 0;
     419             : }
     420             : #endif
     421             : 
     422             : /**
     423             :  * Get the device file from the device number.
     424             :  *
     425             :  * It uses /sys/dev/block/.../uevent.
     426             :  *
     427             :  * For null device (major==0) it fails.
     428             :  */
     429             : #if HAVE_LINUX_DEVICE
     430         403 : static int devresolve_sys(dev_t device, char* path, size_t path_size)
     431             : {
     432             :         struct stat st;
     433             :         char buf[PATH_MAX];
     434             : 
     435             :         /* default device path from device number */
     436         403 :         pathprint(path, path_size, "/sys/dev/block/%u:%u/uevent", major(device), minor(device));
     437             : 
     438         403 :         if (tagread(path, "DEVNAME", buf, sizeof(buf)) != 0) {
     439             :                 /* LCOV_EXCL_START */
     440             :                 log_tag("resolve:sys:%u:%u: failed to read DEVNAME tag '%s'\n", major(device), minor(device), path);
     441             :                 return -1;
     442             :                 /* LCOV_EXCL_STOP */
     443             :         }
     444             : 
     445             :         /* set the real device path */
     446         403 :         pathprint(path, path_size, "/dev/%s", buf);
     447             : 
     448             :         /* check the device */
     449         403 :         if (stat(path, &st) != 0) {
     450             :                 /* LCOV_EXCL_START */
     451             :                 log_tag("resolve:sys:%u:%u: failed to stat '%s'\n", major(device), minor(device), path);
     452             :                 return -1;
     453             :                 /* LCOV_EXCL_STOP */
     454             :         }
     455         403 :         if (st.st_rdev != device) {
     456             :                 /* LCOV_EXCL_START */
     457             :                 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);
     458             :                 return -1;
     459             :                 /* LCOV_EXCL_STOP */
     460             :         }
     461             : 
     462         403 :         log_tag("resolve:sys:%u:%u:%s: found\n", major(device), minor(device), path);
     463             : 
     464         403 :         return 0;
     465             : }
     466             : #endif
     467             : 
     468             : /**
     469             :  * Get the device file from the device number.
     470             :  */
     471             : #if HAVE_LINUX_DEVICE
     472         403 : static int devresolve(uint64_t device, char* path, size_t path_size)
     473             : {
     474         403 :         if (devresolve_sys(device, path, path_size) == 0)
     475         403 :                 return 0;
     476             : 
     477           0 :         return -1;
     478             : }
     479             : #endif
     480             : 
     481             : /**
     482             :  * Cache used by blkid.
     483             :  */
     484             : #if HAVE_BLKID
     485             : static blkid_cache cache = 0;
     486             : #endif
     487             : 
     488             : /**
     489             :  * Get the UUID using the /dev/disk/by-uuid/ links.
     490             :  * It doesn't require root permission, and the uuid are always updated.
     491             :  * It doesn't work with Btrfs file-systems that don't export the main UUID
     492             :  * in /dev/disk/by-uuid/.
     493             :  */
     494             : #if HAVE_LINUX_DEVICE
     495       10908 : static int devuuid_dev(uint64_t device, char* uuid, size_t uuid_size)
     496             : {
     497             :         int ret;
     498             :         DIR* d;
     499             :         struct dirent* dd;
     500             :         struct stat st;
     501             : 
     502             :         /* scan the UUID directory searching for the device */
     503       10908 :         d = opendir("/dev/disk/by-uuid");
     504       10908 :         if (!d) {
     505           0 :                 log_tag("uuid:by-uuid:%u:%u: opendir(/dev/disk/by-uuid) failed, %s\n", major(device), minor(device), strerror(errno));
     506             :                 /* directory missing?, likely we are not in Linux */
     507           0 :                 return -1;
     508             :         }
     509             : 
     510       10908 :         int dir_fd = dirfd(d);
     511       10908 :         if (dir_fd == -1) {
     512           0 :                 log_tag("uuid:by-uuid:%u:%u: dirfd(/dev/disk/by-uuid) failed, %s\n", major(device), minor(device), strerror(errno));
     513           0 :                 return -1;
     514             :         }
     515             : 
     516      174036 :         while ((dd = readdir(d)) != 0) {
     517             :                 /* skip "." and ".." files, UUIDs never start with '.' */
     518      174036 :                 if (dd->d_name[0] == '.')
     519       21816 :                         continue;
     520             : 
     521      152220 :                 ret = fstatat(dir_fd, dd->d_name, &st, 0);
     522      152220 :                 if (ret != 0) {
     523           0 :                         log_tag("uuid:by-uuid:%u:%u: fstatat(%s) failed, %s\n", major(device), minor(device), dd->d_name, strerror(errno));
     524             :                         /* generic error, ignore and continue the search */
     525           0 :                         continue;
     526             :                 }
     527             : 
     528             :                 /* if it matches, we have the uuid */
     529      152220 :                 if (S_ISBLK(st.st_mode) && st.st_rdev == (dev_t)device) {
     530             :                         char buf[PATH_MAX];
     531             :                         char path[PATH_MAX];
     532             : 
     533             :                         /* resolve the link */
     534       10908 :                         pathprint(path, sizeof(path), "/dev/disk/by-uuid/%s", dd->d_name);
     535       10908 :                         ret = readlink(path, buf, sizeof(buf));
     536       10908 :                         if (ret < 0 || ret >= PATH_MAX) {
     537           0 :                                 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));
     538             :                                 /* generic error, ignore and continue the search */
     539           0 :                                 continue;
     540             :                         }
     541       10908 :                         buf[ret] = 0;
     542             : 
     543             :                         /* found */
     544       10908 :                         pathcpy(uuid, uuid_size, dd->d_name);
     545             : 
     546       10908 :                         log_tag("uuid:by-uuid:%u:%u:%s: found %s\n", major(device), minor(device), uuid, buf);
     547             : 
     548       10908 :                         closedir(d);
     549       10908 :                         return 0;
     550             :                 }
     551             :         }
     552             : 
     553           0 :         log_tag("uuid:by-uuid:%u:%u: /dev/disk/by-uuid doesn't contain a matching block device\n", major(device), minor(device));
     554             : 
     555             :         /* not found */
     556           0 :         closedir(d);
     557           0 :         return -1;
     558             : }
     559             : #endif
     560             : 
     561             : /**
     562             :  * Get the UUID using libblkid.
     563             :  * It uses a cache to work without root permission, resulting in UUID
     564             :  * not necessarily recent.
     565             :  * We could call blkid_probe_all() to refresh the UUID, but it would
     566             :  * require root permission to read the superblocks, and resulting in
     567             :  * all the disks spinning.
     568             :  */
     569             : #if HAVE_BLKID
     570           0 : static int devuuid_blkid(uint64_t device, char* uuid, size_t uuid_size)
     571             : {
     572             :         char* devname;
     573             :         char* uuidname;
     574             : 
     575           0 :         devname = blkid_devno_to_devname(device);
     576           0 :         if (!devname) {
     577           0 :                 log_tag("uuid:blkid:%u:%u: blkid_devno_to_devname() failed, %s\n", major(device), minor(device), strerror(errno));
     578             :                 /* device mapping failed */
     579           0 :                 return -1;
     580             :         }
     581             : 
     582           0 :         uuidname = blkid_get_tag_value(cache, "UUID", devname);
     583           0 :         if (!uuidname) {
     584           0 :                 log_tag("uuid:blkid:%u:%u: blkid_get_tag_value(UUID,%s) failed, %s\n", major(device), minor(device), devname, strerror(errno));
     585             :                 /* uuid mapping failed */
     586           0 :                 free(devname);
     587           0 :                 return -1;
     588             :         }
     589             : 
     590           0 :         pathcpy(uuid, uuid_size, uuidname);
     591             : 
     592           0 :         log_tag("uuid:blkid:%u:%u:%s: found %s\n", major(device), minor(device), uuid, devname);
     593             : 
     594           0 :         free(devname);
     595           0 :         free(uuidname);
     596           0 :         return 0;
     597             : }
     598             : #endif
     599             : 
     600             : 
     601             : /**
     602             :  * Get the LABEL using libblkid.
     603             :  * It uses a cache to work without root permission, resulting in LABEL
     604             :  * not necessarily recent.
     605             :  */
     606             : #if HAVE_BLKID
     607        5919 : static int devuuid_label(uint64_t device, char* label, size_t label_size)
     608             : {
     609             :         char* devname;
     610             :         char* labelname;
     611             : 
     612        5919 :         devname = blkid_devno_to_devname(device);
     613        5919 :         if (!devname) {
     614           0 :                 log_tag("label:blkid:%u:%u: blkid_devno_to_devname() failed, %s\n", major(device), minor(device), strerror(errno));
     615             :                 /* device mapping failed */
     616           0 :                 return -1;
     617             :         }
     618             : 
     619        5919 :         labelname = blkid_get_tag_value(cache, "LABEL", devname);
     620        5919 :         if (!labelname) {
     621           0 :                 log_tag("label:blkid:%u:%u: blkid_get_tag_value(LABEL,%s) failed, %s\n", major(device), minor(device), devname, strerror(errno));
     622           0 :                 free(devname);
     623             :                 /* label mapping failed */
     624           0 :                 return -1;
     625             :         }
     626             : 
     627        5919 :         pathcpy(label, label_size, labelname);
     628             : 
     629        5919 :         log_tag("label:blkid:%u:%u:%s: found %s\n", major(device), minor(device), label, devname);
     630             : 
     631        5919 :         free(devname);
     632        5919 :         free(labelname);
     633        5919 :         return 0;
     634             : }
     635             : #endif
     636             : 
     637             : 
     638             : #ifdef __APPLE__
     639             : static int devuuid_darwin(const char* path, char* uuid, size_t uuid_size)
     640             : {
     641             :         CFStringRef path_apple = 0;
     642             :         DASessionRef session = 0;
     643             :         CFURLRef path_appler = 0;
     644             :         int result = -1;
     645             : 
     646             :         *uuid = 0;
     647             : 
     648             :         path_apple = CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8);
     649             :         if (!path_apple)
     650             :                 goto bail;
     651             : 
     652             :         session = DASessionCreate(kCFAllocatorDefault);
     653             :         if (!session)
     654             :                 goto bail;
     655             : 
     656             :         path_appler = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_apple, kCFURLPOSIXPathStyle, false);
     657             :         if (!path_appler)
     658             :                 goto bail;
     659             : 
     660             :         /* find the mount point */
     661             :         DADiskRef disk = NULL;
     662             :         while (path_appler) {
     663             :                 disk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, path_appler);
     664             :                 if (disk)
     665             :                         break;
     666             : 
     667             :                 CFURLRef parent = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, path_appler);
     668             :                 if (!parent)
     669             :                         break;
     670             : 
     671             :                 /* check if we hit the root (parent == child) */
     672             :                 if (CFEqual(parent, path_appler)) {
     673             :                         CFRelease(parent);
     674             :                         break;
     675             :                 }
     676             : 
     677             :                 CFRelease(path_appler);
     678             :                 path_appler = parent;
     679             :         }
     680             : 
     681             :         /* safely extract the UUID */
     682             :         if (disk) {
     683             :                 CFDictionaryRef description = DADiskCopyDescription(disk);
     684             :                 if (description) {
     685             :                         /* key might not exist for NTFS/ExFAT on some drivers */
     686             :                         const void* value = (CFUUIDRef)CFDictionaryGetValue(description, kDADiskDescriptionVolumeUUIDKey);
     687             :                         CFStringRef uuid_string = NULL;
     688             : 
     689             :                         if (value) {
     690             :                                 if (CFGetTypeID(value) == CFUUIDGetTypeID()) {
     691             :                                         uuid_string = CFUUIDCreateString(kCFAllocatorDefault, (CFUUIDRef)value);
     692             :                                 } else if (CFGetTypeID(value) == CFStringGetTypeID()) {
     693             :                                         /* if it's already a string, retain it so we can release it later consistently */
     694             :                                         uuid_string = (CFStringRef)CFRetain(value);
     695             :                                 }
     696             :                         }
     697             : 
     698             :                         if (uuid_string) {
     699             :                                 if (CFStringGetCString(uuid_string, uuid, uuid_size, kCFStringEncodingUTF8)) {
     700             :                                         result = 0; /* success */
     701             :                                 }
     702             :                                 CFRelease(uuid_string);
     703             :                         }
     704             : 
     705             :                         CFRelease(description);
     706             :                 }
     707             :                 CFRelease(disk);
     708             :         }
     709             : 
     710             : bail:
     711             :         /* clean up */
     712             :         if (path_appler) CFRelease(path_appler);
     713             :         if (session) CFRelease(session);
     714             :         if (path_apple) CFRelease(path_apple);
     715             : 
     716             :         return result;
     717             : }
     718             : #endif
     719             : 
     720       10912 : int devuuid(uint64_t device, const char* path, char* uuid, size_t uuid_size)
     721             : {
     722             :         (void)path;
     723             :         (void)device;
     724             : 
     725             : #ifdef __APPLE__
     726             :         if (devuuid_darwin(path, uuid, uuid_size) == 0)
     727             :                 return 0;
     728             : #endif
     729             : 
     730             : #if HAVE_LINUX_DEVICE
     731             :         /* if the major is the null device */
     732       10912 :         if (major(device) == 0) {
     733             :                 /* obtain the real device */
     734           4 :                 if (devdereference(device, &device) != 0) {
     735             :                         /* LCOV_EXCL_START */
     736             :                         return -1;
     737             :                         /* LCOV_EXCL_STOP */
     738             :                 }
     739             :         }
     740             : #endif
     741             : 
     742             :         /* first try with the /dev/disk/by-uuid version */
     743             : #if HAVE_LINUX_DEVICE
     744       10908 :         if (devuuid_dev(device, uuid, uuid_size) == 0)
     745       10908 :                 return 0;
     746             : #else
     747             :         log_tag("uuid:by-uuid:%u:%u: by-uuid not supported\n", major(device), minor(device));
     748             : #endif
     749             : 
     750             :         /* fall back to blkid for other cases */
     751             : #if HAVE_BLKID
     752           0 :         if (devuuid_blkid(device, uuid, uuid_size) == 0)
     753           0 :                 return 0;
     754             : #else
     755             :         log_tag("uuid:blkid:%u:%u: blkid support not compiled in\n", major(device), minor(device));
     756             : #endif
     757             : 
     758           0 :         log_tag("uuid:notfound:%u:%u:\n", major(device), minor(device));
     759             : 
     760             :         /* not supported */
     761             :         (void)uuid;
     762             :         (void)uuid_size;
     763           0 :         return -1;
     764             : }
     765             : 
     766       11432 : int filephy(const char* path, uint64_t size, uint64_t* physical)
     767             : {
     768             : #if HAVE_LINUX_FIEMAP_H
     769             :         /* In Linux get the real physical address of the file */
     770             :         /* Note that FIEMAP doesn't require root permission */
     771             :         int f;
     772             :         struct fiemap* fiemap;
     773             :         size_t fiemap_size;
     774             :         unsigned int blknum;
     775             : 
     776       11432 :         f = open(path, O_RDONLY);
     777       11432 :         if (f == -1) {
     778           0 :                 return -1;
     779             :         }
     780             : 
     781             :         /* first try with FIEMAP */
     782             :         /* if works for ext2, ext3, ext4, xfs, btrfs */
     783       11432 :         fiemap_size = sizeof(struct fiemap) + sizeof(struct fiemap_extent);
     784       11432 :         fiemap = malloc_nofail(fiemap_size);
     785       11432 :         memset(fiemap, 0, fiemap_size);
     786       11432 :         fiemap->fm_start = 0;
     787       11432 :         fiemap->fm_length = ~0ULL;
     788       11432 :         fiemap->fm_flags = FIEMAP_FLAG_SYNC; /* required to ensure that just created files report a valid address and not 0 */
     789       11432 :         fiemap->fm_extent_count = 1; /* we are interested only at the first block */
     790             : 
     791       11432 :         if (ioctl(f, FS_IOC_FIEMAP, fiemap) != -1) {
     792       11432 :                 uint32_t flags = fiemap->fm_extents[0].fe_flags;
     793       11432 :                 uint64_t offset = fiemap->fm_extents[0].fe_physical;
     794             : 
     795             :                 /* check some condition for validating the offset */
     796       11432 :                 if (flags & FIEMAP_EXTENT_DATA_INLINE) {
     797             :                         /* if the data is inline, we don't have an offset to report */
     798           0 :                         *physical = FILEPHY_WITHOUT_OFFSET;
     799       11432 :                 } else if (flags & FIEMAP_EXTENT_UNKNOWN) {
     800             :                         /* if the offset is unknown, we don't have an offset to report */
     801           0 :                         *physical = FILEPHY_WITHOUT_OFFSET;
     802       11432 :                 } else if (offset == 0) {
     803             :                         /* 0 is the general fallback for file-systems when */
     804             :                         /* they don't have an offset to report */
     805          14 :                         *physical = FILEPHY_WITHOUT_OFFSET;
     806             :                 } else {
     807             :                         /* finally report the real offset */
     808       11418 :                         *physical = offset + FILEPHY_REAL_OFFSET;
     809             :                 }
     810             : 
     811       11432 :                 free(fiemap);
     812             : 
     813       11432 :                 if (close(f) == -1)
     814           0 :                         return -1;
     815       11432 :                 return 0;
     816             :         }
     817             : 
     818           0 :         free(fiemap);
     819             : 
     820             :         /* if the file is empty, FIBMAP doesn't work, and we don't even try to use it */
     821           0 :         if (size == 0) {
     822           0 :                 *physical = FILEPHY_WITHOUT_OFFSET;
     823           0 :                 if (close(f) == -1)
     824           0 :                         return -1;
     825           0 :                 return 0;
     826             :         }
     827             : 
     828             :         /* then try with FIBMAP */
     829             :         /* it works for jfs, reiserfs, ntfs-3g */
     830             :         /* in exfat it always returns 0, that it's anyway better than the fake inodes */
     831           0 :         blknum = 0; /* first block */
     832           0 :         if (ioctl(f, FIBMAP, &blknum) != -1) {
     833           0 :                 *physical = blknum + FILEPHY_REAL_OFFSET;
     834           0 :                 if (close(f) == -1)
     835           0 :                         return -1;
     836           0 :                 return 0;
     837             :         }
     838             : 
     839             :         /* otherwise don't use anything, and keep the directory traversal order */
     840             :         /* at now this should happen only for vfat */
     841             :         /* and it's surely better than using fake inodes */
     842           0 :         *physical = FILEPHY_UNREPORTED_OFFSET;
     843           0 :         if (close(f) == -1)
     844           0 :                 return -1;
     845             : #else
     846             :         /* In a generic Unix use a dummy value for all the files */
     847             :         /* We don't want to risk to use the inode without knowing */
     848             :         /* if it really improves performance. */
     849             :         /* In this way we keep them in the directory traversal order */
     850             :         /* that at least keeps files in the same directory together. */
     851             :         /* Note also that in newer file-system with snapshot, like ZFS, */
     852             :         /* the inode doesn't represent even more the disk position, because files */
     853             :         /* are not overwritten in place, but rewritten in another location */
     854             :         /* of the disk. */
     855             :         *physical = FILEPHY_UNREPORTED_OFFSET;
     856             : 
     857             :         (void)path; /* not used here */
     858             :         (void)size;
     859             : #endif
     860             : 
     861           0 :         return 0;
     862             : }
     863             : 
     864             : /* from man statfs */
     865             : #define ADFS_SUPER_MAGIC 0xadf5
     866             : #define AFFS_SUPER_MAGIC 0xADFF
     867             : #define BDEVFS_MAGIC 0x62646576
     868             : #define BEFS_SUPER_MAGIC 0x42465331
     869             : #define BFS_MAGIC 0x1BADFACE
     870             : #define BINFMTFS_MAGIC 0x42494e4d
     871             : #define BTRFS_SUPER_MAGIC 0x9123683E
     872             : #define CGROUP_SUPER_MAGIC 0x27e0eb
     873             : #define CIFS_MAGIC_NUMBER 0xFF534D42
     874             : #define CODA_SUPER_MAGIC 0x73757245
     875             : #define COH_SUPER_MAGIC 0x012FF7B7
     876             : #define CRAMFS_MAGIC 0x28cd3d45
     877             : #define DEBUGFS_MAGIC 0x64626720
     878             : #define DEVFS_SUPER_MAGIC 0x1373
     879             : #define DEVPTS_SUPER_MAGIC 0x1cd1
     880             : #define EFIVARFS_MAGIC 0xde5e81e4
     881             : #define EFS_SUPER_MAGIC 0x00414A53
     882             : #define EXT_SUPER_MAGIC 0x137D
     883             : #define EXT2_OLD_SUPER_MAGIC 0xEF51
     884             : #define EXT4_SUPER_MAGIC 0xEF53 /* also ext2/ext3 */
     885             : #define FUSE_SUPER_MAGIC 0x65735546
     886             : #define FUTEXFS_SUPER_MAGIC 0xBAD1DEA
     887             : #define HFS_SUPER_MAGIC 0x4244
     888             : #define HFSPLUS_SUPER_MAGIC 0x482b
     889             : #define HOSTFS_SUPER_MAGIC 0x00c0ffee
     890             : #define HPFS_SUPER_MAGIC 0xF995E849
     891             : #define HUGETLBFS_MAGIC 0x958458f6
     892             : #define ISOFS_SUPER_MAGIC 0x9660
     893             : #define JFFS2_SUPER_MAGIC 0x72b6
     894             : #define JFS_SUPER_MAGIC 0x3153464a
     895             : #define MINIX_SUPER_MAGIC 0x137F
     896             : #define MINIX_SUPER_MAGIC2 0x138F
     897             : #define MINIX2_SUPER_MAGIC 0x2468
     898             : #define MINIX2_SUPER_MAGIC2 0x2478
     899             : #define MINIX3_SUPER_MAGIC 0x4d5a
     900             : #define MQUEUE_MAGIC 0x19800202
     901             : #define MSDOS_SUPER_MAGIC 0x4d44
     902             : #define NCP_SUPER_MAGIC 0x564c
     903             : #define NFS_SUPER_MAGIC 0x6969
     904             : #define NILFS_SUPER_MAGIC 0x3434
     905             : #define NTFS_SB_MAGIC 0x5346544e
     906             : #define OCFS2_SUPER_MAGIC 0x7461636f
     907             : #define OPENPROM_SUPER_MAGIC 0x9fa1
     908             : #define PIPEFS_MAGIC 0x50495045
     909             : #define PROC_SUPER_MAGIC 0x9fa0
     910             : #define PSTOREFS_MAGIC 0x6165676C
     911             : #define QNX4_SUPER_MAGIC 0x002f
     912             : #define QNX6_SUPER_MAGIC 0x68191122
     913             : #define RAMFS_MAGIC 0x858458f6
     914             : #define REISERFS_SUPER_MAGIC 0x52654973
     915             : #define ROMFS_MAGIC 0x7275
     916             : #define SELINUX_MAGIC 0xf97cff8c
     917             : #define SMACK_MAGIC 0x43415d53
     918             : #define SMB_SUPER_MAGIC 0x517B
     919             : #define SOCKFS_MAGIC 0x534F434B
     920             : #define SQUASHFS_MAGIC 0x73717368
     921             : #define SYSFS_MAGIC 0x62656572
     922             : #define SYSV2_SUPER_MAGIC 0x012FF7B6
     923             : #define SYSV4_SUPER_MAGIC 0x012FF7B5
     924             : #define TMPFS_MAGIC 0x01021994
     925             : #define UDF_SUPER_MAGIC 0x15013346
     926             : #define UFS_MAGIC 0x00011954
     927             : #define USBDEVICE_SUPER_MAGIC 0x9fa2
     928             : #define V9FS_MAGIC 0x01021997
     929             : #define VXFS_SUPER_MAGIC 0xa501FCF5
     930             : #define XENFS_SUPER_MAGIC 0xabba1974
     931             : #define XENIX_SUPER_MAGIC 0x012FF7B4
     932             : #define XFS_SUPER_MAGIC 0x58465342
     933             : #define _XIAFS_SUPER_MAGIC 0x012FD16D
     934             : #define AFS_SUPER_MAGIC 0x5346414F
     935             : #define AUFS_SUPER_MAGIC 0x61756673
     936             : #define ANON_INODE_FS_SUPER_MAGIC 0x09041934
     937             : #define CEPH_SUPER_MAGIC 0x00C36400
     938             : #define ECRYPTFS_SUPER_MAGIC 0xF15F
     939             : #define FAT_SUPER_MAGIC 0x4006
     940             : #define FHGFS_SUPER_MAGIC 0x19830326
     941             : #define FUSEBLK_SUPER_MAGIC 0x65735546
     942             : #define FUSECTL_SUPER_MAGIC 0x65735543
     943             : #define GFS_SUPER_MAGIC 0x1161970
     944             : #define GPFS_SUPER_MAGIC 0x47504653
     945             : #define MTD_INODE_FS_SUPER_MAGIC 0x11307854
     946             : #define INOTIFYFS_SUPER_MAGIC 0x2BAD1DEA
     947             : #define ISOFS_R_WIN_SUPER_MAGIC 0x4004
     948             : #define ISOFS_WIN_SUPER_MAGIC 0x4000
     949             : #define JFFS_SUPER_MAGIC 0x07C0
     950             : #define KAFS_SUPER_MAGIC 0x6B414653
     951             : #define LUSTRE_SUPER_MAGIC 0x0BD00BD0
     952             : #define NFSD_SUPER_MAGIC 0x6E667364
     953             : #define PANFS_SUPER_MAGIC 0xAAD7AAEA
     954             : #define RPC_PIPEFS_SUPER_MAGIC 0x67596969
     955             : #define SECURITYFS_SUPER_MAGIC 0x73636673
     956             : #define UFS_BYTESWAPPED_SUPER_MAGIC 0x54190100
     957             : #define VMHGFS_SUPER_MAGIC 0xBACBACBC
     958             : #define VZFS_SUPER_MAGIC 0x565A4653
     959             : #define ZFS_SUPER_MAGIC 0x2FC12FC1
     960             : 
     961             : struct filesystem_entry {
     962             :         unsigned id;
     963             :         const char* name;
     964             :         int remote;
     965             : } FILESYSTEMS[] = {
     966             :         { ADFS_SUPER_MAGIC, "adfs", 0 },
     967             :         { AFFS_SUPER_MAGIC, "affs", 0 },
     968             :         { AFS_SUPER_MAGIC, "afs", 1 },
     969             :         { AUFS_SUPER_MAGIC, "aufs", 1 },
     970             :         { BEFS_SUPER_MAGIC, "befs", 0 },
     971             :         { BDEVFS_MAGIC, "bdevfs", 0 },
     972             :         { BFS_MAGIC, "bfs", 0 },
     973             :         { BINFMTFS_MAGIC, "binfmt_misc", 0 },
     974             :         { BTRFS_SUPER_MAGIC, "btrfs", 0 },
     975             :         { CEPH_SUPER_MAGIC, "ceph", 1 },
     976             :         { CGROUP_SUPER_MAGIC, "cgroupfs", 0 },
     977             :         { CIFS_MAGIC_NUMBER, "cifs", 1 },
     978             :         { CODA_SUPER_MAGIC, "coda", 1 },
     979             :         { COH_SUPER_MAGIC, "coh", 0 },
     980             :         { CRAMFS_MAGIC, "cramfs", 0 },
     981             :         { DEBUGFS_MAGIC, "debugfs", 0 },
     982             :         { DEVFS_SUPER_MAGIC, "devfs", 0 },
     983             :         { DEVPTS_SUPER_MAGIC, "devpts", 0 },
     984             :         { ECRYPTFS_SUPER_MAGIC, "ecryptfs", 0 },
     985             :         { EFS_SUPER_MAGIC, "efs", 0 },
     986             :         { EXT_SUPER_MAGIC, "ext", 0 },
     987             :         { EXT4_SUPER_MAGIC, "ext4", 0 },
     988             :         { EXT2_OLD_SUPER_MAGIC, "ext2", 0 },
     989             :         { FAT_SUPER_MAGIC, "fat", 0 },
     990             :         { FHGFS_SUPER_MAGIC, "fhgfs", 1 },
     991             :         { FUSEBLK_SUPER_MAGIC, "fuseblk", 1 },
     992             :         { FUSECTL_SUPER_MAGIC, "fusectl", 1 },
     993             :         { FUTEXFS_SUPER_MAGIC, "futexfs", 0 },
     994             :         { GFS_SUPER_MAGIC, "gfs/gfs2", 1 },
     995             :         { GPFS_SUPER_MAGIC, "gpfs", 1 },
     996             :         { HFS_SUPER_MAGIC, "hfs", 0 },
     997             :         { HFSPLUS_SUPER_MAGIC, "hfsplus", 0 },
     998             :         { HPFS_SUPER_MAGIC, "hpfs", 0 },
     999             :         { HUGETLBFS_MAGIC, "hugetlbfs", 0 },
    1000             :         { MTD_INODE_FS_SUPER_MAGIC, "inodefs", 0 },
    1001             :         { INOTIFYFS_SUPER_MAGIC, "inotifyfs", 0 },
    1002             :         { ISOFS_SUPER_MAGIC, "isofs", 0 },
    1003             :         { ISOFS_R_WIN_SUPER_MAGIC, "isofs", 0 },
    1004             :         { ISOFS_WIN_SUPER_MAGIC, "isofs", 0 },
    1005             :         { JFFS_SUPER_MAGIC, "jffs", 0 },
    1006             :         { JFFS2_SUPER_MAGIC, "jffs2", 0 },
    1007             :         { JFS_SUPER_MAGIC, "jfs", 0 },
    1008             :         { KAFS_SUPER_MAGIC, "k-afs", 1 },
    1009             :         { LUSTRE_SUPER_MAGIC, "lustre", 1 },
    1010             :         { MINIX_SUPER_MAGIC, "minix", 0 },
    1011             :         { MINIX_SUPER_MAGIC2, "minix", 0 },
    1012             :         { MINIX2_SUPER_MAGIC, "minix2", 0 },
    1013             :         { MINIX2_SUPER_MAGIC2, "minix2", 0 },
    1014             :         { MINIX3_SUPER_MAGIC, "minix3", 0 },
    1015             :         { MQUEUE_MAGIC, "mqueue", 0 },
    1016             :         { MSDOS_SUPER_MAGIC, "msdos", 0 },
    1017             :         { NCP_SUPER_MAGIC, "novell", 1 },
    1018             :         { NFS_SUPER_MAGIC, "nfs", 1 },
    1019             :         { NFSD_SUPER_MAGIC, "nfsd", 1 },
    1020             :         { NILFS_SUPER_MAGIC, "nilfs", 0 },
    1021             :         { NTFS_SB_MAGIC, "ntfs", 0 },
    1022             :         { OPENPROM_SUPER_MAGIC, "openprom", 0 },
    1023             :         { OCFS2_SUPER_MAGIC, "ocfs2", 1 },
    1024             :         { PANFS_SUPER_MAGIC, "panfs", 1 },
    1025             :         { PIPEFS_MAGIC, "pipefs", 1 },
    1026             :         { PROC_SUPER_MAGIC, "proc", 0 },
    1027             :         { PSTOREFS_MAGIC, "pstorefs", 0 },
    1028             :         { QNX4_SUPER_MAGIC, "qnx4", 0 },
    1029             :         { QNX6_SUPER_MAGIC, "qnx6", 0 },
    1030             :         { RAMFS_MAGIC, "ramfs", 0 },
    1031             :         { REISERFS_SUPER_MAGIC, "reiserfs", 0 },
    1032             :         { ROMFS_MAGIC, "romfs", 0 },
    1033             :         { RPC_PIPEFS_SUPER_MAGIC, "rpc_pipefs", 0 },
    1034             :         { SECURITYFS_SUPER_MAGIC, "securityfs", 0 },
    1035             :         { SELINUX_MAGIC, "selinux", 0 },
    1036             :         { SMB_SUPER_MAGIC, "smb", 1 },
    1037             :         { SOCKFS_MAGIC, "sockfs", 0 },
    1038             :         { SQUASHFS_MAGIC, "squashfs", 0 },
    1039             :         { SYSFS_MAGIC, "sysfs", 0 },
    1040             :         { SYSV2_SUPER_MAGIC, "sysv2", 0 },
    1041             :         { SYSV4_SUPER_MAGIC, "sysv4", 0 },
    1042             :         { TMPFS_MAGIC, "tmpfs", 0 },
    1043             :         { UDF_SUPER_MAGIC, "udf", 0 },
    1044             :         { UFS_MAGIC, "ufs", 0 },
    1045             :         { UFS_BYTESWAPPED_SUPER_MAGIC, "ufs", 0 },
    1046             :         { USBDEVICE_SUPER_MAGIC, "usbdevfs", 0 },
    1047             :         { V9FS_MAGIC, "v9fs", 0 },
    1048             :         { VMHGFS_SUPER_MAGIC, "vmhgfs", 1 },
    1049             :         { VXFS_SUPER_MAGIC, "vxfs", 0 },
    1050             :         { VZFS_SUPER_MAGIC, "vzfs", 0 },
    1051             :         { XENFS_SUPER_MAGIC, "xenfs", 0 },
    1052             :         { XENIX_SUPER_MAGIC, "xenix", 0 },
    1053             :         { XFS_SUPER_MAGIC, "xfs", 0 },
    1054             :         { _XIAFS_SUPER_MAGIC, "xia", 0 },
    1055             :         { ZFS_SUPER_MAGIC, "zfs", 0 },
    1056             :         { 0 }
    1057             : };
    1058             : 
    1059        6596 : 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)
    1060             : {
    1061             : #if HAVE_STATFS
    1062             :         struct statfs st;
    1063             : 
    1064        6596 :         if (statfs(path, &st) != 0) {
    1065             :                 char dir[PATH_MAX];
    1066             :                 char* slash;
    1067             : 
    1068          48 :                 if (errno != ENOENT) {
    1069           0 :                         return -1;
    1070             :                 }
    1071             : 
    1072             :                 /* if it doesn't exist, we assume a file */
    1073             :                 /* and we check for the containing dir */
    1074          48 :                 if (strlen(path) + 1 > sizeof(dir)) {
    1075           0 :                         errno = ENAMETOOLONG;
    1076           0 :                         return -1;
    1077             :                 }
    1078             : 
    1079          48 :                 strcpy(dir, path);
    1080             : 
    1081          48 :                 slash = strrchr(dir, '/');
    1082          48 :                 if (!slash)
    1083           0 :                         return -1;
    1084             : 
    1085          48 :                 *slash = 0;
    1086          48 :                 if (statfs(dir, &st) != 0)
    1087           0 :                         return -1;
    1088             :         }
    1089             : #endif
    1090             : 
    1091             :         /* to get the fs type check "man stat" or "stat -f -t FILE" */
    1092        6596 :         if (has_persistent_inode) {
    1093             : #if HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
    1094         629 :                 switch (st.f_type) {
    1095           0 :                 case FUSEBLK_SUPER_MAGIC : /* FUSE, "fuseblk" in the stat command */
    1096             :                 case MSDOS_SUPER_MAGIC : /* VFAT, "msdos" in the stat command */
    1097           0 :                         *has_persistent_inode = 0;
    1098           0 :                         break;
    1099         629 :                 default :
    1100             :                         /* by default assume yes */
    1101         629 :                         *has_persistent_inode = 1;
    1102         629 :                         break;
    1103             :                 }
    1104             : #else
    1105             :                 /* in Unix inodes are persistent by default */
    1106             :                 *has_persistent_inode = 1;
    1107             : #endif
    1108             :         }
    1109             : 
    1110        6596 :         if (has_syncronized_hardlinks) {
    1111             : #if HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
    1112         629 :                 switch (st.f_type) {
    1113           0 :                 case NTFS_SB_MAGIC : /* NTFS */
    1114             :                 case MSDOS_SUPER_MAGIC : /* VFAT, "msdos" in the stat command */
    1115           0 :                         *has_syncronized_hardlinks = 0;
    1116           0 :                         break;
    1117         629 :                 default :
    1118             :                         /* by default assume yes */
    1119         629 :                         *has_syncronized_hardlinks = 1;
    1120         629 :                         break;
    1121             :                 }
    1122             : #else
    1123             :                 /* in Unix hardlinks share the same metadata by default */
    1124             :                 *has_syncronized_hardlinks = 1;
    1125             : #endif
    1126             :         }
    1127             : 
    1128        6596 :         if (total_space) {
    1129             : #if HAVE_STATFS
    1130        5967 :                 *total_space = st.f_bsize * (uint64_t)st.f_blocks;
    1131             : #else
    1132             :                 *total_space = 0;
    1133             : #endif
    1134             :         }
    1135             : 
    1136        6596 :         if (free_space) {
    1137             : #if HAVE_STATFS
    1138        5967 :                 *free_space = st.f_bsize * (uint64_t)st.f_bfree;
    1139             : #else
    1140             :                 *free_space = 0;
    1141             : #endif
    1142             :         }
    1143             : 
    1144        6596 :         const char* ptype = 0;
    1145             : 
    1146             : #if HAVE_STATFS && HAVE_STRUCT_STATFS_F_FSTYPENAME
    1147             :         /* get the filesystem type directly from the struct (Mac OS X) */
    1148             :         ptype = st.f_fstypename;
    1149             : #elif HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
    1150             :         /* get the filesystem type from f_type (Linux) */
    1151             :         /* from: https://github.com/influxdata/gopsutil/blob/master/disk/disk_linux.go */
    1152      145112 :         for (int i = 0; FILESYSTEMS[i].id != 0; ++i) {
    1153      145112 :                 if (st.f_type == FILESYSTEMS[i].id) {
    1154        6596 :                         ptype = FILESYSTEMS[i].name;
    1155        6596 :                         break;
    1156             :                 }
    1157             :         }
    1158             : #endif
    1159             : 
    1160        6596 :         if (fstype) {
    1161        5967 :                 fstype[0] = 0;
    1162        5967 :                 if (ptype)
    1163        5967 :                         snprintf(fstype, fstype_size, "%s", ptype);
    1164             :         }
    1165             : 
    1166        6596 :         if (fslabel) {
    1167        5967 :                 fslabel[0] = 0;
    1168             : #if HAVE_BLKID
    1169             :                 struct stat fst;
    1170        5967 :                 if (stat(path, &fst) == 0)
    1171        5919 :                         devuuid_label(fst.st_dev, fslabel, fslabel_size);
    1172             : #else
    1173             :                 (void)fslabel_size;
    1174             : #endif
    1175             :         }
    1176             : 
    1177        6596 :         return 0;
    1178             : }
    1179             : 
    1180     4046785 : uint64_t os_tick(void)
    1181             : {
    1182             : #if HAVE_MACH_ABSOLUTE_TIME
    1183             :         /* for Mac OS X */
    1184             :         return mach_absolute_time();
    1185             : #elif HAVE_CLOCK_GETTIME && (defined(CLOCK_MONOTONIC) || defined(CLOCK_MONOTONIC_RAW))
    1186             :         /* for Linux */
    1187             :         struct timespec tv;
    1188             : 
    1189             :         /* nanosecond precision with clock_gettime() */
    1190             : #if defined(CLOCK_MONOTONIC_RAW)
    1191     4046785 :         if (clock_gettime(CLOCK_MONOTONIC_RAW, &tv) != 0) {
    1192             : #else
    1193             :         if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
    1194             : #endif
    1195           0 :                 return 0;
    1196             :         }
    1197             : 
    1198     4046785 :         return tv.tv_sec * 1000000000ULL + tv.tv_nsec;
    1199             : #else
    1200             :         /* other platforms */
    1201             :         struct timeval tv;
    1202             : 
    1203             :         /* microsecond precision with gettimeofday() */
    1204             :         if (gettimeofday(&tv, 0) != 0) {
    1205             :                 return 0;
    1206             :         }
    1207             : 
    1208             :         return tv.tv_sec * 1000000ULL + tv.tv_usec;
    1209             : #endif
    1210             : }
    1211             : 
    1212        3721 : uint64_t os_tick_ms(void)
    1213             : {
    1214             :         struct timeval tv;
    1215             : 
    1216        3721 :         if (gettimeofday(&tv, 0) != 0)
    1217           0 :                 return 0;
    1218             : 
    1219        3721 :         return tv.tv_sec * 1000ULL + tv.tv_usec / 1000;
    1220             : }
    1221             : 
    1222         308 : int randomize(void* ptr, size_t size)
    1223             : {
    1224             :         int f;
    1225             :         ssize_t ret;
    1226             : 
    1227         308 :         f = open("/dev/urandom", O_RDONLY);
    1228         308 :         if (f == -1)
    1229           0 :                 return -1;
    1230             : 
    1231         308 :         ret = read(f, ptr, size);
    1232         308 :         if (ret < 0 || (size_t)ret != size) {
    1233           0 :                 close(f);
    1234           0 :                 return -1;
    1235             :         }
    1236             : 
    1237         308 :         if (close(f) != 0)
    1238           0 :                 return -1;
    1239             : 
    1240         308 :         return 0;
    1241             : }
    1242             : 
    1243             : /**
    1244             :  * Read a file extracting the contained device number in %u:%u format.
    1245             :  * Return 0 on error.
    1246             :  */
    1247             : #if HAVE_LINUX_DEVICE
    1248         286 : static dev_t devread(const char* path)
    1249             : {
    1250             :         int f;
    1251             :         int ret;
    1252             :         int len;
    1253             :         char buf[64];
    1254             :         char* e;
    1255             :         unsigned ma;
    1256             :         unsigned mi;
    1257             : 
    1258         286 :         f = open(path, O_RDONLY);
    1259         286 :         if (f == -1) {
    1260             :                 /* LCOV_EXCL_START */
    1261             :                 log_fatal(errno, "Failed to open '%s'.\n", path);
    1262             :                 return 0;
    1263             :                 /* LCOV_EXCL_STOP */
    1264             :         }
    1265             : 
    1266         286 :         len = read(f, buf, sizeof(buf));
    1267         286 :         if (len < 0) {
    1268             :                 /* LCOV_EXCL_START */
    1269             :                 close(f);
    1270             :                 log_fatal(errno, "Failed to read '%s'.\n", path);
    1271             :                 return 0;
    1272             :                 /* LCOV_EXCL_STOP */
    1273             :         }
    1274         286 :         if (len == sizeof(buf)) {
    1275             :                 /* LCOV_EXCL_START */
    1276             :                 close(f);
    1277             :                 log_fatal(EEXTERNAL, "Too long read '%s'.\n", path);
    1278             :                 return 0;
    1279             :                 /* LCOV_EXCL_STOP */
    1280             :         }
    1281             : 
    1282         286 :         ret = close(f);
    1283         286 :         if (ret != 0) {
    1284             :                 /* LCOV_EXCL_START */
    1285             :                 log_fatal(errno, "Failed to close '%s'.\n", path);
    1286             :                 return 0;
    1287             :                 /* LCOV_EXCL_STOP */
    1288             :         }
    1289             : 
    1290         286 :         buf[len] = 0;
    1291             : 
    1292         286 :         ma = strtoul(buf, &e, 10);
    1293         286 :         if (*e != ':') {
    1294             :                 /* LCOV_EXCL_START */
    1295             :                 log_fatal(EEXTERNAL, "Invalid format in '%s' for '%s'.\n", path, buf);
    1296             :                 return 0;
    1297             :                 /* LCOV_EXCL_STOP */
    1298             :         }
    1299             : 
    1300         286 :         mi = strtoul(e + 1, &e, 10);
    1301         286 :         if (*e != 0 && !isspace(*e)) {
    1302             :                 /* LCOV_EXCL_START */
    1303             :                 log_fatal(EEXTERNAL, "Invalid format in '%s' for '%s'.\n", path, buf);
    1304             :                 return 0;
    1305             :                 /* LCOV_EXCL_STOP */
    1306             :         }
    1307             : 
    1308         286 :         return makedev(ma, mi);
    1309             : }
    1310             : #endif
    1311             : 
    1312             : /**
    1313             :  * Read a device tree filling the specified list of disk_t entries.
    1314             :  */
    1315             : #if HAVE_LINUX_DEVICE
    1316         286 : static int devtree(devinfo_t* parent, dev_t device, tommy_list* list)
    1317             : {
    1318             :         char path[PATH_MAX];
    1319             :         DIR* d;
    1320         286 :         int slaves = 0;
    1321             : 
    1322         286 :         pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/slaves", major(device), minor(device));
    1323             : 
    1324             :         /* check if there is a slaves list */
    1325         286 :         d = opendir(path);
    1326         286 :         if (d != 0) {
    1327             :                 struct dirent* dd;
    1328             : 
    1329         564 :                 while ((dd = readdir(d)) != 0) {
    1330         423 :                         if (dd->d_name[0] != '.') {
    1331             :                                 dev_t subdev;
    1332             : 
    1333             :                                 /* for each slave, expand the full potential tree */
    1334         141 :                                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/slaves/%s/dev", major(device), minor(device), dd->d_name);
    1335             : 
    1336         141 :                                 subdev = devread(path);
    1337         141 :                                 if (!subdev) {
    1338             :                                         /* LCOV_EXCL_START */
    1339             :                                         closedir(d);
    1340             :                                         return -1;
    1341             :                                         /* LCOV_EXCL_STOP */
    1342             :                                 }
    1343             : 
    1344         141 :                                 if (devtree(parent, subdev, list) != 0) {
    1345             :                                         /* LCOV_EXCL_START */
    1346             :                                         closedir(d);
    1347             :                                         return -1;
    1348             :                                         /* LCOV_EXCL_STOP */
    1349             :                                 }
    1350             : 
    1351         141 :                                 ++slaves;
    1352             :                         }
    1353             :                 }
    1354             : 
    1355         141 :                 closedir(d);
    1356             :         }
    1357             : 
    1358             :         /* if no slaves found */
    1359         286 :         if (!slaves) {
    1360             :                 /* this is a raw device */
    1361             :                 devinfo_t* devinfo;
    1362             : 
    1363             :                 /* check if it's a real device */
    1364         145 :                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/device", major(device), minor(device));
    1365         145 :                 if (access(path, F_OK) != 0) {
    1366             :                         /* get the parent device */
    1367         145 :                         pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/../dev", major(device), minor(device));
    1368             : 
    1369         145 :                         device = devread(path);
    1370         145 :                         if (!device) {
    1371             :                                 /* LCOV_EXCL_START */
    1372             :                                 return -1;
    1373             :                                 /* LCOV_EXCL_STOP */
    1374             :                         }
    1375             :                 }
    1376             : 
    1377             :                 /* get the device file */
    1378         145 :                 if (devresolve(device, path, sizeof(path)) != 0) {
    1379             :                         /* LCOV_EXCL_START */
    1380             :                         log_fatal(EEXTERNAL, "Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    1381             :                         return -1;
    1382             :                         /* LCOV_EXCL_STOP */
    1383             :                 }
    1384             : 
    1385         145 :                 devinfo = calloc_nofail(1, sizeof(devinfo_t));
    1386             : 
    1387         145 :                 devinfo->device = device;
    1388         145 :                 pathcpy(devinfo->name, sizeof(devinfo->name), parent->name);
    1389         145 :                 pathcpy(devinfo->smartctl, sizeof(devinfo->smartctl), parent->smartctl);
    1390         145 :                 memcpy(devinfo->smartignore, parent->smartignore, sizeof(devinfo->smartignore));
    1391         145 :                 pathcpy(devinfo->file, sizeof(devinfo->file), path);
    1392         145 :                 devinfo->parent = parent;
    1393             : 
    1394             :                 /* insert in the list */
    1395         145 :                 tommy_list_insert_tail(list, &devinfo->node, devinfo);
    1396             :         }
    1397             : 
    1398         286 :         return 0;
    1399             : }
    1400             : #endif
    1401             : 
    1402             : /**
    1403             :  * Compute disk usage by aggregating access statistics.
    1404             :  */
    1405             : #if HAVE_LINUX_DEVICE
    1406         145 : static int devstat(dev_t device, uint64_t* count)
    1407             : {
    1408             :         char path[PATH_MAX];
    1409             :         char buf[512];
    1410             :         int token;
    1411             :         int ret;
    1412             :         char* i;
    1413             : 
    1414         145 :         *count = 0;
    1415             : 
    1416         145 :         pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/stat", major(device), minor(device));
    1417             : 
    1418         145 :         ret = sysread(path, buf, sizeof(buf));
    1419         145 :         if (ret < 0) {
    1420             :                 /* LCOV_EXCL_START */
    1421             :                 log_fatal(errno, "Failed to read '%s'.\n", path);
    1422             :                 return -1;
    1423             :                 /* LCOV_EXCL_STOP */
    1424             :         }
    1425         145 :         if ((size_t)ret + 1 > sizeof(buf)) {
    1426             :                 /* LCOV_EXCL_START */
    1427             :                 log_fatal(EEXTERNAL, "Too long read '%s'.\n", path);
    1428             :                 return -1;
    1429             :                 /* LCOV_EXCL_STOP */
    1430             :         }
    1431             : 
    1432             :         /* ending 0 */
    1433         145 :         buf[ret] = 0;
    1434             : 
    1435         145 :         i = buf;
    1436         145 :         token = 1; /* token number */
    1437         725 :         while (*i) {
    1438             :                 char* n;
    1439             :                 char* e;
    1440             :                 unsigned long long v;
    1441             : 
    1442             :                 /* skip spaces */
    1443        2206 :                 while (*i && isspace(*i))
    1444        1481 :                         ++i;
    1445             : 
    1446             :                 /* read digits */
    1447         725 :                 n = i;
    1448        5251 :                 while (*i && isdigit(*i))
    1449        4526 :                         ++i;
    1450             : 
    1451         725 :                 if (i == n) /* if no digit, abort */
    1452         145 :                         break;
    1453         725 :                 if (*i == 0 || !isspace(*i)) /* if no space, abort */
    1454             :                         break;
    1455         725 :                 *i++ = 0; /* put a terminator */
    1456             : 
    1457         725 :                 v = strtoull(n, &e, 10);
    1458         725 :                 if (*e != 0)
    1459           0 :                         break;
    1460             : 
    1461             :                 /* sum reads and writes completed */
    1462         725 :                 if (token == 1 || token == 5) {
    1463         290 :                         *count += v;
    1464         290 :                         if (token == 5)
    1465         145 :                                 break; /* stop here */
    1466             :                 }
    1467             : 
    1468         580 :                 ++token;
    1469             :         }
    1470             : 
    1471         145 :         return 0;
    1472             : }
    1473             : #endif
    1474             : 
    1475             : /**
    1476             :  * Get SMART attributes.
    1477             :  */
    1478             : #if HAVE_LINUX_DEVICE
    1479          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)
    1480             : {
    1481             :         char cmd[PATH_MAX + 64];
    1482             :         char file[PATH_MAX];
    1483             :         FILE* f;
    1484             :         int ret;
    1485             :         const char* x;
    1486             : 
    1487          26 :         x = find_smartctl();
    1488          26 :         if (!x) {
    1489             :                 /* LCOV_EXCL_START */
    1490             :                 log_fatal(EEXTERNAL, "Cannot find smartctl.\n");
    1491             :                 return -1;
    1492             :                 /* LCOV_EXCL_STOP */
    1493             :         }
    1494             : 
    1495          26 :         if (devresolve(device, file, sizeof(file)) != 0) {
    1496             :                 /* LCOV_EXCL_START */
    1497             :                 log_fatal(EEXTERNAL, "Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    1498             :                 return -1;
    1499             :                 /* LCOV_EXCL_STOP */
    1500             :         }
    1501             : 
    1502             :         /* if there is a custom smartctl command */
    1503          26 :         if (smartctl[0]) {
    1504             :                 char option[PATH_MAX];
    1505           0 :                 snprintf(option, sizeof(option), smartctl, file);
    1506           0 :                 snprintf(cmd, sizeof(cmd), "%s -a %s", x, option);
    1507             :         } else {
    1508          26 :                 snprintf(cmd, sizeof(cmd), "%s -a %s", x, file);
    1509             :         }
    1510             : 
    1511          26 :         log_tag("smartctl:%s:%s:run: %s\n", file, name, cmd);
    1512             : 
    1513          26 :         f = popen(cmd, "r");
    1514          26 :         if (!f) {
    1515             :                 /* LCOV_EXCL_START */
    1516             :                 log_tag("device:%s:%s:shell\n", file, name);
    1517             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (from popen).\n", cmd);
    1518             :                 return -1;
    1519             :                 /* LCOV_EXCL_STOP */
    1520             :         }
    1521             : 
    1522          26 :         if (smartctl_attribute(f, file, name, smart, info, serial, family, model, interface) != 0) {
    1523             :                 /* LCOV_EXCL_START */
    1524             :                 pclose(f);
    1525             :                 log_tag("device:%s:%s:shell\n", file, name);
    1526             :                 return -1;
    1527             :                 /* LCOV_EXCL_STOP */
    1528             :         }
    1529             : 
    1530          26 :         ret = pclose(f);
    1531             : 
    1532          26 :         log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
    1533             : 
    1534          26 :         if (!WIFEXITED(ret)) {
    1535             :                 /* LCOV_EXCL_START */
    1536             :                 log_tag("device:%s:%s:abort\n", file, name);
    1537             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (not exited).\n", cmd);
    1538             :                 return -1;
    1539             :                 /* LCOV_EXCL_STOP */
    1540             :         }
    1541          26 :         if (WEXITSTATUS(ret) == 127) {
    1542             :                 /* LCOV_EXCL_START */
    1543             :                 log_tag("device:%s:%s:shell\n", file, name);
    1544             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (from sh).\n", cmd);
    1545             :                 return -1;
    1546             :                 /* LCOV_EXCL_STOP */
    1547             :         }
    1548             : 
    1549             :         /* store the return smartctl return value */
    1550          26 :         smart[SMART_FLAGS].raw = WEXITSTATUS(ret);
    1551             : 
    1552          26 :         return 0;
    1553             : }
    1554             : #endif
    1555             : 
    1556             : /**
    1557             :  * Get device attributes.
    1558             :  */
    1559             : #if HAVE_LINUX_DEVICE
    1560          54 : static void devattr(dev_t device, uint64_t* info, char* serial, char* family, char* model, char* interface)
    1561             : {
    1562             :         char path[PATH_MAX];
    1563             :         char buf[512];
    1564             :         int ret;
    1565             : 
    1566             :         (void)family; /* not available, smartctl uses an internal database to get it */
    1567             : 
    1568          54 :         if (info[INFO_SIZE] == SMART_UNASSIGNED) {
    1569          12 :                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/size", major(device), minor(device));
    1570          12 :                 if (sysattr(path, buf, sizeof(buf)) == 0) {
    1571             :                         char* e;
    1572             :                         uint64_t v;
    1573          12 :                         v = strtoul(buf, &e, 10);
    1574          12 :                         if (*e == 0)
    1575          12 :                                 info[INFO_SIZE] = v * 512;
    1576             :                 }
    1577             :         }
    1578             : 
    1579          54 :         if (info[INFO_ROTATION_RATE] == SMART_UNASSIGNED) {
    1580          12 :                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/queue/rotational", major(device), minor(device));
    1581          12 :                 if (sysattr(path, buf, sizeof(buf)) == 0) {
    1582             :                         char* e;
    1583             :                         uint64_t v;
    1584          12 :                         v = strtoul(buf, &e, 10);
    1585          12 :                         if (*e == 0)
    1586          12 :                                 info[INFO_ROTATION_RATE] = v;
    1587             :                 }
    1588             :         }
    1589             : 
    1590          54 :         if (*model == 0) {
    1591          12 :                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/device/model", major(device), minor(device));
    1592          12 :                 if (sysattr(path, buf, sizeof(buf)) == 0) {
    1593          12 :                         if (buf[0] != 0)
    1594          12 :                                 pathcpy(model, SMART_MAX, buf);
    1595             :                 }
    1596             :         }
    1597             : 
    1598          54 :         if (*serial == 0) {
    1599          54 :                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/device/serial", major(device), minor(device));
    1600          54 :                 if (sysattr(path, buf, sizeof(buf)) == 0) {
    1601           0 :                         if (buf[0] != 0)
    1602           0 :                                 pathcpy(serial, SMART_MAX, buf);
    1603             :                 }
    1604             :         }
    1605             : 
    1606          54 :         if (*serial == 0) {
    1607             :                 // --- Page 0x80: Unit Serial Number ---
    1608          54 :                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/device/vpd_pg80", major(device), minor(device));
    1609          54 :                 ret = sysread(path, buf, sizeof(buf));
    1610          54 :                 if (ret > 4) {
    1611          54 :                         unsigned len = (unsigned char)buf[3];
    1612          54 :                         if (4 + len <= (size_t)ret && 4 + len + 1 <= sizeof(buf)) {
    1613          54 :                                 char* attr = buf + 4;
    1614          54 :                                 attr[len] = 0;
    1615          54 :                                 strtrim(attr);
    1616          54 :                                 if (*attr)
    1617          54 :                                         pathcpy(serial, SMART_MAX, attr);
    1618             :                         }
    1619             :                 }
    1620             :         }
    1621             : 
    1622             :         /* always override interface if it's usb */
    1623          54 :         pathprint(path, sizeof(path), "/sys/dev/block/%u:%u", major(device), minor(device));
    1624          54 :         ret = readlink(path, buf, sizeof(buf));
    1625          54 :         if (ret > 0 && (unsigned)ret < sizeof(buf)) {
    1626          54 :                 buf[ret] = 0;
    1627          54 :                 if (strstr(buf, "usb") != 0)
    1628           0 :                         strcpy(interface, "USB");
    1629             :         }
    1630          54 : }
    1631             : #endif
    1632             : 
    1633             : /**
    1634             :  * Get POWER state.
    1635             :  */
    1636             : #if HAVE_LINUX_DEVICE
    1637          28 : 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)
    1638             : {
    1639             :         char cmd[PATH_MAX + 64];
    1640             :         char file[PATH_MAX];
    1641             :         FILE* f;
    1642             :         int ret;
    1643             :         const char* x;
    1644             : 
    1645          28 :         x = find_smartctl();
    1646          28 :         if (!x) {
    1647             :                 /* LCOV_EXCL_START */
    1648             :                 log_fatal(EEXTERNAL, "Cannot find smartctl.\n");
    1649             :                 return -1;
    1650             :                 /* LCOV_EXCL_STOP */
    1651             :         }
    1652             : 
    1653          28 :         if (devresolve(device, file, sizeof(file)) != 0) {
    1654             :                 /* LCOV_EXCL_START */
    1655             :                 log_fatal(EEXTERNAL, "Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    1656             :                 return -1;
    1657             :                 /* LCOV_EXCL_STOP */
    1658             :         }
    1659             : 
    1660             :         /* if there is a custom smartctl command */
    1661          28 :         if (smartctl[0]) {
    1662             :                 char option[PATH_MAX];
    1663           0 :                 snprintf(option, sizeof(option), smartctl, file);
    1664           0 :                 snprintf(cmd, sizeof(cmd), "%s -n standby,3 -a %s", x, option);
    1665             :         } else {
    1666          28 :                 snprintf(cmd, sizeof(cmd), "%s -n standby,3 -a %s", x, file);
    1667             :         }
    1668             : 
    1669          28 :         log_tag("smartctl:%s:%s:run: %s\n", file, name, cmd);
    1670             : 
    1671          28 :         f = popen(cmd, "r");
    1672          28 :         if (!f) {
    1673             :                 /* LCOV_EXCL_START */
    1674             :                 log_tag("device:%s:%s:shell\n", file, name);
    1675             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (from popen).\n", cmd);
    1676             :                 return -1;
    1677             :                 /* LCOV_EXCL_STOP */
    1678             :         }
    1679             : 
    1680          28 :         if (smartctl_attribute(f, file, name, smart, info, serial, family, model, interface) != 0) {
    1681             :                 /* LCOV_EXCL_START */
    1682             :                 pclose(f);
    1683             :                 log_tag("device:%s:%s:shell\n", file, name);
    1684             :                 return -1;
    1685             :                 /* LCOV_EXCL_STOP */
    1686             :         }
    1687             : 
    1688          28 :         ret = pclose(f);
    1689             : 
    1690          28 :         log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
    1691             : 
    1692          28 :         if (!WIFEXITED(ret)) {
    1693             :                 /* LCOV_EXCL_START */
    1694             :                 log_tag("device:%s:%s:abort\n", file, name);
    1695             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (not exited).\n", cmd);
    1696             :                 return -1;
    1697             :                 /* LCOV_EXCL_STOP */
    1698             :         }
    1699          28 :         if (WEXITSTATUS(ret) == 127) {
    1700             :                 /* LCOV_EXCL_START */
    1701             :                 log_tag("device:%s:%s:shell\n", file, name);
    1702             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (from sh).\n", cmd);
    1703             :                 return -1;
    1704             :                 /* LCOV_EXCL_STOP */
    1705             :         }
    1706             : 
    1707          28 :         if (WEXITSTATUS(ret) == 3) {
    1708          12 :                 log_tag("attr:%s:%s:power:standby\n", file, name);
    1709          12 :                 *power = POWER_STANDBY;
    1710             :         } else {
    1711          16 :                 log_tag("attr:%s:%s:power:active\n", file, name);
    1712          16 :                 *power = POWER_ACTIVE;
    1713             : 
    1714             :                 /* store the smartctl return value */
    1715          16 :                 if (smart)
    1716          16 :                         smart[SMART_FLAGS].raw = WEXITSTATUS(ret);
    1717             :         }
    1718             : 
    1719          28 :         return 0;
    1720             : }
    1721             : #endif
    1722             : 
    1723             : /**
    1724             :  * Spin down a specific device.
    1725             :  */
    1726             : #if HAVE_LINUX_DEVICE
    1727          17 : static int devdown(dev_t device, const char* name, const char* smartctl)
    1728             : {
    1729             :         char cmd[PATH_MAX + 64];
    1730             :         char file[PATH_MAX];
    1731             :         FILE* f;
    1732             :         int ret;
    1733             :         const char* x;
    1734             : 
    1735          17 :         x = find_smartctl();
    1736          17 :         if (!x) {
    1737             :                 /* LCOV_EXCL_START */
    1738             :                 log_fatal(EEXTERNAL, "Cannot find smartctl.\n");
    1739             :                 return -1;
    1740             :                 /* LCOV_EXCL_STOP */
    1741             :         }
    1742             : 
    1743          17 :         if (devresolve(device, file, sizeof(file)) != 0) {
    1744             :                 /* LCOV_EXCL_START */
    1745             :                 log_fatal(EEXTERNAL, "Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    1746             :                 return -1;
    1747             :                 /* LCOV_EXCL_STOP */
    1748             :         }
    1749             : 
    1750             :         /* if there is a custom smartctl command */
    1751          17 :         if (smartctl[0]) {
    1752             :                 char option[PATH_MAX];
    1753           5 :                 snprintf(option, sizeof(option), smartctl, file);
    1754           5 :                 snprintf(cmd, sizeof(cmd), "%s -s standby,now %s", x, option);
    1755             :         } else {
    1756          12 :                 snprintf(cmd, sizeof(cmd), "%s -s standby,now %s", x, file);
    1757             :         }
    1758             : 
    1759          17 :         log_tag("smartctl:%s:%s:run: %s\n", file, name, cmd);
    1760             : 
    1761          17 :         f = popen(cmd, "r");
    1762          17 :         if (!f) {
    1763             :                 /* LCOV_EXCL_START */
    1764             :                 log_tag("device:%s:%s:shell\n", file, name);
    1765             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (from popen).\n", cmd);
    1766             :                 return -1;
    1767             :                 /* LCOV_EXCL_STOP */
    1768             :         }
    1769             : 
    1770          17 :         if (smartctl_flush(f, file, name) != 0) {
    1771             :                 /* LCOV_EXCL_START */
    1772             :                 pclose(f);
    1773             :                 log_tag("device:%s:%s:shell\n", file, name);
    1774             :                 return -1;
    1775             :                 /* LCOV_EXCL_STOP */
    1776             :         }
    1777             : 
    1778          17 :         ret = pclose(f);
    1779             : 
    1780          17 :         log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
    1781             : 
    1782          17 :         if (!WIFEXITED(ret)) {
    1783             :                 /* LCOV_EXCL_START */
    1784             :                 log_tag("device:%s:%s:abort\n", file, name);
    1785             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (not exited).\n", cmd);
    1786             :                 return -1;
    1787             :                 /* LCOV_EXCL_STOP */
    1788             :         }
    1789          17 :         if (WEXITSTATUS(ret) == 127) {
    1790             :                 /* LCOV_EXCL_START */
    1791             :                 log_tag("device:%s:%s:shell\n", file, name);
    1792             :                 log_fatal(EEXTERNAL, "Failed to run '%s' (from sh).\n", cmd);
    1793             :                 return -1;
    1794             :                 /* LCOV_EXCL_STOP */
    1795             :         }
    1796          17 :         if (WEXITSTATUS(ret) != 0) {
    1797             :                 /* LCOV_EXCL_START */
    1798             :                 log_tag("device:%s:%s:exit:%d\n", file, name, WEXITSTATUS(ret));
    1799             :                 log_fatal(EEXTERNAL, "Failed to run '%s' with return code %xh.\n", cmd, WEXITSTATUS(ret));
    1800             :                 return -1;
    1801             :                 /* LCOV_EXCL_STOP */
    1802             :         }
    1803             : 
    1804          12 :         log_tag("attr:%s:%s:power:down\n", file, name);
    1805             : 
    1806          12 :         return 0;
    1807             : }
    1808             : #endif
    1809             : 
    1810             : /**
    1811             :  * Spin down a specific device if it's up.
    1812             :  */
    1813             : #if HAVE_LINUX_DEVICE
    1814           0 : static int devdownifup(dev_t device, const char* name, const char* smartctl, int* power)
    1815             : {
    1816           0 :         *power = POWER_UNKNOWN;
    1817             : 
    1818           0 :         if (devprobe(device, name, smartctl, power, 0, 0, 0, 0, 0, 0) != 0)
    1819           0 :                 return -1;
    1820             : 
    1821           0 :         if (*power == POWER_ACTIVE)
    1822           0 :                 return devdown(device, name, smartctl);
    1823             : 
    1824           0 :         return 0;
    1825             : }
    1826             : #endif
    1827             : 
    1828             : /**
    1829             :  * Spin up a specific device.
    1830             :  */
    1831             : #if HAVE_LINUX_DEVICE
    1832          42 : static int devup(dev_t device, const char* name)
    1833             : {
    1834             :         char file[PATH_MAX];
    1835             :         int ret;
    1836             :         int f;
    1837             :         void* buf;
    1838             :         uint64_t size;
    1839             :         uint64_t offset;
    1840             : 
    1841          42 :         if (devresolve(device, file, sizeof(file)) != 0) {
    1842             :                 /* LCOV_EXCL_START */
    1843             :                 log_fatal(EEXTERNAL, "Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    1844             :                 return -1;
    1845             :                 /* LCOV_EXCL_STOP */
    1846             :         }
    1847             : 
    1848             :         /* O_DIRECT requires memory aligned to the block size */
    1849          42 :         if (posix_memalign(&buf, 4096, 4096) != 0) {
    1850             :                 /* LCOV_EXCL_START */
    1851             :                 log_fatal(errno, "Failed to allocate aligned memory for device '%u:%u'.\n", major(device), minor(device));
    1852             :                 return -1;
    1853             :                 /* LCOV_EXCL_STOP */
    1854             :         }
    1855             : 
    1856          42 :         f = open(file, O_RDONLY | O_DIRECT);
    1857          42 :         if (f < 0) {
    1858             :                 /* LCOV_EXCL_START */
    1859             :                 free(buf);
    1860             :                 log_tag("device:%s:%s:error:%d\n", file, name, errno);
    1861             :                 log_fatal(errno, "Failed to open device '%u:%u'.\n", major(device), minor(device));
    1862             :                 return -1;
    1863             :                 /* LCOV_EXCL_STOP */
    1864             :         }
    1865             : 
    1866          12 :         if (ioctl(f, BLKGETSIZE64, &size) < 0) {
    1867             :                 /* LCOV_EXCL_START */
    1868             :                 close(f);
    1869             :                 free(buf);
    1870             :                 log_tag("device:%s:%s:error:%d\n", file, name, errno);
    1871             :                 log_fatal(errno, "Failed to get device size '%u:%u'.\n", major(device), minor(device));
    1872             :                 return -1;
    1873             :                 /* LCOV_EXCL_STOP */
    1874             :         }
    1875             : 
    1876             :         /* select a random offset */
    1877          12 :         offset = (random_u64() % (size / 4096)) * 4096;
    1878             : 
    1879             : #if HAVE_POSIX_FADVISE
    1880             :         /* clear cache */
    1881          12 :         ret = posix_fadvise(f, offset, 4096, POSIX_FADV_DONTNEED);
    1882          12 :         if (ret != 0) {
    1883             :                 /* LCOV_EXCL_START */
    1884             :                 close(f);
    1885             :                 free(buf);
    1886             :                 log_tag("device:%s:%s:error:%d\n", file, name, errno);
    1887             :                 log_fatal(errno, "Failed to advise device '%u:%u'.\n", major(device), minor(device));
    1888             :                 return -1;
    1889             :                 /* LCOV_EXCL_STOP */
    1890             :         }
    1891             : #endif
    1892             : 
    1893          12 :         ret = pread(f, buf, 4096, offset);
    1894          12 :         if (ret < 0) {
    1895             :                 /* LCOV_EXCL_START */
    1896             :                 close(f);
    1897             :                 free(buf);
    1898             :                 log_tag("device:%s:%s:error:%d\n", file, name, errno);
    1899             :                 log_fatal(errno, "Failed to read device '%u:%u'.\n", major(device), minor(device));
    1900             :                 return -1;
    1901             :                 /* LCOV_EXCL_STOP */
    1902             :         }
    1903             : 
    1904          12 :         ret = close(f);
    1905          12 :         if (ret < 0) {
    1906             :                 /* LCOV_EXCL_START */
    1907             :                 free(buf);
    1908             :                 log_tag("device:%s:%s:error:%d\n", file, name, errno);
    1909             :                 log_fatal(errno, "Failed to close device '%u:%u'.\n", major(device), minor(device));
    1910             :                 return -1;
    1911             :                 /* LCOV_EXCL_STOP */
    1912             :         }
    1913             : 
    1914          12 :         log_tag("attr:%s:%s:power:up\n", file, name);
    1915             : 
    1916          12 :         free(buf);
    1917          12 :         return 0;
    1918             : }
    1919             : #endif
    1920             : 
    1921             : /**
    1922             :  * Thread for spinning up.
    1923             :  *
    1924             :  * Note that filling up the devinfo object is done inside this thread,
    1925             :  * to avoid to block the main thread if the device need to be spin up
    1926             :  * to handle stat/resolve requests.
    1927             :  */
    1928          42 : static void* thread_spinup(void* arg)
    1929             : {
    1930             : #if HAVE_LINUX_DEVICE
    1931          42 :         devinfo_t* devinfo = arg;
    1932             :         uint64_t start;
    1933             : 
    1934          42 :         start = os_tick_ms();
    1935             : 
    1936          42 :         if (devup(devinfo->device, devinfo->name) != 0) {
    1937             :                 /* LCOV_EXCL_START */
    1938             :                 return (void*)-1;
    1939             :                 /* LCOV_EXCL_STOP */
    1940             :         }
    1941             : 
    1942          12 :         msg_status("Spunup device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
    1943             : 
    1944             :         /* after the spin up, get SMART info */
    1945          12 :         if (devsmart(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smart, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf) != 0) {
    1946             :                 /* LCOV_EXCL_START */
    1947             :                 return (void*)-1;
    1948             :                 /* LCOV_EXCL_STOP */
    1949             :         }
    1950             : 
    1951             :         /*
    1952             :          * Retrieve some attributes directly from the system.
    1953             :          *
    1954             :          * smartctl intentionally skips queries on devices in standby mode
    1955             :          * to prevent accidentally spinning them up.
    1956             :          */
    1957          12 :         devattr(devinfo->device, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf);
    1958             : 
    1959          12 :         return 0;
    1960             : #else
    1961             :         (void)arg;
    1962             :         return (void*)-1;
    1963             : #endif
    1964             : }
    1965             : 
    1966             : /**
    1967             :  * Thread for spinning down.
    1968             :  */
    1969          17 : static void* thread_spindown(void* arg)
    1970             : {
    1971             : #if HAVE_LINUX_DEVICE
    1972          17 :         devinfo_t* devinfo = arg;
    1973             :         uint64_t start;
    1974             : 
    1975          17 :         start = os_tick_ms();
    1976             : 
    1977          17 :         if (devdown(devinfo->device, devinfo->name, devinfo->smartctl) != 0) {
    1978             :                 /* LCOV_EXCL_START */
    1979             :                 return (void*)-1;
    1980             :                 /* LCOV_EXCL_STOP */
    1981             :         }
    1982             : 
    1983          12 :         msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
    1984             : 
    1985          12 :         return 0;
    1986             : #else
    1987             :         (void)arg;
    1988             :         return (void*)-1;
    1989             : #endif
    1990             : }
    1991             : 
    1992             : /**
    1993             :  * Thread for spinning down.
    1994             :  */
    1995           0 : static void* thread_spindownifup(void* arg)
    1996             : {
    1997             : #if HAVE_LINUX_DEVICE
    1998           0 :         devinfo_t* devinfo = arg;
    1999             :         uint64_t start;
    2000             :         int power;
    2001             : 
    2002           0 :         start = os_tick_ms();
    2003             : 
    2004           0 :         if (devdownifup(devinfo->device, devinfo->name, devinfo->smartctl, &power) != 0) {
    2005             :                 /* LCOV_EXCL_START */
    2006             :                 return (void*)-1;
    2007             :                 /* LCOV_EXCL_STOP */
    2008             :         }
    2009             : 
    2010           0 :         if (power == POWER_ACTIVE)
    2011           0 :                 msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, os_tick_ms() - start);
    2012             : 
    2013           0 :         return 0;
    2014             : #else
    2015             :         (void)arg;
    2016             :         return (void*)-1;
    2017             : #endif
    2018             : }
    2019             : 
    2020             : /**
    2021             :  * Thread for getting smart info.
    2022             :  */
    2023          14 : static void* thread_smart(void* arg)
    2024             : {
    2025             : #if HAVE_LINUX_DEVICE
    2026          14 :         devinfo_t* devinfo = arg;
    2027             : 
    2028          14 :         if (devsmart(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smart, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf) != 0) {
    2029             :                 /* LCOV_EXCL_START */
    2030             :                 return (void*)-1;
    2031             :                 /* LCOV_EXCL_STOP */
    2032             :         }
    2033             : 
    2034             :         /*
    2035             :          * Retrieve some attributes directly from the system.
    2036             :          *
    2037             :          * smartctl intentionally skips queries on devices in standby mode
    2038             :          * to prevent accidentally spinning them up.
    2039             :          */
    2040          14 :         devattr(devinfo->device, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf);
    2041             : 
    2042          14 :         return 0;
    2043             : #else
    2044             :         (void)arg;
    2045             :         return (void*)-1;
    2046             : #endif
    2047             : }
    2048             : 
    2049             : /**
    2050             :  * Thread for getting power info.
    2051             :  */
    2052          28 : static void* thread_probe(void* arg)
    2053             : {
    2054             : #if HAVE_LINUX_DEVICE
    2055          28 :         devinfo_t* devinfo = arg;
    2056             : 
    2057          28 :         if (devprobe(devinfo->device, devinfo->name, devinfo->smartctl, &devinfo->power, devinfo->smart, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf) != 0) {
    2058             :                 /* LCOV_EXCL_START */
    2059             :                 return (void*)-1;
    2060             :                 /* LCOV_EXCL_STOP */
    2061             :         }
    2062             : 
    2063             :         /*
    2064             :          * Retrieve some attributes directly from the system.
    2065             :          *
    2066             :          * smartctl intentionally skips queries on devices in standby mode
    2067             :          * to prevent accidentally spinning them up.
    2068             :          */
    2069          28 :         devattr(devinfo->device, devinfo->info, devinfo->serial, devinfo->family, devinfo->model, devinfo->interf);
    2070             : 
    2071          28 :         return 0;
    2072             : #else
    2073             :         (void)arg;
    2074             :         return (void*)-1;
    2075             : #endif
    2076             : }
    2077             : 
    2078           7 : static int device_thread(tommy_list* list, void* (*func)(void* arg))
    2079             : {
    2080           7 :         int fail = 0;
    2081             :         tommy_node* i;
    2082             : 
    2083             : #if HAVE_THREAD
    2084             :         /* start all threads */
    2085         108 :         for (i = tommy_list_head(list); i != 0; i = i->next) {
    2086         101 :                 devinfo_t* devinfo = i->data;
    2087             : 
    2088         101 :                 thread_create(&devinfo->thread, func, devinfo);
    2089             :         }
    2090             : 
    2091             :         /* join all threads */
    2092         108 :         for (i = tommy_list_head(list); i != 0; i = i->next) {
    2093         101 :                 devinfo_t* devinfo = i->data;
    2094             :                 void* retval;
    2095             : 
    2096         101 :                 thread_join(devinfo->thread, &retval);
    2097             : 
    2098         101 :                 if (retval != 0)
    2099          35 :                         ++fail;
    2100             :         }
    2101             : #else
    2102             :         for (i = tommy_list_head(list); i != 0; i = i->next) {
    2103             :                 devinfo_t* devinfo = i->data;
    2104             : 
    2105             :                 if (func(devinfo) != 0)
    2106             :                         ++fail;
    2107             :         }
    2108             : #endif
    2109           7 :         if (fail != 0) {
    2110             :                 /* LCOV_EXCL_START */
    2111             :                 return -1;
    2112             :                 /* LCOV_EXCL_STOP */
    2113             :         }
    2114             : 
    2115           5 :         return 0;
    2116             : }
    2117             : 
    2118           9 : int devquery(tommy_list* high, tommy_list* low, int operation)
    2119             : {
    2120           9 :         void* (*func)(void* arg) = 0;
    2121             : 
    2122             : #if HAVE_LINUX_DEVICE
    2123             :         tommy_node* i;
    2124             :         struct stat st;
    2125             : 
    2126             :         /* sysfs interface is required */
    2127           9 :         if (stat("/sys/dev/block", &st) != 0) {
    2128             :                 /* LCOV_EXCL_START */
    2129             :                 log_fatal(EEXTERNAL, "Missing interface /sys/dev/block.\n");
    2130             :                 return -1;
    2131             :                 /* LCOV_EXCL_STOP */
    2132             :         }
    2133             : 
    2134             :         /* for each high device */
    2135         154 :         for (i = tommy_list_head(high); i != 0; i = i->next) {
    2136         145 :                 devinfo_t* devinfo = i->data;
    2137         145 :                 uint64_t device = devinfo->device;
    2138             :                 uint64_t access_stat;
    2139             : 
    2140             : #if HAVE_SYNCFS
    2141         145 :                 if (operation == DEVICE_DOWN) {
    2142             :                         /* flush the high level filesystem before spinning down */
    2143          17 :                         int f = open(devinfo->mount, O_RDONLY);
    2144          17 :                         if (f >= 0) {
    2145          17 :                                 syncfs(f);
    2146          17 :                                 close(f);
    2147             :                         }
    2148             :                 }
    2149             : #endif
    2150             : 
    2151             :                 /* if the major is the null device, find the real one */
    2152         145 :                 if (major(device) == 0) {
    2153             :                         /* obtain the real device */
    2154           0 :                         if (devdereference(device, &device) != 0) {
    2155             :                                 /* LCOV_EXCL_START */
    2156             :                                 log_fatal(EEXTERNAL, "Failed to dereference device '%u:%u'.\n", major(device), minor(device));
    2157             :                                 return -1;
    2158             :                                 /* LCOV_EXCL_STOP */
    2159             :                         }
    2160             :                 }
    2161             : 
    2162             :                 /* retrieve access stat for the high level device */
    2163         145 :                 if (devstat(device, &access_stat) == 0) {
    2164             :                         /* cumulate access stat in the first split */
    2165         145 :                         if (devinfo->split)
    2166          39 :                                 devinfo->split->access_stat += access_stat;
    2167             :                         else
    2168         106 :                                 devinfo->access_stat += access_stat;
    2169             :                 }
    2170             : 
    2171             :                 /* get the device file */
    2172         145 :                 if (devresolve(device, devinfo->file, sizeof(devinfo->file)) != 0) {
    2173             :                         /* LCOV_EXCL_START */
    2174             :                         log_fatal(EEXTERNAL, "Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    2175             :                         return -1;
    2176             :                         /* LCOV_EXCL_STOP */
    2177             :                 }
    2178             : 
    2179             :                 /* expand the tree of devices */
    2180         145 :                 if (devtree(devinfo, device, low) != 0) {
    2181             :                         /* LCOV_EXCL_START */
    2182             :                         log_fatal(EEXTERNAL, "Failed to expand device '%u:%u'.\n", major(device), minor(device));
    2183             :                         return -1;
    2184             :                         /* LCOV_EXCL_STOP */
    2185             :                 }
    2186             :         }
    2187             : #else
    2188             :         (void)high;
    2189             : #endif
    2190             : 
    2191           9 :         switch (operation) {
    2192           2 :         case DEVICE_UP : func = thread_spinup; break;
    2193           2 :         case DEVICE_DOWN : func = thread_spindown; break;
    2194           1 :         case DEVICE_SMART : func = thread_smart; break;
    2195           2 :         case DEVICE_PROBE : func = thread_probe; break;
    2196           0 :         case DEVICE_DOWNIFUP : func = thread_spindownifup; break;
    2197             :         }
    2198             : 
    2199           9 :         if (!func)
    2200           2 :                 return 0;
    2201             : 
    2202           7 :         return device_thread(low, func);
    2203             : }
    2204             : 
    2205         297 : void os_init(int opt)
    2206             : {
    2207             : #if HAVE_BLKID
    2208             :         int ret;
    2209         297 :         ret = blkid_get_cache(&cache, NULL);
    2210         297 :         if (ret != 0) {
    2211             :                 /* LCOV_EXCL_START */
    2212             :                 log_fatal(EEXTERNAL, "WARNING Failed to get blkid cache\n");
    2213             :                 /* LCOV_EXCL_STOP */
    2214             :         }
    2215             : #endif
    2216             : 
    2217             :         (void)opt;
    2218         297 : }
    2219             : 
    2220         286 : void os_done(void)
    2221             : {
    2222             : #if HAVE_BLKID
    2223         286 :         if (cache != 0)
    2224         286 :                 blkid_put_cache(cache);
    2225             : #endif
    2226         286 : }
    2227             : 
    2228             : /* LCOV_EXCL_START */
    2229             : void os_abort(void)
    2230             : {
    2231             : #if HAVE_BACKTRACE && HAVE_BACKTRACE_SYMBOLS
    2232             :         void* stack[32];
    2233             :         char** messages;
    2234             :         size_t size;
    2235             :         unsigned i;
    2236             : #endif
    2237             : 
    2238             :         printf("Stacktrace of " PACKAGE " v" VERSION);
    2239             : #ifdef _linux
    2240             :         printf(", linux");
    2241             : #endif
    2242             : #ifdef __GNUC__
    2243             :         printf(", gcc " __VERSION__);
    2244             : #endif
    2245             :         printf(", %d-bit", (int)sizeof(void*) * 8);
    2246             :         printf(", PATH_MAX=%d", PATH_MAX);
    2247             :         printf("\n");
    2248             : 
    2249             : #if HAVE_BACKTRACE && HAVE_BACKTRACE_SYMBOLS
    2250             :         size = backtrace(stack, 32);
    2251             : 
    2252             :         messages = backtrace_symbols(stack, size);
    2253             : 
    2254             :         for (i = 1; i < size; ++i) {
    2255             :                 const char* msg;
    2256             : 
    2257             :                 if (messages)
    2258             :                         msg = messages[i];
    2259             :                 else
    2260             :                         msg = "<unknown>";
    2261             : 
    2262             :                 printf("[bt] %02u: %s\n", i, msg);
    2263             : 
    2264             :                 if (messages) {
    2265             :                         int ret;
    2266             :                         char addr2line[1024];
    2267             :                         size_t j = 0;
    2268             :                         while (msg[j] != '(' && msg[j] != ' ' && msg[j] != 0)
    2269             :                                 ++j;
    2270             : 
    2271             :                         snprintf(addr2line, sizeof(addr2line), "addr2line %p -e %.*s", stack[i], (unsigned)j, msg);
    2272             : 
    2273             :                         ret = system(addr2line);
    2274             :                         if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0)
    2275             :                                 printf("exit:%d\n", WEXITSTATUS(ret));
    2276             :                         if (WIFSIGNALED(ret))
    2277             :                                 printf("signal:%d\n", WTERMSIG(ret));
    2278             :                 }
    2279             :         }
    2280             : #endif
    2281             : 
    2282             :         printf("Please report this error to the SnapRAID Forum:\n");
    2283             :         printf("https://sourceforge.net/p/snapraid/discussion/1677233/\n");
    2284             : 
    2285             :         abort();
    2286             : }
    2287             : /* LCOV_EXCL_STOP */
    2288             : 
    2289           0 : void os_clear(void)
    2290             : {
    2291             :         /* ANSI codes */
    2292           0 :         printf("\033[H"); /* cursor at topleft */
    2293           0 :         printf("\033[2J"); /* clear screen */
    2294           0 : }
    2295             : 
    2296         135 : size_t direct_size(void)
    2297             : {
    2298             :         long size;
    2299             : 
    2300         135 :         size = sysconf(_SC_PAGESIZE);
    2301             : 
    2302         135 :         if (size == -1) {
    2303             :                 /* LCOV_EXCL_START */
    2304             :                 log_fatal(EEXTERNAL, "No page size\n");
    2305             :                 exit(EXIT_FAILURE);
    2306             :                 /* LCOV_EXCL_STOP */
    2307             :         }
    2308             : 
    2309         135 :         return size;
    2310             : }
    2311             : 
    2312             : #if HAVE_LINUX_DEVICE
    2313             : 
    2314             : /* List of possible ambient temperature labels */
    2315             : const char* AMBIENT_LABEL[] = {
    2316             :         "systin",
    2317             :         "auxtin",
    2318             :         "mb",
    2319             :         "m/b",
    2320             :         "board",
    2321             :         "motherboard",
    2322             :         "system",
    2323             :         "chassis",
    2324             :         "case",
    2325             :         "room",
    2326             :         "ambient",
    2327             :         0
    2328             : };
    2329             : 
    2330           1 : int ambient_temperature(void)
    2331             : {
    2332             :         DIR* dir;
    2333             :         struct dirent* entry;
    2334           1 :         int lowest_temp = 0;
    2335             : 
    2336           1 :         dir = opendir("/sys/class/hwmon");
    2337           1 :         if (!dir)
    2338           0 :                 return 0;
    2339             : 
    2340             :         /* iterate through hwmon devices */
    2341           5 :         while ((entry = readdir(dir)) != NULL) {
    2342             :                 char path[PATH_MAX];
    2343             :                 DIR* hwmon_dir;
    2344             :                 struct dirent* hwmon_entry;
    2345             : 
    2346           4 :                 if (strncmp(entry->d_name, "hwmon", 5) != 0)
    2347           2 :                         continue;
    2348             : 
    2349           2 :                 pathprint(path, sizeof(path), "/sys/class/hwmon/%s", entry->d_name);
    2350             : 
    2351             :                 /* iterate through temp*_input files */
    2352           2 :                 hwmon_dir = opendir(path);
    2353           2 :                 if (!hwmon_dir)
    2354           0 :                         continue;
    2355             : 
    2356          66 :                 while ((hwmon_entry = readdir(hwmon_dir)) != NULL) {
    2357             :                         char value[128];
    2358             :                         char name[128];
    2359             :                         char label[128];
    2360             :                         char* dash;
    2361             :                         char* e;
    2362             :                         long temp;
    2363             : 
    2364          64 :                         if (strncmp(hwmon_entry->d_name, "temp", 4) != 0)
    2365          63 :                                 continue;
    2366             : 
    2367          18 :                         dash = strrchr(hwmon_entry->d_name, '_');
    2368          18 :                         if (dash == 0)
    2369           0 :                                 continue;
    2370             : 
    2371          18 :                         if (strcmp(dash, "_input") != 0)
    2372          14 :                                 continue;
    2373             : 
    2374             :                         /* read the temperature */
    2375           4 :                         pathprint(path, sizeof(path), "/sys/class/hwmon/%s/%s", entry->d_name, hwmon_entry->d_name);
    2376             : 
    2377           4 :                         if (sysattr(path, value, sizeof(value)) != 0)
    2378           0 :                                 continue;
    2379             : 
    2380           4 :                         temp = strtol(value, &e, 10) / 1000;
    2381           4 :                         if (*e != 0 && !isspace(*e))
    2382           0 :                                 continue;
    2383             : 
    2384             :                         /* cut the file name at "_input" */
    2385           4 :                         *dash = 0;
    2386             : 
    2387             :                         /* read the corresponding name */
    2388           4 :                         pathprint(path, sizeof(path), "/sys/class/hwmon/%s/name", entry->d_name);
    2389           4 :                         if (sysattr(path, name, sizeof(name)) != 0) {
    2390             :                                 /* fallback to using the hwmon name */
    2391           0 :                                 pathcpy(name, sizeof(name), entry->d_name);
    2392             :                         }
    2393             : 
    2394             :                         /* read the corresponding label file */
    2395           4 :                         pathprint(path, sizeof(path), "/sys/class/hwmon/%s/%s_label", entry->d_name, hwmon_entry->d_name);
    2396           4 :                         if (sysattr(path, label, sizeof(label)) != 0) {
    2397             :                                 /* fallback to using the temp* name (e.g., temp1, temp2) */
    2398           0 :                                 pathcpy(label, sizeof(label), hwmon_entry->d_name);
    2399             :                         }
    2400             : 
    2401           4 :                         log_tag("thermal:ambient:device:%s:%s:%s:%s:%ld\n", entry->d_name, name, hwmon_entry->d_name, label, temp);
    2402             : 
    2403             :                         /* check if temperature is in reasonable range */
    2404           4 :                         if (temp < 15 || temp > 40)
    2405           3 :                                 continue;
    2406             : 
    2407             :                         /* lower case */
    2408           1 :                         strlwr(label);
    2409             : 
    2410             :                         /* check if label matches possible ambient labels */
    2411           3 :                         for (int i = 0; AMBIENT_LABEL[i]; ++i) {
    2412           3 :                                 if (worddigitstr(label, AMBIENT_LABEL[i]) != 0) {
    2413           1 :                                         log_tag("thermal:ambient:candidate:%ld\n", temp);
    2414           1 :                                         if (lowest_temp == 0 || lowest_temp > temp)
    2415           1 :                                                 lowest_temp = temp;
    2416           1 :                                         break;
    2417             :                                 }
    2418             :                         }
    2419             : 
    2420             :                         /* accept also generic "temp1" */
    2421           1 :                         if (strcmp(label, "temp1") == 0) {
    2422           0 :                                 log_tag("thermal:ambient:candidate:%ld\n", temp);
    2423           0 :                                 if (lowest_temp == 0 || lowest_temp > temp)
    2424           0 :                                         lowest_temp = temp;
    2425             :                         }
    2426             :                 }
    2427             : 
    2428           2 :                 closedir(hwmon_dir);
    2429             :         }
    2430             : 
    2431           1 :         closedir(dir);
    2432             : 
    2433           1 :         return lowest_temp;
    2434             : }
    2435             : #else
    2436             : int ambient_temperature(void)
    2437             : {
    2438             :         return 0;
    2439             : }
    2440             : #endif
    2441             : 
    2442             : #endif
    2443             : 

Generated by: LCOV version 1.0