LCOV - code coverage report
Current view: top level - cmdline - unix.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 314 453 69.3 %
Date: 2017-11-06 22:14:04 Functions: 28 31 90.3 %

          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     1717312 : int open_noatime(const char* file, int flags)
      32             : {
      33             : #ifdef O_NOATIME
      34     1717312 :         int f = open(file, flags | O_NOATIME);
      35             : 
      36             :         /* only root is allowed to use O_NOATIME, in case retry without it */
      37     1717625 :         if (f == -1 && errno == EPERM)
      38           0 :                 f = open(file, flags);
      39     1717360 :         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             : /**
      72             :  * Get the device file from the device number.
      73             :  *
      74             :  * It uses /proc/self/mountinfo.
      75             :  *
      76             :  * Null devices (major==0) are resolved to the device indicated in mountinfo.
      77             :  */
      78             : #if HAVE_LINUX_DEVICE
      79           4 : static int devresolve_proc(uint64_t device, char* path, size_t path_size)
      80             : {
      81             :         FILE* f;
      82             :         char match[32];
      83             : 
      84             :         /* generate the matching string */
      85           4 :         snprintf(match, sizeof(match), "%u:%u", major(device), minor(device));
      86             : 
      87           4 :         f = fopen("/proc/self/mountinfo", "r");
      88           4 :         if (!f) {
      89           0 :                 log_tag("resolve:proc:%u:%u: failed to open /proc/self/mountinfo\n", major(device), minor(device));
      90           0 :                 return -1;
      91             :         }
      92             : 
      93             :         /*
      94             :          * mountinfo format
      95             :          * 0 - mount ID
      96             :          * 1 - parent ID
      97             :          * 2 - major:minor
      98             :          * 3 - root
      99             :          * 4 - mount point
     100             :          * 5 - options
     101             :          * 6 - "-" (separator)
     102             :          * 7 - fs
     103             :          * 8 - mount source - /dev/device
     104             :          */
     105             : 
     106             :         while (1) {
     107             :                 char buf[256];
     108             :                 char* first_map[8];
     109             :                 unsigned first_mac;
     110             :                 char* second_map[8];
     111             :                 unsigned second_mac;
     112             :                 char* s;
     113             :                 struct stat st;
     114             :                 char* separator;
     115             :                 char* majorminor;
     116             :                 char* mountpoint;
     117             :                 char* fs;
     118             :                 char* mountsource;
     119             : 
     120         156 :                 s = fgets(buf, sizeof(buf), f);
     121         156 :                 if (s == 0)
     122           4 :                         break;
     123             : 
     124             :                 /* find the separator position */
     125         152 :                 separator = strstr(s, " - ");
     126         152 :                 if (!separator)
     127           0 :                         continue;
     128             : 
     129             :                 /* skip the separator */
     130         152 :                 *separator = 0;
     131         152 :                 separator += 3;
     132             : 
     133             :                 /* split the line */
     134         152 :                 first_mac = strsplit(first_map, 8, s, " \t\r\n");
     135         152 :                 second_mac = strsplit(second_map, 8, separator, " \t\r\n");
     136             : 
     137             :                 /* if too short, it's the wrong line */
     138         152 :                 if (first_mac < 5)
     139           0 :                         continue;
     140         152 :                 if (second_mac < 2)
     141           0 :                         continue;
     142             : 
     143         152 :                 majorminor = first_map[2];
     144         152 :                 mountpoint = first_map[4];
     145         152 :                 fs = second_map[0];
     146         152 :                 mountsource = second_map[1];
     147             : 
     148             :                 /* compare major:minor from mountinfo */
     149         152 :                 if (strcmp(majorminor, match) == 0) {
     150             :                         /*
     151             :                          * Accept only /dev/... mountsource
     152             :                          *
     153             :                          * This excludes ZFS that uses a bare label for mountsource, like "tank".
     154             :                          *
     155             :                          * 410 408 0:193 / /XXX rw,relatime shared:217 - zfs tank/system/data/var/lib/docker/XXX rw,xattr,noacl
     156             :                          *
     157             :                          * Also excludes AUTOFS unmounted devices that point to a fake filesystem
     158             :                          * used to remount them at the first use.
     159             :                          *
     160             :                          * 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
     161             :                          */
     162           0 :                         if (strncmp(mountsource, "/dev/", 5) != 0) {
     163           0 :                                 log_tag("resolve:proc:%u:%u: match skipped for not /dev/ mountsource for %s %s\n", major(device), minor(device), fs, mountsource);
     164           0 :                                 continue;
     165             :                         }
     166             : 
     167           0 :                         pathcpy(path, path_size, mountsource);
     168             : 
     169           0 :                         log_tag("resolve:proc:%u:%u: found device %s matching device %s\n", major(device), minor(device), path, match);
     170             : 
     171           0 :                         fclose(f);
     172           0 :                         return 0;
     173             :                 }
     174             : 
     175             :                 /* get the device of the mount point */
     176             :                 /* in Btrfs it could be different than the one in mountinfo */
     177         152 :                 if (stat(mountpoint, &st) == 0 && st.st_dev == device) {
     178           0 :                         if (strncmp(mountsource, "/dev/", 5) != 0) {
     179           0 :                                 log_tag("resolve:proc:%u:%u: match skipped for not /dev/ mountsource for %s %s\n", major(device), minor(device), fs, mountsource);
     180           0 :                                 continue;
     181             :                         }
     182             : 
     183           0 :                         pathcpy(path, path_size, mountsource);
     184             : 
     185           0 :                         log_tag("resolve:proc:%u:%u: found device %s matching mountpoint %s\n", major(device), minor(device), path, mountpoint);
     186             : 
     187           0 :                         fclose(f);
     188           0 :                         return 0;
     189             :                 }
     190         152 :         }
     191             : 
     192           4 :         log_tag("resolve:proc:%u:%u: not found\n", major(device), minor(device));
     193             : 
     194           4 :         fclose(f);
     195           4 :         return -1;
     196             : }
     197             : #endif
     198             : 
     199             : /**
     200             :  * Get the device of a virtual superblock.
     201             :  *
     202             :  * This is intended to resolve the case of Btrfs filesystems that
     203             :  * create a virtual superblock (major==0) not backed by any low
     204             :  * level device.
     205             :  *
     206             :  * See:
     207             :  * Bug 711881 - too funny btrfs st_dev numbers
     208             :  * https://bugzilla.redhat.com/show_bug.cgi?id=711881
     209             :  */
     210             : #if HAVE_LINUX_DEVICE
     211           4 : static int devdereference(uint64_t device, uint64_t* new_device)
     212             : {
     213             :         char path[PATH_MAX];
     214             :         struct stat st;
     215             : 
     216             :         /* use the proc interface to get the device containing the filesystem */
     217           4 :         if (devresolve_proc(device, path, sizeof(path)) != 0) {
     218             :                 /* LCOV_EXCL_START */
     219             :                 return -1;
     220             :                 /* LCOV_EXCL_STOP */
     221             :         }
     222             : 
     223             :         /* check the device */
     224           0 :         if (stat(path, &st) != 0) {
     225             :                 /* LCOV_EXCL_START */
     226             :                 log_tag("dereference:%u:%u: failed to stat %s\n", major(device), minor(device), path);
     227             :                 return -1;
     228             :                 /* LCOV_EXCL_STOP */
     229             :         }
     230             : 
     231           0 :         if (major(st.st_rdev) == 0) {
     232             :                 /* LCOV_EXCL_START */
     233             :                 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));
     234             :                 return -1;
     235             :                 /* LCOV_EXCL_STOP */
     236             :         }
     237             : 
     238           0 :         *new_device = st.st_rdev;
     239           0 :         log_tag("dereference:%u:%u: found %u:%u\n", major(device), minor(device), major(st.st_rdev), minor(st.st_rdev));
     240           0 :         return 0;
     241             : }
     242             : #endif
     243             : 
     244             : /**
     245             :  * Read a file extracting the specified tag TAG=VALUE format.
     246             :  * Return !=0 on error.
     247             :  */
     248             : #if HAVE_LINUX_DEVICE
     249         189 : static int tagread(const char* path, const char* tag, char* value, size_t value_size)
     250             : {
     251             :         int f;
     252             :         int ret;
     253             :         int len;
     254             :         char buf[512];
     255             :         size_t tag_len;
     256             :         char* i;
     257             :         char* e;
     258             : 
     259         189 :         f = open(path, O_RDONLY);
     260         190 :         if (f == -1) {
     261             :                 /* LCOV_EXCL_START */
     262             :                 log_fatal("Failed to open '%s'.\n", path);
     263             :                 return 0;
     264             :                 /* LCOV_EXCL_STOP */
     265             :         }
     266             : 
     267         190 :         len = read(f, buf, sizeof(buf));
     268         191 :         if (len < 0) {
     269             :                 /* LCOV_EXCL_START */
     270             :                 close(f);
     271             :                 log_fatal("Failed to read '%s'.\n", path);
     272             :                 return -1;
     273             :                 /* LCOV_EXCL_STOP */
     274             :         }
     275         191 :         if (len == sizeof(buf)) {
     276             :                 /* LCOV_EXCL_START */
     277             :                 close(f);
     278             :                 log_fatal("Too long read '%s'.\n", path);
     279             :                 return -1;
     280             :                 /* LCOV_EXCL_STOP */
     281             :         }
     282             : 
     283         191 :         ret = close(f);
     284         199 :         if (ret != 0) {
     285             :                 /* LCOV_EXCL_START */
     286             :                 log_fatal("Failed to close '%s'.\n", path);
     287             :                 return -1;
     288             :                 /* LCOV_EXCL_STOP */
     289             :         }
     290             : 
     291         199 :         buf[len] = 0;
     292         199 :         tag_len = strlen(tag);
     293             : 
     294        3414 :         for (i = buf; *i; ++i) {
     295        3415 :                 char* p = i;
     296             : 
     297             :                 /* start with a space */
     298        3415 :                 if (p != buf) {
     299        3225 :                         if (!isspace(*p))
     300        2834 :                                 continue;
     301         382 :                         ++p;
     302             :                 }
     303             : 
     304         572 :                 if (strncmp(p, tag, tag_len) != 0)
     305         381 :                         continue;
     306         191 :                 p += tag_len;
     307             : 
     308             :                 /* end with a = */
     309         191 :                 if (*p != '=')
     310           0 :                         continue;
     311         191 :                 ++p;
     312             : 
     313             :                 /* found */
     314         191 :                 i = p;
     315         191 :                 break;
     316             :         }
     317         190 :         if (!*i) {
     318             :                 /* LCOV_EXCL_START */
     319             :                 log_fatal("Missing tag '%s' for '%s'.\n", tag, path);
     320             :                 return -1;
     321             :                 /* LCOV_EXCL_STOP */
     322             :         }
     323             : 
     324             :         /* terminate at the first space */
     325         190 :         e = i;
     326        1018 :         while (*e != 0 && !isspace(*e))
     327         638 :                 ++e;
     328         191 :         *e = 0;
     329             : 
     330         191 :         if (!*i) {
     331             :                 /* LCOV_EXCL_START */
     332             :                 log_fatal("Empty tag '%s' for '%s'.\n", tag, path);
     333             :                 return -1;
     334             :                 /* LCOV_EXCL_STOP */
     335             :         }
     336             : 
     337         191 :         pathprint(value, value_size, "%s", i);
     338             : 
     339         189 :         return 0;
     340             : }
     341             : #endif
     342             : 
     343             : /**
     344             :  * Get the device file from the device number.
     345             :  *
     346             :  * It uses /sys/dev/block/.../uevent.
     347             :  *
     348             :  * For null device (major==0) it fails.
     349             :  */
     350             : #if HAVE_LINUX_DEVICE
     351         191 : static int devresolve_sys(dev_t device, char* path, size_t path_size)
     352             : {
     353             :         struct stat st;
     354             :         char buf[PATH_MAX];
     355             : 
     356             :         /* default device path from device number */
     357         191 :         pathprint(path, path_size, "/sys/dev/block/%u:%u/uevent", major(device), minor(device));
     358             : 
     359         189 :         if (tagread(path, "DEVNAME", buf, sizeof(buf)) != 0) {
     360             :                 /* LCOV_EXCL_START */
     361             :                 log_tag("resolve:sys:%u:%u: failed to read DEVNAME tag '%s'\n", major(device), minor(device), path);
     362             :                 return -1;
     363             :                 /* LCOV_EXCL_STOP */
     364             :         }
     365             : 
     366             :         /* set the real device path */
     367         189 :         pathprint(path, path_size, "/dev/%s", buf);
     368             : 
     369             :         /* check the device */
     370         187 :         if (stat(path, &st) != 0) {
     371             :                 /* LCOV_EXCL_START */
     372             :                 log_tag("resolve:sys:%u:%u: failed to stat '%s'\n", major(device), minor(device), path);
     373             :                 return -1;
     374             :                 /* LCOV_EXCL_STOP */
     375             :         }
     376         189 :         if (st.st_rdev != device) {
     377             :                 /* LCOV_EXCL_START */
     378             :                 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);
     379             :                 return -1;
     380             :                 /* LCOV_EXCL_STOP */
     381             :         }
     382             : 
     383         189 :         log_tag("resolve:sys:%u:%u:%s: found\n", major(device), minor(device), path);
     384             : 
     385         191 :         return 0;
     386             : }
     387             : #endif
     388             : 
     389             : /**
     390             :  * Get the device file from the device number.
     391             :  */
     392             : #if HAVE_LINUX_DEVICE
     393         191 : static int devresolve(uint64_t device, char* path, size_t path_size)
     394             : {
     395         191 :         if (devresolve_sys(device, path, path_size) == 0)
     396         191 :                 return 0;
     397             : 
     398           0 :         return -1;
     399             : }
     400             : #endif
     401             : 
     402             : /**
     403             :  * Cache used by blkid.
     404             :  */
     405             : #if HAVE_BLKID
     406             : static blkid_cache cache = 0;
     407             : #endif
     408             : 
     409             : /**
     410             :  * Get the UUID using the /dev/disk/by-uuid/ links.
     411             :  * It doesn't require root permission, and the uuid are always updated.
     412             :  * It doesn't work with Btrfs file-systems that don't export the main UUID
     413             :  * in /dev/disk/by-uuid/.
     414             :  */
     415             : #if HAVE_LINUX_DEVICE
     416        5626 : static int devuuid_dev(uint64_t device, char* uuid, size_t uuid_size)
     417             : {
     418             :         int ret;
     419             :         DIR* d;
     420             :         struct dirent* dd;
     421             :         struct stat st;
     422             : 
     423             :         /* scan the UUID directory searching for the device */
     424        5626 :         d = opendir("/dev/disk/by-uuid");
     425        5626 :         if (!d) {
     426           0 :                 log_tag("uuid:by-uuidd:%u:%u: opendir(/dev/disk/by-uuid) failed, %s\n", major(device), minor(device), strerror(errno));
     427             :                 /* directory missing?, likely we are not in Linux */
     428           0 :                 return -1;
     429             :         }
     430             : 
     431      146276 :         while ((dd = readdir(d)) != 0) {
     432             :                 /* skip "." and ".." files, UUIDs never start with '.' */
     433      140650 :                 if (dd->d_name[0] == '.')
     434       11252 :                         continue;
     435             : 
     436      129398 :                 ret = fstatat(dirfd(d), dd->d_name, &st, 0);
     437      129398 :                 if (ret != 0) {
     438           0 :                         log_tag("uuid:by-uuidd:%u:%u: fstatat(%s) failed, %s\n", major(device), minor(device), dd->d_name, strerror(errno));
     439             :                         /* generic error, ignore and continue the search */
     440           0 :                         continue;
     441             :                 }
     442             : 
     443             :                 /* if it matches, we have the uuid */
     444      129398 :                 if (S_ISBLK(st.st_mode) && st.st_rdev == (dev_t)device) {
     445             :                         char buf[PATH_MAX];
     446             :                         char path[PATH_MAX];
     447             : 
     448             :                         /* resolve the link */
     449        5626 :                         pathprint(path, sizeof(path), "/dev/disk/by-uuid/%s", dd->d_name);
     450        5626 :                         ret = readlink(path, buf, sizeof(buf));
     451        5626 :                         if (ret < 0 || ret == sizeof(buf)) {
     452           0 :                                 log_tag("uuid:by-uuidd:%u:%u: readlink(/dev/disk/by-uuid/%s) failed, %s\n", major(device), minor(device), dd->d_name, strerror(errno));
     453             :                                 /* generic error, ignore and continue the search */
     454           0 :                                 continue;
     455             :                         }
     456        5626 :                         buf[ret] = 0;
     457             : 
     458             :                         /* found */
     459        5626 :                         pathcpy(uuid, uuid_size, dd->d_name);
     460             : 
     461        5626 :                         log_tag("uuid:by-uuid:%u:%u:%s: found %s\n", major(device), minor(device), uuid, buf);
     462             : 
     463        5626 :                         closedir(d);
     464        5626 :                         return 0;
     465             :                 }
     466             :         }
     467             : 
     468           0 :         log_tag("uuid:by-uuidd:%u:%u: /dev/disk/by-uuid doesn't contain a matching block device\n", major(device), minor(device));
     469             : 
     470             :         /* not found */
     471           0 :         closedir(d);
     472           0 :         return -1;
     473             : }
     474             : #endif
     475             : 
     476             : /**
     477             :  * Get the UUID using liblkid.
     478             :  * It uses a cache to work without root permission, resulting in UUID
     479             :  * not necessarily recent.
     480             :  * We could call blkid_probe_all() to refresh the UUID, but it would
     481             :  * require root permission to read the superblocks, and resulting in
     482             :  * all the disks spinning.
     483             :  */
     484             : #if HAVE_BLKID
     485           0 : static int devuuid_blkid(uint64_t device, char* uuid, size_t uuid_size)
     486             : {
     487             :         char* devname;
     488             :         char* uuidname;
     489             : 
     490           0 :         devname = blkid_devno_to_devname(device);
     491           0 :         if (!devname) {
     492           0 :                 log_tag("uuid:blkid:%u:%u: blkid_devno_to_devname() failed, %s\n", major(device), minor(device), strerror(errno));
     493             :                 /* device mapping failed */
     494           0 :                 return -1;
     495             :         }
     496             : 
     497           0 :         uuidname = blkid_get_tag_value(cache, "UUID", devname);
     498           0 :         if (!uuidname) {
     499           0 :                 log_tag("uuid:blkid:%u:%u: blkid_get_tag_value(UUID,%s) failed, %s\n", major(device), minor(device), devname, strerror(errno));
     500             :                 /* uuid mapping failed */
     501           0 :                 free(devname);
     502           0 :                 return -1;
     503             :         }
     504             : 
     505           0 :         pathcpy(uuid, uuid_size, uuidname);
     506             : 
     507           0 :         log_tag("uuid:blkid:%u:%u:%s: found %s\n", major(device), minor(device), uuid, devname);
     508             : 
     509           0 :         free(devname);
     510           0 :         free(uuidname);
     511           0 :         return 0;
     512             : }
     513             : #endif
     514             : 
     515        5630 : int devuuid(uint64_t device, char* uuid, size_t uuid_size)
     516             : {
     517             : #if HAVE_LINUX_DEVICE
     518             :         /* if the major is the null device */
     519        5630 :         if (major(device) == 0) {
     520             :                 /* obtain the real device */
     521           4 :                 if (devdereference(device, &device) != 0) {
     522             :                         /* LCOV_EXCL_START */
     523             :                         return -1;
     524             :                         /* LCOV_EXCL_STOP */
     525             :                 }
     526             :         }
     527             : #endif
     528             : 
     529             :         /* first try with the /dev/disk/by-uuid version */
     530             : #if HAVE_LINUX_DEVICE
     531        5626 :         if (devuuid_dev(device, uuid, uuid_size) == 0)
     532        5626 :                 return 0;
     533             : #else
     534             :         log_tag("uuid:by-uuidd:%u:%u: by-uuid not supported\n", major(device), minor(device));
     535             : #endif
     536             : 
     537             :         /* fall back to blkid for other cases */
     538             : #if HAVE_BLKID
     539           0 :         if (devuuid_blkid(device, uuid, uuid_size) == 0)
     540           0 :                 return 0;
     541             : #else
     542             :         log_tag("uuid:blkid:%u:%u: blkid not supported\n", major(device), minor(device));
     543             : #endif
     544             : 
     545           0 :         log_tag("uuid:notfound:%u:%u:\n", major(device), minor(device));
     546             : 
     547             :         /* not supported */
     548             :         (void)uuid;
     549             :         (void)uuid_size;
     550           0 :         return -1;
     551             : }
     552             : 
     553       11432 : int filephy(const char* path, uint64_t size, uint64_t* physical)
     554             : {
     555             : #if HAVE_LINUX_FIEMAP_H
     556             :         /* In Linux get the real physical address of the file */
     557             :         /* Note that FIEMAP doesn't require root permission */
     558             :         int f;
     559             :         struct {
     560             :                 struct fiemap fiemap;
     561             :                 struct fiemap_extent extent;
     562             :         } fm;
     563             :         unsigned int blknum;
     564             : 
     565       11432 :         f = open(path, O_RDONLY);
     566       11432 :         if (f == -1) {
     567           0 :                 return -1;
     568             :         }
     569             : 
     570             :         /* first try with FIEMAP */
     571             :         /* if works for ext2, ext3, ext4, xfs, btrfs */
     572       11432 :         memset(&fm, 0, sizeof(fm));
     573       11432 :         fm.fiemap.fm_start = 0;
     574       11432 :         fm.fiemap.fm_length = ~0ULL;
     575       11432 :         fm.fiemap.fm_flags = FIEMAP_FLAG_SYNC; /* required to ensure that just created files report a valid address and not 0 */
     576       11432 :         fm.fiemap.fm_extent_count = 1; /* we are interested only at the first block */
     577             : 
     578       11432 :         if (ioctl(f, FS_IOC_FIEMAP, &fm) != -1) {
     579       11432 :                 uint32_t flags = fm.fiemap.fm_extents[0].fe_flags;
     580       11432 :                 uint64_t offset = fm.fiemap.fm_extents[0].fe_physical;
     581             : 
     582             :                 /* check some condition for validating the offset */
     583       11432 :                 if (flags & FIEMAP_EXTENT_DATA_INLINE) {
     584             :                         /* if the data is inline, we don't have an offset to report */
     585           0 :                         *physical = FILEPHY_WITHOUT_OFFSET;
     586       11432 :                 } else if (flags & FIEMAP_EXTENT_UNKNOWN) {
     587             :                         /* if the offset is unknown, we don't have an offset to report */
     588           0 :                         *physical = FILEPHY_WITHOUT_OFFSET;
     589       11432 :                 } else if (offset == 0) {
     590             :                         /* 0 is the general fallback for file-systems when */
     591             :                         /* they don't have an offset to report */
     592          14 :                         *physical = FILEPHY_WITHOUT_OFFSET;
     593             :                 } else {
     594             :                         /* finally report the real offset */
     595       11418 :                         *physical = offset + FILEPHY_REAL_OFFSET;
     596             :                 }
     597             : 
     598       11432 :                 if (close(f) == -1)
     599           0 :                         return -1;
     600       11432 :                 return 0;
     601             :         }
     602             : 
     603             :         /* if the file is empty, FIBMAP doesn't work, and we don't even try to use it */
     604           0 :         if (size == 0) {
     605           0 :                 *physical = FILEPHY_WITHOUT_OFFSET;
     606           0 :                 if (close(f) == -1)
     607           0 :                         return -1;
     608           0 :                 return 0;
     609             :         }
     610             : 
     611             :         /* then try with FIBMAP */
     612             :         /* it works for jfs, reiserfs, ntfs-3g */
     613             :         /* in exfat it always returns 0, that it's anyway better than the fake inodes */
     614           0 :         blknum = 0; /* first block */
     615           0 :         if (ioctl(f, FIBMAP, &blknum) != -1) {
     616           0 :                 *physical = blknum + FILEPHY_REAL_OFFSET;
     617           0 :                 if (close(f) == -1)
     618           0 :                         return -1;
     619           0 :                 return 0;
     620             :         }
     621             : 
     622             :         /* otherwise don't use anything, and keep the directory traversal order */
     623             :         /* at now this should happen only for vfat */
     624             :         /* and it's surely better than using fake inodes */
     625           0 :         *physical = FILEPHY_UNREPORTED_OFFSET;
     626           0 :         if (close(f) == -1)
     627           0 :                 return -1;
     628             : #else
     629             :         /* In a generic Unix use a dummy value for all the files */
     630             :         /* We don't want to risk to use the inode without knowing */
     631             :         /* if it really improves performance. */
     632             :         /* In this way we keep them in the directory traversal order */
     633             :         /* that at least keeps files in the same directory together. */
     634             :         /* Note also that in newer file-system with snapshot, like ZFS, */
     635             :         /* the inode doesn't represent evenmore the disk position, because files */
     636             :         /* are not overwritten in place, but rewritten in another location */
     637             :         /* of the disk. */
     638             :         *physical = FILEPHY_UNREPORTED_OFFSET;
     639             : 
     640             :         (void)path; /* not used here */
     641             :         (void)size;
     642             : #endif
     643             : 
     644           0 :         return 0;
     645             : }
     646             : 
     647        5483 : int fsinfo(const char* path, int* has_persistent_inode, int* has_syncronized_hardlinks, uint64_t* total_space, uint64_t* free_space)
     648             : {
     649             :         char type[64];
     650             :         const char* ptype;
     651             : 
     652             : #if HAVE_STATFS
     653             :         struct statfs st;
     654             : 
     655        5483 :         if (statfs(path, &st) != 0) {
     656             :                 char dir[PATH_MAX];
     657             :                 char* slash;
     658             : 
     659          24 :                 if (errno != ENOENT) {
     660           0 :                         return -1;
     661             :                 }
     662             : 
     663             :                 /* if it doesn't exist, we assume a file */
     664             :                 /* and we check for the containing dir */
     665          24 :                 if (strlen(path) + 1 > sizeof(dir)) {
     666           0 :                         errno = ENAMETOOLONG;
     667           0 :                         return -1;
     668             :                 }
     669             : 
     670          24 :                 strcpy(dir, path);
     671             : 
     672          24 :                 slash = strrchr(dir, '/');
     673          24 :                 if (!slash)
     674           0 :                         return -1;
     675             : 
     676          24 :                 *slash = 0;
     677          24 :                 if (statfs(dir, &st) != 0)
     678           0 :                         return -1;
     679             :         }
     680             : #endif
     681             : 
     682             :         /* to get the fs type check "man stat" or "stat -f -t FILE" */
     683        5483 :         if (has_persistent_inode) {
     684             : #if HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
     685         575 :                 switch (st.f_type) {
     686             :                 case 0x65735546 : /* FUSE, "fuseblk" in the stat command */
     687             :                 case 0x4d44 : /* VFAT, "msdos" in the stat command */
     688           0 :                         *has_persistent_inode = 0;
     689           0 :                         break;
     690             :                 default :
     691             :                         /* by default assume yes */
     692         575 :                         *has_persistent_inode = 1;
     693         575 :                         break;
     694             :                 }
     695             : #else
     696             :                 /* in Unix inodes are persistent by default */
     697             :                 *has_persistent_inode = 1;
     698             : #endif
     699             :         }
     700             : 
     701        5483 :         if (has_syncronized_hardlinks) {
     702             : #if HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
     703         575 :                 switch (st.f_type) {
     704             :                 case 0x5346544E : /* NTFS */
     705             :                 case 0x4d44 : /* VFAT, "msdos" in the stat command */
     706           0 :                         *has_syncronized_hardlinks = 0;
     707           0 :                         break;
     708             :                 default :
     709             :                         /* by default assume yes */
     710         575 :                         *has_syncronized_hardlinks = 1;
     711         575 :                         break;
     712             :                 }
     713             : #else
     714             :                 /* in Unix hardlinks share the same metadata by default */
     715             :                 *has_syncronized_hardlinks = 1;
     716             : #endif
     717             :         }
     718             : 
     719        5483 :         if (total_space) {
     720             : #if HAVE_STATFS
     721        4908 :                 *total_space = st.f_bsize * (uint64_t)st.f_blocks;
     722             : #else
     723             :                 *total_space = 0;
     724             : #endif
     725             :         }
     726             : 
     727        5483 :         if (free_space) {
     728             : #if HAVE_STATFS
     729        4908 :                 *free_space = st.f_bsize * (uint64_t)st.f_bfree;
     730             : #else
     731             :                 *free_space = 0;
     732             : #endif
     733             :         }
     734             : 
     735             : #if HAVE_STATFS && HAVE_STRUCT_STATFS_F_FSTYPENAME
     736             :         /* get the filesystem type directly from the struct (Mac OS X) */
     737             :         (void)type;
     738             :         ptype = st.f_fstypename;
     739             : #elif HAVE_STATFS && HAVE_STRUCT_STATFS_F_TYPE
     740             :         /* get the filesystem type from f_type (Linux) */
     741             :         /* from: https://github.com/influxdata/gopsutil/blob/master/disk/disk_linux.go */
     742        5483 :         switch (st.f_type) {
     743           0 :         case 0x65735546 : ptype = "fuseblk"; break;
     744           0 :         case 0x4D44 : ptype = "vfat/msdos"; break;
     745        5483 :         case 0xEF53 : ptype = "ext2/3/4"; break;
     746           0 :         case 0x6969 : ptype = "nfs"; break; /* remote */
     747           0 :         case 0x6E667364 : ptype = "nfsd"; break; /* remote */
     748           0 :         case 0x517B : ptype = "smb"; break; /* remote */
     749           0 :         case 0x5346544E : ptype = "ntfs"; break;
     750           0 :         case 0x52654973 : ptype = "reiserfs"; break;
     751           0 :         case 0x3153464A : ptype = "jfs"; break;
     752           0 :         case 0x58465342 : ptype = "xfs"; break;
     753           0 :         case 0x9123683E : ptype = "btrfs"; break;
     754           0 :         case 0x2FC12FC1 : ptype = "zfs"; break;
     755             :         default :
     756           0 :                 snprintf(type, sizeof(type), "0x%X", (unsigned)st.f_type);
     757           0 :                 ptype = type;
     758             :         }
     759             : #else
     760             :         (void)type;
     761             :         ptype = "unknown";
     762             : #endif
     763             : 
     764        5483 :         log_tag("statfs:%s: %s \n", ptype, path);
     765             : 
     766        5483 :         return 0;
     767             : }
     768             : 
     769     3818188 : uint64_t tick(void)
     770             : {
     771             : #if HAVE_MACH_ABSOLUTE_TIME
     772             :         /* for Mac OS X */
     773             :         return mach_absolute_time();
     774             : #elif HAVE_CLOCK_GETTIME && (defined(CLOCK_MONOTONIC) || defined(CLOCK_MONOTONIC_RAW))
     775             :         /* for Linux */
     776             :         struct timespec tv;
     777             : 
     778             :         /* nanosecond precision with clock_gettime() */
     779             : #if defined(CLOCK_MONOTONIC_RAW)
     780     3818188 :         if (clock_gettime(CLOCK_MONOTONIC_RAW, &tv) != 0) {
     781             : #else
     782             :         if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
     783             : #endif
     784           0 :                 return 0;
     785             :         }
     786             : 
     787     3818188 :         return tv.tv_sec * 1000000000ULL + tv.tv_nsec;
     788             : #else
     789             :         /* other platforms */
     790             :         struct timeval tv;
     791             : 
     792             :         /* microsecond precision with gettimeofday() */
     793             :         if (gettimeofday(&tv, 0) != 0) {
     794             :                 return 0;
     795             :         }
     796             : 
     797             :         return tv.tv_sec * 1000000ULL + tv.tv_usec;
     798             : #endif
     799             : }
     800             : 
     801          65 : uint64_t tick_ms(void)
     802             : {
     803             :         struct timeval tv;
     804             : 
     805          65 :         if (gettimeofday(&tv, 0) != 0)
     806           0 :                 return 0;
     807             : 
     808          65 :         return tv.tv_sec * 1000ULL + tv.tv_usec / 1000;
     809             : }
     810             : 
     811         282 : int randomize(void* ptr, size_t size)
     812             : {
     813             :         int f;
     814             :         ssize_t ret;
     815             : 
     816         282 :         f = open("/dev/urandom", O_RDONLY);
     817         282 :         if (f == -1)
     818           0 :                 return -1;
     819             : 
     820         282 :         ret = read(f, ptr, size);
     821         282 :         if (ret < 0 || (size_t)ret != size) {
     822           0 :                 close(f);
     823           0 :                 return -1;
     824             :         }
     825             : 
     826         282 :         if (close(f) != 0)
     827           0 :                 return -1;
     828             : 
     829         282 :         return 0;
     830             : }
     831             : 
     832             : /**
     833             :  * Read a file extracting the contained device number in %u:%u format.
     834             :  * Return 0 on error.
     835             :  */
     836             : #if HAVE_LINUX_DEVICE
     837          79 : static dev_t devread(const char* path)
     838             : {
     839             :         int f;
     840             :         int ret;
     841             :         int len;
     842             :         char buf[64];
     843             :         char* e;
     844             :         unsigned ma;
     845             :         unsigned mi;
     846             : 
     847          79 :         f = open(path, O_RDONLY);
     848          79 :         if (f == -1) {
     849             :                 /* LCOV_EXCL_START */
     850             :                 log_fatal("Failed to open '%s'.\n", path);
     851             :                 return 0;
     852             :                 /* LCOV_EXCL_STOP */
     853             :         }
     854             : 
     855          79 :         len = read(f, buf, sizeof(buf));
     856          79 :         if (len < 0) {
     857             :                 /* LCOV_EXCL_START */
     858             :                 close(f);
     859             :                 log_fatal("Failed to read '%s'.\n", path);
     860             :                 return 0;
     861             :                 /* LCOV_EXCL_STOP */
     862             :         }
     863          79 :         if (len == sizeof(buf)) {
     864             :                 /* LCOV_EXCL_START */
     865             :                 close(f);
     866             :                 log_fatal("Too long read '%s'.\n", path);
     867             :                 return 0;
     868             :                 /* LCOV_EXCL_STOP */
     869             :         }
     870             : 
     871          79 :         ret = close(f);
     872          79 :         if (ret != 0) {
     873             :                 /* LCOV_EXCL_START */
     874             :                 log_fatal("Failed to close '%s'.\n", path);
     875             :                 return 0;
     876             :                 /* LCOV_EXCL_STOP */
     877             :         }
     878             : 
     879          79 :         buf[len] = 0;
     880             : 
     881          79 :         ma = strtoul(buf, &e, 10);
     882          79 :         if (*e != ':') {
     883             :                 /* LCOV_EXCL_START */
     884             :                 log_fatal("Invalid format in '%s' for '%s'.\n", path, buf);
     885             :                 return 0;
     886             :                 /* LCOV_EXCL_STOP */
     887             :         }
     888             : 
     889          79 :         mi = strtoul(e + 1, &e, 10);
     890          79 :         if (*e != 0 && !isspace(*e)) {
     891             :                 /* LCOV_EXCL_START */
     892             :                 log_fatal("Invalid format in '%s' for '%s'.\n", path, buf);
     893             :                 return 0;
     894             :                 /* LCOV_EXCL_STOP */
     895             :         }
     896             : 
     897          79 :         return makedev(ma, mi);
     898             : }
     899             : #endif
     900             : 
     901             : /**
     902             :  * Read a device tree filling the specified list of disk_t entries.
     903             :  */
     904             : #if HAVE_LINUX_DEVICE
     905          65 : static int devtree(const char* name, const char* custom, dev_t device, devinfo_t* parent, tommy_list* list)
     906             : {
     907             :         char path[PATH_MAX];
     908             :         DIR* d;
     909          65 :         int slaves = 0;
     910             : 
     911          65 :         pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/slaves", major(device), minor(device));
     912             : 
     913             :         /* check if there is a slaves list */
     914          65 :         d = opendir(path);
     915          65 :         if (d != 0) {
     916             :                 struct dirent* dd;
     917             : 
     918           0 :                 while ((dd = readdir(d)) != 0) {
     919           0 :                         if (dd->d_name[0] != '.') {
     920             :                                 /* for each slave, expand the full potential tree */
     921           0 :                                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/slaves/%s/dev", major(device), minor(device), dd->d_name);
     922             : 
     923           0 :                                 device = devread(path);
     924           0 :                                 if (!device) {
     925             :                                         /* LCOV_EXCL_START */
     926             :                                         closedir(d);
     927             :                                         return -1;
     928             :                                         /* LCOV_EXCL_STOP */
     929             :                                 }
     930             : 
     931           0 :                                 if (devtree(name, custom, device, parent, list) != 0) {
     932             :                                         /* LCOV_EXCL_START */
     933             :                                         closedir(d);
     934             :                                         return -1;
     935             :                                         /* LCOV_EXCL_STOP */
     936             :                                 }
     937             : 
     938           0 :                                 ++slaves;
     939             :                         }
     940             :                 }
     941             : 
     942           0 :                 closedir(d);
     943             :         }
     944             : 
     945             :         /* if no slaves found */
     946          65 :         if (!slaves) {
     947             :                 /* this is a raw device */
     948             :                 devinfo_t* devinfo;
     949             : 
     950             :                 /* check if it's a real device */
     951          65 :                 pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/device", major(device), minor(device));
     952          65 :                 if (access(path, F_OK) != 0) {
     953             :                         /* get the parent device */
     954          65 :                         pathprint(path, sizeof(path), "/sys/dev/block/%u:%u/../dev", major(device), minor(device));
     955             : 
     956          65 :                         device = devread(path);
     957          65 :                         if (!device) {
     958             :                                 /* LCOV_EXCL_START */
     959             :                                 return -1;
     960             :                                 /* LCOV_EXCL_STOP */
     961             :                         }
     962             :                 }
     963             : 
     964             :                 /* get the device file */
     965          65 :                 if (devresolve(device, path, sizeof(path)) != 0) {
     966             :                         /* LCOV_EXCL_START */
     967             :                         log_fatal("Failed to resolve device '%u:%u'.\n", major(device), minor(device));
     968             :                         return -1;
     969             :                         /* LCOV_EXCL_STOP */
     970             :                 }
     971             : 
     972          65 :                 devinfo = calloc_nofail(1, sizeof(devinfo_t));
     973             : 
     974          65 :                 devinfo->device = device;
     975          65 :                 pathcpy(devinfo->name, sizeof(devinfo->name), name);
     976          65 :                 pathcpy(devinfo->smartctl, sizeof(devinfo->smartctl), custom);
     977          65 :                 pathcpy(devinfo->file, sizeof(devinfo->file), path);
     978          65 :                 devinfo->parent = parent;
     979             : 
     980             :                 /* insert in the list */
     981          65 :                 tommy_list_insert_tail(list, &devinfo->node, devinfo);
     982             :         }
     983             : 
     984          65 :         return 0;
     985             : }
     986             : #endif
     987             : 
     988             : /**
     989             :  * Scan all the devices.
     990             :  *
     991             :  * If a device is already in, it's not added again.
     992             :  */
     993             : #if HAVE_LINUX_DEVICE
     994           1 : static int devscan(tommy_list* list)
     995             : {
     996             :         char dir[PATH_MAX];
     997             :         DIR* d;
     998             :         struct dirent* dd;
     999             : 
    1000           1 :         pathprint(dir, sizeof(dir), "/sys/dev/block/");
    1001             : 
    1002             :         /* check if there is a slaves list */
    1003           1 :         d = opendir(dir);
    1004           1 :         if (d == 0) {
    1005             :                 /* LCOV_EXCL_START */
    1006             :                 log_fatal("Failed to open dir '%s'.\n", dir);
    1007             :                 return -1;
    1008             :                 /* LCOV_EXCL_STOP */
    1009             :         }
    1010             : 
    1011          76 :         while ((dd = readdir(d)) != 0) {
    1012             :                 char path[PATH_MAX];
    1013             :                 tommy_node* i;
    1014             :                 dev_t device;
    1015             :                 devinfo_t* devinfo;
    1016             : 
    1017          74 :                 if (dd->d_name[0] == '.')
    1018          63 :                         continue;
    1019             : 
    1020          72 :                 pathprint(path, sizeof(path), "/sys/dev/block/%s/device", dd->d_name);
    1021             : 
    1022             :                 /* check if it's a real device */
    1023          72 :                 if (access(path, F_OK) != 0)
    1024          58 :                         continue;
    1025             : 
    1026          14 :                 pathprint(path, sizeof(path), "/sys/dev/block/%s/dev", dd->d_name);
    1027             : 
    1028          14 :                 device = devread(path);
    1029          14 :                 if (!device) {
    1030             :                         /* LCOV_EXCL_START */
    1031             :                         log_tag("scan:skip: Skipping device %s because failed to read its device number.\n", dd->d_name);
    1032             :                         continue;
    1033             :                         /* LCOV_EXCL_STOP */
    1034             :                 }
    1035             : 
    1036             :                 /* check if already present */
    1037         482 :                 for (i = tommy_list_head(list); i != 0; i = i->next) {
    1038         469 :                         devinfo = i->data;
    1039         469 :                         if (devinfo->device == device)
    1040           1 :                                 break;
    1041             :                 }
    1042             : 
    1043             :                 /* if already present */
    1044          14 :                 if (i != 0)
    1045           1 :                         continue;
    1046             : 
    1047             :                 /* get the device file */
    1048          13 :                 if (devresolve(device, path, sizeof(path)) != 0) {
    1049             :                         /* LCOV_EXCL_START */
    1050             :                         log_tag("scan:skip: Skipping device %u:%u because failed to resolve.\n", major(device), minor(device));
    1051             :                         continue;
    1052             :                         /* LCOV_EXCL_STOP */
    1053             :                 }
    1054             : 
    1055          13 :                 devinfo = calloc_nofail(1, sizeof(devinfo_t));
    1056             : 
    1057          13 :                 devinfo->device = device;
    1058          13 :                 pathcpy(devinfo->file, sizeof(devinfo->file), path);
    1059             : 
    1060             :                 /* insert in the list */
    1061          13 :                 tommy_list_insert_tail(list, &devinfo->node, devinfo);
    1062             :         }
    1063             : 
    1064           1 :         closedir(d);
    1065           1 :         return 0;
    1066             : }
    1067             : #endif
    1068             : 
    1069             : /**
    1070             :  * Get SMART attributes.
    1071             :  */
    1072             : #if HAVE_LINUX_DEVICE
    1073          43 : static int devsmart(dev_t device, const char* name, const char* custom, uint64_t* smart, char* serial, char* vendor, char* model)
    1074             : {
    1075             :         char cmd[128];
    1076             :         char file[128];
    1077             :         FILE* f;
    1078             :         int ret;
    1079             : 
    1080          43 :         if (devresolve(device, file, sizeof(file)) != 0) {
    1081             :                 /* LCOV_EXCL_START */
    1082             :                 log_fatal("Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    1083             :                 return -1;
    1084             :                 /* LCOV_EXCL_STOP */
    1085             :         }
    1086             : 
    1087             :         /* if there is a custom command */
    1088          43 :         if (custom[0]) {
    1089             :                 char option[128];
    1090           5 :                 snprintf(option, sizeof(option), custom, file);
    1091           5 :                 snprintf(cmd, sizeof(cmd), "smartctl -a %s", option);
    1092             :         } else {
    1093          38 :                 snprintf(cmd, sizeof(cmd), "smartctl -a %s", file);
    1094             :         }
    1095             : 
    1096          43 :         log_tag("smartctl:%s:%s:run: %s\n", file, name, cmd);
    1097             : 
    1098          43 :         f = popen(cmd, "r");
    1099          43 :         if (!f) {
    1100             :                 /* LCOV_EXCL_START */
    1101             :                 log_fatal("Failed to run '%s' (from popen).\n", cmd);
    1102             :                 return -1;
    1103             :                 /* LCOV_EXCL_STOP */
    1104             :         }
    1105             : 
    1106          43 :         if (smartctl_attribute(f, file, name, smart, serial, vendor, model) != 0) {
    1107             :                 /* LCOV_EXCL_START */
    1108             :                 pclose(f);
    1109             :                 return -1;
    1110             :                 /* LCOV_EXCL_STOP */
    1111             :         }
    1112             : 
    1113          43 :         ret = pclose(f);
    1114             : 
    1115          43 :         log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
    1116             : 
    1117          43 :         if (!WIFEXITED(ret)) {
    1118             :                 /* LCOV_EXCL_START */
    1119             :                 log_fatal("Failed to run '%s' (not exited).\n", cmd);
    1120             :                 return -1;
    1121             :                 /* LCOV_EXCL_STOP */
    1122             :         }
    1123          43 :         if (WEXITSTATUS(ret) == 127) {
    1124             :                 /* LCOV_EXCL_START */
    1125             :                 log_fatal("Failed to run '%s' (from sh).\n", cmd);
    1126             :                 return -1;
    1127             :                 /* LCOV_EXCL_STOP */
    1128             :         }
    1129             : 
    1130             :         /* store the return smartctl return value */
    1131           0 :         smart[SMART_FLAGS] = WEXITSTATUS(ret);
    1132             : 
    1133           0 :         return 0;
    1134             : }
    1135             : #endif
    1136             : 
    1137             : /**
    1138             :  * Spin down a specific device.
    1139             :  */
    1140             : #if HAVE_LINUX_DEVICE
    1141           5 : static int devdown(dev_t device, const char* name, const char* custom)
    1142             : {
    1143             :         char cmd[128];
    1144             :         char file[128];
    1145             :         FILE* f;
    1146             :         int ret;
    1147             : 
    1148           5 :         if (devresolve(device, file, sizeof(file)) != 0) {
    1149             :                 /* LCOV_EXCL_START */
    1150             :                 log_fatal("Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    1151             :                 return -1;
    1152             :                 /* LCOV_EXCL_STOP */
    1153             :         }
    1154             : 
    1155             :         /* if there is a custom command */
    1156           5 :         if (custom[0]) {
    1157             :                 char option[128];
    1158           5 :                 snprintf(option, sizeof(option), custom, file);
    1159           5 :                 snprintf(cmd, sizeof(cmd), "smartctl -s standby,now %s", option);
    1160             :         } else {
    1161           0 :                 snprintf(cmd, sizeof(cmd), "smartctl -s standby,now %s", file);
    1162             :         }
    1163             : 
    1164           5 :         log_tag("smartctl:%s:%s:run: %s\n", file, name, cmd);
    1165             : 
    1166           5 :         f = popen(cmd, "r");
    1167           5 :         if (!f) {
    1168             :                 /* LCOV_EXCL_START */
    1169             :                 log_fatal("Failed to run '%s' (from popen).\n", cmd);
    1170             :                 return -1;
    1171             :                 /* LCOV_EXCL_STOP */
    1172             :         }
    1173             : 
    1174           5 :         if (smartctl_flush(f, file, name) != 0) {
    1175             :                 /* LCOV_EXCL_START */
    1176             :                 pclose(f);
    1177             :                 return -1;
    1178             :                 /* LCOV_EXCL_STOP */
    1179             :         }
    1180             : 
    1181           5 :         ret = pclose(f);
    1182             : 
    1183           5 :         log_tag("smartctl:%s:%s:ret: %x\n", file, name, ret);
    1184             : 
    1185           5 :         if (!WIFEXITED(ret)) {
    1186             :                 /* LCOV_EXCL_START */
    1187             :                 log_fatal("Failed to run '%s' (not exited).\n", cmd);
    1188             :                 return -1;
    1189             :                 /* LCOV_EXCL_STOP */
    1190             :         }
    1191           5 :         if (WEXITSTATUS(ret) == 127) {
    1192             :                 /* LCOV_EXCL_START */
    1193             :                 log_fatal("Failed to run '%s' (from sh).\n", cmd);
    1194             :                 return -1;
    1195             :                 /* LCOV_EXCL_STOP */
    1196             :         }
    1197           0 :         if (WEXITSTATUS(ret) != 0) {
    1198             :                 /* LCOV_EXCL_START */
    1199             :                 log_fatal("Failed to run '%s' with return code %xh.\n", cmd, WEXITSTATUS(ret));
    1200             :                 return -1;
    1201             :                 /* LCOV_EXCL_STOP */
    1202             :         }
    1203             : 
    1204           0 :         return 0;
    1205             : }
    1206             : #endif
    1207             : 
    1208             : /**
    1209             :  * Spin up a device.
    1210             :  *
    1211             :  * There isn't a defined way to spin up a device,
    1212             :  * so we just do a generic write.
    1213             :  */
    1214          30 : static int devup(const char* mountpoint)
    1215             : {
    1216             :         int ret;
    1217             :         char path[PATH_MAX];
    1218             : 
    1219             :         /* add a temporary name used for writing */
    1220          30 :         pathprint(path, sizeof(path), "%s.snapraid-spinup", mountpoint);
    1221             : 
    1222             :         /* do a generic write, and immediately undo it */
    1223          30 :         ret = mkdir(path, 0);
    1224          30 :         if (ret != 0 && errno != EEXIST) {
    1225             :                 /* LCOV_EXCL_START */
    1226             :                 log_fatal("Failed to create dir '%s'.\n", path);
    1227             :                 return -1;
    1228             :                 /* LCOV_EXCL_STOP */
    1229             :         }
    1230             : 
    1231             :         /* remove the just created dir */
    1232          30 :         rmdir(path);
    1233             : 
    1234          30 :         return 0;
    1235             : }
    1236             : 
    1237             : /**
    1238             :  * Thread for spinning up.
    1239             :  *
    1240             :  * Note that filling up the devinfo object is done inside this thread,
    1241             :  * to avoid to block the main thread if the device need to be spin up
    1242             :  * to handle stat/resolve requests.
    1243             :  */
    1244          30 : static void* thread_spinup(void* arg)
    1245             : {
    1246          30 :         devinfo_t* devinfo = arg;
    1247             :         struct stat st;
    1248             :         uint64_t start;
    1249             : 
    1250          30 :         start = tick_ms();
    1251             : 
    1252             :         /* first get the device number, this usually doesn't trigger a thread_spinup */
    1253          30 :         if (stat(devinfo->mount, &st) != 0) {
    1254             :                 /* LCOV_EXCL_START */
    1255             :                 log_fatal("Failed to stat device '%s'.\n", devinfo->mount);
    1256             :                 return (void*)-1;
    1257             :                 /* LCOV_EXCL_STOP */
    1258             :         }
    1259             : 
    1260             :         /* set the device number for printing */
    1261          30 :         devinfo->device = st.st_dev;
    1262             : 
    1263          30 :         if (devup(devinfo->mount) != 0) {
    1264             :                 /* LCOV_EXCL_START */
    1265             :                 return (void*)-1;
    1266             :                 /* LCOV_EXCL_STOP */
    1267             :         }
    1268             : 
    1269          30 :         msg_status("Spunup device '%u:%u' for disk '%s' in %" PRIu64 " ms.\n", major(devinfo->device), minor(devinfo->device), devinfo->name, tick_ms() - start);
    1270             : 
    1271          30 :         return 0;
    1272             : }
    1273             : 
    1274             : /**
    1275             :  * Thread for spinning down.
    1276             :  */
    1277           5 : static void* thread_spindown(void* arg)
    1278             : {
    1279             : #if HAVE_LINUX_DEVICE
    1280           5 :         devinfo_t* devinfo = arg;
    1281             :         uint64_t start;
    1282             : 
    1283           5 :         start = tick_ms();
    1284             : 
    1285           5 :         if (devdown(devinfo->device, devinfo->name, devinfo->smartctl) != 0) {
    1286             :                 /* LCOV_EXCL_START */
    1287             :                 return (void*)-1;
    1288             :                 /* LCOV_EXCL_STOP */
    1289             :         }
    1290             : 
    1291           0 :         msg_status("Spundown device '%s' for disk '%s' in %" PRIu64 " ms.\n", devinfo->file, devinfo->name, tick_ms() - start);
    1292             : 
    1293           0 :         return 0;
    1294             : #else
    1295             :         (void)arg;
    1296             :         return (void*)-1;
    1297             : #endif
    1298             : }
    1299             : 
    1300             : /**
    1301             :  * Thread for getting smart info.
    1302             :  */
    1303          43 : static void* thread_smart(void* arg)
    1304             : {
    1305             : #if HAVE_LINUX_DEVICE
    1306          43 :         devinfo_t* devinfo = arg;
    1307             : 
    1308          43 :         if (devsmart(devinfo->device, devinfo->name, devinfo->smartctl, devinfo->smart, devinfo->smart_serial, devinfo->smart_vendor, devinfo->smart_model) != 0) {
    1309             :                 /* LCOV_EXCL_START */
    1310             :                 return (void*)-1;
    1311             :                 /* LCOV_EXCL_STOP */
    1312             :         }
    1313             : 
    1314           0 :         return 0;
    1315             : #else
    1316             :         (void)arg;
    1317             :         return (void*)-1;
    1318             : #endif
    1319             : }
    1320             : 
    1321           3 : static int device_thread(tommy_list* list, void* (*func)(void* arg))
    1322             : {
    1323           3 :         int fail = 0;
    1324             :         tommy_node* i;
    1325             : 
    1326             : #if HAVE_PTHREAD
    1327             :         /* start all threads */
    1328          81 :         for (i = tommy_list_head(list); i != 0; i = i->next) {
    1329          78 :                 devinfo_t* devinfo = i->data;
    1330             : 
    1331          78 :                 thread_create(&devinfo->thread, 0, func, devinfo);
    1332             :         }
    1333             : 
    1334             :         /* join all threads */
    1335          81 :         for (i = tommy_list_head(list); i != 0; i = i->next) {
    1336          78 :                 devinfo_t* devinfo = i->data;
    1337             :                 void* retval;
    1338             : 
    1339          78 :                 thread_join(devinfo->thread, &retval);
    1340             : 
    1341          78 :                 if (retval != 0)
    1342          48 :                         ++fail;
    1343             :         }
    1344             : #else
    1345             :         for (i = tommy_list_head(list); i != 0; i = i->next) {
    1346             :                 devinfo_t* devinfo = i->data;
    1347             : 
    1348             :                 if (func(devinfo) != 0)
    1349             :                         ++fail;
    1350             :         }
    1351             : #endif
    1352           3 :         if (fail != 0) {
    1353             :                 /* LCOV_EXCL_START */
    1354             :                 return -1;
    1355             :                 /* LCOV_EXCL_STOP */
    1356             :         }
    1357             : 
    1358           1 :         return 0;
    1359             : }
    1360             : 
    1361           4 : int devquery(tommy_list* high, tommy_list* low, int operation, int others)
    1362             : {
    1363             :         tommy_node* i;
    1364           4 :         void* (*func)(void* arg) = 0;
    1365             : 
    1366             : #if HAVE_LINUX_DEVICE
    1367           4 :         if (operation != DEVICE_UP) {
    1368             :                 struct stat st;
    1369             :                 /* sysfs interface is required */
    1370           3 :                 if (stat("/sys/dev/block", &st) != 0) {
    1371             :                         /* LCOV_EXCL_START */
    1372             :                         log_fatal("Missing interface /sys/dev/block.\n");
    1373             :                         return -1;
    1374             :                         /* LCOV_EXCL_STOP */
    1375             :                 }
    1376             : 
    1377             :                 /* for each device */
    1378         136 :                 for (i = tommy_list_head(high); i != 0; i = i->next) {
    1379          65 :                         devinfo_t* devinfo = i->data;
    1380          65 :                         uint64_t device = devinfo->device;
    1381             : 
    1382             :                         /* if the major is the null device, find the real one */
    1383          65 :                         if (major(device) == 0) {
    1384             :                                 /* obtain the real device */
    1385           0 :                                 if (devdereference(device, &device) != 0) {
    1386             :                                         /* LCOV_EXCL_START */
    1387             :                                         log_fatal("Failed to dereference device '%u:%u'.\n", major(device), minor(device));
    1388             :                                         return -1;
    1389             :                                         /* LCOV_EXCL_STOP */
    1390             :                                 }
    1391             :                         }
    1392             : 
    1393             :                         /* get the device file */
    1394          65 :                         if (devresolve(device, devinfo->file, sizeof(devinfo->file)) != 0) {
    1395             :                                 /* LCOV_EXCL_START */
    1396             :                                 log_fatal("Failed to resolve device '%u:%u'.\n", major(device), minor(device));
    1397             :                                 return -1;
    1398             :                                 /* LCOV_EXCL_STOP */
    1399             :                         }
    1400             : 
    1401             :                         /* expand the tree of devices */
    1402          65 :                         if (devtree(devinfo->name, devinfo->smartctl, device, devinfo, low) != 0) {
    1403             :                                 /* LCOV_EXCL_START */
    1404             :                                 log_fatal("Failed to expand device '%u:%u'.\n", major(device), minor(device));
    1405             :                                 return -1;
    1406             :                                 /* LCOV_EXCL_STOP */
    1407             :                         }
    1408             :                 }
    1409             :         }
    1410             : #endif
    1411             : 
    1412           4 :         if (operation == DEVICE_UP) {
    1413             :                 /* duplicate the high */
    1414          31 :                 for (i = tommy_list_head(high); i != 0; i = i->next) {
    1415          30 :                         devinfo_t* devinfo = i->data;
    1416             :                         devinfo_t* entry;
    1417             : 
    1418          30 :                         entry = calloc_nofail(1, sizeof(devinfo_t));
    1419             : 
    1420          30 :                         entry->device = devinfo->device;
    1421          30 :                         pathcpy(entry->name, sizeof(entry->name), devinfo->name);
    1422          30 :                         pathcpy(entry->mount, sizeof(entry->mount), devinfo->mount);
    1423             : 
    1424             :                         /* insert in the high */
    1425          30 :                         tommy_list_insert_tail(low, &entry->node, entry);
    1426             :                 }
    1427             :         }
    1428             : 
    1429             : #if HAVE_LINUX_DEVICE
    1430             :         /* add other devices */
    1431           4 :         if (others) {
    1432           1 :                 if (devscan(low) != 0) {
    1433             :                         /* LCOV_EXCL_START */
    1434             :                         log_fatal("Failed to list other devices.\n");
    1435             :                         return -1;
    1436             :                         /* LCOV_EXCL_STOP */
    1437             :                 }
    1438             :         }
    1439             : #else
    1440             :         (void)others;
    1441             : #endif
    1442             : 
    1443           4 :         switch (operation) {
    1444           1 :         case DEVICE_UP : func = thread_spinup; break;
    1445           1 :         case DEVICE_DOWN : func = thread_spindown; break;
    1446           1 :         case DEVICE_SMART : func = thread_smart; break;
    1447             :         }
    1448             : 
    1449           4 :         if (!func)
    1450           1 :                 return 0;
    1451             : 
    1452           3 :         return device_thread(low, func);
    1453             : }
    1454             : 
    1455         271 : void os_init(int opt)
    1456             : {
    1457             : #if HAVE_BLKID
    1458             :         int ret;
    1459         271 :         ret = blkid_get_cache(&cache, NULL);
    1460         271 :         if (ret != 0) {
    1461             :                 /* LCOV_EXCL_START */
    1462             :                 log_fatal("WARNING Failed to get blkid cache\n");
    1463             :                 /* LCOV_EXCL_STOP */
    1464             :         }
    1465             : #endif
    1466             : 
    1467             :         (void)opt;
    1468         271 : }
    1469             : 
    1470         248 : void os_done(void)
    1471             : {
    1472             : #if HAVE_BLKID
    1473         248 :         if (cache != 0)
    1474         248 :                 blkid_put_cache(cache);
    1475             : #endif
    1476         248 : }
    1477             : 
    1478             : /* LCOV_EXCL_START */
    1479             : void os_abort(void)
    1480             : {
    1481             : #if HAVE_BACKTRACE && HAVE_BACKTRACE_SYMBOLS
    1482             :         void* stack[32];
    1483             :         char** messages;
    1484             :         size_t size;
    1485             :         unsigned i;
    1486             : #endif
    1487             : 
    1488             :         printf("Stacktrace of " PACKAGE " v" VERSION);
    1489             : #ifdef _linux
    1490             :         printf(", linux");
    1491             : #endif
    1492             : #ifdef __GNUC__
    1493             :         printf(", gcc " __VERSION__);
    1494             : #endif
    1495             :         printf(", %d-bit", (int)sizeof(void *) * 8);
    1496             :         printf(", PATH_MAX=%d", PATH_MAX);
    1497             :         printf("\n");
    1498             : 
    1499             : #if HAVE_BACKTRACE && HAVE_BACKTRACE_SYMBOLS
    1500             :         size = backtrace(stack, 32);
    1501             : 
    1502             :         messages = backtrace_symbols(stack, size);
    1503             : 
    1504             :         for (i = 1; i < size; ++i) {
    1505             :                 const char* msg;
    1506             : 
    1507             :                 if (messages)
    1508             :                         msg = messages[i];
    1509             :                 else
    1510             :                         msg = "<unknown>";
    1511             : 
    1512             :                 printf("[bt] %02u: %s\n", i, msg);
    1513             : 
    1514             :                 if (messages) {
    1515             :                         int ret;
    1516             :                         char addr2line[1024];
    1517             :                         size_t j = 0;
    1518             :                         while (msg[j] != '(' && msg[j] != ' ' && msg[j] != 0)
    1519             :                                 ++j;
    1520             : 
    1521             :                         snprintf(addr2line, sizeof(addr2line), "addr2line %p -e %.*s", stack[i], (unsigned)j, msg);
    1522             : 
    1523             :                         ret = system(addr2line);
    1524             :                         if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0)
    1525             :                                 printf("exit:%d\n", WEXITSTATUS(ret));
    1526             :                         if (WIFSIGNALED(ret))
    1527             :                                 printf("signal:%d\n", WTERMSIG(ret));
    1528             :                 }
    1529             :         }
    1530             : #endif
    1531             : 
    1532             :         printf("Please report this error to the SnapRAID Forum:\n");
    1533             :         printf("https://sourceforge.net/p/snapraid/discussion/1677233/\n");
    1534             : 
    1535             :         abort();
    1536             : }
    1537             : /* LCOV_EXCL_STOP */
    1538             : 
    1539           1 : void os_clear(void)
    1540             : {
    1541             :         /* ANSI codes */
    1542           1 :         printf("\033[H"); /* cursor at topleft */
    1543           1 :         printf("\033[2J"); /* clear screen */
    1544           1 : }
    1545             : 
    1546           7 : size_t direct_size(void)
    1547             : {
    1548             :         long size;
    1549             : 
    1550           7 :         size = sysconf(_SC_PAGESIZE);
    1551             : 
    1552           7 :         if (size == -1) {
    1553             :                 /* LCOV_EXCL_START */
    1554             :                 log_fatal("No page size\n");
    1555             :                 exit(EXIT_FAILURE);
    1556             :                 /* LCOV_EXCL_STOP */
    1557             :         }
    1558             : 
    1559           7 :         return size;
    1560             : }
    1561             : 
    1562             : #endif
    1563             : 

Generated by: LCOV version 1.13