LCOV - code coverage report
Current view: top level - lib/fs - path.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 177 210 84.3 %
Date: 2021-11-24 03:28:48 Functions: 18 19 94.7 %

          Line data    Source code
       1             : /* Copyright (c) 2003, Roger Dingledine
       2             :  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
       3             :  * Copyright (c) 2007-2021, The Tor Project, Inc. */
       4             : /* See LICENSE for licensing information */
       5             : 
       6             : /**
       7             :  * \file path.c
       8             :  *
       9             :  * \brief Manipulate strings that contain filesystem paths.
      10             :  **/
      11             : 
      12             : #include "lib/fs/path.h"
      13             : #include "lib/malloc/malloc.h"
      14             : #include "lib/log/log.h"
      15             : #include "lib/log/util_bug.h"
      16             : #include "lib/container/smartlist.h"
      17             : #include "lib/sandbox/sandbox.h"
      18             : #include "lib/string/printf.h"
      19             : #include "lib/string/util_string.h"
      20             : #include "lib/string/compat_ctype.h"
      21             : #include "lib/string/compat_string.h"
      22             : #include "lib/fs/files.h"
      23             : #include "lib/fs/dir.h"
      24             : #include "lib/fs/userdb.h"
      25             : 
      26             : #ifdef HAVE_SYS_TYPES_H
      27             : #include <sys/types.h>
      28             : #endif
      29             : #ifdef HAVE_SYS_STAT_H
      30             : #include <sys/stat.h>
      31             : #endif
      32             : #ifdef HAVE_UNISTD_H
      33             : #include <unistd.h>
      34             : #endif
      35             : 
      36             : #ifdef _WIN32
      37             : #include <windows.h>
      38             : #include <shlwapi.h>
      39             : #else /* !(defined(_WIN32)) */
      40             : #include <dirent.h>
      41             : #include <glob.h>
      42             : #endif /* defined(_WIN32) */
      43             : 
      44             : #include <errno.h>
      45             : #include <string.h>
      46             : 
      47             : /** Removes enclosing quotes from <b>path</b> and unescapes quotes between the
      48             :  * enclosing quotes. Backslashes are not unescaped. Return the unquoted
      49             :  * <b>path</b> on success or 0 if <b>path</b> is not quoted correctly. */
      50             : char *
      51          17 : get_unquoted_path(const char *path)
      52             : {
      53          17 :   size_t len = strlen(path);
      54             : 
      55          17 :   if (len == 0) {
      56           1 :     return tor_strdup("");
      57             :   }
      58             : 
      59          16 :   int has_start_quote = (path[0] == '\"');
      60          16 :   int has_end_quote = (len > 0 && path[len-1] == '\"');
      61          16 :   if (has_start_quote != has_end_quote || (len == 1 && has_start_quote)) {
      62             :     return NULL;
      63             :   }
      64             : 
      65          11 :   char *unquoted_path = tor_malloc(len - has_start_quote - has_end_quote + 1);
      66          11 :   char *s = unquoted_path;
      67          11 :   size_t i;
      68          60 :   for (i = has_start_quote; i < len - has_end_quote; i++) {
      69          41 :     if (path[i] == '\"' && (i > 0 && path[i-1] == '\\')) {
      70           4 :       *(s-1) = path[i];
      71          37 :     } else if (path[i] != '\"') {
      72          34 :       *s++ = path[i];
      73             :     } else {  /* unescaped quote */
      74           3 :       tor_free(unquoted_path);
      75           3 :       return NULL;
      76             :     }
      77             :   }
      78           8 :   *s = '\0';
      79           8 :   return unquoted_path;
      80             : }
      81             : 
      82             : /** Expand any homedir prefix on <b>filename</b>; return a newly allocated
      83             :  * string. */
      84             : char *
      85         991 : expand_filename(const char *filename)
      86             : {
      87         991 :   tor_assert(filename);
      88             : #ifdef _WIN32
      89             :   /* Might consider using GetFullPathName() as described here:
      90             :    * http://etutorials.org/Programming/secure+programming/
      91             :    *     Chapter+3.+Input+Validation/3.7+Validating+Filenames+and+Paths/
      92             :    */
      93             :   return tor_strdup(filename);
      94             : #else /* !defined(_WIN32) */
      95         991 :   if (*filename == '~') {
      96         530 :     char *home, *result=NULL;
      97         530 :     const char *rest;
      98             : 
      99         530 :     if (filename[1] == '/' || filename[1] == '\0') {
     100         530 :       home = getenv("HOME");
     101         530 :       if (!home) {
     102           3 :         log_warn(LD_CONFIG, "Couldn't find $HOME environment variable while "
     103             :                  "expanding \"%s\"; defaulting to \"\".", filename);
     104           3 :         home = tor_strdup("");
     105             :       } else {
     106         527 :         home = tor_strdup(home);
     107             :       }
     108         530 :       rest = strlen(filename)>=2?(filename+2):"";
     109             :     } else {
     110             : #ifdef HAVE_PWD_H
     111           0 :       char *username, *slash;
     112           0 :       slash = strchr(filename, '/');
     113           0 :       if (slash)
     114           0 :         username = tor_strndup(filename+1,slash-filename-1);
     115             :       else
     116           0 :         username = tor_strdup(filename+1);
     117           0 :       if (!(home = get_user_homedir(username))) {
     118           0 :         log_warn(LD_CONFIG,"Couldn't get homedir for \"%s\"",username);
     119           0 :         tor_free(username);
     120           0 :         return NULL;
     121             :       }
     122           0 :       tor_free(username);
     123           0 :       rest = slash ? (slash+1) : "";
     124             : #else /* !defined(HAVE_PWD_H) */
     125             :       log_warn(LD_CONFIG, "Couldn't expand homedir on system without pwd.h");
     126             :       return tor_strdup(filename);
     127             : #endif /* defined(HAVE_PWD_H) */
     128             :     }
     129         530 :     tor_assert(home);
     130             :     /* Remove trailing slash. */
     131         530 :     if (strlen(home)>1 && !strcmpend(home,PATH_SEPARATOR)) {
     132           3 :       home[strlen(home)-1] = '\0';
     133             :     }
     134         530 :     tor_asprintf(&result,"%s"PATH_SEPARATOR"%s",home,rest);
     135         530 :     tor_free(home);
     136         530 :     return result;
     137             :   } else {
     138         461 :     return tor_strdup(filename);
     139             :   }
     140             : #endif /* defined(_WIN32) */
     141             : }
     142             : 
     143             : /** Return true iff <b>filename</b> is a relative path. */
     144             : int
     145        1222 : path_is_relative(const char *filename)
     146             : {
     147        1222 :   if (filename && filename[0] == '/')
     148             :     return 0;
     149             : #ifdef _WIN32
     150             :   else if (filename && filename[0] == '\\')
     151             :     return 0;
     152             :   else if (filename && strlen(filename)>3 && TOR_ISALPHA(filename[0]) &&
     153             :            filename[1] == ':' && filename[2] == '\\')
     154             :     return 0;
     155             : #endif /* defined(_WIN32) */
     156             :   else
     157           9 :     return 1;
     158             : }
     159             : 
     160             : /** Clean up <b>name</b> so that we can use it in a call to "stat".  On Unix,
     161             :  * we do nothing.  On Windows, we remove a trailing slash, unless the path is
     162             :  * the root of a disk. */
     163             : void
     164        1121 : clean_fname_for_stat(char *name)
     165             : {
     166             : #ifdef _WIN32
     167             :   size_t len = strlen(name);
     168             :   if (!len)
     169             :     return;
     170             :   if (name[len-1]=='\\' || name[len-1]=='/') {
     171             :     if (len == 1 || (len==3 && name[1]==':'))
     172             :       return;
     173             :     name[len-1]='\0';
     174             :   }
     175             : #else /* !defined(_WIN32) */
     176        1121 :   (void)name;
     177             : #endif /* defined(_WIN32) */
     178        1121 : }
     179             : 
     180             : /** Modify <b>fname</b> to contain the name of its parent directory.  Doesn't
     181             :  * actually examine the filesystem; does a purely syntactic modification.
     182             :  *
     183             :  * The parent of the root director is considered to be itself.
     184             :  *
     185             :  * Path separators are the forward slash (/) everywhere and additionally
     186             :  * the backslash (\) on Win32.
     187             :  *
     188             :  * Cuts off any number of trailing path separators but otherwise ignores
     189             :  * them for purposes of finding the parent directory.
     190             :  *
     191             :  * Returns 0 if a parent directory was successfully found, -1 otherwise (fname
     192             :  * did not have any path separators or only had them at the end).
     193             :  * */
     194             : int
     195          23 : get_parent_directory(char *fname)
     196             : {
     197          23 :   char *cp;
     198          23 :   int at_end = 1;
     199          23 :   tor_assert(fname);
     200             : #ifdef _WIN32
     201             :   /* If we start with, say, c:, then don't consider that the start of the path
     202             :    */
     203             :   if (fname[0] && fname[1] == ':') {
     204             :     fname += 2;
     205             :   }
     206             : #endif /* defined(_WIN32) */
     207             :   /* Now we want to remove all path-separators at the end of the string,
     208             :    * and to remove the end of the string starting with the path separator
     209             :    * before the last non-path-separator.  In perl, this would be
     210             :    *   s#[/]*$##; s#/[^/]*$##;
     211             :    * on a unixy platform.
     212             :    */
     213          23 :   cp = fname + strlen(fname);
     214          23 :   at_end = 1;
     215         119 :   while (--cp >= fname) {
     216         117 :     int is_sep = (*cp == '/'
     217             : #ifdef _WIN32
     218             :                   || *cp == '\\'
     219             : #endif
     220             :                   );
     221         117 :     if (is_sep) {
     222          38 :       if (cp == fname) {
     223             :         /* This is the first separator in the file name; don't remove it! */
     224           4 :         cp[1] = '\0';
     225           4 :         return 0;
     226             :       }
     227          34 :       *cp = '\0';
     228          34 :       if (! at_end)
     229             :         return 0;
     230             :     } else {
     231             :       at_end = 0;
     232             :     }
     233             :   }
     234             :   return -1;
     235             : }
     236             : 
     237             : #ifndef _WIN32
     238             : /** Return a newly allocated string containing the output of getcwd(). Return
     239             :  * NULL on failure. (We can't just use getcwd() into a PATH_MAX buffer, since
     240             :  * Hurd hasn't got a PATH_MAX.)
     241             :  */
     242             : static char *
     243         149 : alloc_getcwd(void)
     244             : {
     245             : #ifdef HAVE_GET_CURRENT_DIR_NAME
     246             :   /* Glibc makes this nice and simple for us. */
     247         149 :   char *cwd = get_current_dir_name();
     248         149 :   char *result = NULL;
     249         149 :   if (cwd) {
     250             :     /* We make a copy here, in case tor_malloc() is not malloc(). */
     251         149 :     result = tor_strdup(cwd);
     252         149 :     raw_free(cwd); // alias for free to avoid tripping check-spaces.
     253             :   }
     254         149 :   return result;
     255             : #else /* !defined(HAVE_GET_CURRENT_DIR_NAME) */
     256             :   size_t size = 1024;
     257             :   char *buf = NULL;
     258             :   char *ptr = NULL;
     259             : 
     260             :   while (ptr == NULL) {
     261             :     buf = tor_realloc(buf, size);
     262             :     ptr = getcwd(buf, size);
     263             : 
     264             :     if (ptr == NULL && errno != ERANGE) {
     265             :       tor_free(buf);
     266             :       return NULL;
     267             :     }
     268             : 
     269             :     size *= 2;
     270             :   }
     271             :   return buf;
     272             : #endif /* defined(HAVE_GET_CURRENT_DIR_NAME) */
     273             : }
     274             : #endif /* !defined(_WIN32) */
     275             : 
     276             : /** Expand possibly relative path <b>fname</b> to an absolute path.
     277             :  * Return a newly allocated string, which may be a duplicate of <b>fname</b>.
     278             :  */
     279             : char *
     280         460 : make_path_absolute(const char *fname)
     281             : {
     282             : #ifdef _WIN32
     283             :   char *absfname_malloced = _fullpath(NULL, fname, 1);
     284             : 
     285             :   /* We don't want to assume that tor_free can free a string allocated
     286             :    * with malloc.  On failure, return fname (it's better than nothing). */
     287             :   char *absfname = tor_strdup(absfname_malloced ? absfname_malloced : fname);
     288             :   if (absfname_malloced) raw_free(absfname_malloced);
     289             : 
     290             :   return absfname;
     291             : #else /* !defined(_WIN32) */
     292         460 :   char *absfname = NULL, *path = NULL;
     293             : 
     294         460 :   tor_assert(fname);
     295             : 
     296         460 :   if (fname[0] == '/') {
     297         311 :     absfname = tor_strdup(fname);
     298             :   } else {
     299         149 :     path = alloc_getcwd();
     300         149 :     if (path) {
     301         149 :       tor_asprintf(&absfname, "%s/%s", path, fname);
     302         149 :       tor_free(path);
     303             :     } else {
     304             :       /* LCOV_EXCL_START Can't make getcwd fail. */
     305             :       /* If getcwd failed, the best we can do here is keep using the
     306             :        * relative path.  (Perhaps / isn't readable by this UID/GID.) */
     307             :       log_warn(LD_GENERAL, "Unable to find current working directory: %s",
     308             :                strerror(errno));
     309             :       absfname = tor_strdup(fname);
     310             :       /* LCOV_EXCL_STOP */
     311             :     }
     312             :   }
     313         460 :   return absfname;
     314             : #endif /* defined(_WIN32) */
     315             : }
     316             : 
     317             : /* The code below implements tor_glob and get_glob_opened_files. Because it is
     318             :  * not easy to understand it by looking at individual functions, the big
     319             :  * picture explanation here should be read first.
     320             :  *
     321             :  * Purpose of the functions:
     322             :  * - tor_glob - receives a pattern and returns all the paths that result from
     323             :  *   its glob expansion, globs can be present on all path components.
     324             :  * - get_glob_opened_files - receives a pattern and returns all the paths that
     325             :  *   are opened during its expansion (the paths before any path fragment that
     326             :  *   contains a glob as they have to be opened to check for glob matches). This
     327             :  *   is used to get the paths that have to be added to the seccomp sandbox
     328             :  *   allowed list.
     329             :  *
     330             :  * Due to OS API differences explained below, the implementation of tor_glob is
     331             :  * completely different for Windows and POSIX systems, so we ended up with
     332             :  * three different implementations:
     333             :  * - tor_glob for POSIX - as POSIX glob does everything we need, we simply call
     334             :  *   it and process the results. This is completely implemented in tor_glob.
     335             :  * - tor_glob for WIN32 - because the WIN32 API only supports expanding globs
     336             :  *   in the last path fragment, we need to expand the globs in each path
     337             :  *   fragment manually and call recursively to get the same behaviour as POSIX
     338             :  *   glob. When there are no globs in pattern, we know we are on the last path
     339             :  *   fragment and collect the full path.
     340             :  * - get_glob_opened_files - because the paths before any path fragment with a
     341             :  *   glob will be opened to check for matches, we need to collect them and we
     342             :  *   need to expand the globs in each path fragments and call recursively until
     343             :  *   we find no more globs.
     344             :  *
     345             :  * As seen from the description above, both tor_glob for WIN32 and
     346             :  * get_glob_opened_files receive a pattern and return a list of paths and have
     347             :  * to expand all path fragments that contain globs and call themselves
     348             :  * recursively. The differences are:
     349             :  * - get_glob_opened_files collects paths before path fragments with globs
     350             :  *   while tor_glob for WIN32 collects full paths resulting from the expansion
     351             :  *   of all globs.
     352             :  * - get_glob_opened_files can call tor_glob to expand path fragments with
     353             :  *   globs while tor_glob for WIN32 cannot because it IS tor_glob. For tor_glob
     354             :  *   for WIN32, an auxiliary function has to be used for this purpose.
     355             :  *
     356             :  * To avoid code duplication, the logic of tor_glob for WIN32 and
     357             :  * get_glob_opened_files is implemented in get_glob_paths. The differences are
     358             :  * configured by the extra function parameters:
     359             :  * - final - if true, returns a list of paths obtained from expanding pattern
     360             :  *   (implements tor_glob). Otherwise, returns the paths before path fragments
     361             :  *   with globs (implements get_glob_opened_files).
     362             :  * - unglob - function used to expand a path fragment. The function signature
     363             :  *   is defined by the unglob_fn typedef. Two implementations are available:
     364             :  *   - unglob_win32 - uses tor_listdir and PathMatchSpec (for tor_glob WIN32)
     365             :  *   - unglob_opened_files - uses tor_glob (for get_glob_opened_files)
     366             :  */
     367             : 
     368             : /** Returns true if the character at position <b>pos</b> in <b>pattern</b> is
     369             :  * considered a glob. Returns false otherwise. Takes escaping into account on
     370             :  * systems where escaping globs is supported. */
     371             : static inline bool
     372        9642 : is_glob_char(const char *pattern, int pos)
     373             : {
     374        9642 :   bool is_glob = pattern[pos] == '*' || pattern[pos] == '?';
     375             : #ifdef _WIN32
     376             :   return is_glob;
     377             : #else /* !defined(_WIN32) */
     378        9642 :   bool is_escaped = pos > 0 && pattern[pos-1] == '\\';
     379        9642 :   return is_glob && !is_escaped;
     380             : #endif /* defined(_WIN32) */
     381             : }
     382             : 
     383             : /** Expands the first path fragment of <b>pattern</b> that contains globs. The
     384             :  * path fragment is between <b>prev_sep</b> and <b>next_sep</b>. If the path
     385             :  * fragment is the last fragment of <b>pattern</b>, <b>next_sep</b> will be the
     386             :  * index of the last char. Returns a list of paths resulting from the glob
     387             :  * expansion of the path fragment. Anything after <b>next_sep</b> is not
     388             :  * included in the returned list. Returns NULL on failure. */
     389             : typedef struct smartlist_t * unglob_fn(const char *pattern, int prev_sep,
     390             :                                        int next_sep);
     391             : 
     392             : /** Adds <b>path</b> to <b>result</b> if it exists and is a file type we can
     393             :  * handle. Returns false if <b>path</b> is a file type we cannot handle,
     394             :  * returns true otherwise. Used on tor_glob for WIN32. */
     395             : static bool
     396           0 : add_non_glob_path(const char *path, struct smartlist_t *result)
     397             : {
     398           0 :   file_status_t file_type = file_status(path);
     399           0 :   if (file_type == FN_ERROR) {
     400             :     return false;
     401           0 :   } else if (file_type != FN_NOENT) {
     402           0 :     char *to_add = tor_strdup(path);
     403           0 :     clean_fname_for_stat(to_add);
     404           0 :     smartlist_add(result, to_add);
     405             :   }
     406             :   /* If WIN32 tor_glob is called with a non-existing path, we want it to
     407             :    * return an empty list instead of error to match the regular version */
     408             :   return true;
     409             : }
     410             : 
     411             : /** Auxiliary function used by get_glob_opened_files and WIN32 tor_glob.
     412             :  * Returns a list of paths obtained from <b>pattern</b> using <b>unglob</b> to
     413             :  * expand each path fragment. If <b>final</b> is true, the paths are the result
     414             :  * of the glob expansion of <b>pattern</b> (implements tor_glob). Otherwise,
     415             :  * the paths are the paths opened by glob while expanding <b>pattern</b>
     416             :  * (implements get_glob_opened_files). Returns NULL on failure. */
     417             : static struct smartlist_t *
     418          31 : get_glob_paths(const char *pattern, unglob_fn unglob, bool final)
     419             : {
     420          31 :   smartlist_t *result = smartlist_new();
     421          31 :   int i, prev_sep = -1, next_sep = -1;
     422          31 :   bool is_glob = false, error_found = false, is_sep = false, is_last = false;
     423             : 
     424             :   // find first path fragment with globs
     425        2047 :   for (i = 0; pattern[i]; i++) {
     426        2007 :     is_glob = is_glob || is_glob_char(pattern, i);
     427        2007 :     is_last = !pattern[i+1];
     428        2007 :     is_sep = pattern[i] == *PATH_SEPARATOR || pattern[i] == '/';
     429        2007 :     if (is_sep || is_last) {
     430         177 :       prev_sep = next_sep;
     431         177 :       next_sep = i; // next_sep+1 is start of next fragment or end of string
     432         177 :       if (is_glob) {
     433             :         break;
     434             :       }
     435             :     }
     436             :   }
     437             : 
     438          31 :   if (!is_glob) { // pattern fully expanded or no glob in pattern
     439           9 :     if (final && !add_non_glob_path(pattern, result)) {
     440           0 :       error_found = true;
     441           0 :       goto end;
     442             :     }
     443           9 :     return result;
     444             :   }
     445             : 
     446          22 :   if (!final) {
     447             :     // add path before the glob to result
     448          22 :     int len = prev_sep < 1 ? prev_sep + 1 : prev_sep; // handle /*
     449          22 :     char *path_until_glob = tor_strndup(pattern, len);
     450          22 :     smartlist_add(result, path_until_glob);
     451             :   }
     452             : 
     453          22 :   smartlist_t *unglobbed_paths = unglob(pattern, prev_sep, next_sep);
     454          22 :   if (!unglobbed_paths) {
     455             :     error_found = true;
     456             :   } else {
     457             :     // for each path for current fragment, add the rest of the pattern
     458             :     // and call recursively to get all expanded paths
     459          37 :     SMARTLIST_FOREACH_BEGIN(unglobbed_paths, char *, current_path) {
     460          15 :       char *next_path;
     461          15 :       tor_asprintf(&next_path, "%s"PATH_SEPARATOR"%s", current_path,
     462          15 :                    &pattern[next_sep+1]);
     463          15 :       smartlist_t *opened_next = get_glob_paths(next_path, unglob, final);
     464          15 :       tor_free(next_path);
     465          15 :       if (!opened_next) {
     466           0 :         error_found = true;
     467           0 :         break;
     468             :       }
     469          15 :       smartlist_add_all(result, opened_next);
     470          15 :       smartlist_free(opened_next);
     471          15 :     } SMARTLIST_FOREACH_END(current_path);
     472          37 :     SMARTLIST_FOREACH(unglobbed_paths, char *, p, tor_free(p));
     473          22 :     smartlist_free(unglobbed_paths);
     474             :   }
     475             : 
     476          22 : end:
     477          22 :   if (error_found) {
     478           0 :     SMARTLIST_FOREACH(result, char *, p, tor_free(p));
     479           0 :     smartlist_free(result);
     480           0 :     result = NULL;
     481             :   }
     482             :   return result;
     483             : }
     484             : 
     485             : #ifdef _WIN32
     486             : /** Expands globs in <b>pattern</b> for the path fragment between
     487             :  * <b>prev_sep</b> and <b>next_sep</b> using the WIN32 API. Returns NULL on
     488             :  * failure. Used by the WIN32 implementation of tor_glob. Implements unglob_fn,
     489             :  * see its description for more details. */
     490             : static struct smartlist_t *
     491             : unglob_win32(const char *pattern, int prev_sep, int next_sep)
     492             : {
     493             :   smartlist_t *result = smartlist_new();
     494             :   int len = prev_sep < 1 ? prev_sep + 1 : prev_sep; // handle /*
     495             :   char *path_until_glob = tor_strndup(pattern, len);
     496             : 
     497             :   if (!is_file(file_status(path_until_glob))) {
     498             :     smartlist_t *filenames = tor_listdir(path_until_glob);
     499             :     if (!filenames) {
     500             :       smartlist_free(result);
     501             :       result = NULL;
     502             :     } else {
     503             :       SMARTLIST_FOREACH_BEGIN(filenames, char *, filename) {
     504             :         TCHAR tpattern[MAX_PATH] = {0};
     505             :         TCHAR tfile[MAX_PATH] = {0};
     506             :         char *full_path;
     507             :         tor_asprintf(&full_path, "%s"PATH_SEPARATOR"%s",
     508             :                      path_until_glob, filename);
     509             :         char *path_curr_glob = tor_strndup(pattern, next_sep + 1);
     510             :         // *\ must return only dirs, remove \ from the pattern so it matches
     511             :         if (is_dir(file_status(full_path))) {
     512             :           clean_fname_for_stat(path_curr_glob);
     513             :         }
     514             : #ifdef UNICODE
     515             :         mbstowcs(tpattern, path_curr_glob, MAX_PATH);
     516             :         mbstowcs(tfile, full_path, MAX_PATH);
     517             : #else /* !defined(UNICODE) */
     518             :         strlcpy(tpattern, path_curr_glob, MAX_PATH);
     519             :         strlcpy(tfile, full_path, MAX_PATH);
     520             : #endif /* defined(UNICODE) */
     521             :         if (PathMatchSpec(tfile, tpattern)) {
     522             :           smartlist_add(result, full_path);
     523             :         } else {
     524             :           tor_free(full_path);
     525             :         }
     526             :         tor_free(path_curr_glob);
     527             :       } SMARTLIST_FOREACH_END(filename);
     528             :       SMARTLIST_FOREACH(filenames, char *, p, tor_free(p));
     529             :       smartlist_free(filenames);
     530             :     }
     531             :   }
     532             :   tor_free(path_until_glob);
     533             :   return result;
     534             : }
     535             : #elif HAVE_GLOB
     536             : #ifdef GLOB_ALTDIRFUNC  // prevent warning about unused functions
     537             : /** Same as opendir but calls sandbox_intern_string before */
     538             : static DIR *
     539          30 : prot_opendir(const char *name)
     540             : {
     541          30 :   if (sandbox_interned_string_is_missing(name)) {
     542           0 :     errno = EPERM;
     543           0 :     return NULL;
     544             :   }
     545          30 :   return opendir(sandbox_intern_string(name));
     546             : }
     547             : 
     548             : /** Same as stat but calls sandbox_intern_string before */
     549             : static int
     550           6 : prot_stat(const char *pathname, struct stat *buf)
     551             : {
     552           6 :   if (sandbox_interned_string_is_missing(pathname)) {
     553           0 :     errno = EPERM;
     554           0 :     return -1;
     555             :   }
     556           6 :   return stat(sandbox_intern_string(pathname), buf);
     557             : }
     558             : 
     559             : /** Same as lstat but calls sandbox_intern_string before */
     560             : static int
     561           7 : prot_lstat(const char *pathname, struct stat *buf)
     562             : {
     563           7 :   if (sandbox_interned_string_is_missing(pathname)) {
     564           0 :     errno = EPERM;
     565           0 :     return -1;
     566             :   }
     567           7 :   return lstat(sandbox_intern_string(pathname), buf);
     568             : }
     569             : /** As closedir, but has the right type for gl_closedir */
     570             : static void
     571          28 : wrap_closedir(void *arg)
     572             : {
     573          28 :   closedir(arg);
     574          28 : }
     575             : #endif /* defined(GLOB_ALTDIRFUNC) */
     576             : 
     577             : /** Function passed to glob to handle processing errors. <b>epath</b> is the
     578             :  * path that caused the error and <b>eerrno</b> is the errno set by the
     579             :  * function that failed. We want to ignore ENOENT and ENOTDIR because, in BSD
     580             :  * systems, these are not ignored automatically, which makes glob fail when
     581             :  * globs expand to non-existing paths and GLOB_ERR is set.
     582             :  */
     583             : static int
     584           1 : glob_errfunc(const char *epath, int eerrno)
     585             : {
     586           1 :     (void)epath;
     587           1 :     return eerrno == ENOENT || eerrno == ENOTDIR ? 0 : -1;
     588             : }
     589             : #endif /* defined(HAVE_GLOB) */
     590             : 
     591             : /** Return a new list containing the paths that match the pattern
     592             :  * <b>pattern</b>. Return NULL on error. On POSIX systems, errno is set by the
     593             :  * glob function or is set to EPERM if glob tried to access a file not allowed
     594             :  * by the seccomp sandbox.
     595             :  */
     596             : struct smartlist_t *
     597          30 : tor_glob(const char *pattern)
     598             : {
     599          30 :   smartlist_t *result = NULL;
     600             : 
     601             : #ifdef _WIN32
     602             :   // PathMatchSpec does not support forward slashes, change them to backslashes
     603             :   char *pattern_normalized = tor_strdup(pattern);
     604             :   tor_strreplacechar(pattern_normalized, '/', *PATH_SEPARATOR);
     605             :   result = get_glob_paths(pattern_normalized, unglob_win32, true);
     606             :   tor_free(pattern_normalized);
     607             : #elif HAVE_GLOB /* !(defined(_WIN32)) */
     608          30 :   glob_t matches;
     609          30 :   int flags = GLOB_NOSORT;
     610             : #ifdef GLOB_ALTDIRFUNC
     611             :   /* use functions that call sandbox_intern_string */
     612          30 :   flags |= GLOB_ALTDIRFUNC;
     613          30 :   typedef void *(*gl_opendir)(const char * name);
     614          30 :   typedef struct dirent *(*gl_readdir)(void *);
     615          30 :   typedef void (*gl_closedir)(void *);
     616          30 :   matches.gl_opendir = (gl_opendir) &prot_opendir;
     617          30 :   matches.gl_readdir = (gl_readdir) &readdir;
     618          30 :   matches.gl_closedir = (gl_closedir) &wrap_closedir;
     619          30 :   matches.gl_stat = &prot_stat;
     620          30 :   matches.gl_lstat = &prot_lstat;
     621             : #endif /* defined(GLOB_ALTDIRFUNC) */
     622             :   // use custom error handler to workaround BSD quirks and do not set GLOB_ERR
     623             :   // because it would make glob fail on error even if the error handler ignores
     624             :   // the error
     625          30 :   int ret = glob(pattern, flags, glob_errfunc, &matches);
     626          30 :   if (ret == GLOB_NOMATCH) {
     627           6 :     return smartlist_new();
     628          24 :   } else if (ret != 0) {
     629             :     return NULL;
     630             :   }
     631             : 
     632             :   // #40141, !249: workaround for glibc bug where patterns ending in path
     633             :   // separator match files and folders instead of folders only.
     634             :   // this could be in #ifdef __GLIBC__ but: 1. it might affect other libcs too,
     635             :   // and 2. it doesn't cost much to stat each match again since libc is already
     636             :   // supposed to do it (otherwise the file may be on slow NFS or something)
     637          23 :   size_t pattern_len = strlen(pattern);
     638          23 :   bool dir_only = pattern_len > 0 && pattern[pattern_len-1] == *PATH_SEPARATOR;
     639             : 
     640          23 :   result = smartlist_new();
     641          23 :   size_t i;
     642         117 :   for (i = 0; i < matches.gl_pathc; i++) {
     643          71 :     char *match = tor_strdup(matches.gl_pathv[i]);
     644          71 :     size_t len = strlen(match);
     645          71 :     if (len > 0 && match[len-1] == *PATH_SEPARATOR) {
     646           5 :       match[len-1] = '\0';
     647             :     }
     648             : 
     649          71 :     if (!dir_only || (dir_only && is_dir(file_status(match)))) {
     650          70 :       smartlist_add(result, match);
     651             :     } else {
     652           1 :       tor_free(match);
     653             :     }
     654             :   }
     655          23 :   globfree(&matches);
     656             : #else
     657             :   (void)pattern;
     658             :   return result;
     659             : #endif /* defined(_WIN32) || ... */
     660             : 
     661          23 :   return result;
     662             : }
     663             : 
     664             : /** Returns true if <b>s</b> contains characters that can be globbed.
     665             :  * Returns false otherwise. */
     666             : bool
     667         147 : has_glob(const char *s)
     668             : {
     669         147 :   int i;
     670        7780 :   for (i = 0; s[i]; i++) {
     671        7645 :     if (is_glob_char(s, i)) {
     672             :       return true;
     673             :     }
     674             :   }
     675             :   return false;
     676             : }
     677             : 
     678             : /** Expands globs in <b>pattern</b> for the path fragment between
     679             :  * <b>prev_sep</b> and <b>next_sep</b> using tor_glob. Returns NULL on
     680             :  * failure. Used by get_glob_opened_files. Implements unglob_fn, see its
     681             :  * description for more details. */
     682             : static struct smartlist_t *
     683          22 : unglob_opened_files(const char *pattern, int prev_sep, int next_sep)
     684             : {
     685          22 :   (void)prev_sep;
     686          22 :   smartlist_t *result = smartlist_new();
     687             :   // if the following fragments have no globs, we're done
     688          22 :   if (has_glob(&pattern[next_sep+1])) {
     689             :     // if there is a glob after next_sep, we know next_sep is a separator and
     690             :     // not the last char and glob_path will have the path without the separator
     691           3 :     char *glob_path = tor_strndup(pattern, next_sep);
     692           3 :     smartlist_t *child_paths = tor_glob(glob_path);
     693           3 :     tor_free(glob_path);
     694           3 :     if (!child_paths) {
     695           0 :       smartlist_free(result);
     696           0 :       result = NULL;
     697             :     } else {
     698           3 :       smartlist_add_all(result, child_paths);
     699           3 :       smartlist_free(child_paths);
     700             :     }
     701             :   }
     702          22 :   return result;
     703             : }
     704             : 
     705             : /** Returns a list of files that are opened by the tor_glob function when
     706             :  * called with <b>pattern</b>. Returns NULL on error. The purpose of this
     707             :  * function is to create a list of files to be added to the sandbox white list
     708             :  * before the sandbox is enabled. */
     709             : struct smartlist_t *
     710          16 : get_glob_opened_files(const char *pattern)
     711             : {
     712          16 :   return get_glob_paths(pattern, unglob_opened_files, false);
     713             : }

Generated by: LCOV version 1.14