tor  0.4.1.0-alpha-dev
path.c
Go to the documentation of this file.
1 /* Copyright (c) 2003, Roger Dingledine
2  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3  * Copyright (c) 2007-2019, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
5 
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/string/printf.h"
17 #include "lib/string/util_string.h"
19 #include "lib/fs/userdb.h"
20 
21 #ifdef HAVE_UNISTD_H
22 #include <unistd.h>
23 #endif
24 
25 #include <errno.h>
26 #include <string.h>
27 
31 char *
32 get_unquoted_path(const char *path)
33 {
34  size_t len = strlen(path);
35 
36  if (len == 0) {
37  return tor_strdup("");
38  }
39 
40  int has_start_quote = (path[0] == '\"');
41  int has_end_quote = (len > 0 && path[len-1] == '\"');
42  if (has_start_quote != has_end_quote || (len == 1 && has_start_quote)) {
43  return NULL;
44  }
45 
46  char *unquoted_path = tor_malloc(len - has_start_quote - has_end_quote + 1);
47  char *s = unquoted_path;
48  size_t i;
49  for (i = has_start_quote; i < len - has_end_quote; i++) {
50  if (path[i] == '\"' && (i > 0 && path[i-1] == '\\')) {
51  *(s-1) = path[i];
52  } else if (path[i] != '\"') {
53  *s++ = path[i];
54  } else { /* unescaped quote */
55  tor_free(unquoted_path);
56  return NULL;
57  }
58  }
59  *s = '\0';
60  return unquoted_path;
61 }
62 
65 char *
66 expand_filename(const char *filename)
67 {
68  tor_assert(filename);
69 #ifdef _WIN32
70  /* Might consider using GetFullPathName() as described here:
71  * http://etutorials.org/Programming/secure+programming/
72  * Chapter+3.+Input+Validation/3.7+Validating+Filenames+and+Paths/
73  */
74  return tor_strdup(filename);
75 #else /* !(defined(_WIN32)) */
76  if (*filename == '~') {
77  char *home, *result=NULL;
78  const char *rest;
79 
80  if (filename[1] == '/' || filename[1] == '\0') {
81  home = getenv("HOME");
82  if (!home) {
83  log_warn(LD_CONFIG, "Couldn't find $HOME environment variable while "
84  "expanding \"%s\"; defaulting to \"\".", filename);
85  home = tor_strdup("");
86  } else {
87  home = tor_strdup(home);
88  }
89  rest = strlen(filename)>=2?(filename+2):"";
90  } else {
91 #ifdef HAVE_PWD_H
92  char *username, *slash;
93  slash = strchr(filename, '/');
94  if (slash)
95  username = tor_strndup(filename+1,slash-filename-1);
96  else
97  username = tor_strdup(filename+1);
98  if (!(home = get_user_homedir(username))) {
99  log_warn(LD_CONFIG,"Couldn't get homedir for \"%s\"",username);
100  tor_free(username);
101  return NULL;
102  }
103  tor_free(username);
104  rest = slash ? (slash+1) : "";
105 #else /* !(defined(HAVE_PWD_H)) */
106  log_warn(LD_CONFIG, "Couldn't expand homedir on system without pwd.h");
107  return tor_strdup(filename);
108 #endif /* defined(HAVE_PWD_H) */
109  }
110  tor_assert(home);
111  /* Remove trailing slash. */
112  if (strlen(home)>1 && !strcmpend(home,PATH_SEPARATOR)) {
113  home[strlen(home)-1] = '\0';
114  }
115  tor_asprintf(&result,"%s"PATH_SEPARATOR"%s",home,rest);
116  tor_free(home);
117  return result;
118  } else {
119  return tor_strdup(filename);
120  }
121 #endif /* defined(_WIN32) */
122 }
123 
125 int
126 path_is_relative(const char *filename)
127 {
128  if (filename && filename[0] == '/')
129  return 0;
130 #ifdef _WIN32
131  else if (filename && filename[0] == '\\')
132  return 0;
133  else if (filename && strlen(filename)>3 && TOR_ISALPHA(filename[0]) &&
134  filename[1] == ':' && filename[2] == '\\')
135  return 0;
136 #endif /* defined(_WIN32) */
137  else
138  return 1;
139 }
140 
144 void
146 {
147 #ifdef _WIN32
148  size_t len = strlen(name);
149  if (!len)
150  return;
151  if (name[len-1]=='\\' || name[len-1]=='/') {
152  if (len == 1 || (len==3 && name[1]==':'))
153  return;
154  name[len-1]='\0';
155  }
156 #else /* !(defined(_WIN32)) */
157  (void)name;
158 #endif /* defined(_WIN32) */
159 }
160 
175 int
177 {
178  char *cp;
179  int at_end = 1;
180  tor_assert(fname);
181 #ifdef _WIN32
182  /* If we start with, say, c:, then don't consider that the start of the path
183  */
184  if (fname[0] && fname[1] == ':') {
185  fname += 2;
186  }
187 #endif /* defined(_WIN32) */
188  /* Now we want to remove all path-separators at the end of the string,
189  * and to remove the end of the string starting with the path separator
190  * before the last non-path-separator. In perl, this would be
191  * s#[/]*$##; s#/[^/]*$##;
192  * on a unixy platform.
193  */
194  cp = fname + strlen(fname);
195  at_end = 1;
196  while (--cp >= fname) {
197  int is_sep = (*cp == '/'
198 #ifdef _WIN32
199  || *cp == '\\'
200 #endif
201  );
202  if (is_sep) {
203  if (cp == fname) {
204  /* This is the first separator in the file name; don't remove it! */
205  cp[1] = '\0';
206  return 0;
207  }
208  *cp = '\0';
209  if (! at_end)
210  return 0;
211  } else {
212  at_end = 0;
213  }
214  }
215  return -1;
216 }
217 
218 #ifndef _WIN32
219 
223 static char *
225 {
226 #ifdef HAVE_GET_CURRENT_DIR_NAME
227  /* Glibc makes this nice and simple for us. */
228  char *cwd = get_current_dir_name();
229  char *result = NULL;
230  if (cwd) {
231  /* We make a copy here, in case tor_malloc() is not malloc(). */
232  result = tor_strdup(cwd);
233  raw_free(cwd); // alias for free to avoid tripping check-spaces.
234  }
235  return result;
236 #else /* !(defined(HAVE_GET_CURRENT_DIR_NAME)) */
237  size_t size = 1024;
238  char *buf = NULL;
239  char *ptr = NULL;
240 
241  while (ptr == NULL) {
242  buf = tor_realloc(buf, size);
243  ptr = getcwd(buf, size);
244 
245  if (ptr == NULL && errno != ERANGE) {
246  tor_free(buf);
247  return NULL;
248  }
249 
250  size *= 2;
251  }
252  return buf;
253 #endif /* defined(HAVE_GET_CURRENT_DIR_NAME) */
254 }
255 #endif /* !defined(_WIN32) */
256 
259 char *
260 make_path_absolute(char *fname)
261 {
262 #ifdef _WIN32
263  char *absfname_malloced = _fullpath(NULL, fname, 1);
264 
265  /* We don't want to assume that tor_free can free a string allocated
266  * with malloc. On failure, return fname (it's better than nothing). */
267  char *absfname = tor_strdup(absfname_malloced ? absfname_malloced : fname);
268  if (absfname_malloced) raw_free(absfname_malloced);
269 
270  return absfname;
271 #else /* !(defined(_WIN32)) */
272  char *absfname = NULL, *path = NULL;
273 
274  tor_assert(fname);
275 
276  if (fname[0] == '/') {
277  absfname = tor_strdup(fname);
278  } else {
279  path = alloc_getcwd();
280  if (path) {
281  tor_asprintf(&absfname, "%s/%s", path, fname);
282  tor_free(path);
283  } else {
284  /* LCOV_EXCL_START Can't make getcwd fail. */
285  /* If getcwd failed, the best we can do here is keep using the
286  * relative path. (Perhaps / isn't readable by this UID/GID.) */
287  log_warn(LD_GENERAL, "Unable to find current working directory: %s",
288  strerror(errno));
289  absfname = tor_strdup(fname);
290  /* LCOV_EXCL_STOP */
291  }
292  }
293  return absfname;
294 #endif /* defined(_WIN32) */
295 }
Header for printf.c.
int path_is_relative(const char *filename)
Definition: path.c:126
#define LD_GENERAL
Definition: log.h:58
int strcmpend(const char *s1, const char *s2)
Definition: util_string.c:245
#define tor_free(p)
Definition: malloc.h:52
Header for util_string.c.
Headers for util_malloc.c.
void clean_fname_for_stat(char *name)
Definition: path.c:145
tor_assert(buffer)
int tor_asprintf(char **strp, const char *fmt,...)
Definition: printf.c:75
char * get_user_homedir(const char *username)
Definition: userdb.c:127
Header for path.c.
char * get_unquoted_path(const char *path)
Definition: path.c:32
char * expand_filename(const char *filename)
Definition: path.c:66
Locale-independent character-type inspection (header)
static char * alloc_getcwd(void)
Definition: path.c:224
Headers for log.c.
Header for userdb.c.
Macros to manage assertions, fatal and non-fatal.
int get_parent_directory(char *fname)
Definition: path.c:176
#define LD_CONFIG
Definition: log.h:64
char * make_path_absolute(char *fname)
Definition: path.c:260