Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 1 | #include "cache.h" |
| 2 | |
Junio C Hamano | 90b4a71 | 2008-09-09 01:27:07 -0700 | [diff] [blame] | 3 | /* |
| 4 | * Do not use this for inspecting *tracked* content. When path is a |
| 5 | * symlink to a directory, we do not want to say it is a directory when |
| 6 | * dealing with tracked content in the working tree. |
| 7 | */ |
| 8 | int is_directory(const char *path) |
| 9 | { |
| 10 | struct stat st; |
| 11 | return (!stat(path, &st) && S_ISDIR(st.st_mode)); |
| 12 | } |
| 13 | |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 14 | /* removes the last path component from 'path' except if 'path' is root */ |
| 15 | static void strip_last_component(struct strbuf *path) |
| 16 | { |
| 17 | size_t offset = offset_1st_component(path->buf); |
| 18 | size_t len = path->len; |
| 19 | |
| 20 | /* Find start of the last component */ |
| 21 | while (offset < len && !is_dir_sep(path->buf[len - 1])) |
| 22 | len--; |
| 23 | /* Skip sequences of multiple path-separators */ |
| 24 | while (offset < len && is_dir_sep(path->buf[len - 1])) |
| 25 | len--; |
| 26 | |
| 27 | strbuf_setlen(path, len); |
| 28 | } |
| 29 | |
| 30 | /* get (and remove) the next component in 'remaining' and place it in 'next' */ |
| 31 | static void get_next_component(struct strbuf *next, struct strbuf *remaining) |
| 32 | { |
| 33 | char *start = NULL; |
| 34 | char *end = NULL; |
| 35 | |
| 36 | strbuf_reset(next); |
| 37 | |
| 38 | /* look for the next component */ |
| 39 | /* Skip sequences of multiple path-separators */ |
| 40 | for (start = remaining->buf; is_dir_sep(*start); start++) |
| 41 | ; /* nothing */ |
| 42 | /* Find end of the path component */ |
| 43 | for (end = start; *end && !is_dir_sep(*end); end++) |
| 44 | ; /* nothing */ |
| 45 | |
| 46 | strbuf_add(next, start, end - start); |
| 47 | /* remove the component from 'remaining' */ |
| 48 | strbuf_remove(remaining, 0, end - remaining->buf); |
| 49 | } |
| 50 | |
Johannes Sixt | e9a379c | 2016-12-21 22:51:35 +0100 | [diff] [blame] | 51 | /* copies root part from remaining to resolved, canonicalizing it on the way */ |
| 52 | static void get_root_part(struct strbuf *resolved, struct strbuf *remaining) |
| 53 | { |
| 54 | int offset = offset_1st_component(remaining->buf); |
| 55 | |
| 56 | strbuf_reset(resolved); |
| 57 | strbuf_add(resolved, remaining->buf, offset); |
| 58 | #ifdef GIT_WINDOWS_NATIVE |
| 59 | convert_slashes(resolved->buf); |
| 60 | #endif |
| 61 | strbuf_remove(remaining, 0, offset); |
| 62 | } |
| 63 | |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 64 | /* We allow "recursive" symbolic links. Only within reason, though. */ |
Brandon Williams | 7aeb81f | 2017-01-09 10:50:23 -0800 | [diff] [blame] | 65 | #ifndef MAXSYMLINKS |
| 66 | #define MAXSYMLINKS 32 |
| 67 | #endif |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 68 | |
Carlos Martín Nieto | e2a57aa | 2011-03-17 12:26:46 +0100 | [diff] [blame] | 69 | /* |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 70 | * Return the real path (i.e., absolute path, with symlinks resolved |
| 71 | * and extra slashes removed) equivalent to the specified path. (If |
| 72 | * you want an absolute path but don't mind links, use |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 73 | * absolute_path().) Places the resolved realpath in the provided strbuf. |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 74 | * |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 75 | * The directory part of path (i.e., everything up to the last |
| 76 | * dir_sep) must denote a valid, existing directory, but the last |
| 77 | * component need not exist. If die_on_error is set, then die with an |
| 78 | * informative error message if there is a problem. Otherwise, return |
| 79 | * NULL on errors (without generating any output). |
Carlos Martín Nieto | e2a57aa | 2011-03-17 12:26:46 +0100 | [diff] [blame] | 80 | */ |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 81 | char *strbuf_realpath(struct strbuf *resolved, const char *path, |
| 82 | int die_on_error) |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 83 | { |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 84 | struct strbuf remaining = STRBUF_INIT; |
| 85 | struct strbuf next = STRBUF_INIT; |
| 86 | struct strbuf symlink = STRBUF_INIT; |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 87 | char *retval = NULL; |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 88 | int num_symlinks = 0; |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 89 | struct stat st; |
| 90 | |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 91 | if (!*path) { |
| 92 | if (die_on_error) |
| 93 | die("The empty string is not a valid path"); |
| 94 | else |
| 95 | goto error_out; |
| 96 | } |
Michael Haggerty | 3efe5d1 | 2012-09-07 00:41:01 +0200 | [diff] [blame] | 97 | |
Johannes Sixt | e9a379c | 2016-12-21 22:51:35 +0100 | [diff] [blame] | 98 | strbuf_addstr(&remaining, path); |
| 99 | get_root_part(resolved, &remaining); |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 100 | |
Johannes Sixt | e9a379c | 2016-12-21 22:51:35 +0100 | [diff] [blame] | 101 | if (!resolved->len) { |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 102 | /* relative path; can use CWD as the initial resolved path */ |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 103 | if (strbuf_getcwd(resolved)) { |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 104 | if (die_on_error) |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 105 | die_errno("unable to get current working directory"); |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 106 | else |
| 107 | goto error_out; |
| 108 | } |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 109 | } |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 110 | |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 111 | /* Iterate over the remaining path components */ |
| 112 | while (remaining.len > 0) { |
| 113 | get_next_component(&next, &remaining); |
| 114 | |
| 115 | if (next.len == 0) { |
| 116 | continue; /* empty component */ |
| 117 | } else if (next.len == 1 && !strcmp(next.buf, ".")) { |
| 118 | continue; /* '.' component */ |
| 119 | } else if (next.len == 2 && !strcmp(next.buf, "..")) { |
| 120 | /* '..' component; strip the last path component */ |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 121 | strip_last_component(resolved); |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 122 | continue; |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 123 | } |
| 124 | |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 125 | /* append the next component and resolve resultant path */ |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 126 | if (!is_dir_sep(resolved->buf[resolved->len - 1])) |
| 127 | strbuf_addch(resolved, '/'); |
| 128 | strbuf_addbuf(resolved, &next); |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 129 | |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 130 | if (lstat(resolved->buf, &st)) { |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 131 | /* error out unless this was the last component */ |
| 132 | if (errno != ENOENT || remaining.len) { |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 133 | if (die_on_error) |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 134 | die_errno("Invalid path '%s'", |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 135 | resolved->buf); |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 136 | else |
| 137 | goto error_out; |
| 138 | } |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 139 | } else if (S_ISLNK(st.st_mode)) { |
| 140 | ssize_t len; |
| 141 | strbuf_reset(&symlink); |
| 142 | |
| 143 | if (num_symlinks++ > MAXSYMLINKS) { |
Brandon Williams | 0b9864a | 2017-01-09 10:50:24 -0800 | [diff] [blame] | 144 | errno = ELOOP; |
| 145 | |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 146 | if (die_on_error) |
| 147 | die("More than %d nested symlinks " |
| 148 | "on path '%s'", MAXSYMLINKS, path); |
| 149 | else |
| 150 | goto error_out; |
| 151 | } |
| 152 | |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 153 | len = strbuf_readlink(&symlink, resolved->buf, |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 154 | st.st_size); |
| 155 | if (len < 0) { |
| 156 | if (die_on_error) |
| 157 | die_errno("Invalid symlink '%s'", |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 158 | resolved->buf); |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 159 | else |
| 160 | goto error_out; |
| 161 | } |
| 162 | |
| 163 | if (is_absolute_path(symlink.buf)) { |
| 164 | /* absolute symlink; set resolved to root */ |
Johannes Sixt | e9a379c | 2016-12-21 22:51:35 +0100 | [diff] [blame] | 165 | get_root_part(resolved, &symlink); |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 166 | } else { |
| 167 | /* |
| 168 | * relative symlink |
| 169 | * strip off the last component since it will |
| 170 | * be replaced with the contents of the symlink |
| 171 | */ |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 172 | strip_last_component(resolved); |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 173 | } |
| 174 | |
| 175 | /* |
| 176 | * if there are still remaining components to resolve |
| 177 | * then append them to symlink |
| 178 | */ |
| 179 | if (remaining.len) { |
| 180 | strbuf_addch(&symlink, '/'); |
| 181 | strbuf_addbuf(&symlink, &remaining); |
| 182 | } |
| 183 | |
| 184 | /* |
| 185 | * use the symlink as the remaining components that |
Ville Skyttä | 6412757 | 2017-06-25 13:20:41 +0300 | [diff] [blame] | 186 | * need to be resolved |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 187 | */ |
| 188 | strbuf_swap(&symlink, &remaining); |
| 189 | } |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 190 | } |
| 191 | |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 192 | retval = resolved->buf; |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 193 | |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 194 | error_out: |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 195 | strbuf_release(&remaining); |
| 196 | strbuf_release(&next); |
| 197 | strbuf_release(&symlink); |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 198 | |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 199 | if (!retval) |
| 200 | strbuf_reset(resolved); |
| 201 | |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 202 | return retval; |
| 203 | } |
| 204 | |
Han-Wen Nienhuys | d83d846 | 2017-09-26 13:21:48 +0200 | [diff] [blame] | 205 | /* |
| 206 | * Resolve `path` into an absolute, cleaned-up path. The return value |
| 207 | * comes from a shared buffer. |
| 208 | */ |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 209 | const char *real_path(const char *path) |
| 210 | { |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 211 | static struct strbuf realpath = STRBUF_INIT; |
| 212 | return strbuf_realpath(&realpath, path, 1); |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 213 | } |
Johannes Sixt | 10c4c88 | 2008-07-21 21:19:55 +0200 | [diff] [blame] | 214 | |
Michael Haggerty | e3e46cd | 2012-10-28 17:16:22 +0100 | [diff] [blame] | 215 | const char *real_path_if_valid(const char *path) |
| 216 | { |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 217 | static struct strbuf realpath = STRBUF_INIT; |
| 218 | return strbuf_realpath(&realpath, path, 0); |
Michael Haggerty | e3e46cd | 2012-10-28 17:16:22 +0100 | [diff] [blame] | 219 | } |
| 220 | |
Johannes Schindelin | ce83ead | 2017-03-08 16:43:40 +0100 | [diff] [blame] | 221 | char *real_pathdup(const char *path, int die_on_error) |
Brandon Williams | 7241764 | 2016-12-12 10:16:54 -0800 | [diff] [blame] | 222 | { |
| 223 | struct strbuf realpath = STRBUF_INIT; |
| 224 | char *retval = NULL; |
| 225 | |
Johannes Schindelin | ce83ead | 2017-03-08 16:43:40 +0100 | [diff] [blame] | 226 | if (strbuf_realpath(&realpath, path, die_on_error)) |
Brandon Williams | 7241764 | 2016-12-12 10:16:54 -0800 | [diff] [blame] | 227 | retval = strbuf_detach(&realpath, NULL); |
| 228 | |
| 229 | strbuf_release(&realpath); |
| 230 | |
| 231 | return retval; |
| 232 | } |
| 233 | |
Carlos Martín Nieto | e2a57aa | 2011-03-17 12:26:46 +0100 | [diff] [blame] | 234 | /* |
| 235 | * Use this to get an absolute path from a relative one. If you want |
| 236 | * to resolve links, you should use real_path. |
Carlos Martín Nieto | e2a57aa | 2011-03-17 12:26:46 +0100 | [diff] [blame] | 237 | */ |
| 238 | const char *absolute_path(const char *path) |
Johannes Sixt | 10c4c88 | 2008-07-21 21:19:55 +0200 | [diff] [blame] | 239 | { |
René Scharfe | 679eebe | 2014-07-28 20:33:55 +0200 | [diff] [blame] | 240 | static struct strbuf sb = STRBUF_INIT; |
| 241 | strbuf_reset(&sb); |
| 242 | strbuf_add_absolute_path(&sb, path); |
| 243 | return sb.buf; |
Johannes Sixt | 10c4c88 | 2008-07-21 21:19:55 +0200 | [diff] [blame] | 244 | } |
Dmitry Ivankov | 0687628 | 2011-08-11 15:15:38 +0600 | [diff] [blame] | 245 | |
René Scharfe | b1edb40 | 2017-01-26 18:47:45 +0100 | [diff] [blame] | 246 | char *absolute_pathdup(const char *path) |
| 247 | { |
| 248 | struct strbuf sb = STRBUF_INIT; |
| 249 | strbuf_add_absolute_path(&sb, path); |
| 250 | return strbuf_detach(&sb, NULL); |
| 251 | } |
| 252 | |
Jeff King | e4da43b | 2017-03-20 21:28:49 -0400 | [diff] [blame] | 253 | char *prefix_filename(const char *pfx, const char *arg) |
Dmitry Ivankov | 0687628 | 2011-08-11 15:15:38 +0600 | [diff] [blame] | 254 | { |
Jeff King | e4da43b | 2017-03-20 21:28:49 -0400 | [diff] [blame] | 255 | struct strbuf path = STRBUF_INIT; |
Jeff King | 116fb64 | 2017-03-20 21:22:28 -0400 | [diff] [blame] | 256 | size_t pfx_len = pfx ? strlen(pfx) : 0; |
| 257 | |
Jeff King | af10e8b | 2017-03-20 21:30:41 -0400 | [diff] [blame] | 258 | if (!pfx_len) |
| 259 | ; /* nothing to prefix */ |
| 260 | else if (is_absolute_path(arg)) |
Dmitry Ivankov | 0687628 | 2011-08-11 15:15:38 +0600 | [diff] [blame] | 261 | pfx_len = 0; |
Jeff King | af10e8b | 2017-03-20 21:30:41 -0400 | [diff] [blame] | 262 | else |
Antoine Pelisse | fc2b621 | 2013-12-14 12:31:16 +0100 | [diff] [blame] | 263 | strbuf_add(&path, pfx, pfx_len); |
Jeff King | af10e8b | 2017-03-20 21:30:41 -0400 | [diff] [blame] | 264 | |
Antoine Pelisse | fc2b621 | 2013-12-14 12:31:16 +0100 | [diff] [blame] | 265 | strbuf_addstr(&path, arg); |
Jeff King | af10e8b | 2017-03-20 21:30:41 -0400 | [diff] [blame] | 266 | #ifdef GIT_WINDOWS_NATIVE |
Johannes Sixt | 8e9b208 | 2016-04-02 21:03:14 +0200 | [diff] [blame] | 267 | convert_slashes(path.buf + pfx_len); |
Dmitry Ivankov | 0687628 | 2011-08-11 15:15:38 +0600 | [diff] [blame] | 268 | #endif |
Jeff King | e4da43b | 2017-03-20 21:28:49 -0400 | [diff] [blame] | 269 | return strbuf_detach(&path, NULL); |
Dmitry Ivankov | 0687628 | 2011-08-11 15:15:38 +0600 | [diff] [blame] | 270 | } |