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 | /* |
brian m. carlson | be6e0da | 2020-12-13 00:25:28 +0000 | [diff] [blame] | 70 | * If set, any number of trailing components may be missing; otherwise, only one |
| 71 | * may be. |
Carlos Martín Nieto | e2a57aa | 2011-03-17 12:26:46 +0100 | [diff] [blame] | 72 | */ |
brian m. carlson | be6e0da | 2020-12-13 00:25:28 +0000 | [diff] [blame] | 73 | #define REALPATH_MANY_MISSING (1 << 0) |
| 74 | /* Should we die if there's an error? */ |
| 75 | #define REALPATH_DIE_ON_ERROR (1 << 1) |
| 76 | |
| 77 | static char *strbuf_realpath_1(struct strbuf *resolved, const char *path, |
| 78 | int flags) |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 79 | { |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 80 | struct strbuf remaining = STRBUF_INIT; |
| 81 | struct strbuf next = STRBUF_INIT; |
| 82 | struct strbuf symlink = STRBUF_INIT; |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 83 | char *retval = NULL; |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 84 | int num_symlinks = 0; |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 85 | struct stat st; |
| 86 | |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 87 | if (!*path) { |
brian m. carlson | be6e0da | 2020-12-13 00:25:28 +0000 | [diff] [blame] | 88 | if (flags & REALPATH_DIE_ON_ERROR) |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 89 | die("The empty string is not a valid path"); |
| 90 | else |
| 91 | goto error_out; |
| 92 | } |
Michael Haggerty | 3efe5d1 | 2012-09-07 00:41:01 +0200 | [diff] [blame] | 93 | |
Johannes Sixt | e9a379c | 2016-12-21 22:51:35 +0100 | [diff] [blame] | 94 | strbuf_addstr(&remaining, path); |
| 95 | get_root_part(resolved, &remaining); |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 96 | |
Johannes Sixt | e9a379c | 2016-12-21 22:51:35 +0100 | [diff] [blame] | 97 | if (!resolved->len) { |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 98 | /* relative path; can use CWD as the initial resolved path */ |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 99 | if (strbuf_getcwd(resolved)) { |
brian m. carlson | be6e0da | 2020-12-13 00:25:28 +0000 | [diff] [blame] | 100 | if (flags & REALPATH_DIE_ON_ERROR) |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 101 | die_errno("unable to get current working directory"); |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 102 | else |
| 103 | goto error_out; |
| 104 | } |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 105 | } |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 106 | |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 107 | /* Iterate over the remaining path components */ |
| 108 | while (remaining.len > 0) { |
| 109 | get_next_component(&next, &remaining); |
| 110 | |
| 111 | if (next.len == 0) { |
| 112 | continue; /* empty component */ |
| 113 | } else if (next.len == 1 && !strcmp(next.buf, ".")) { |
| 114 | continue; /* '.' component */ |
| 115 | } else if (next.len == 2 && !strcmp(next.buf, "..")) { |
| 116 | /* '..' component; strip the last path component */ |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 117 | strip_last_component(resolved); |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 118 | continue; |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 119 | } |
| 120 | |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 121 | /* append the next component and resolve resultant path */ |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 122 | if (!is_dir_sep(resolved->buf[resolved->len - 1])) |
| 123 | strbuf_addch(resolved, '/'); |
| 124 | strbuf_addbuf(resolved, &next); |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 125 | |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 126 | if (lstat(resolved->buf, &st)) { |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 127 | /* error out unless this was the last component */ |
brian m. carlson | be6e0da | 2020-12-13 00:25:28 +0000 | [diff] [blame] | 128 | if (errno != ENOENT || |
| 129 | (!(flags & REALPATH_MANY_MISSING) && remaining.len)) { |
| 130 | if (flags & REALPATH_DIE_ON_ERROR) |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 131 | die_errno("Invalid path '%s'", |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 132 | resolved->buf); |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 133 | else |
| 134 | goto error_out; |
| 135 | } |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 136 | } else if (S_ISLNK(st.st_mode)) { |
| 137 | ssize_t len; |
| 138 | strbuf_reset(&symlink); |
| 139 | |
| 140 | if (num_symlinks++ > MAXSYMLINKS) { |
Brandon Williams | 0b9864a | 2017-01-09 10:50:24 -0800 | [diff] [blame] | 141 | errno = ELOOP; |
| 142 | |
brian m. carlson | be6e0da | 2020-12-13 00:25:28 +0000 | [diff] [blame] | 143 | if (flags & REALPATH_DIE_ON_ERROR) |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 144 | die("More than %d nested symlinks " |
| 145 | "on path '%s'", MAXSYMLINKS, path); |
| 146 | else |
| 147 | goto error_out; |
| 148 | } |
| 149 | |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 150 | len = strbuf_readlink(&symlink, resolved->buf, |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 151 | st.st_size); |
| 152 | if (len < 0) { |
brian m. carlson | be6e0da | 2020-12-13 00:25:28 +0000 | [diff] [blame] | 153 | if (flags & REALPATH_DIE_ON_ERROR) |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 154 | die_errno("Invalid symlink '%s'", |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 155 | resolved->buf); |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 156 | else |
| 157 | goto error_out; |
| 158 | } |
| 159 | |
| 160 | if (is_absolute_path(symlink.buf)) { |
| 161 | /* absolute symlink; set resolved to root */ |
Johannes Sixt | e9a379c | 2016-12-21 22:51:35 +0100 | [diff] [blame] | 162 | get_root_part(resolved, &symlink); |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 163 | } else { |
| 164 | /* |
| 165 | * relative symlink |
| 166 | * strip off the last component since it will |
| 167 | * be replaced with the contents of the symlink |
| 168 | */ |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 169 | strip_last_component(resolved); |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 170 | } |
| 171 | |
| 172 | /* |
| 173 | * if there are still remaining components to resolve |
| 174 | * then append them to symlink |
| 175 | */ |
| 176 | if (remaining.len) { |
| 177 | strbuf_addch(&symlink, '/'); |
| 178 | strbuf_addbuf(&symlink, &remaining); |
| 179 | } |
| 180 | |
| 181 | /* |
| 182 | * use the symlink as the remaining components that |
Ville Skyttä | 6412757 | 2017-06-25 13:20:41 +0300 | [diff] [blame] | 183 | * need to be resolved |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 184 | */ |
| 185 | strbuf_swap(&symlink, &remaining); |
| 186 | } |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 187 | } |
| 188 | |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 189 | retval = resolved->buf; |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 190 | |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 191 | error_out: |
Brandon Williams | 05b458c | 2016-12-12 10:16:52 -0800 | [diff] [blame] | 192 | strbuf_release(&remaining); |
| 193 | strbuf_release(&next); |
| 194 | strbuf_release(&symlink); |
Dmitry Potapov | 5b8e6f8 | 2008-06-28 00:46:42 +0400 | [diff] [blame] | 195 | |
Brandon Williams | a1ae484 | 2016-12-12 10:16:53 -0800 | [diff] [blame] | 196 | if (!retval) |
| 197 | strbuf_reset(resolved); |
| 198 | |
Michael Haggerty | 038e55f | 2012-10-28 17:16:20 +0100 | [diff] [blame] | 199 | return retval; |
| 200 | } |
| 201 | |
brian m. carlson | be6e0da | 2020-12-13 00:25:28 +0000 | [diff] [blame] | 202 | /* |
| 203 | * Return the real path (i.e., absolute path, with symlinks resolved |
| 204 | * and extra slashes removed) equivalent to the specified path. (If |
| 205 | * you want an absolute path but don't mind links, use |
| 206 | * absolute_path().) Places the resolved realpath in the provided strbuf. |
| 207 | * |
| 208 | * The directory part of path (i.e., everything up to the last |
| 209 | * dir_sep) must denote a valid, existing directory, but the last |
| 210 | * component need not exist. If die_on_error is set, then die with an |
| 211 | * informative error message if there is a problem. Otherwise, return |
| 212 | * NULL on errors (without generating any output). |
| 213 | */ |
| 214 | char *strbuf_realpath(struct strbuf *resolved, const char *path, |
| 215 | int die_on_error) |
| 216 | { |
| 217 | return strbuf_realpath_1(resolved, path, |
| 218 | die_on_error ? REALPATH_DIE_ON_ERROR : 0); |
| 219 | } |
| 220 | |
| 221 | /* |
| 222 | * Just like strbuf_realpath, but allows an arbitrary number of path |
| 223 | * components to be missing. |
| 224 | */ |
| 225 | char *strbuf_realpath_forgiving(struct strbuf *resolved, const char *path, |
| 226 | int die_on_error) |
| 227 | { |
| 228 | return strbuf_realpath_1(resolved, path, |
| 229 | ((die_on_error ? REALPATH_DIE_ON_ERROR : 0) | |
| 230 | REALPATH_MANY_MISSING)); |
| 231 | } |
| 232 | |
Johannes Schindelin | ce83ead | 2017-03-08 16:43:40 +0100 | [diff] [blame] | 233 | char *real_pathdup(const char *path, int die_on_error) |
Brandon Williams | 7241764 | 2016-12-12 10:16:54 -0800 | [diff] [blame] | 234 | { |
| 235 | struct strbuf realpath = STRBUF_INIT; |
| 236 | char *retval = NULL; |
| 237 | |
Johannes Schindelin | ce83ead | 2017-03-08 16:43:40 +0100 | [diff] [blame] | 238 | if (strbuf_realpath(&realpath, path, die_on_error)) |
Brandon Williams | 7241764 | 2016-12-12 10:16:54 -0800 | [diff] [blame] | 239 | retval = strbuf_detach(&realpath, NULL); |
| 240 | |
| 241 | strbuf_release(&realpath); |
| 242 | |
| 243 | return retval; |
| 244 | } |
| 245 | |
Carlos Martín Nieto | e2a57aa | 2011-03-17 12:26:46 +0100 | [diff] [blame] | 246 | /* |
| 247 | * Use this to get an absolute path from a relative one. If you want |
Alexandr Miloslavskiy | 3d7747e | 2020-03-10 13:11:22 +0000 | [diff] [blame] | 248 | * to resolve links, you should use strbuf_realpath. |
Carlos Martín Nieto | e2a57aa | 2011-03-17 12:26:46 +0100 | [diff] [blame] | 249 | */ |
| 250 | const char *absolute_path(const char *path) |
Johannes Sixt | 10c4c88 | 2008-07-21 21:19:55 +0200 | [diff] [blame] | 251 | { |
René Scharfe | 679eebe | 2014-07-28 20:33:55 +0200 | [diff] [blame] | 252 | static struct strbuf sb = STRBUF_INIT; |
| 253 | strbuf_reset(&sb); |
| 254 | strbuf_add_absolute_path(&sb, path); |
| 255 | return sb.buf; |
Johannes Sixt | 10c4c88 | 2008-07-21 21:19:55 +0200 | [diff] [blame] | 256 | } |
Dmitry Ivankov | 0687628 | 2011-08-11 15:15:38 +0600 | [diff] [blame] | 257 | |
René Scharfe | b1edb40 | 2017-01-26 18:47:45 +0100 | [diff] [blame] | 258 | char *absolute_pathdup(const char *path) |
| 259 | { |
| 260 | struct strbuf sb = STRBUF_INIT; |
| 261 | strbuf_add_absolute_path(&sb, path); |
| 262 | return strbuf_detach(&sb, NULL); |
| 263 | } |
| 264 | |
Jeff King | e4da43b | 2017-03-20 21:28:49 -0400 | [diff] [blame] | 265 | char *prefix_filename(const char *pfx, const char *arg) |
Dmitry Ivankov | 0687628 | 2011-08-11 15:15:38 +0600 | [diff] [blame] | 266 | { |
Jeff King | e4da43b | 2017-03-20 21:28:49 -0400 | [diff] [blame] | 267 | struct strbuf path = STRBUF_INIT; |
Jeff King | 116fb64 | 2017-03-20 21:22:28 -0400 | [diff] [blame] | 268 | size_t pfx_len = pfx ? strlen(pfx) : 0; |
| 269 | |
Jeff King | af10e8b | 2017-03-20 21:30:41 -0400 | [diff] [blame] | 270 | if (!pfx_len) |
| 271 | ; /* nothing to prefix */ |
| 272 | else if (is_absolute_path(arg)) |
Dmitry Ivankov | 0687628 | 2011-08-11 15:15:38 +0600 | [diff] [blame] | 273 | pfx_len = 0; |
Jeff King | af10e8b | 2017-03-20 21:30:41 -0400 | [diff] [blame] | 274 | else |
Antoine Pelisse | fc2b621 | 2013-12-14 12:31:16 +0100 | [diff] [blame] | 275 | strbuf_add(&path, pfx, pfx_len); |
Jeff King | af10e8b | 2017-03-20 21:30:41 -0400 | [diff] [blame] | 276 | |
Antoine Pelisse | fc2b621 | 2013-12-14 12:31:16 +0100 | [diff] [blame] | 277 | strbuf_addstr(&path, arg); |
Jeff King | af10e8b | 2017-03-20 21:30:41 -0400 | [diff] [blame] | 278 | #ifdef GIT_WINDOWS_NATIVE |
Johannes Sixt | 8e9b208 | 2016-04-02 21:03:14 +0200 | [diff] [blame] | 279 | convert_slashes(path.buf + pfx_len); |
Dmitry Ivankov | 0687628 | 2011-08-11 15:15:38 +0600 | [diff] [blame] | 280 | #endif |
Jeff King | e4da43b | 2017-03-20 21:28:49 -0400 | [diff] [blame] | 281 | return strbuf_detach(&path, NULL); |
Dmitry Ivankov | 0687628 | 2011-08-11 15:15:38 +0600 | [diff] [blame] | 282 | } |