LCOV - code coverage report
Current view: top level - cmdline - support.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 842 981 85.8 %
Date: 2026-04-29 15:04:44 Functions: 85 87 97.7 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-3.0-or-later
       2             : // Copyright (C) 2011 Andrea Mazzoleni
       3             : 
       4             : #include "portable.h"
       5             : 
       6             : #include "support.h"
       7             : 
       8             : /****************************************************************************/
       9             : /* lock */
      10             : 
      11             : /**
      12             :  * Locks used externally.
      13             :  */
      14             : #if HAVE_THREAD
      15             : static thread_mutex_t msg_lock;
      16             : static thread_mutex_t memory_lock;
      17             : static thread_mutex_t random_lock;
      18             : #endif
      19             : 
      20    22918145 : void lock_msg(void)
      21             : {
      22             : #if HAVE_THREAD
      23    22918145 :         thread_mutex_lock(&msg_lock);
      24             : #endif
      25    22918145 : }
      26             : 
      27    22918145 : void unlock_msg(void)
      28             : {
      29             : #if HAVE_THREAD
      30    22918145 :         thread_mutex_unlock(&msg_lock);
      31             : #endif
      32    22918145 : }
      33             : 
      34    22103384 : void lock_memory(void)
      35             : {
      36             : #if HAVE_THREAD
      37    22103384 :         thread_mutex_lock(&memory_lock);
      38             : #endif
      39    22103384 : }
      40             : 
      41    22103384 : void unlock_memory(void)
      42             : {
      43             : #if HAVE_THREAD
      44    22103384 :         thread_mutex_unlock(&memory_lock);
      45             : #endif
      46    22103384 : }
      47             : 
      48          14 : void lock_random(void)
      49             : {
      50             : #if HAVE_THREAD
      51          14 :         thread_mutex_lock(&random_lock);
      52             : #endif
      53          14 : }
      54             : 
      55          14 : void unlock_random(void)
      56             : {
      57             : #if HAVE_THREAD
      58          14 :         thread_mutex_unlock(&random_lock);
      59             : #endif
      60          14 : }
      61             : 
      62             : 
      63         355 : void lock_init(void)
      64             : {
      65             : #if HAVE_THREAD
      66             :         /* initialize the locks as first operation as log_fatal depends on them */
      67         355 :         thread_mutex_init(&msg_lock);
      68         355 :         thread_mutex_init(&memory_lock);
      69         355 :         thread_mutex_init(&random_lock);
      70             : #endif
      71         355 : }
      72             : 
      73         318 : void lock_done(void)
      74             : {
      75             : #if HAVE_THREAD
      76         318 :         thread_mutex_destroy(&msg_lock);
      77         318 :         thread_mutex_destroy(&memory_lock);
      78         318 :         thread_mutex_destroy(&random_lock);
      79             : #endif
      80         318 : }
      81             : 
      82             : /****************************************************************************/
      83             : /* random */
      84             : 
      85             : static uint64_t random_state = 1;
      86             : 
      87           1 : void random_seed(uint64_t seed)
      88             : {
      89           1 :         random_state = seed;
      90           1 : }
      91             : 
      92         322 : void random_reseed(void)
      93             : {
      94         322 :         random_state = os_tick();
      95         322 : }
      96             : 
      97           1 : unsigned char random_u8(void)
      98             : {
      99           1 :         return random_u64() & 0xFF;
     100             : }
     101             : 
     102             : /*
     103             :  * SplitMix64
     104             :  *
     105             :  * - Zero-safe: Any 64-bit seed is valid (even 0).
     106             :  * - Full period: Guaranteed to visit every 2^64 value.
     107             :  * - Logic: Uses a Weyl sequence + MurmurHash3-style finalizer.
     108             :  *
     109             :  * See: https://rosettacode.org/wiki/Pseudo-random_numbers/Splitmix64
     110             :  */
     111          14 : uint64_t random_u64(void)
     112             : {
     113             :         uint64_t z;
     114             : 
     115          14 :         lock_random();
     116             : 
     117          14 :         z = random_state += 0x9E3779B97F4A7C15ULL;
     118             : 
     119          14 :         unlock_random();
     120             : 
     121          14 :         z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ULL;
     122          14 :         z = (z ^ (z >> 27)) * 0x94D049BB133111EBULL;
     123             : 
     124          14 :         return z ^ (z >> 31);
     125             : }
     126             : 
     127             : /****************************************************************************/
     128             : /* error */
     129             : 
     130     1952206 : int is_hw(int err)
     131             : {
     132             :         /* LCOV_EXCL_START */
     133             :         if (err == EIO)
     134             :                 return 1;
     135             : #ifdef EFSCORRUPTED
     136             :         if (err == EFSCORRUPTED) /* used by XFS inside the kernel - never expected in user mode as converted to EUCLEAN */
     137             :                 return 1;
     138             : #endif
     139             : #ifdef EUCLEAN
     140             :         if (err == EUCLEAN) /* used by XFS at user level instead of EFSCORRUPTED */
     141             :                 return 1;
     142             : #endif
     143             : #ifdef EBADCRC
     144             :         if (err == EBADCRC) /* used by BTRFS - never expected in user mode as converted to EIO */
     145             :                 return 1;
     146             : #endif
     147             : #ifdef EBADMSG
     148             :         if (err == EBADMSG) /* used by BTRFS - never expected in user mode as converted to EIO */
     149             :                 return 1;
     150             : #endif
     151             : #ifdef ENOMEDIUM
     152             :         if (err == ENOMEDIUM) /* medium removed */
     153             :                 return 1;
     154             : #endif
     155             : #ifdef EHWPOISON
     156             :         if (err == EHWPOISON) /* hardware memory error */
     157             :                 return 1;
     158             : #endif
     159             :         /* LCOV_EXCL_STOP */
     160     1952194 :         return 0;
     161             : }
     162             : 
     163           0 : void log_fatal_errno(int err, const char* name)
     164             : {
     165             :         /* LCOV_EXCL_START */
     166             :         if (err == EIO) {
     167             :                 log_fatal(err, "DANGER! Unexpected input/output error in disk %s. It isn't possible to continue.\n", name);
     168             : #ifdef EFSCORRUPTED
     169             :         } else if (err == EFSCORRUPTED) {
     170             :                 log_fatal(err, "DANGER! Unexpected corrupted error in disk %s. It isn't possible to continue.\n", name);
     171             : #endif
     172             : #ifdef EUCLEAN
     173             :         } else if (err == EUCLEAN) {
     174             :                 log_fatal(err, "DANGER! Unexpected corrupted error in disk %s. It isn't possible to continue.\n", name);
     175             : #endif
     176             : #ifdef EBADCRC
     177             :         } else if (err == EBADCRC) {
     178             :                 log_fatal(err, "DANGER! Unexpected bad crc error in disk %s. It isn't possible to continue.\n", name);
     179             : #endif
     180             : #ifdef EBADMSG
     181             :         } else if (err == EBADMSG) {
     182             :                 log_fatal(err, "DANGER! Unexpected bad crc error in disk %s. It isn't possible to continue.\n", name);
     183             : #endif
     184             : #ifdef ENOMEDIUM
     185             :         } else if (err == ENOMEDIUM) {
     186             :                 log_fatal(err, "DANGER! Unexpected medium removed in disk %s. It isn't possible to continue.\n", name);
     187             : #endif
     188             : #ifdef EHWPOISON
     189             :         } else if (err == EHWPOISON) {
     190             :                 log_fatal(err, "DANGER! Unexpected memory error in disk %s. It isn't possible to continue.\n", name);
     191             : #endif
     192             :         } else if (err == EACCES) {
     193             :                 log_fatal(err, "WARNING! Grant permission in the disk %s. It isn't possible to continue.\n", name);
     194             :         } else if (err == ENOSPC) {
     195             :                 log_fatal(err, "WARNING! Ensure there is free space on the disk %s. It isn't possible to continue.\n", name);
     196             :         } else {
     197             :                 log_fatal(err, "WARNING! Without a working %s disk, it isn't possible to continue.\n", name);
     198             :         }
     199             :         /* LCOV_EXCL_STOP */
     200           0 : }
     201             : 
     202          27 : void log_error_errno(int err, const char* name)
     203             : {
     204             :         /* LCOV_EXCL_START */
     205             :         if (err == EIO) {
     206             :                 log_fatal(err, "DANGER! Unexpected input/output error in disk %s.\n", name);
     207             : #ifdef EFSCORRUPTED
     208             :         } else if (err == EFSCORRUPTED) {
     209             :                 log_fatal(err, "DANGER! Unexpected corrupted error in disk %s.\n", name);
     210             : #endif
     211             : #ifdef EUCLEAN
     212             :         } else if (err == EUCLEAN) {
     213             :                 log_fatal(err, "DANGER! Unexpected corrupted error in disk %s.\n", name);
     214             : #endif
     215             : #ifdef EBADCRC
     216             :         } else if (err == EBADCRC) {
     217             :                 log_fatal(err, "DANGER! Unexpected bad crc error in disk %s.\n", name);
     218             : #endif
     219             : #ifdef EBADMSG
     220             :         } else if (err == EBADMSG) {
     221             :                 log_fatal(err, "DANGER! Unexpected bad crc error in disk %s.\n", name);
     222             : #endif
     223             : #ifdef ENOMEDIUM
     224             :         } else if (err == ENOMEDIUM) {
     225             :                 log_fatal(err, "DANGER! Unexpected medium removed in disk %s.\n", name);
     226             : #endif
     227             : #ifdef EHWPOISON
     228             :         } else if (err == EHWPOISON) {
     229             :                 log_fatal(err, "DANGER! Unexpected memory error in disk %s.\n", name);
     230             : #endif
     231             :                 /* LCOV_EXCL_STOP */
     232          27 :         } else if (err == EACCES) {
     233          12 :                 log_error(err, "WARNING! Grant permission in the disk %s.\n", name);
     234          15 :         } else if (err == ENOSPC) {
     235           0 :                 log_error(err, "WARNING! Ensure there is free space on the disk %s.\n", name);
     236          15 :         } else if (err == ENOENT) {
     237          15 :                 log_error(err, "WARNING! You cannot modify files while running.\n");
     238             :         }
     239          27 : }
     240             : 
     241             : /****************************************************************************/
     242             : /* print */
     243             : 
     244             : int msg_level = 0;
     245             : FILE* stdlog = 0;
     246             : int msg_line_prev = 0; /**< Previous line width on stdout */
     247             : int msg_line_curr = 0; /**< Current line width on stdout */
     248             : 
     249             : /*
     250             :  * Note that in the following functions we always flush both
     251             :  * stdout and stderr, because we want to ensure that they mixes
     252             :  * well when redirected to files
     253             :  *
     254             :  * The buffering is similar at the "line buffered" one, that
     255             :  * is not available on Windows, so we emulate it in this way.
     256             :  *
     257             :  * For stdlog flushing is limited. To ensure flushing the
     258             :  * caller should use log_flush().
     259             :  */
     260             : 
     261       44507 : static void vmsg(FILE* out, const char* format, va_list ap)
     262             : {
     263       44507 :         char* dup = strdup_nofail(format);
     264       44507 :         int len = strlen(dup);
     265       44507 :         int written = 0;
     266       44507 :         char control = 0;
     267             : 
     268       44507 :         if (len > 0) {
     269       44507 :                 if (dup[len - 1] == '\n') {
     270       44497 :                         dup[len - 1] = 0;
     271       44497 :                         control = '\n';
     272          10 :                 } else if (dup[len - 1] == '\r') {
     273           0 :                         dup[len - 1] = 0;
     274           0 :                         control = '\r';
     275             :                 }
     276             :         }
     277             : 
     278       44507 :         if (dup[0]) {
     279       44397 :                 written = vfprintf(out, dup, ap);
     280             :         }
     281             : 
     282       44507 :         switch (control) {
     283          10 :         case 0 :
     284          10 :                 msg_line_curr += written;
     285          10 :                 break;
     286       44497 :         case '\n' :
     287       44497 :                 msg_line_curr += written;
     288             :                 /* writes spaces to overwrite any previous char */
     289       44497 :                 while (msg_line_curr < msg_line_prev) {
     290           0 :                         fprintf(out, " ");
     291           0 :                         --msg_line_prev;
     292             :                 }
     293       44497 :                 msg_line_prev = 0;
     294       44497 :                 msg_line_curr = 0;
     295       44497 :                 fprintf(out, "\n");
     296       44497 :                 break;
     297           0 :         case '\r' :
     298           0 :                 msg_line_curr += written;
     299             :                 /* writes spaces to overwrite any previous char */
     300           0 :                 while (msg_line_curr < msg_line_prev) {
     301           0 :                         fprintf(out, " ");
     302           0 :                         --msg_line_prev;
     303             :                 }
     304           0 :                 msg_line_prev = msg_line_curr;
     305           0 :                 msg_line_curr = 0;
     306           0 :                 fprintf(out, "\r");
     307             :         }
     308             : 
     309       44507 :         free(dup);
     310       44507 : }
     311             : 
     312         810 : void log_fatal(int err, const char* format, ...)
     313             : {
     314             :         va_list ap;
     315             : 
     316         810 :         lock_msg();
     317             : 
     318         810 :         if (stdlog) {
     319         106 :                 if (is_hw(err))
     320          12 :                         fprintf(stdlog, "msg:fatal_hardware: ");
     321             :                 else
     322          94 :                         fprintf(stdlog, "msg:fatal: ");
     323             : 
     324         106 :                 va_start(ap, format);
     325         106 :                 vfprintf(stdlog, format, ap);
     326         106 :                 va_end(ap);
     327             : 
     328         106 :                 fflush(stdlog);
     329             :         }
     330             : 
     331         810 :         va_start(ap, format);
     332         810 :         vmsg(stderr, format, ap);
     333         810 :         va_end(ap);
     334             : 
     335         810 :         fflush(stderr);
     336             : 
     337         810 :         unlock_msg();
     338         810 : }
     339             : 
     340      390123 : void log_error(int err, const char* format, ...)
     341             : {
     342             :         va_list ap;
     343             : 
     344      390123 :         lock_msg();
     345             : 
     346      390123 :         if (stdlog) {
     347      388284 :                 if (is_hw(err))
     348           0 :                         fprintf(stdlog, "msg:error_hardware: ");
     349             :                 else
     350      388284 :                         fprintf(stdlog, "msg:error: ");
     351             : 
     352      388284 :                 va_start(ap, format);
     353      388284 :                 vfprintf(stdlog, format, ap);
     354      388284 :                 va_end(ap);
     355             : 
     356      388284 :                 fflush(stdlog);
     357             :         } else {
     358        1839 :                 va_start(ap, format);
     359        1839 :                 vmsg(stderr, format, ap);
     360        1839 :                 va_end(ap);
     361             : 
     362        1839 :                 fflush(stderr);
     363             :         }
     364             : 
     365      390123 :         unlock_msg();
     366      390123 : }
     367             : 
     368        4882 : void log_expected(int err, const char* format, ...)
     369             : {
     370             :         va_list ap;
     371             : 
     372        4882 :         lock_msg();
     373             : 
     374        4882 :         if (stdlog) {
     375        4882 :                 if (is_hw(err))
     376           0 :                         fprintf(stdlog, "msg:error_hardware: "); /* we never expect an hardware error */
     377             :                 else
     378        4882 :                         fprintf(stdlog, "msg:expected: ");
     379             : 
     380        4882 :                 va_start(ap, format);
     381        4882 :                 vfprintf(stdlog, format, ap);
     382        4882 :                 va_end(ap);
     383             : 
     384        4882 :                 fflush(stdlog);
     385             :         }
     386             : 
     387        4882 :         unlock_msg();
     388        4882 : }
     389             : 
     390    22045868 : void log_tag(const char* format, ...)
     391             : {
     392             :         va_list ap;
     393             : 
     394    22045868 :         lock_msg();
     395             : 
     396    22045868 :         if (stdlog) {
     397     2729288 :                 va_start(ap, format);
     398     2729288 :                 vfprintf(stdlog, format, ap);
     399     2729288 :                 va_end(ap);
     400             : 
     401             :                 /* here we intentionally don't flush to make the output faster */
     402             :         }
     403             : 
     404    22045868 :         unlock_msg();
     405    22045868 : }
     406             : 
     407        3918 : void log_flush(void)
     408             : {
     409        3918 :         lock_msg();
     410             : 
     411        3918 :         if (stdlog)
     412         366 :                 fflush(stdlog);
     413        3918 :         fflush(stdout);
     414        3918 :         fflush(stderr);
     415             : 
     416        3918 :         unlock_msg();
     417        3918 : }
     418             : 
     419         832 : void msg_status(const char* format, ...)
     420             : {
     421             :         va_list ap;
     422             : 
     423         832 :         lock_msg();
     424             : 
     425         832 :         if (stdlog) {
     426         454 :                 fprintf(stdlog, "msg:status: ");
     427             : 
     428         454 :                 va_start(ap, format);
     429         454 :                 vfprintf(stdlog, format, ap);
     430         454 :                 va_end(ap);
     431             : 
     432         454 :                 fflush(stdlog);
     433             :         }
     434             : 
     435         832 :         if (msg_level >= MSG_STATUS) {
     436         832 :                 va_start(ap, format);
     437         832 :                 vmsg(stdout, format, ap);
     438         832 :                 va_end(ap);
     439             :         }
     440             : 
     441         832 :         unlock_msg();
     442         832 : }
     443             : 
     444      334328 : void msg_info(const char* format, ...)
     445             : {
     446             :         va_list ap;
     447             : 
     448      334328 :         lock_msg();
     449             : 
     450             :         /*
     451             :          * Don't output in stdlog as these messages
     452             :          * are always paired with a msg_tag() call
     453             :          */
     454             : 
     455      334328 :         if (msg_level >= MSG_INFO) {
     456       40749 :                 va_start(ap, format);
     457       40749 :                 vmsg(stdout, format, ap);
     458       40749 :                 va_end(ap);
     459             : 
     460       40749 :                 fflush(stdout);
     461             :         }
     462             : 
     463      334328 :         unlock_msg();
     464      334328 : }
     465             : 
     466        5649 : void msg_progress(const char* format, ...)
     467             : {
     468             :         va_list ap;
     469             : 
     470        5649 :         lock_msg();
     471             : 
     472        5649 :         if (stdlog) {
     473         997 :                 fprintf(stdlog, "msg:progress: ");
     474             : 
     475         997 :                 va_start(ap, format);
     476         997 :                 vfprintf(stdlog, format, ap);
     477         997 :                 va_end(ap);
     478             : 
     479         997 :                 fflush(stdlog);
     480             :         }
     481             : 
     482        5649 :         if (msg_level >= MSG_PROGRESS) {
     483         177 :                 va_start(ap, format);
     484         177 :                 vmsg(stdout, format, ap);
     485         177 :                 va_end(ap);
     486             : 
     487         177 :                 fflush(stdout);
     488             :         }
     489             : 
     490        5649 :         unlock_msg();
     491        5649 : }
     492             : 
     493        2388 : void msg_bar(const char* format, ...)
     494             : {
     495             :         va_list ap;
     496             : 
     497        2388 :         lock_msg();
     498             : 
     499             :         /*
     500             :          * Don't output in stdlog as these messages
     501             :          * are intended for screen only
     502             :          * also don't flush stdout as they are intended to be partial messages
     503             :          */
     504             : 
     505        2388 :         if (msg_level >= MSG_BAR) {
     506           0 :                 va_start(ap, format);
     507           0 :                 vmsg(stdout, format, ap);
     508           0 :                 va_end(ap);
     509             :         }
     510             : 
     511        2388 :         unlock_msg();
     512        2388 : }
     513             : 
     514      128677 : void msg_verbose(const char* format, ...)
     515             : {
     516             :         va_list ap;
     517             : 
     518      128677 :         lock_msg();
     519             : 
     520      128677 :         if (stdlog) {
     521      124511 :                 fprintf(stdlog, "msg:verbose: ");
     522             : 
     523      124511 :                 va_start(ap, format);
     524      124511 :                 vfprintf(stdlog, format, ap);
     525      124511 :                 va_end(ap);
     526             : 
     527      124511 :                 fflush(stdlog);
     528             :         }
     529             : 
     530      128677 :         if (msg_level >= MSG_VERBOSE) {
     531         100 :                 va_start(ap, format);
     532         100 :                 vmsg(stdout, format, ap);
     533         100 :                 va_end(ap);
     534             : 
     535         100 :                 fflush(stdout);
     536             :         }
     537             : 
     538      128677 :         unlock_msg();
     539      128677 : }
     540             : 
     541         670 : void msg_flush(void)
     542             : {
     543         670 :         lock_msg();
     544             : 
     545         670 :         fflush(stdout);
     546         670 :         fflush(stderr);
     547             : 
     548         670 :         unlock_msg();
     549         670 : }
     550             : 
     551          69 : void printc(char c, size_t pad)
     552             : {
     553         126 :         while (pad) {
     554             :                 /* group writes in long pieces */
     555             :                 char buf[128];
     556          57 :                 size_t len = pad;
     557             : 
     558          57 :                 if (len >= sizeof(buf))
     559           0 :                         len = sizeof(buf) - 1;
     560             : 
     561          57 :                 memset(buf, c, len);
     562          57 :                 buf[len] = 0;
     563             : 
     564          57 :                 fputs(buf, stdout);
     565             : 
     566          57 :                 pad -= len;
     567             :         }
     568          69 : }
     569             : 
     570          16 : void printr(const char* str, size_t pad)
     571             : {
     572             :         size_t len;
     573             : 
     574          16 :         len = strlen(str);
     575             : 
     576          16 :         if (len < pad)
     577          16 :                 printc(' ', pad - len);
     578             : 
     579          16 :         fputs(str, stdout);
     580          16 : }
     581             : 
     582         202 : void printl(const char* str, size_t pad)
     583             : {
     584             :         size_t len;
     585             : 
     586         202 :         fputs(str, stdout);
     587             : 
     588         202 :         len = strlen(str);
     589             : 
     590         202 :         if (len < pad)
     591          34 :                 printc(' ', pad - len);
     592         202 : }
     593             : 
     594          18 : void printp(double v, size_t pad)
     595             : {
     596             :         char buf[64];
     597          18 :         const char* s = "%";
     598             : 
     599          18 :         if (v > 0.1)
     600           7 :                 snprintf(buf, sizeof(buf), "%5.2f%s", v, s);
     601          11 :         else if (v > 0.01)
     602           4 :                 snprintf(buf, sizeof(buf), "%6.3f%s", v, s);
     603           7 :         else if (v > 0.001)
     604           2 :                 snprintf(buf, sizeof(buf), "%7.4f%s", v, s);
     605           5 :         else if (v > 0.0001)
     606           1 :                 snprintf(buf, sizeof(buf), "%8.5f%s", v, s);
     607           4 :         else if (v > 0.00001)
     608           2 :                 snprintf(buf, sizeof(buf), "%9.6f%s", v, s);
     609           2 :         else if (v > 0.000001)
     610           0 :                 snprintf(buf, sizeof(buf), "%10.7f%s", v, s);
     611           2 :         else if (v > 0.0000001)
     612           1 :                 snprintf(buf, sizeof(buf), "%11.8f%s", v, s);
     613           1 :         else if (v > 0.00000001)
     614           0 :                 snprintf(buf, sizeof(buf), "%12.9f%s", v, s);
     615           1 :         else if (v > 0.000000001)
     616           1 :                 snprintf(buf, sizeof(buf), "%13.10f%s", v, s);
     617           0 :         else if (v > 0.0000000001)
     618           0 :                 snprintf(buf, sizeof(buf), "%14.11f%s", v, s);
     619           0 :         else if (v > 0.00000000001)
     620           0 :                 snprintf(buf, sizeof(buf), "%15.12f%s", v, s);
     621           0 :         else if (v > 0.000000000001)
     622           0 :                 snprintf(buf, sizeof(buf), "%16.13f%s", v, s);
     623             :         else
     624           0 :                 snprintf(buf, sizeof(buf), "%17.14f%s", v, s);
     625          18 :         printl(buf, pad);
     626          18 : }
     627             : 
     628             : #define charcat(c) \
     629             :         do { \
     630             :                 if (p == end) { \
     631             :                         goto bail; \
     632             :                 } \
     633             :                 *p++ = (c); \
     634             :         } while (0)
     635             : 
     636     2962320 : const char* esc_tag(const char* str, char* buffer)
     637             : {
     638     2962320 :         char* begin = buffer;
     639     2962320 :         char* end = begin + ESC_MAX;
     640     2962320 :         char* p = begin;
     641             : 
     642             :         /* copy string with escaping */
     643    38478889 :         while (*str) {
     644    35516569 :                 char c = *str++;
     645             : 
     646    35516569 :                 switch (c) {
     647           3 :                 case '\n' :
     648           3 :                         charcat('\\');
     649           3 :                         charcat('n');
     650           3 :                         break;
     651           3 :                 case '\r' :
     652           3 :                         charcat('\\');
     653           3 :                         charcat('r');
     654           3 :                         break;
     655           4 :                 case ':' :
     656           4 :                         charcat('\\');
     657           4 :                         charcat('d');
     658           4 :                         break;
     659         143 :                 case '\\' :
     660         143 :                         charcat('\\');
     661         143 :                         charcat('\\');
     662         143 :                         break;
     663    35516416 :                 default :
     664    35516416 :                         charcat(c);
     665    35516416 :                         break;
     666             :                 }
     667             :         }
     668             : 
     669             :         /* put final 0 */
     670     2962320 :         if (p == end)
     671           0 :                 goto bail;
     672     2962320 :         *p = 0;
     673             : 
     674     2962320 :         return begin;
     675             : 
     676           0 : bail:
     677             :         /* LCOV_EXCL_START */
     678             :         log_fatal(EINTERNAL, "Escape for log is too long\n");
     679             :         exit(EXIT_FAILURE);
     680             :         /* LCOV_EXCL_STOP */
     681             : }
     682             : 
     683             : #ifdef _WIN32
     684             : static int needs_quote(const char* arg)
     685             : {
     686             :         while (*arg) {
     687             :                 char c = *arg;
     688             :                 if (c == ' ' || c == '\t' || c == '\n' || c == '\r' ||
     689             :                         c == '&' || c == '|' || c == '(' || c == ')' ||
     690             :                         c == '<' || c == '>' || c == '^' || c == '"' ||
     691             :                         c == '%' || c == '!' || c == '=' || c == ';' ||
     692             :                         (unsigned char)c < 32 || c == 127)
     693             :                         return 1;
     694             :                 ++arg;
     695             :         }
     696             : 
     697             :         return 0;
     698             : }
     699             : #endif
     700             : 
     701             : struct stream {
     702             :         const char* str;
     703             :         unsigned idx;
     704             :         const char** map;
     705             :         unsigned max;
     706             : };
     707             : 
     708     5580751 : static inline char ssget(struct stream* ss)
     709             : {
     710             :         /* get the next char */
     711     5580751 :         char c = *ss->str++;
     712             : 
     713             :         /* if one string is finished, go to the next */
     714     5616846 :         while (c == 0 && ss->idx + 1 < ss->max) {
     715       36095 :                 ss->str = ss->map[++ss->idx];
     716       36095 :                 c = *ss->str++;
     717             :         }
     718             : 
     719     5580751 :         return c;
     720             : }
     721             : 
     722             : #ifdef _WIN32
     723             : const char* esc_shell_multi(const char** str_map, unsigned str_max, char* buffer)
     724             : {
     725             :         char* begin = buffer;
     726             :         char* end = begin + ESC_MAX;
     727             :         char* p = begin;
     728             :         struct stream ss;
     729             :         char c;
     730             :         unsigned i;
     731             :         int has_quote;
     732             : 
     733             :         has_quote = 0;
     734             :         for (i = 0; i < str_max; ++i) {
     735             :                 if (needs_quote(str_map[i]))
     736             :                         has_quote = 1;
     737             :         }
     738             : 
     739             :         ss.idx = 0;
     740             :         ss.str = str_map[0];
     741             :         ss.map = str_map;
     742             :         ss.max = str_max;
     743             : 
     744             :         if (!has_quote) {
     745             :                 c = ssget(&ss);
     746             :                 while (c) {
     747             :                         charcat(c);
     748             :                         c = ssget(&ss);
     749             :                 }
     750             :         } else {
     751             :                 /* starting quote */
     752             :                 charcat('"');
     753             : 
     754             :                 c = ssget(&ss);
     755             :                 while (c) {
     756             :                         int bl = 0;
     757             :                         while (c == '\\') {
     758             :                                 ++bl;
     759             :                                 c = ssget(&ss);
     760             :                         }
     761             : 
     762             :                         if (c == 0) {
     763             :                                 /* double backslashes before closing quote */
     764             :                                 bl = bl * 2;
     765             :                                 while (bl--)
     766             :                                         charcat('\\');
     767             :                                 break;
     768             :                         } else if (c == '"') {
     769             :                                 /* double backslashes + escape the quote */
     770             :                                 bl = bl * 2 + 1;
     771             :                                 while (bl--)
     772             :                                         charcat('\\');
     773             :                                 charcat('"');
     774             :                         } else {
     775             :                                 /* normal backslashes */
     776             :                                 while (bl--)
     777             :                                         charcat('\\');
     778             :                                 charcat(c);
     779             :                         }
     780             : 
     781             :                         c = ssget(&ss);
     782             :                 }
     783             : 
     784             :                 /* ending quote */
     785             :                 charcat('"');
     786             :         }
     787             : 
     788             :         /* put final 0 */
     789             :         charcat(0);
     790             : 
     791             :         return begin;
     792             : 
     793             : bail:
     794             :         /* LCOV_EXCL_START */
     795             :         log_fatal(EINTERNAL, "Escape for shell is too long\n");
     796             :         exit(EXIT_FAILURE);
     797             :         /* LCOV_EXCL_STOP */
     798             : }
     799             : #else
     800      389684 : const char* esc_shell_multi(const char** str_map, unsigned str_max, char* buffer)
     801             : {
     802      389684 :         char* begin = buffer;
     803      389684 :         char* end = begin + ESC_MAX;
     804      389684 :         char* p = begin;
     805             :         struct stream ss;
     806             :         char c;
     807             : 
     808      389684 :         ss.idx = 0;
     809      389684 :         ss.str = str_map[0];
     810      389684 :         ss.map = str_map;
     811      389684 :         ss.max = str_max;
     812             : 
     813      389684 :         c = ssget(&ss);
     814     5580751 :         while (c) {
     815     5191067 :                 switch (c) {
     816             :                 /* special chars that need to be quoted */
     817       86022 :                 case ' ' : /* space */
     818             :                 case '\t' : /* tab */
     819             :                 case '\n' : /* newline */
     820             :                 case '\r' : /* carriage return */
     821             :                 case '~' : /* home */
     822             :                 case '`' : /* command */
     823             :                 case '#' : /* comment */
     824             :                 case '$' : /* variable */
     825             :                 case '&' : /* background job */
     826             :                 case '*' : /* wildcard */
     827             :                 case '(' : /* shell */
     828             :                 case ')' : /* shell */
     829             :                 case '\\' : /* quote */
     830             :                 case '|' : /* pipe */
     831             :                 case '[' : /* wildcard */
     832             :                 case ']' : /* wildcard */
     833             :                 case '{' : /* code */
     834             :                 case '}' : /* code */
     835             :                 case ';' : /* separator */
     836             :                 case '\'' : /* quote */
     837             :                 case '"' : /* quote */
     838             :                 case '<' : /* redirect */
     839             :                 case '>' : /* redirect */
     840             :                 case '?' : /* wildcard */
     841             :                 case '=' : /* assignment */
     842             :                 case '!' : /* history expansion */
     843       86022 :                         charcat('\\');
     844       86022 :                         charcat(c);
     845       86022 :                         break;
     846     5105045 :                 default :
     847             :                         /* check for control characters */
     848     5105045 :                         if ((unsigned char)c < 32 || c == 127) {
     849           6 :                                 charcat('\\');
     850           6 :                                 charcat(c);
     851             :                         } else {
     852     5105039 :                                 charcat(c);
     853             :                         }
     854     5105045 :                         break;
     855             :                 }
     856             : 
     857     5191067 :                 c = ssget(&ss);
     858             :         }
     859             : 
     860             :         /* put final 0 */
     861      389684 :         charcat(0);
     862             : 
     863      389684 :         return begin;
     864             : 
     865           0 : bail:
     866             :         /* LCOV_EXCL_START */
     867             :         log_fatal(EINTERNAL, "Escape for shell is too long\n");
     868             :         exit(EXIT_FAILURE);
     869             :         /* LCOV_EXCL_STOP */
     870             : }
     871             : #endif
     872             : 
     873        4150 : char* strpolish(char* s)
     874             : {
     875        4150 :         char* i = s;
     876             : 
     877      209627 :         while (*i) {
     878      205477 :                 if (isspace(*i) || !isprint(*i))
     879       59087 :                         *i = ' ';
     880      205477 :                 ++i;
     881             :         }
     882             : 
     883        4150 :         return s;
     884             : }
     885             : 
     886        1400 : unsigned strsplit(char** split_map, unsigned split_max, char* str, const char* delimiters)
     887             : {
     888        1400 :         unsigned mac = 0;
     889             : 
     890             :         /* skip initial delimiters */
     891        1400 :         str += strspn(str, delimiters);
     892             : 
     893        6889 :         while (*str != 0 || mac == split_max) {
     894             :                 /* start of the token */
     895        5489 :                 split_map[mac] = str;
     896        5489 :                 ++mac;
     897             : 
     898             :                 /* find the first delimiter or the end of the string */
     899        5489 :                 str += strcspn(str, delimiters);
     900             : 
     901             :                 /* put the final terminator if missing */
     902        5489 :                 if (*str != 0)
     903        4089 :                         *str++ = 0;
     904             : 
     905             :                 /* skip trailing delimiters */
     906        5489 :                 str += strspn(str, delimiters);
     907             :         }
     908             : 
     909        1400 :         return mac;
     910             : }
     911             : 
     912         557 : char* strtrim(char* str)
     913             : {
     914             :         char* begin;
     915             :         char* end;
     916             : 
     917         557 :         begin = str;
     918         964 :         while (begin[0] && isspace((unsigned char)begin[0]))
     919         407 :                 ++begin;
     920             : 
     921         557 :         end = begin + strlen(begin);
     922        2607 :         while (end > begin && isspace((unsigned char)end[-1]))
     923        2050 :                 --end;
     924             : 
     925         557 :         end[0] = 0;
     926             : 
     927         557 :         if (begin != end)
     928         557 :                 memmove(str, begin, end - begin + 1);
     929             : 
     930         557 :         return str;
     931             : }
     932             : 
     933           2 : char* strlwr(char* str)
     934             : {
     935           2 :         char* s = str;
     936             : 
     937          24 :         while (*s) {
     938          22 :                 *s = tolower((unsigned char)*s);
     939          22 :                 ++s;
     940             :         }
     941             : 
     942           2 :         return str;
     943             : }
     944             : 
     945          11 : char* worddigitstr(const char* haystack, const char* needle)
     946             : {
     947          11 :         size_t len = strlen(needle);
     948             :         const char* s;
     949             : 
     950          11 :         if (len == 0)
     951           1 :                 return NULL;
     952             : 
     953          11 :         for (s = haystack; (s = strstr(s, needle)) != NULL; ++s) {
     954             : 
     955             :                 /* left boundary */
     956           8 :                 if (s == haystack || isspace((unsigned char)s[-1]) || isdigit((unsigned char)s[-1])) {
     957             :                         /* right boundary */
     958           7 :                         if (s[len] == '\0' || isspace((unsigned char)s[len]) || isdigit((unsigned char)s[len])) {
     959           7 :                                 return (char*)s;
     960             :                         }
     961             :                 }
     962             :         }
     963             : 
     964           3 :         return NULL;
     965             : }
     966             : 
     967             : /****************************************************************************/
     968             : /* path */
     969             : 
     970     2015340 : void pathcpy(char* dst, size_t size, const char* src)
     971             : {
     972     2015340 :         size_t len = strlen(src);
     973             : 
     974     2015340 :         if (len + 1 > size) {
     975             :                 /* LCOV_EXCL_START */
     976             :                 log_fatal(EINTERNAL, "Path too long '%s'\n", src);
     977             :                 os_abort();
     978             :                 /* LCOV_EXCL_STOP */
     979             :         }
     980             : 
     981     2015340 :         memcpy(dst, src, len + 1);
     982     2015340 : }
     983             : 
     984        2308 : void pathcat(char* dst, size_t size, const char* src)
     985             : {
     986        2308 :         size_t dst_len = strlen(dst);
     987        2308 :         size_t src_len = strlen(src);
     988             : 
     989        2308 :         if (dst_len + src_len + 1 > size) {
     990             :                 /* LCOV_EXCL_START */
     991             :                 log_fatal(EINTERNAL, "Path too long '%s%s'\n", dst, src);
     992             :                 os_abort();
     993             :                 /* LCOV_EXCL_STOP */
     994             :         }
     995             : 
     996        2308 :         memcpy(dst + dst_len, src, src_len + 1);
     997        2308 : }
     998             : 
     999     3848518 : void pathcatl(char* dst, size_t dst_len, size_t size, const char* src)
    1000             : {
    1001     3848518 :         size_t src_len = strlen(src);
    1002             : 
    1003     3848518 :         if (dst_len + src_len + 1 > size) {
    1004             :                 /* LCOV_EXCL_START */
    1005             :                 log_fatal(EINTERNAL, "Path too long '%s%s'\n", dst, src);
    1006             :                 os_abort();
    1007             :                 /* LCOV_EXCL_STOP */
    1008             :         }
    1009             : 
    1010     3848518 :         memcpy(dst + dst_len, src, src_len + 1);
    1011     3848518 : }
    1012             : 
    1013         880 : void pathcatc(char* dst, size_t size, char c)
    1014             : {
    1015         880 :         size_t dst_len = strlen(dst);
    1016             : 
    1017         880 :         if (dst_len + 2 > size) {
    1018             :                 /* LCOV_EXCL_START */
    1019             :                 log_fatal(EINTERNAL, "Path too long '%s%c'\n", dst, c);
    1020             :                 os_abort();
    1021             :                 /* LCOV_EXCL_STOP */
    1022             :         }
    1023             : 
    1024         880 :         dst[dst_len] = c;
    1025         880 :         dst[dst_len + 1] = 0;
    1026         880 : }
    1027             : 
    1028       19237 : void pathimport(char* dst, size_t size, const char* src)
    1029             : {
    1030       19237 :         pathcpy(dst, size, src);
    1031             : 
    1032             : #ifdef _WIN32
    1033             :         /*
    1034             :          * Convert the Windows dir separator '\' to C '/',
    1035             :          * and the Windows escaping char '^' to the fnmatch '\'
    1036             :          */
    1037             :         while (*dst) {
    1038             :                 switch (*dst) {
    1039             :                 case '\\' :
    1040             :                         *dst = '/';
    1041             :                         break;
    1042             :                 case '^' :
    1043             :                         *dst = '\\';
    1044             :                         break;
    1045             :                 }
    1046             :                 ++dst;
    1047             :         }
    1048             : #endif
    1049       19237 : }
    1050             : 
    1051       12234 : void pathexport(char* dst, size_t size, const char* src)
    1052             : {
    1053       12234 :         pathcpy(dst, size, src);
    1054             : 
    1055             : #ifdef _WIN32
    1056             :         /* invert the import */
    1057             :         while (*dst) {
    1058             :                 switch (*dst) {
    1059             :                 case '/' :
    1060             :                         *dst = '\\';
    1061             :                         break;
    1062             :                 case '\\' :
    1063             :                         *dst = '^';
    1064             :                         break;
    1065             :                 }
    1066             :                 ++dst;
    1067             :         }
    1068             : #endif
    1069       12234 : }
    1070             : 
    1071    44528016 : void pathprint(char* dst, size_t size, const char* format, ...)
    1072             : {
    1073             :         size_t len;
    1074             :         va_list ap;
    1075             : 
    1076    44528016 :         va_start(ap, format);
    1077    44528016 :         len = vsnprintf(dst, size, format, ap);
    1078    44528016 :         va_end(ap);
    1079             : 
    1080    44528016 :         if (len >= size) {
    1081             :                 /* LCOV_EXCL_START */
    1082             :                 if (size > 0) {
    1083             :                         dst[size - 1] = 0;
    1084             :                         log_fatal(EINTERNAL, "Path too long '%s...'\n", dst);
    1085             :                 } else {
    1086             :                         log_fatal(EINTERNAL, "Path too long for empty size'\n");
    1087             :                 }
    1088             :                 os_abort();
    1089             :                 /* LCOV_EXCL_STOP */
    1090             :         }
    1091    44528016 : }
    1092             : 
    1093        8612 : void pathslash(char* dst, size_t size)
    1094             : {
    1095        8612 :         size_t len = strlen(dst);
    1096             : 
    1097        8612 :         if (len > 0 && dst[len - 1] != '/') {
    1098        6755 :                 if (len + 2 >= size) {
    1099             :                         /* LCOV_EXCL_START */
    1100             :                         log_fatal(EINTERNAL, "Path too long '%s/'\n", dst);
    1101             :                         os_abort();
    1102             :                         /* LCOV_EXCL_STOP */
    1103             :                 }
    1104             : 
    1105        6755 :                 dst[len] = '/';
    1106        6755 :                 dst[len + 1] = 0;
    1107             :         }
    1108        8612 : }
    1109             : 
    1110       62894 : void pathcut(char* dst)
    1111             : {
    1112       62894 :         char* slash = strrchr(dst, '/');
    1113             : 
    1114       62894 :         if (slash)
    1115       62894 :                 slash[1] = 0;
    1116             :         else
    1117           0 :                 dst[0] = 0;
    1118       62894 : }
    1119             : 
    1120           0 : void pathup(char* dst)
    1121             : {
    1122           0 :         size_t len = strlen(dst);
    1123             : 
    1124           0 :         if (len == 0)
    1125           0 :                 return;
    1126             : 
    1127             :         /* if ending with slash, remove it */
    1128           0 :         if (dst[len - 1] == '/')
    1129           0 :                 --len;
    1130             : 
    1131             :         /* search previous / */
    1132           0 :         while (len > 0 && dst[len - 1] != '/')
    1133           0 :                 --len;
    1134             : 
    1135             :         /* cut everything after the slash (if there are no more slash, return the empty string) */
    1136           0 :         dst[len] = 0;
    1137             : }
    1138             : 
    1139    54963440 : int pathcmp(const char* a, const char* b)
    1140             : {
    1141             : #ifdef _WIN32
    1142             :         char ai[PATH_MAX];
    1143             :         char bi[PATH_MAX];
    1144             : 
    1145             :         /* import to convert \ to / */
    1146             :         pathimport(ai, sizeof(ai), a);
    1147             :         pathimport(bi, sizeof(bi), b);
    1148             : 
    1149             :         /* case insensitive compare in Windows */
    1150             :         return stricmp(ai, bi);
    1151             : #else
    1152    54963440 :         return strcmp(a, b);
    1153             : #endif
    1154             : }
    1155             : 
    1156      127913 : int path_is_root_of(const char* root, const char* path)
    1157             : {
    1158      307307 :         while (*root && *path) {
    1159             : #ifdef _WIN32
    1160             :                 /* case insensitive compare in Windows */
    1161             :                 char r = tolower((unsigned char)*root);
    1162             :                 char p = tolower((unsigned char)*path);
    1163             :                 if (r == '\\')
    1164             :                         r = '/';
    1165             :                 if (p == '\\')
    1166             :                         p = '/';
    1167             :                 if (r != p)
    1168             :                         return 0;
    1169             : #else
    1170      248113 :                 if (*root != *path)
    1171       68719 :                         return 0;
    1172             : #endif
    1173      179394 :                 ++root;
    1174      179394 :                 ++path;
    1175             :         }
    1176             : 
    1177       59194 :         return *root == 0 && *path != 0;
    1178             : }
    1179             : 
    1180             : /****************************************************************************/
    1181             : /* file-system */
    1182             : 
    1183      807453 : int mkancestor(const char* file)
    1184             : {
    1185             :         char dir[PATH_MAX];
    1186             :         struct stat st;
    1187             :         char* c;
    1188             : 
    1189      807453 :         pathcpy(dir, sizeof(dir), file);
    1190             : 
    1191      807453 :         c = strrchr(dir, '/');
    1192      807453 :         if (!c) {
    1193             :                 /* no ancestor */
    1194           0 :                 return 0;
    1195             :         }
    1196             : 
    1197             :         /* clear the file */
    1198      807453 :         *c = 0;
    1199             : 
    1200             :         /* if it's the root dir */
    1201      807453 :         if (*dir == 0) {
    1202             :                 /* nothing more to do */
    1203           0 :                 return 0;
    1204             :         }
    1205             : 
    1206             : #ifdef _WIN32
    1207             :         /* if it's a drive specification like "C:" */
    1208             :         if (isalpha(dir[0]) && dir[1] == ':' && dir[2] == 0) {
    1209             :                 /* nothing more to do */
    1210             :                 return 0;
    1211             :         }
    1212             : #endif
    1213             : 
    1214             :         /*
    1215             :          * Check if the dir already exists using lstat().
    1216             :          *
    1217             :          * Note that in Windows when dealing with read-only media
    1218             :          * you cannot try to create the directory, and expecting
    1219             :          * the EEXIST error because the call will fail with ERROR_WRITE_PROTECTED.
    1220             :          *
    1221             :          * Also in Windows it's better to use lstat() than stat() because it
    1222             :          * doesn't need to open the dir with CreateFile().
    1223             :          */
    1224      807453 :         if (lstat(dir, &st) == 0) {
    1225             :                 /* it already exists */
    1226      807362 :                 return 0;
    1227             :         }
    1228             : 
    1229             :         /* recursively create them all */
    1230          91 :         if (mkancestor(dir) != 0) {
    1231             :                 /* LCOV_EXCL_START */
    1232             :                 return -1;
    1233             :                 /* LCOV_EXCL_STOP */
    1234             :         }
    1235             : 
    1236             :         /* create it */
    1237          91 :         if (mkdir(dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) {
    1238             :                 /* LCOV_EXCL_START */
    1239             :                 log_fatal(errno, "Error creating directory '%s'. %s.\n", dir, strerror(errno));
    1240             :                 return -1;
    1241             :                 /* LCOV_EXCL_STOP */
    1242             :         }
    1243             : 
    1244          91 :         return 0;
    1245             : }
    1246             : 
    1247      132146 : int fmtime(int f, int64_t mtime_sec, int mtime_nsec)
    1248             : {
    1249             : #if HAVE_FUTIMENS
    1250             :         struct timespec tv[2];
    1251             : #else
    1252             :         struct timeval tv[2];
    1253             : #endif
    1254             :         int ret;
    1255             : 
    1256             : #if HAVE_FUTIMENS /* futimens() is preferred because it gives nanosecond precision */
    1257      132146 :         tv[0].tv_sec = mtime_sec;
    1258      132146 :         if (mtime_nsec != STAT_NSEC_INVALID)
    1259      132146 :                 tv[0].tv_nsec = mtime_nsec;
    1260             :         else
    1261           0 :                 tv[0].tv_nsec = 0;
    1262      132146 :         tv[1].tv_sec = tv[0].tv_sec;
    1263      132146 :         tv[1].tv_nsec = tv[0].tv_nsec;
    1264             : 
    1265      132146 :         ret = futimens(f, tv);
    1266             : #elif HAVE_FUTIMES /* fallback to futimes() if nanosecond precision is not available */
    1267             :         tv[0].tv_sec = mtime_sec;
    1268             :         if (mtime_nsec != STAT_NSEC_INVALID)
    1269             :                 tv[0].tv_usec = mtime_nsec / 1000;
    1270             :         else
    1271             :                 tv[0].tv_usec = 0;
    1272             :         tv[1].tv_sec = tv[0].tv_sec;
    1273             :         tv[1].tv_usec = tv[0].tv_usec;
    1274             : 
    1275             :         ret = futimes(f, tv);
    1276             : #elif HAVE_FUTIMESAT /* fallback to futimesat() for Solaris, it only has futimesat() */
    1277             :         tv[0].tv_sec = mtime_sec;
    1278             :         if (mtime_nsec != STAT_NSEC_INVALID)
    1279             :                 tv[0].tv_usec = mtime_nsec / 1000;
    1280             :         else
    1281             :                 tv[0].tv_usec = 0;
    1282             :         tv[1].tv_sec = tv[0].tv_sec;
    1283             :         tv[1].tv_usec = tv[0].tv_usec;
    1284             : 
    1285             :         ret = futimesat(f, 0, tv);
    1286             : #else
    1287             : #error No function available to set file timestamps with sub-second precision
    1288             : #endif
    1289             : 
    1290      132146 :         return ret;
    1291             : }
    1292             : 
    1293       11645 : int lmtime(const char* path, int64_t mtime_sec, int mtime_nsec)
    1294             : {
    1295             : #if HAVE_UTIMENSAT
    1296             :         struct timespec tv[2];
    1297             : #else
    1298             :         struct timeval tv[2];
    1299             : #endif
    1300             :         int ret;
    1301             : 
    1302             : #if HAVE_UTIMENSAT /* utimensat() is preferred because it gives nanosecond precision */
    1303       11645 :         tv[0].tv_sec = mtime_sec;
    1304       11645 :         if (mtime_nsec != STAT_NSEC_INVALID)
    1305       11645 :                 tv[0].tv_nsec = mtime_nsec;
    1306             :         else
    1307           0 :                 tv[0].tv_nsec = 0;
    1308       11645 :         tv[1].tv_sec = tv[0].tv_sec;
    1309       11645 :         tv[1].tv_nsec = tv[0].tv_nsec;
    1310             : 
    1311       11645 :         ret = utimensat(AT_FDCWD, path, tv, AT_SYMLINK_NOFOLLOW);
    1312             : #elif HAVE_LUTIMES /* fallback to lutimes() if nanosecond precision is not available */
    1313             :         tv[0].tv_sec = mtime_sec;
    1314             :         if (mtime_nsec != STAT_NSEC_INVALID)
    1315             :                 tv[0].tv_usec = mtime_nsec / 1000;
    1316             :         else
    1317             :                 tv[0].tv_usec = 0;
    1318             :         tv[1].tv_sec = tv[0].tv_sec;
    1319             :         tv[1].tv_usec = tv[0].tv_usec;
    1320             : 
    1321             :         ret = lutimes(path, tv);
    1322             : #elif HAVE_FUTIMESAT /* fallback to futimesat() for Solaris, it only has futimesat() */
    1323             :         tv[0].tv_sec = mtime_sec;
    1324             :         if (mtime_nsec != STAT_NSEC_INVALID)
    1325             :                 tv[0].tv_usec = mtime_nsec / 1000;
    1326             :         else
    1327             :                 tv[0].tv_usec = 0;
    1328             :         tv[1].tv_sec = tv[0].tv_sec;
    1329             :         tv[1].tv_usec = tv[0].tv_usec;
    1330             : 
    1331             :         ret = futimesat(AT_FDCWD, path, tv);
    1332             : #else
    1333             : #error No function available to set file timestamps with sub-second precision
    1334             : #endif
    1335             : 
    1336       11645 :         return ret;
    1337             : }
    1338             : 
    1339             : /****************************************************************************/
    1340             : /* advise */
    1341             : 
    1342     2625392 : void advise_init(struct advise_struct* advise, int mode)
    1343             : {
    1344     2625392 :         advise->mode = mode;
    1345     2625392 :         advise->dirty_begin = 0;
    1346     2625392 :         advise->dirty_end = 0;
    1347     2625392 : }
    1348             : 
    1349     2625392 : int advise_flags(struct advise_struct* advise)
    1350             : {
    1351     2625392 :         int flags = 0;
    1352             : 
    1353     2625392 :         if (advise->mode == ADVISE_SEQUENTIAL
    1354     2614050 :                 || advise->mode == ADVISE_FLUSH
    1355     2602708 :                 || advise->mode == ADVISE_FLUSH_WINDOW
    1356     2591366 :                 || advise->mode == ADVISE_DISCARD
    1357     2591366 :                 || advise->mode == ADVISE_DISCARD_WINDOW
    1358             :         )
    1359       45368 :                 flags |= O_SEQUENTIAL;
    1360             : 
    1361             : #if HAVE_DIRECT_IO
    1362     2625392 :         if (advise->mode == ADVISE_DIRECT)
    1363       11342 :                 flags |= O_DIRECT;
    1364             : #endif
    1365             : 
    1366     2625392 :         return flags;
    1367             : }
    1368             : 
    1369     2538754 : int advise_open(struct advise_struct* advise, int f)
    1370             : {
    1371             :         (void)advise;
    1372             :         (void)f;
    1373             : 
    1374             : #if HAVE_POSIX_FADVISE
    1375     2538754 :         if (advise->mode == ADVISE_SEQUENTIAL
    1376     2527412 :                 || advise->mode == ADVISE_FLUSH
    1377     2516070 :                 || advise->mode == ADVISE_FLUSH_WINDOW
    1378     2504728 :                 || advise->mode == ADVISE_DISCARD
    1379     2504728 :                 || advise->mode == ADVISE_DISCARD_WINDOW
    1380             :         ) {
    1381             :                 int ret;
    1382             : 
    1383             :                 /*
    1384             :                  * Advise noreuse access, this avoids to pollute the page cache
    1385             :                  * supported from Linux Kernel 6.3 with this commit: https://github.com/torvalds/linux/commit/17e810229cb3068b692fa078bd9b3a6527e0866a
    1386             :                  */
    1387       45368 :                 ret = posix_fadvise_wrapper(f, 0, 0, POSIX_FADV_NOREUSE);
    1388       45368 :                 if (ret == ENOSYS) {
    1389             :                         /* call is not supported */
    1390           0 :                         ret = 0;
    1391             :                 }
    1392       45368 :                 if (ret != 0) {
    1393             :                         /* LCOV_EXCL_START */
    1394             :                         errno = ret; /* posix_fadvise return the error code */
    1395             :                         return -1;
    1396             :                         /* LCOV_EXCL_STOP */
    1397             :                 }
    1398             : 
    1399             :                 /* advise sequential access, this doubles the read-ahead window size */
    1400       45368 :                 ret = posix_fadvise_wrapper(f, 0, 0, POSIX_FADV_SEQUENTIAL);
    1401       45368 :                 if (ret == ENOSYS) {
    1402             :                         /* call is not supported, like in armhf, see posix_fadvise manpage */
    1403           0 :                         ret = 0;
    1404             :                 }
    1405       45368 :                 if (ret != 0) {
    1406             :                         /* LCOV_EXCL_START */
    1407             :                         errno = ret; /* posix_fadvise return the error code */
    1408             :                         return -1;
    1409             :                         /* LCOV_EXCL_STOP */
    1410             :                 }
    1411             :         }
    1412             : #endif
    1413             : 
    1414     2538754 :         return 0;
    1415             : }
    1416             : 
    1417      970106 : int advise_write(struct advise_struct* advise, int f, data_off_t offset, data_off_t size)
    1418             : {
    1419             :         data_off_t flush_offset;
    1420             :         data_off_t flush_size;
    1421             :         data_off_t discard_offset;
    1422             :         data_off_t discard_size;
    1423             : 
    1424             :         (void)f;
    1425             :         (void)flush_offset;
    1426             :         (void)flush_size;
    1427             :         (void)discard_offset;
    1428             :         (void)discard_size;
    1429             : 
    1430      970106 :         flush_offset = 0;
    1431      970106 :         flush_size = 0;
    1432      970106 :         discard_offset = 0;
    1433      970106 :         discard_size = 0;
    1434             : 
    1435             :         /*
    1436             :          * Follow Linus recommendations about fast writes.
    1437             :          *
    1438             :          * Linus "Unexpected splice "always copy" behavior observed"
    1439             :          * http://thread.gmane.org/gmane.linux.kernel/987247/focus=988070
    1440             :          * ---
    1441             :          * I have had _very_ good experiences with even a rather trivial
    1442             :          * file writer that basically used (iirc) 8MB windows, and the logic was very
    1443             :          * trivial:
    1444             :          *
    1445             :          *  - before writing a new 8M window, do "start writeback"
    1446             :          *    (SYNC_FILE_RANGE_WRITE) on the previous window, and do
    1447             :          *    a wait (SYNC_FILE_RANGE_WAIT_AFTER) on the window before that.
    1448             :          *
    1449             :          * in fact, in its simplest form, you can do it like this (this is from my
    1450             :          * "overwrite disk images" program that I use on old disks):
    1451             :          *
    1452             :          * for (index = 0; index < max_index ;index++) {
    1453             :          *   if (write(fd, buffer, BUFSIZE) != BUFSIZE)
    1454             :          *     break;
    1455             :          *   // This won't block, but will start writeout asynchronously
    1456             :          *   sync_file_range(fd, index*BUFSIZE, BUFSIZE, SYNC_FILE_RANGE_WRITE);
    1457             :          *   // This does a blocking write-and-wait on any old ranges
    1458             :          *   if (index)
    1459             :          *     sync_file_range(fd, (index-1)*BUFSIZE, BUFSIZE, SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE|SYNC_FILE_RANGE_WAIT_AFTER);
    1460             :          * }
    1461             :          *
    1462             :          * and even if you don't actually do a discard (maybe we should add a
    1463             :          * SYNC_FILE_RANGE_DISCARD bit, right now you'd need to do a separate
    1464             :          * fadvise(FADV_DONTNEED) to throw it out) the system behavior is pretty
    1465             :          * nice, because the heavy writer gets good IO performance _and_ leaves only
    1466             :          * easy-to-free pages around after itself.
    1467             :          * ---
    1468             :          *
    1469             :          * Linus "Unexpected splice "always copy" behavior observed"
    1470             :          * http://thread.gmane.org/gmane.linux.kernel/987247/focus=988176
    1471             :          * ---
    1472             :          * The behavior for dirty page writeback is _not_ well defined, and
    1473             :          * if you do POSIX_FADV_DONTNEED, I would suggest you do it as part of that
    1474             :          * writeback logic, ie you do it only on ranges that you have just waited on.
    1475             :          *
    1476             :          * IOW, in my example, you'd couple the
    1477             :          *
    1478             :          *   sync_file_range(fd, (index-1)*BUFSIZE, BUFSIZE, SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE|SYNC_FILE_RANGE_WAIT_AFTER);
    1479             :          *
    1480             :          * with a
    1481             :          *
    1482             :          *   posix_fadvise(fd, (index-1)*BUFSIZE, BUFSIZE, POSIX_FADV_DONTNEED);
    1483             :          *
    1484             :          * afterwards to throw out the pages that you just waited for.
    1485             :          * ---
    1486             :          */
    1487             : 
    1488      970106 :         switch (advise->mode) {
    1489        4687 :         case ADVISE_FLUSH :
    1490        4687 :                 flush_offset = offset;
    1491        4687 :                 flush_size = size;
    1492        4687 :                 break;
    1493           0 :         case ADVISE_DISCARD :
    1494           0 :                 discard_offset = offset;
    1495           0 :                 discard_size = size;
    1496           0 :                 break;
    1497        4687 :         case ADVISE_FLUSH_WINDOW :
    1498             :                 /* if the dirty range can be extended */
    1499        4687 :                 if (advise->dirty_end == offset) {
    1500             :                         /* extent the dirty range */
    1501        4687 :                         advise->dirty_end += size;
    1502             : 
    1503             :                         /* if we reached the window size */
    1504        4687 :                         if (advise->dirty_end - advise->dirty_begin >= ADVISE_WINDOW_SIZE) {
    1505             :                                 /* flush the window  */
    1506           0 :                                 flush_offset = advise->dirty_begin;
    1507           0 :                                 flush_size = ADVISE_WINDOW_SIZE;
    1508             : 
    1509             :                                 /* remove it from the dirty range */
    1510           0 :                                 advise->dirty_begin += ADVISE_WINDOW_SIZE;
    1511             :                         }
    1512             :                 } else {
    1513             :                         /* otherwise flush the existing dirty */
    1514           0 :                         flush_offset = advise->dirty_begin;
    1515           0 :                         flush_size = advise->dirty_end - advise->dirty_begin;
    1516             : 
    1517             :                         /* and set the new range as dirty */
    1518           0 :                         advise->dirty_begin = offset;
    1519           0 :                         advise->dirty_end = offset + size;
    1520             :                 }
    1521        4687 :                 break;
    1522        4687 :         case ADVISE_DISCARD_WINDOW :
    1523             :                 /* if the dirty range can be extended */
    1524        4687 :                 if (advise->dirty_end == offset) {
    1525             :                         /* extent the dirty range */
    1526        4687 :                         advise->dirty_end += size;
    1527             : 
    1528             :                         /* if we reached the double window size */
    1529        4687 :                         if (advise->dirty_end - advise->dirty_begin >= 2 * ADVISE_WINDOW_SIZE) {
    1530             :                                 /* discard the first window */
    1531           0 :                                 discard_offset = advise->dirty_begin;
    1532           0 :                                 discard_size = ADVISE_WINDOW_SIZE;
    1533             : 
    1534             :                                 /* remove it from the dirty range */
    1535           0 :                                 advise->dirty_begin += ADVISE_WINDOW_SIZE;
    1536             : 
    1537             :                                 /* flush the second window */
    1538           0 :                                 flush_offset = advise->dirty_begin;
    1539           0 :                                 flush_size = ADVISE_WINDOW_SIZE;
    1540             :                         }
    1541             :                 } else {
    1542             :                         /* otherwise discard the existing dirty */
    1543           0 :                         discard_offset = advise->dirty_begin;
    1544           0 :                         discard_size = advise->dirty_end - advise->dirty_begin;
    1545             : 
    1546             :                         /* and set the new range as dirty */
    1547           0 :                         advise->dirty_begin = offset;
    1548           0 :                         advise->dirty_end = offset + size;
    1549             :                 }
    1550        4687 :                 break;
    1551             :         }
    1552             : 
    1553             : #if HAVE_SYNC_FILE_RANGE
    1554      970106 :         if (flush_size != 0) {
    1555             :                 int ret;
    1556             : 
    1557             :                 /* start writing immediately */
    1558        4687 :                 ret = sync_file_range(f, flush_offset, flush_size, SYNC_FILE_RANGE_WRITE);
    1559        4687 :                 if (ret != 0) {
    1560             :                         /* LCOV_EXCL_START */
    1561             :                         return -1;
    1562             :                         /* LCOV_EXCL_STOP */
    1563             :                 }
    1564             :         }
    1565             : #endif
    1566             : 
    1567             : #if HAVE_SYNC_FILE_RANGE && HAVE_POSIX_FADVISE
    1568      970106 :         if (discard_size != 0) {
    1569             :                 int ret;
    1570             : 
    1571             :                 /* send the data to the disk and wait until it's written */
    1572           0 :                 ret = sync_file_range(f, discard_offset, discard_size, SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER);
    1573           0 :                 if (ret != 0) {
    1574             :                         /* LCOV_EXCL_START */
    1575             :                         return -1;
    1576             :                         /* LCOV_EXCL_STOP */
    1577             :                 }
    1578             : 
    1579             :                 /* flush the data from the cache */
    1580           0 :                 ret = posix_fadvise_wrapper(f, discard_offset, discard_size, POSIX_FADV_DONTNEED);
    1581           0 :                 if (ret == ENOSYS) {
    1582             :                         /* call is not supported */
    1583           0 :                         ret = 0;
    1584             :                 }
    1585           0 :                 if (ret != 0) {
    1586             :                         /* LCOV_EXCL_START */
    1587             :                         errno = ret; /* posix_fadvise return the error code */
    1588             :                         return -1;
    1589             :                         /* LCOV_EXCL_STOP */
    1590             :                 }
    1591             :         }
    1592             : #endif
    1593             : 
    1594      970106 :         return 0;
    1595             : }
    1596             : 
    1597     9186634 : int advise_read(struct advise_struct* advise, int f, data_off_t offset, data_off_t size)
    1598             : {
    1599             :         (void)advise;
    1600             :         (void)f;
    1601             :         (void)offset;
    1602             :         (void)size;
    1603             : 
    1604             : #if HAVE_POSIX_FADVISE
    1605     9186634 :         if (advise->mode == ADVISE_DISCARD
    1606     9186634 :                 || advise->mode == ADVISE_DISCARD_WINDOW
    1607             :         ) {
    1608             :                 int ret;
    1609             : 
    1610             :                 /* flush the data from the cache */
    1611       27825 :                 ret = posix_fadvise_wrapper(f, offset, size, POSIX_FADV_DONTNEED);
    1612       27825 :                 if (ret == ENOSYS) {
    1613             :                         /* call is not supported */
    1614           0 :                         ret = 0;
    1615             :                 }
    1616       27825 :                 if (ret != 0) {
    1617             :                         /* LCOV_EXCL_START */
    1618             :                         errno = ret; /* posix_fadvise return the error code */
    1619             :                         return -1;
    1620             :                         /* LCOV_EXCL_STOP */
    1621             :                 }
    1622             :         }
    1623             : #endif
    1624             : 
    1625             :         /*
    1626             :          * Here we cannot call posix_fadvise(..., POSIX_FADV_WILLNEED) for the next block
    1627             :          * because it may be blocking.
    1628             :          *
    1629             :          * Ted Ts'o "posix_fadvise(POSIX_FADV_WILLNEED) waits before returning?"
    1630             :          * https://lkml.org/lkml/2010/12/6/122
    1631             :          * ---
    1632             :          * readahead and posix_fadvise(POSIX_FADV_WILLNEED) work exactly the same
    1633             :          * way, and in fact share mostly the same code path (see
    1634             :          * force_page_cache_readahead() in mm/readahead.c).
    1635             :          *
    1636             :          * They are asynchronous in that there is no guarantee the pages will be
    1637             :          * in the page cache by the time they return.  But at the same time, they
    1638             :          * are not guaranteed to be non-blocking.  That is, the work of doing the
    1639             :          * readahead does not take place in a kernel thread.  So if you try to
    1640             :          * request I/O than will fit in the request queue, the system call will
    1641             :          * block until some I/O is completed so that more I/O requested cam be
    1642             :          * loaded onto the request queue.
    1643             :          *
    1644             :          * The only way to fix this would be to either put the work on a kernel
    1645             :          * thread (i.e., some kind of workqueue) or in a userspace thread.  For
    1646             :          * ion programmer wondering what to do today, I'd suggest the
    1647             :          * latter since it will be more portable across various kernel versions.
    1648             :          *
    1649             :          * This does leave the question about whether we should change the kernel
    1650             :          * to allow readahead() and posix_fadvise(POSIX_FADV_WILLNEED) to be
    1651             :          * non-blocking and do this work in a workqueue (or via some kind of
    1652             :          * callback/continuation scheme).  My worry is just doing this if a user
    1653             :          * application does something crazy, like request gigabytes and gigabytes
    1654             :          * of readahead, and then repented of their craziness, there should be a
    1655             :          * way of cancelling the readahead request.  Today, the user can just
    1656             :          * kill the application.  But if we simply shove the work to a kernel
    1657             :          * thread, it becomes a lot harder to cancel the readahead request.  We'd
    1658             :          * have to invent a new API, and then have a way to know whether the user
    1659             :          * has access to kill a particular readahead request, etc.
    1660             :          * ---
    1661             :          */
    1662             : 
    1663     9186634 :         return 0;
    1664             : }
    1665             : 
    1666             : /****************************************************************************/
    1667             : /* memory */
    1668             : 
    1669             : /**
    1670             :  * Total amount of memory allocated.
    1671             :  */
    1672             : static size_t mcounter;
    1673             : 
    1674         550 : size_t malloc_counter_get(void)
    1675             : {
    1676             :         size_t ret;
    1677             : 
    1678         550 :         lock_memory();
    1679             : 
    1680         550 :         ret = mcounter;
    1681             : 
    1682         550 :         unlock_memory();
    1683             : 
    1684         550 :         return ret;
    1685             : }
    1686             : 
    1687    22102834 : void malloc_counter_inc(size_t inc)
    1688             : {
    1689    22102834 :         lock_memory();
    1690             : 
    1691    22102834 :         mcounter += inc;
    1692             : 
    1693    22102834 :         unlock_memory();
    1694    22102834 : }
    1695             : 
    1696             : /* LCOV_EXCL_START */
    1697             : static ssize_t malloc_print(int f, const char* str)
    1698             : {
    1699             :         ssize_t len = 0;
    1700             : 
    1701             :         while (str[len])
    1702             :                 ++len;
    1703             :         return write(f, str, len);
    1704             : }
    1705             : /* LCOV_EXCL_STOP */
    1706             : 
    1707             : /* LCOV_EXCL_START */
    1708             : static ssize_t malloc_printn(int f, size_t value)
    1709             : {
    1710             :         char buf[32];
    1711             :         int i;
    1712             : 
    1713             :         if (!value)
    1714             :                 return write(f, "0", 1);
    1715             : 
    1716             :         i = sizeof(buf);
    1717             :         while (value) {
    1718             :                 buf[--i] = (value % 10) + '0';
    1719             :                 value /= 10;
    1720             :         }
    1721             : 
    1722             :         return write(f, buf + i, sizeof(buf) - i);
    1723             : }
    1724             : /* LCOV_EXCL_STOP */
    1725             : 
    1726             : /* LCOV_EXCL_START */
    1727             : void malloc_fail(size_t size)
    1728             : {
    1729             :         /* don't use printf to avoid any possible extra allocation */
    1730             :         int f = 2; /* stderr */
    1731             : 
    1732             :         malloc_print(f, "Failed for Low Memory!\n");
    1733             :         malloc_print(f, "Allocating ");
    1734             :         malloc_printn(f, size);
    1735             :         malloc_print(f, " bytes.\n");
    1736             :         malloc_print(f, "Already allocated ");
    1737             :         malloc_printn(f, malloc_counter_get());
    1738             :         malloc_print(f, " bytes.\n");
    1739             :         if (sizeof(void*) == 4) {
    1740             :                 malloc_print(f, "You are currently using a 32 bits executable.\n");
    1741             :                 malloc_print(f, "If you have more than 4GB of memory, please upgrade to a 64 bits one.\n");
    1742             :         }
    1743             : }
    1744             : /* LCOV_EXCL_STOP */
    1745             : 
    1746    15651066 : void* malloc_nofail(size_t size)
    1747             : {
    1748    15651066 :         void* ptr = malloc(size);
    1749             : 
    1750    15651066 :         if (!ptr) {
    1751             :                 /* LCOV_EXCL_START */
    1752             :                 malloc_fail(size);
    1753             :                 exit(EXIT_FAILURE);
    1754             :                 /* LCOV_EXCL_STOP */
    1755             :         }
    1756             : 
    1757             : #ifndef CHECKER /* Don't preinitialize when running for valgrind */
    1758             :         /*
    1759             :          * Here we preinitialize the memory to ensure that the OS is really allocating it
    1760             :          * and not only reserving the addressable space.
    1761             :          * Otherwise we are risking that the OOM (Out Of Memory) killer in Linux will kill the process.
    1762             :          * Filling the memory doesn't ensure to disable OOM, but it increase a lot the chances to
    1763             :          * get a real error from malloc() instead than a process killed.
    1764             :          * Note that calloc() doesn't have the same effect.
    1765             :          */
    1766    15651066 :         memset(ptr, 0xA5, size);
    1767             : #endif
    1768             : 
    1769    15651066 :         malloc_counter_inc(size);
    1770             : 
    1771    15651066 :         return ptr;
    1772             : }
    1773             : 
    1774      157611 : void* calloc_nofail(size_t count, size_t size)
    1775             : {
    1776             :         void* ptr;
    1777             : 
    1778      157611 :         size *= count;
    1779             : 
    1780             :         /* see the note in malloc_nofail() of why we don't use calloc() */
    1781      157611 :         ptr = malloc(size);
    1782             : 
    1783      157611 :         if (!ptr) {
    1784             :                 /* LCOV_EXCL_START */
    1785             :                 malloc_fail(size);
    1786             :                 exit(EXIT_FAILURE);
    1787             :                 /* LCOV_EXCL_STOP */
    1788             :         }
    1789             : 
    1790      157611 :         memset(ptr, 0, size);
    1791             : 
    1792      157611 :         malloc_counter_inc(size);
    1793             : 
    1794      157611 :         return ptr;
    1795             : }
    1796             : 
    1797     6294157 : char* strdup_nofail(const char* str)
    1798             : {
    1799             :         size_t size;
    1800             :         char* ptr;
    1801             : 
    1802     6294157 :         size = strlen(str) + 1;
    1803             : 
    1804     6294157 :         ptr = malloc(size);
    1805             : 
    1806     6294157 :         if (!ptr) {
    1807             :                 /* LCOV_EXCL_START */
    1808             :                 malloc_fail(size);
    1809             :                 exit(EXIT_FAILURE);
    1810             :                 /* LCOV_EXCL_STOP */
    1811             :         }
    1812             : 
    1813     6294157 :         memcpy(ptr, str, size);
    1814             : 
    1815     6294157 :         malloc_counter_inc(size);
    1816             : 
    1817     6294157 :         return ptr;
    1818             : }
    1819             : 
    1820             : /****************************************************************************/
    1821             : /* smartctl */
    1822             : 
    1823             : /**
    1824             :  * Match a string with the specified pattern.
    1825             :  * Like sscanf() a space match any sequence of spaces.
    1826             :  * Return 0 if it matches.
    1827             :  */
    1828       27794 : static int smatch(const char* str, const char* pattern)
    1829             : {
    1830       32012 :         while (*pattern) {
    1831       31841 :                 if (isspace(*pattern)) {
    1832         417 :                         ++pattern;
    1833         960 :                         while (isspace(*str))
    1834         543 :                                 ++str;
    1835       31424 :                 } else if (*pattern == *str) {
    1836        3801 :                         ++pattern;
    1837        3801 :                         ++str;
    1838             :                 } else
    1839       27623 :                         return -1;
    1840             :         }
    1841             : 
    1842         171 :         return 0;
    1843             : }
    1844             : 
    1845         249 : int is_sep(char c)
    1846             : {
    1847             :         return c == '.'
    1848         249 :                || c == ','
    1849          84 :                || c == '\''
    1850          84 :                || c == ' ' /* 0xa0 converted by space for no code page */
    1851         498 :                || c == (char)0xa0;  /* french */
    1852             : }
    1853             : 
    1854             : /*
    1855             :  * snumber()
    1856             :  *
    1857             :  * Matches a literal prefix (the second argument), then parses the following
    1858             :  * unsigned 64-bit integer, allowing thousand separators.
    1859             :  *
    1860             :  * Whitespace is flexible around the prefix and before the number.
    1861             :  * Commas are ignored during number parsing but basic validation is applied.
    1862             :  *
    1863             :  * Returns:
    1864             :  *   1 - successfully parsed a valid uint64_t
    1865             :  *   0 - prefix mismatch, no digits found, invalid format or overflow
    1866             :  */
    1867       44371 : int snumber(const char* str, const char* prefix, uint64_t* value)
    1868             : {
    1869       44371 :         const char* s = str;
    1870       44371 :         const char* f = prefix;
    1871             : 
    1872             :         /* match literal prefix, tolerating whitespace differences */
    1873       45731 :         while (*f) {
    1874             :                 /* skip whitespace in both strings */
    1875       45805 :                 while (isspace((unsigned char)*f))
    1876         116 :                         ++f;
    1877       45805 :                 while (isspace((unsigned char)*s))
    1878         116 :                         ++s;
    1879             : 
    1880       45689 :                 if (*f == 0)
    1881           0 :                         break;
    1882       45689 :                 if (*s != *f)
    1883       44329 :                         return 0; /* prefix mismatch */
    1884             : 
    1885        1360 :                 ++s;
    1886        1360 :                 ++f;
    1887             :         }
    1888             : 
    1889             :         /* skip whitespace before the number starts */
    1890         210 :         while (isspace((unsigned char)*s))
    1891         168 :                 ++s;
    1892             : 
    1893          42 :         uint64_t num = 0;
    1894          42 :         int digits_seen = 0;
    1895          42 :         int sep_seen = 0;
    1896             : 
    1897             :         /* parse digits, ignoring commas */
    1898         792 :         while (*s) {
    1899         792 :                 if (*s >= '0' && *s <= '9') {
    1900         543 :                         unsigned digit = (unsigned)(*s - '0');
    1901             : 
    1902             :                         /* check for uint64_t overflow before multiplying */
    1903         543 :                         if (num > (UINT64_MAX - digit) / 10ULL)
    1904           0 :                                 return 0;
    1905             : 
    1906         543 :                         num = num * 10ULL + digit;
    1907         543 :                         ++digits_seen;
    1908         543 :                         sep_seen = 0;
    1909         249 :                 } else if (is_sep(*s)) {
    1910             :                         /* disallow leading separators */
    1911         207 :                         if (digits_seen == 0)
    1912           0 :                                 return 0;
    1913             : 
    1914             :                         /* if two consecutive separators, stop parsing */
    1915         207 :                         if (sep_seen)
    1916           0 :                                 break;
    1917             : 
    1918         207 :                         sep_seen = 1;
    1919             :                 } else {
    1920             :                         /* any other character ends the number */
    1921          42 :                         break;
    1922             :                 }
    1923             : 
    1924         750 :                 ++s;
    1925             :         }
    1926             : 
    1927             :         /* require at least one digit */
    1928          42 :         if (digits_seen == 0)
    1929           0 :                 return 0;
    1930             : 
    1931          42 :         *value = num;
    1932          42 :         return 1;
    1933             : }
    1934             : 
    1935          66 : int smartctl_attribute(FILE* f, const char* file, const char* name, struct smart_attr* smart, uint64_t* info, char* serial, char* family, char* model, char* inter)
    1936             : {
    1937             :         unsigned i;
    1938             :         int inside;
    1939             :         struct smart_attr dummy_smart[SMART_COUNT];
    1940             :         uint64_t dummy_info[INFO_COUNT];
    1941             :         char dummy_serial[SMART_MAX];
    1942             :         char dummy_family[SMART_MAX];
    1943             :         char dummy_model[SMART_MAX];
    1944             :         char dummy_interface[SMART_MAX];
    1945             : 
    1946             :         /* dummy attributes */
    1947          66 :         if (!smart)
    1948          12 :                 smart = dummy_smart;
    1949          66 :         if (!info)
    1950          12 :                 info = dummy_info;
    1951          66 :         if (!serial)
    1952          12 :                 serial = dummy_serial;
    1953          66 :         if (!family)
    1954          12 :                 family = dummy_family;
    1955          66 :         if (!model)
    1956          12 :                 model = dummy_model;
    1957          66 :         if (!inter)
    1958          12 :                 inter = dummy_interface;
    1959             : 
    1960             :         /* preclear attributes */
    1961          66 :         *serial = 0;
    1962          66 :         *family = 0;
    1963          66 :         *model = 0;
    1964          66 :         *inter = 0;
    1965          66 :         memset(smart, 0, sizeof(struct smart_attr) * SMART_COUNT);
    1966       17226 :         for (i = 0; i < SMART_COUNT; ++i)
    1967       17160 :                 smart[i].raw = SMART_UNASSIGNED;
    1968         198 :         for (i = 0; i < INFO_COUNT; ++i)
    1969         132 :                 info[i] = SMART_UNASSIGNED;
    1970             : 
    1971             :         /* read the file */
    1972          66 :         inside = 0;
    1973        4081 :         while (1) {
    1974             :                 char buf[256];
    1975             :                 unsigned id;
    1976             :                 uint64_t raw;
    1977             :                 uint64_t norm;
    1978             :                 uint64_t worst;
    1979             :                 uint64_t thresh;
    1980             :                 char* s;
    1981             : 
    1982        4147 :                 s = fgets(buf, sizeof(buf), f);
    1983        4147 :                 if (s == 0)
    1984          66 :                         break;
    1985             : 
    1986             :                 /* remove extraneous chars */
    1987        4081 :                 s = strpolish(buf);
    1988             : 
    1989        4081 :                 log_tag("smartctl:%s:%s:out: %s\n", file, name, s);
    1990             : 
    1991             :                 /* skip initial spaces */
    1992        9664 :                 while (isspace(*s))
    1993        5583 :                         ++s;
    1994             : 
    1995        4081 :                 if (*s == 0) {
    1996         360 :                         inside = 0;
    1997             :                         /* common */
    1998        3721 :                 } else if (smatch(s, "Rotation Rate: Solid State") == 0) {
    1999           3 :                         info[INFO_ROTATION_RATE] = 0;
    2000        3718 :                 } else if (sscanf(s, "Rotation Rate: %" SCNu64, &info[INFO_ROTATION_RATE]) == 1) {
    2001        3679 :                 } else if (snumber(s, "User Capacity:", &raw) == 1) {
    2002          42 :                         info[INFO_SIZE] = raw;
    2003        3637 :                 } else if (sscanf(s, "Model Family: %63[^\n]", family) == 1) {
    2004          36 :                         strtrim(family);
    2005        3601 :                 } else if (sscanf(s, "Device Model: %63[^\n]", model) == 1) {
    2006          42 :                         strtrim(model);
    2007        3559 :                 } else if (sscanf(s, "Serial number: %63s", serial) == 1) { /* SCSI */
    2008           0 :                         strtrim(serial);
    2009        3559 :                 } else if (sscanf(s, "Serial Number: %63s", serial) == 1) { /*ATA / NVMe */
    2010          42 :                         strtrim(serial);
    2011             : /* SCSI */
    2012             : /*
    2013             :    Vendor:               WD
    2014             :    Product:              WD4001FYYG-01SL3
    2015             :    Revision:             VR08
    2016             :    Compliance:           SPC-4
    2017             :    User Capacity:        4,000,787,030,016 bytes [4.00 TB]
    2018             :    Logical block size:   512 bytes
    2019             :    Rotation Rate:        7200 rpm
    2020             :    Form Factor:          3.5 inches
    2021             :    Logical Unit id:      0x50000c0f01f55dd0
    2022             :    Serial number:        WMC1F0D41KD5
    2023             :    Device type:          disk
    2024             :    Transport protocol:   SAS (SPL-3)
    2025             :    Local Time is:        Fri Jan 28 14:14:51 2022 CET
    2026             :    SMART support is:     Available - device has SMART capability.
    2027             :    SMART support is:     Enabled
    2028             :    Temperature Warning:  Enabled
    2029             : 
    2030             :    Current Drive Temperature:    38 C
    2031             :    Drive Trip Temperature:              65 C
    2032             : 
    2033             :    Manufactured in week 25 of year 2012
    2034             :    Specified cycle count over device lifetime:  10000
    2035             :    Accumulated start-stop cycles:  103
    2036             :    Specified load-unload count over device lifetime:  300000
    2037             :    Accumulated load-unload cycles:  103
    2038             :    Elements in grown defect list: 5
    2039             : 
    2040             :    Vendor (Seagate) cache information
    2041             :    Blocks sent to initiator = 569127595
    2042             :    Blocks received from initiator = 2633690060
    2043             :    Blocks read from cache and sent to initiator = 1881027254
    2044             :    Number of read and write commands whose size <= segment size = 181284019
    2045             :    Number of read and write commands whose size > segment size = 0
    2046             : 
    2047             :    Vendor (Seagate/Hitachi) factory information
    2048             :    number of hours powered up = 25024.80
    2049             :    number of minutes until next internal SMART test = 42
    2050             : 
    2051             :  */
    2052        3517 :                 } else if (sscanf(s, "Transport protocol: %63[^\n]", inter) == 1) {
    2053           0 :                         if (strcmp(inter, "Fibre channel (FCP-4)") == 0)
    2054           0 :                                 strcpy(inter, "Fibre");
    2055           0 :                         else if (strcmp(inter, "SSA") == 0)
    2056           0 :                                 strcpy(inter, "SSA");
    2057           0 :                         else if (strcmp(inter, "IEEE 1394 (SBP-3)") == 0)
    2058           0 :                                 strcpy(inter, "FireWire");
    2059           0 :                         else if (strcmp(inter, "RDMA (SRP)") == 0)
    2060           0 :                                 strcpy(inter, "SCSI");
    2061           0 :                         else if (strcmp(inter, "iSCSI") == 0)
    2062           0 :                                 strcpy(inter, "iSCSI");
    2063           0 :                         else if (strcmp(inter, "SAS (SPL-4)") == 0)
    2064           0 :                                 strcpy(inter, "SAS");
    2065           0 :                         else if (strcmp(inter, "ADT") == 0)
    2066           0 :                                 strcpy(inter, "SCSI");
    2067           0 :                         else if (strcmp(inter, "ATA (ACS-2)") == 0)
    2068           0 :                                 strcpy(inter, "SATA");
    2069           0 :                         else if (strcmp(inter, "UAS") == 0)
    2070           0 :                                 strcpy(inter, "USB");
    2071           0 :                         else if (strcmp(inter, "SOP") == 0)
    2072           0 :                                 strcpy(inter, "NVMe");
    2073           0 :                         else if (strcmp(inter, "PCIe") == 0)
    2074           0 :                                 strcpy(inter, "PCIe");
    2075             :                         else
    2076           0 :                                 strcpy(inter, "SCSI");
    2077             :                         /* map generic attributes to SMART attr */
    2078        3517 :                 } else if (sscanf(s, "Elements in grown defect list: %" SCNu64, &smart[SMART_REALLOCATED_SECTOR_COUNT].raw) == 1) {
    2079           0 :                         pathcpy(smart[SMART_REALLOCATED_SECTOR_COUNT].name, sizeof(smart[SMART_REALLOCATED_SECTOR_COUNT].name), "Elements_In_Grown_Defect_List");
    2080        3517 :                 } else if (sscanf(s, "Current Drive Temperature: %" SCNu64, &smart[SMART_TEMPERATURE_CELSIUS].raw) == 1) {
    2081           0 :                         pathcpy(smart[SMART_TEMPERATURE_CELSIUS].name, sizeof(smart[SMART_TEMPERATURE_CELSIUS].name), "Current_Drive_Temperature");
    2082        3517 :                 } else if (sscanf(s, "Drive Trip Temperature: %" SCNu64, &smart[SMART_AIRFLOW_TEMPERATURE_CELSIUS].raw) == 1) {
    2083           0 :                         pathcpy(smart[SMART_AIRFLOW_TEMPERATURE_CELSIUS].name, sizeof(smart[SMART_AIRFLOW_TEMPERATURE_CELSIUS].name), "Drive_Trip_Temperature");
    2084        3517 :                 } else if (sscanf(s, "Accumulated start-stop cycles: %" SCNu64, &smart[SMART_START_STOP_COUNT].raw) == 1) {
    2085           0 :                         pathcpy(smart[SMART_START_STOP_COUNT].name, sizeof(smart[SMART_START_STOP_COUNT].name), "Accumulated_Start-Stop_Cycles");
    2086        3517 :                 } else if (sscanf(s, "Accumulated load-unload cycles: %" SCNu64, &smart[SMART_LOAD_CYCLE_COUNT].raw) == 1) {
    2087           0 :                         pathcpy(smart[SMART_LOAD_CYCLE_COUNT].name, sizeof(smart[SMART_LOAD_CYCLE_COUNT].name), "Accumulated_Load-Unload_Cycles");
    2088        3517 :                 } else if (sscanf(s, "  number of hours powered up = %" SCNu64, &smart[SMART_POWER_ON_HOURS].raw) == 1) { /* note "n" of "number" lower case */
    2089           0 :                         pathcpy(smart[SMART_POWER_ON_HOURS].name, sizeof(smart[SMART_POWER_ON_HOURS].name), "Number_Of_Hours_Powered_Up");
    2090             :                         /* special entries */
    2091        3517 :                 } else if (sscanf(s, "Non-medium error count: %" SCNu64, &raw) == 1) {
    2092           0 :                         smart[SMART_ERROR_PROTOCOL].raw = raw;
    2093             : /* ATA */
    2094             : /*
    2095             :    ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE
    2096             :    1 Raw_Read_Error_Rate     0x002f   200   200   051    Pre-fail  Always       -       0
    2097             :    3 Spin_Up_Time            0x0027   172   169   021    Pre-fail  Always       -       8383
    2098             :    4 Start_Stop_Count        0x0032   100   100   000    Old_age   Always       -       24
    2099             :    5 Reallocated_Sector_Ct   0x0033   200   200   140    Pre-fail  Always       -       0
    2100             :    7 Seek_Error_Rate         0x002e   200   200   000    Old_age   Always       -       0
    2101             :    9 Power_On_Hours          0x0032   083   083   000    Old_age   Always       -       12471
    2102             :    10 Spin_Retry_Count        0x0032   100   253   000    Old_age   Always       -       0
    2103             :    11 Calibration_Retry_Count 0x0032   100   253   000    Old_age   Always       -       0
    2104             :    12 Power_Cycle_Count       0x0032   100   100   000    Old_age   Always       -       24
    2105             :    192 Power-Off_Retract_Count 0x0032   200   200   000    Old_age   Always       -       9
    2106             :    193 Load_Cycle_Count        0x0032   157   157   000    Old_age   Always       -       131565
    2107             :    194 Temperature_Celsius     0x0022   127   119   000    Old_age   Always       -       25
    2108             :    196 Reallocated_Event_Count 0x0032   200   200   000    Old_age   Always       -       0
    2109             :    197 Current_Pending_Sector  0x0032   200   200   000    Old_age   Always       -       0
    2110             :    198 Offline_Uncorrectable   0x0030   100   253   000    Old_age   Offline      -       0
    2111             :    199 UDMA_CRC_Error_Count    0x0032   200   200   000    Old_age   Always       -       0
    2112             :    200 Multi_Zone_Error_Rate   0x0008   200   200   000    Old_age   Offline      -       0
    2113             :  */
    2114        3517 :                 } else if (smatch(s, "ATA Version is:") == 0) {
    2115          42 :                         strcpy(inter, "ATA");
    2116        3475 :                 } else if (smatch(s, "SATA Version is:") == 0) {
    2117          42 :                         strcpy(inter, "SATA");
    2118        3433 :                 } else if (smatch(s, "Transport Type:   PCIe") == 0) {
    2119           0 :                         strcpy(inter, "PCIe");
    2120        3433 :                 } else if (smatch(s, "Transport Type:   Parallel") == 0) {
    2121           0 :                         strcpy(inter, "PATA");
    2122             :                         /* special entries */
    2123        3433 :                 } else if (smatch(s, "No Errors Logged") == 0) {
    2124          42 :                         smart[SMART_ERROR_PROTOCOL].raw = 0;
    2125        3391 :                 } else if (sscanf(s, "ATA Error Count: %" SCNu64, &raw) == 1) {
    2126           0 :                         smart[SMART_ERROR_PROTOCOL].raw = raw;
    2127             : /* NVME */
    2128             : /*
    2129             :    Critical Warning:                   0x00
    2130             :    Temperature:                        55 Celsius
    2131             :    Available Spare:                    100%
    2132             :    Available Spare Threshold:          10%
    2133             :    Percentage Used:                    8%
    2134             :    Data Units Read:                    213,006,510 [109 TB]
    2135             :    Data Units Written:                 549,370,112 [281 TB]
    2136             :    Host Read Commands:                 11,210,192,197
    2137             :    Host Write Commands:                20,687,602,229
    2138             :    Controller Busy Time:               14,055
    2139             :    Power Cycles:                       39
    2140             :    Power On Hours:                     4,204
    2141             :    Unsafe Shutdowns:                   9
    2142             :    Media and Data Integrity Errors:    0
    2143             :    Error Information Log Entries:      1,479,242
    2144             :    Warning  Comp. Temperature Time:    0
    2145             :    Critical Comp. Temperature Time:    0
    2146             :    Temperature Sensor 2:               75 Celsius
    2147             :    Thermal Temp. 1 Total Time:         58745
    2148             :  */
    2149        3391 :                 } else if (smatch(s, "NVMe Version:") == 0) {
    2150           0 :                         strcpy(inter, "NVMe");
    2151             :                         /* special entries */
    2152        3391 :                 } else if (snumber(s, "Media and Data Integrity Errors:", &raw) == 1) {
    2153           0 :                         smart[SMART_ERROR_MEDIUM].raw = raw;
    2154        3391 :                 } else if (snumber(s, "Error Information Log Entries:", &raw) == 1) {
    2155           0 :                         smart[SMART_ERROR_PROTOCOL].raw = raw;
    2156        3391 :                 } else if (sscanf(s, "Percentage Used: %" SCNu64 "%%", &raw) == 1) {
    2157           0 :                         smart[SMART_WEAR_LEVEL].raw = raw; /* take care that it can be greather than 100%, meaning that it's used over the expected lifetime */
    2158             :                         /* map generic attributes to SMART attr */
    2159        3391 :                 } else if (sscanf(s, "Temperature: %" SCNu64, &smart[SMART_TEMPERATURE_CELSIUS].raw) == 1) {
    2160           0 :                         pathcpy(smart[SMART_TEMPERATURE_CELSIUS].name, sizeof(smart[SMART_TEMPERATURE_CELSIUS].name), "Temperature");
    2161        3391 :                 } else if (snumber(s, "Power On Hours:", &smart[SMART_POWER_ON_HOURS].raw) == 1) {
    2162           0 :                         pathcpy(smart[SMART_POWER_ON_HOURS].name, sizeof(smart[SMART_POWER_ON_HOURS].name), "Power_On_Hours");
    2163        3391 :                 } else if (snumber(s, "Power Cycles:", &smart[SMART_POWER_CYCLE_COUNT].raw) == 1) {
    2164           0 :                         pathcpy(smart[SMART_POWER_CYCLE_COUNT].name, sizeof(smart[SMART_POWER_CYCLE_COUNT].name), "Power_Cycles");
    2165             :                         /* map specific attributes to UNUSED SMART attr */
    2166        3391 :                 } else if (sscanf(s, "Critical Warning: %" SCNx64, &smart[SMART_NVME_CRITICAL_WARNING].raw) == 1) {
    2167           0 :                         pathcpy(smart[SMART_NVME_CRITICAL_WARNING].name, sizeof(smart[SMART_NVME_CRITICAL_WARNING].name), "Critical_Warning");
    2168        3391 :                 } else if (sscanf(s, "Available Spare: %" SCNu64 "%%", &smart[SMART_NVME_AVAILABLE_SPARE].raw) == 1) {
    2169           0 :                         pathcpy(smart[SMART_NVME_AVAILABLE_SPARE].name, sizeof(smart[SMART_NVME_AVAILABLE_SPARE].name), "Available_Spare");
    2170        3391 :                 } else if (snumber(s, "Data Units Read:", &smart[SMART_NVME_DATA_UNITS_READ].raw) == 1) {
    2171           0 :                         pathcpy(smart[SMART_NVME_DATA_UNITS_READ].name, sizeof(smart[SMART_NVME_DATA_UNITS_READ].name), "Data_Units_Read");
    2172        3391 :                 } else if (snumber(s, "Data Units Written:", &smart[SMART_NVME_DATA_UNITS_WRITTEN].raw) == 1) {
    2173           0 :                         pathcpy(smart[SMART_NVME_DATA_UNITS_WRITTEN].name, sizeof(smart[SMART_NVME_DATA_UNITS_WRITTEN].name), "Data_Units_Written");
    2174        3391 :                 } else if (snumber(s, "Host Read Commands:", &smart[SMART_NVME_HOST_READ_COMMANDS].raw) == 1) {
    2175           0 :                         pathcpy(smart[SMART_NVME_HOST_READ_COMMANDS].name, sizeof(smart[SMART_NVME_HOST_READ_COMMANDS].name), "Host_Read_Commands");
    2176        3391 :                 } else if (snumber(s, "Host Write Commands:", &smart[SMART_NVME_HOST_WRITE_COMMANDS].raw) == 1) {
    2177           0 :                         pathcpy(smart[SMART_NVME_HOST_WRITE_COMMANDS].name, sizeof(smart[SMART_NVME_HOST_WRITE_COMMANDS].name), "Host_Write_Commands");
    2178        3391 :                 } else if (snumber(s, "Controller Busy Time:", &smart[SMART_NVME_CONTROLLER_BUSY_TIME].raw) == 1) {
    2179           0 :                         pathcpy(smart[SMART_NVME_CONTROLLER_BUSY_TIME].name, sizeof(smart[SMART_NVME_CONTROLLER_BUSY_TIME].name), "Controller_Busy_Time");
    2180        3391 :                 } else if (snumber(s, "Unsafe Shutdowns:", &smart[SMART_NVME_UNSAFE_SHUTDOWNS].raw) == 1) {
    2181           0 :                         pathcpy(smart[SMART_NVME_UNSAFE_SHUTDOWNS].name, sizeof(smart[SMART_NVME_UNSAFE_SHUTDOWNS].name), "Unsafe_Shutdowns");
    2182             :                         /* smartctl doesn't print this with command, but %d */
    2183        3391 :                 } else if (snumber(s, "Warning  Comp. Temperature Time:", &smart[SMART_NVME_WARNING_COMP_TEMPERATURE_TIME].raw) == 1) {
    2184           0 :                         pathcpy(smart[SMART_NVME_WARNING_COMP_TEMPERATURE_TIME].name, sizeof(smart[SMART_NVME_WARNING_COMP_TEMPERATURE_TIME].name), "Warning_Comp_Temperature_Time");
    2185             :                         /* smartctl doesn't print this with command, but %d */
    2186        3391 :                 } else if (snumber(s, "Critical Comp. Temperature Time:", &smart[SMART_NVME_CRITICAL_COMP_TEMPERATURE_TIME].raw) == 1) {
    2187           0 :                         pathcpy(smart[SMART_NVME_CRITICAL_COMP_TEMPERATURE_TIME].name, sizeof(smart[SMART_NVME_CRITICAL_COMP_TEMPERATURE_TIME].name), "Critical_Comp_Temperature_Time");
    2188             :                         /* ATA Attributes table */
    2189        3391 :                 } else if (smatch(s, "ID#") == 0) {
    2190          42 :                         inside = 1;
    2191        3349 :                 } else if (inside) {
    2192         759 :                         char id_name[128] = { 0 };
    2193         759 :                         char type[64] = { 0 };
    2194         759 :                         char updated[64] = { 0 };
    2195         759 :                         char when_failed[64] = { 0 };
    2196             :                         uint64_t min, max, avg;
    2197         759 :                         int format_minmax = 0;
    2198         759 :                         int format_avg = 0;
    2199             :                         int flags;
    2200             : 
    2201             :                         /* 194 Temperature_Celsius     0x0002   240   240   000    Old_age   Always       -       27 (Min/Max 16/60) */
    2202         759 :                         if (sscanf(s, "%u %127s %*s %" SCNu64 " %" SCNu64 " %" SCNu64 " %63s %63s %63s %" SCNu64 " (Min/Max %" SCNu64 "/%" SCNu64 ")", &id, id_name, &norm, &worst, &thresh, type, updated, when_failed, &raw, &min, &max) == 11) {
    2203          36 :                                 format_minmax = 1;
    2204             :                                 /*  3 Spin_Up_Time            0x0007   149   149   024    Pre-fail  Always       -       442 (Average 441) */
    2205         723 :                         } else if (sscanf(s, "%u %127s %*s %" SCNu64 " %" SCNu64 " %" SCNu64 " %63s %63s %63s %" SCNu64 " (Average %" SCNu64 ")", &id, id_name, &norm, &worst, &thresh, type, updated, when_failed, &raw, &avg) == 10) {
    2206          30 :                                 format_avg = 1;
    2207         693 :                         } else if (sscanf(s, "%u %127s %*s %" SCNu64 " %" SCNu64 " %" SCNu64 " %63s %63s %63s %" SCNu64, &id, id_name, &norm, &worst, &thresh, type, updated, when_failed, &raw) == 9) {
    2208             :                         } else {
    2209           0 :                                 log_fatal(EEXTERNAL, "Invalid smartctl line '%s'.\n", s);
    2210           0 :                                 return -1;
    2211             :                         }
    2212             : 
    2213         759 :                         if (id >= 256) {
    2214             :                                 /* LCOV_EXCL_START */
    2215             :                                 log_fatal(EEXTERNAL, "Invalid SMART id '%u'.\n", id);
    2216             :                                 return -1;
    2217             :                                 /* LCOV_EXCL_STOP */
    2218             :                         }
    2219             : 
    2220         759 :                         flags = 0;
    2221         759 :                         if (strcmp(type, "Pre-fail") == 0)
    2222         189 :                                 flags |= SMART_ATTR_TYPE_PREFAIL;
    2223         570 :                         else if (strcmp(type, "Old_age") == 0)
    2224         570 :                                 flags |= SMART_ATTR_TYPE_OLDAGE;
    2225         759 :                         if (strcmp(updated, "Always") == 0)
    2226         639 :                                 flags |= SMART_ATTR_UPDATE_ALWAYS;
    2227         120 :                         else if (strcmp(updated, "Offline") == 0)
    2228         120 :                                 flags |= SMART_ATTR_UPDATE_OFFLINE;
    2229         759 :                         if (strcmp(when_failed, "FAILING_NOW") == 0)
    2230           0 :                                 flags |= SMART_ATTR_WHEN_FAILED_NOW;
    2231         759 :                         else if (strcmp(when_failed, "In_the_past") == 0)
    2232           6 :                                 flags |= SMART_ATTR_WHEN_FAILED_PAST;
    2233         753 :                         else if (strcmp(when_failed, "-") == 0)
    2234         753 :                                 flags |= SMART_ATTR_WHEN_FAILED_NEVER;
    2235             : 
    2236             :                         /* revert the min/max decoding done by smartctl */
    2237         759 :                         if (format_minmax
    2238          36 :                                 && raw <= 0xFFFFUL
    2239          36 :                                 && min <= 0xFFFFUL
    2240          36 :                                 && max <= 0xFFFFUL) {
    2241          36 :                                 raw |= min << 16;
    2242          36 :                                 raw |= max << 32;
    2243             :                         }
    2244             : 
    2245             :                         /* revert the avg decoding done by smartctl */
    2246         759 :                         if (format_avg
    2247          30 :                                 && raw <= 0xFFFFUL
    2248          30 :                                 && avg <= 0xFFFFUL) {
    2249          30 :                                 raw |= avg << 16;
    2250             :                         }
    2251             : 
    2252         759 :                         smart[id].raw = raw;
    2253         759 :                         smart[id].norm = norm;
    2254         759 :                         smart[id].worst = worst;
    2255         759 :                         smart[id].thresh = thresh;
    2256         759 :                         smart[id].flags = flags;
    2257         759 :                         pathcpy(smart[id].name, sizeof(smart[id].name), id_name);
    2258             : 
    2259             :                         /*
    2260             :                          * Map normalized health percentage to our unified wear level for SSDs
    2261             :                          * 177: Wear_Leveling_Count (Samsung/Crucial)
    2262             :                          * 231: SSD_Life_Left (Kingston/WD)
    2263             :                          * 233: Media_Wearout_Indicator (Intel)
    2264             :                          */
    2265         759 :                         if (norm <= 100) {
    2266         564 :                                 if (strcmp(id_name, "Wear_Leveling_Count") == 0
    2267         561 :                                         || strcmp(id_name, "SSD_Life_Left") == 0
    2268         561 :                                         || strcmp(id_name, "Media_Wearout_Indicator") == 0) {
    2269           3 :                                         smart[SMART_WEAR_LEVEL].raw = 100 - norm;
    2270             :                                 }
    2271             :                         }
    2272             :                 }
    2273             :         }
    2274             : 
    2275          66 :         return 0;
    2276             : }
    2277             : 
    2278          85 : int smartctl_flush(FILE* f, const char* file, const char* name)
    2279             : {
    2280             :         /* read the file */
    2281          68 :         while (1) {
    2282             :                 char buf[256];
    2283             :                 char* s;
    2284             : 
    2285          85 :                 s = fgets(buf, sizeof(buf), f);
    2286          85 :                 if (s == 0)
    2287          17 :                         break;
    2288             : 
    2289             :                 /* remove extraneous chars */
    2290          68 :                 s = strpolish(buf);
    2291             : 
    2292          68 :                 log_tag("smartctl:%s:%s:out: %s\n", file, name, s);
    2293             :         }
    2294             : 
    2295          17 :         return 0;
    2296             : }
    2297             : 
    2298      145178 : int smart_temp(devinfo_t* devinfo)
    2299             : {
    2300      145178 :         uint64_t t = devinfo->smart[SMART_TEMPERATURE_CELSIUS].raw;
    2301      145178 :         if (t == SMART_UNASSIGNED)
    2302          16 :                 t = devinfo->smart[SMART_AIRFLOW_TEMPERATURE_CELSIUS].raw;
    2303      145178 :         if (t == SMART_UNASSIGNED)
    2304          12 :                 return -1;
    2305             : 
    2306             :         /* mask out min/max values */
    2307      145166 :         t &= 0xFFFFUL;
    2308             : 
    2309      145166 :         if (t == 0)
    2310           0 :                 return -1;
    2311      145166 :         if (t > 100)
    2312           0 :                 return -1;
    2313             : 
    2314      145166 :         return t;
    2315             : }
    2316             : 
    2317             : /****************************************************************************/
    2318             : /* thread */
    2319             : 
    2320             : #if HAVE_THREAD
    2321        3687 : void thread_mutex_init(thread_mutex_t* mutex)
    2322             : {
    2323        3687 :         if (pthread_mutex_init(mutex, 0) != 0) {
    2324             :                 /* LCOV_EXCL_START */
    2325             :                 log_fatal(EINTERNAL, "Failed call to pthread_mutex_init().\n");
    2326             :                 os_abort();
    2327             :                 /* LCOV_EXCL_STOP */
    2328             :         }
    2329        3687 : }
    2330             : 
    2331        3426 : void thread_mutex_destroy(thread_mutex_t* mutex)
    2332             : {
    2333        3426 :         if (pthread_mutex_destroy(mutex) != 0) {
    2334             :                 /* LCOV_EXCL_START */
    2335             :                 log_fatal(EINTERNAL, "Failed call to pthread_mutex_destroy().\n");
    2336             :                 os_abort();
    2337             :                 /* LCOV_EXCL_STOP */
    2338             :         }
    2339        3426 : }
    2340             : 
    2341    95972203 : void thread_mutex_lock(thread_mutex_t* mutex)
    2342             : {
    2343    95972203 :         if (pthread_mutex_lock(mutex) != 0) {
    2344             :                 /* LCOV_EXCL_START */
    2345             :                 log_fatal(EINTERNAL, "Failed call to pthread_mutex_lock().\n");
    2346             :                 os_abort();
    2347             :                 /* LCOV_EXCL_STOP */
    2348             :         }
    2349    95972203 : }
    2350             : 
    2351    95972080 : void thread_mutex_unlock(thread_mutex_t* mutex)
    2352             : {
    2353    95972080 :         if (pthread_mutex_unlock(mutex) != 0) {
    2354             :                 /* LCOV_EXCL_START */
    2355             :                 log_fatal(EINTERNAL, "Failed call to pthread_mutex_unlock().\n");
    2356             :                 os_abort();
    2357             :                 /* LCOV_EXCL_STOP */
    2358             :         }
    2359    95972080 : }
    2360             : 
    2361         452 : void thread_cond_init(thread_cond_t* cond)
    2362             : {
    2363         452 :         if (pthread_cond_init(cond, 0) != 0) {
    2364             :                 /* LCOV_EXCL_START */
    2365             :                 log_fatal(EINTERNAL, "Failed call to pthread_cond_init().\n");
    2366             :                 os_abort();
    2367             :                 /* LCOV_EXCL_STOP */
    2368             :         }
    2369         452 : }
    2370             : 
    2371         404 : void thread_cond_destroy(thread_cond_t* cond)
    2372             : {
    2373         404 :         if (pthread_cond_destroy(cond) != 0) {
    2374             :                 /* LCOV_EXCL_START */
    2375             :                 log_fatal(EINTERNAL, "Failed call to pthread_cond_destroy().\n");
    2376             :                 os_abort();
    2377             :                 /* LCOV_EXCL_STOP */
    2378             :         }
    2379         404 : }
    2380             : 
    2381        5705 : void thread_cond_signal(thread_cond_t* cond)
    2382             : {
    2383        5705 :         if (pthread_cond_signal(cond) != 0) {
    2384             :                 /* LCOV_EXCL_START */
    2385             :                 log_fatal(EINTERNAL, "Failed call to pthread_cond_signal().\n");
    2386             :                 os_abort();
    2387             :                 /* LCOV_EXCL_STOP */
    2388             :         }
    2389        5705 : }
    2390             : 
    2391      281421 : void thread_cond_broadcast(thread_cond_t* cond)
    2392             : {
    2393      281421 :         if (pthread_cond_broadcast(cond) != 0) {
    2394             :                 /* LCOV_EXCL_START */
    2395             :                 log_fatal(EINTERNAL, "Failed call to pthread_cond_broadcast().\n");
    2396             :                 os_abort();
    2397             :                 /* LCOV_EXCL_STOP */
    2398             :         }
    2399      281421 : }
    2400             : 
    2401     1596114 : void thread_cond_wait(thread_cond_t* cond, thread_mutex_t* mutex)
    2402             : {
    2403     1596114 :         if (pthread_cond_wait(cond, mutex) != 0) {
    2404             :                 /* LCOV_EXCL_START */
    2405             :                 log_fatal(EINTERNAL, "Failed call to pthread_cond_wait().\n");
    2406             :                 os_abort();
    2407             :                 /* LCOV_EXCL_STOP */
    2408             :         }
    2409     1595991 : }
    2410             : 
    2411             : /**
    2412             :  * Implementation note about conditional variables.
    2413             :  *
    2414             :  * The conditional variables can be signaled inside or outside the mutex,
    2415             :  * what is better it's debatable but in general doing that outside the mutex,
    2416             :  * reduces the number of context switches.
    2417             :  *
    2418             :  * But when testing with helgrind and drd, this disallows such tools to
    2419             :  * to see the dependency between the signal and the wait.
    2420             :  *
    2421             :  * To avoid it we signal everything inside the mutex. And we do this in both
    2422             :  * test mode (with CHECKER defined) and release mode (CHECKER not defined),
    2423             :  * to be on the safe side and avoid any difference in behaviour between test and
    2424             :  * release.
    2425             :  *
    2426             :  * Here some interesting discussion:
    2427             :  *
    2428             :  * Condvars: signal with mutex locked or not?
    2429             :  * http://www.domaigne.com/blog/computing/condvars-signal-with-mutex-locked-or-not/
    2430             :  *
    2431             :  * Calling pthread_cond_signal without locking mutex
    2432             :  * http://stackoverflow.com/questions/4544234/calling-pthread-cond-signal-without-locking-mutex/4544494#4544494
    2433             :  */
    2434             : 
    2435             : /**
    2436             :  * Control when to signal the condition variables.
    2437             :  */
    2438             : int thread_cond_signal_outside = 0;
    2439             : 
    2440        5705 : void thread_cond_signal_and_unlock(thread_cond_t* cond, thread_mutex_t* mutex)
    2441             : {
    2442        5705 :         if (thread_cond_signal_outside) {
    2443             :                 /*
    2444             :                  * Without the thread checker unlock before signaling,
    2445             :                  * this reduces the number of context switches
    2446             :                  */
    2447           0 :                 thread_mutex_unlock(mutex);
    2448             :         }
    2449             : 
    2450        5705 :         thread_cond_signal(cond);
    2451             : 
    2452        5705 :         if (!thread_cond_signal_outside) {
    2453             :                 /*
    2454             :                  * With the thread checker unlock after signaling
    2455             :                  * to make explicit the condition and mutex relation
    2456             :                  */
    2457        5705 :                 thread_mutex_unlock(mutex);
    2458             :         }
    2459        5705 : }
    2460             : 
    2461      281219 : void thread_cond_broadcast_and_unlock(thread_cond_t* cond, thread_mutex_t* mutex)
    2462             : {
    2463      281219 :         if (thread_cond_signal_outside) {
    2464             :                 /*
    2465             :                  * Without the thread checker unlock before signaling,
    2466             :                  * this reduces the number of context switches
    2467             :                  */
    2468           0 :                 thread_mutex_unlock(mutex);
    2469             :         }
    2470             : 
    2471      281219 :         thread_cond_broadcast(cond);
    2472             : 
    2473      281219 :         if (!thread_cond_signal_outside) {
    2474             :                 /*
    2475             :                  * With the thread checker unlock after signaling
    2476             :                  * to make explicit the condition and mutex relation
    2477             :                  */
    2478      281219 :                 thread_mutex_unlock(mutex);
    2479             :         }
    2480      281219 : }
    2481             : 
    2482        3134 : void thread_create(thread_id_t* thread, void* (*func)(void*), void* arg)
    2483             : {
    2484        3134 :         if (pthread_create(thread, 0, func, arg) != 0) {
    2485             :                 /* LCOV_EXCL_START */
    2486             :                 log_fatal(EINTERNAL, "Failed call to pthread_create().\n");
    2487             :                 os_abort();
    2488             :                 /* LCOV_EXCL_STOP */
    2489             :         }
    2490        3134 : }
    2491             : 
    2492        3011 : void thread_join(thread_id_t thread, void** retval)
    2493             : {
    2494        3011 :         if (pthread_join(thread, retval) != 0) {
    2495             :                 /* LCOV_EXCL_START */
    2496             :                 log_fatal(EINTERNAL, "Failed call to pthread_join().\n");
    2497             :                 os_abort();
    2498             :                 /* LCOV_EXCL_STOP */
    2499             :         }
    2500        3011 : }
    2501             : 
    2502      116206 : void thread_yield(void)
    2503             : {
    2504             : #ifdef __MINGW32__
    2505             :         Sleep(0);
    2506             : #else
    2507      116206 :         sched_yield();
    2508             : #endif
    2509      116206 : }
    2510             : #endif
    2511             : 
    2512             : /****************************************************************************/
    2513             : /* wnmatch */
    2514             : 
    2515             : /**
    2516             :  * Helper function for case-insensitive character comparison
    2517             :  */
    2518    89838573 : static inline int char_match(char p, char t)
    2519             : {
    2520             : #ifdef WIN32
    2521             :         return tolower((unsigned char)p) == tolower((unsigned char)t);
    2522             : #else
    2523    89838573 :         return p == t;
    2524             : #endif
    2525             : }
    2526             : 
    2527             : /**
    2528             :  * Match character class [...]
    2529             :  * Return 0 if NOT matched
    2530             :  */
    2531          28 : static const char* match_class(const char* p, char t)
    2532             : {
    2533          28 :         int negate = 0;
    2534          28 :         int matched = 0;
    2535             : 
    2536          28 :         if (*p == '!' || *p == '^') {
    2537           9 :                 negate = 1;
    2538           9 :                 ++p;
    2539             :         }
    2540             : 
    2541          77 :         while (*p && *p != ']') {
    2542          49 :                 if (p[1] == '-' && p[2] != ']' && p[2] != 0) {
    2543             :                         /* range [a-z] */
    2544          22 :                         char start = *p;
    2545          22 :                         char end = p[2];
    2546             : #ifdef WIN32
    2547             :                         start = tolower((unsigned char)start);
    2548             :                         end = tolower((unsigned char)end);
    2549             :                         t = tolower((unsigned char)t);
    2550             : #endif
    2551          22 :                         if (t >= start && t <= end)
    2552          10 :                                 matched = 1;
    2553          22 :                         p += 3;
    2554             :                 } else {
    2555             :                         /* single character */
    2556          27 :                         if (char_match(*p, t))
    2557           5 :                                 matched = 1;
    2558          27 :                         ++p;
    2559             :                 }
    2560             :         }
    2561             : 
    2562          28 :         if (*p == ']')
    2563          28 :                 ++p;
    2564             : 
    2565          28 :         if (negate)
    2566           9 :                 matched = !matched;
    2567          28 :         if (!matched)
    2568          14 :                 return 0;
    2569             : 
    2570          14 :         return p;
    2571             : }
    2572             : 
    2573   105627041 : int wnmatch_sub(const char* p, const char* t, int match_sub)
    2574             : {
    2575   105627041 :         char p1 = 0; /* previous char */
    2576   109465476 :         while (*p) {
    2577   109324544 :                 char p0 = *p;
    2578   109324544 :                 switch (p0) {
    2579          20 :                 case '?' :
    2580             :                         /* ? matches any single character except / */
    2581          20 :                         if (*t == 0 || *t == '/')
    2582           4 :                                 return 1;
    2583          16 :                         ++p;
    2584          16 :                         ++t;
    2585          16 :                         break;
    2586    13066467 :                 case '*' :
    2587             :                         /* check for ** */
    2588    13066467 :                         if (p[1] == '*') {
    2589             :                                 /* skip the ** */
    2590         104 :                                 p += 2;
    2591             : 
    2592             :                                 /* munge all * */
    2593         114 :                                 while (*p == '*')
    2594          10 :                                         ++p;
    2595             : 
    2596             :                                 /* if its not near a slash, it's like a single * */
    2597         104 :                                 if (p1 == '/' || *p == '/') {
    2598             :                                         /* a ** at end matches everything */
    2599          84 :                                         if (*p == 0)
    2600          10 :                                                 return 0;
    2601             : 
    2602             :                                         /*
    2603             :                                          * In between slashes matches to nothing
    2604             :                                          *
    2605             :                                          * Check for /##/ or ^##/ (^ start of string)
    2606             :                                          *
    2607             :                                          * This is required for:
    2608             :                                          * "/##/file.txt" matching "/file.txt"
    2609             :                                          * "##/file.txt" matching "file.txt"
    2610             :                                          * "x##/file.txt" NOT matching "xfile.txt"
    2611             :                                          */
    2612          74 :                                         if (*p == '/' && (p1 == 0 || p1 == '/')) {
    2613             :                                                 /* try reducing to nothing */
    2614          71 :                                                 if (wnmatch_sub(p + 1, t, match_sub) == 0)
    2615          27 :                                                         return 0;
    2616             :                                                 /* otherwise / should match in the text */
    2617             :                                         }
    2618             : 
    2619             :                                         /* try matching with 0 or more characters */
    2620         275 :                                         while (*t) {
    2621         263 :                                                 if (wnmatch_sub(p, t, match_sub) == 0)
    2622          35 :                                                         return 0;
    2623         228 :                                                 ++t;
    2624             :                                         }
    2625             : 
    2626             :                                         /* try matching at the end */
    2627          12 :                                         return wnmatch_sub(p, t, match_sub);
    2628             :                                 }
    2629             :                         } else {
    2630             :                                 /* skip the * */
    2631    13066363 :                                 ++p;
    2632             :                         }
    2633             : 
    2634             :                         /* a * at end matches rest of segment */
    2635    13066383 :                         if (*p == 0) {
    2636        1430 :                                 while (*t && *t != '/')
    2637        1285 :                                         ++t;
    2638         145 :                                 return *t != 0;
    2639             :                         }
    2640             : 
    2641             :                         /* try matching with 0 or more characters */
    2642    92125282 :                         while (*t && *t != '/') {
    2643    79183187 :                                 if (wnmatch_sub(p, t, match_sub) == 0)
    2644      124143 :                                         return 0;
    2645    79059044 :                                 ++t;
    2646             :                         }
    2647             : 
    2648             :                         /* try matching at the end */
    2649    12942095 :                         return wnmatch_sub(p, t, match_sub);
    2650          31 :                 case '[' :
    2651             :                         /* character class */
    2652          31 :                         if (*t == 0 || *t == '/')
    2653           3 :                                 return 1;
    2654             : 
    2655          28 :                         p = match_class(p + 1, *t);
    2656          28 :                         if (!p)
    2657          14 :                                 return 1;
    2658             : 
    2659          14 :                         ++t;
    2660          14 :                         break;
    2661    96258026 :                 default :
    2662             :                         /* literal character */
    2663    96258026 :                         if (*t == 0 || !char_match(*p, *t))
    2664    92419621 :                                 return 1;
    2665     3838405 :                         ++p;
    2666     3838405 :                         ++t;
    2667     3838405 :                         break;
    2668             :                 }
    2669     3838435 :                 p1 = p0;
    2670             :         }
    2671             : 
    2672             :         /* if we match sub directory */
    2673      140932 :         if (match_sub) {
    2674             :                 /* match successfully only if we are at a directory border */
    2675        3056 :                 return *t != '/';
    2676             :         } else {
    2677             :                 /* match successfully if we've consumed all text */
    2678      137876 :                 return *t != 0;
    2679             :         }
    2680             : }
    2681             : 

Generated by: LCOV version 1.0