Git fork
at reftables-rust 327 lines 8.6 kB view raw
1#include "git-compat-util.h" 2#include "abspath.h" 3#include "strbuf.h" 4 5/* 6 * Do not use this for inspecting *tracked* content. When path is a 7 * symlink to a directory, we do not want to say it is a directory when 8 * dealing with tracked content in the working tree. 9 */ 10int is_directory(const char *path) 11{ 12 struct stat st; 13 return (!stat(path, &st) && S_ISDIR(st.st_mode)); 14} 15 16/* removes the last path component from 'path' except if 'path' is root */ 17static void strip_last_component(struct strbuf *path) 18{ 19 size_t offset = offset_1st_component(path->buf); 20 size_t len = path->len; 21 22 /* Find start of the last component */ 23 while (offset < len && !is_dir_sep(path->buf[len - 1])) 24 len--; 25 /* Skip sequences of multiple path-separators */ 26 while (offset < len && is_dir_sep(path->buf[len - 1])) 27 len--; 28 29 strbuf_setlen(path, len); 30} 31 32/* get (and remove) the next component in 'remaining' and place it in 'next' */ 33static void get_next_component(struct strbuf *next, struct strbuf *remaining) 34{ 35 char *start = NULL; 36 char *end = NULL; 37 38 strbuf_reset(next); 39 40 /* look for the next component */ 41 /* Skip sequences of multiple path-separators */ 42 for (start = remaining->buf; is_dir_sep(*start); start++) 43 ; /* nothing */ 44 /* Find end of the path component */ 45 for (end = start; *end && !is_dir_sep(*end); end++) 46 ; /* nothing */ 47 48 strbuf_add(next, start, end - start); 49 /* remove the component from 'remaining' */ 50 strbuf_remove(remaining, 0, end - remaining->buf); 51} 52 53/* copies root part from remaining to resolved, canonicalizing it on the way */ 54static void get_root_part(struct strbuf *resolved, struct strbuf *remaining) 55{ 56 int offset = offset_1st_component(remaining->buf); 57 58 strbuf_reset(resolved); 59 strbuf_add(resolved, remaining->buf, offset); 60#ifdef GIT_WINDOWS_NATIVE 61 convert_slashes(resolved->buf); 62#endif 63 strbuf_remove(remaining, 0, offset); 64} 65 66/* We allow "recursive" symbolic links. Only within reason, though. */ 67#ifndef MAXSYMLINKS 68#define MAXSYMLINKS 32 69#endif 70 71/* 72 * If set, any number of trailing components may be missing; otherwise, only one 73 * may be. 74 */ 75#define REALPATH_MANY_MISSING (1 << 0) 76/* Should we die if there's an error? */ 77#define REALPATH_DIE_ON_ERROR (1 << 1) 78 79static char *strbuf_realpath_1(struct strbuf *resolved, const char *path, 80 int flags) 81{ 82 struct strbuf remaining = STRBUF_INIT; 83 struct strbuf next = STRBUF_INIT; 84 struct strbuf symlink = STRBUF_INIT; 85 char *retval = NULL; 86 int num_symlinks = 0; 87 struct stat st; 88 89 if (!*path) { 90 if (flags & REALPATH_DIE_ON_ERROR) 91 die("The empty string is not a valid path"); 92 else 93 goto error_out; 94 } 95 96 strbuf_addstr(&remaining, path); 97 get_root_part(resolved, &remaining); 98 99 if (!resolved->len) { 100 /* relative path; can use CWD as the initial resolved path */ 101 if (strbuf_getcwd(resolved)) { 102 if (flags & REALPATH_DIE_ON_ERROR) 103 die_errno("unable to get current working directory"); 104 else 105 goto error_out; 106 } 107 } 108 109 /* Iterate over the remaining path components */ 110 while (remaining.len > 0) { 111 get_next_component(&next, &remaining); 112 113 if (next.len == 0) { 114 continue; /* empty component */ 115 } else if (next.len == 1 && !strcmp(next.buf, ".")) { 116 continue; /* '.' component */ 117 } else if (next.len == 2 && !strcmp(next.buf, "..")) { 118 /* '..' component; strip the last path component */ 119 strip_last_component(resolved); 120 continue; 121 } 122 123 /* append the next component and resolve resultant path */ 124 if (!is_dir_sep(resolved->buf[resolved->len - 1])) 125 strbuf_addch(resolved, '/'); 126 strbuf_addbuf(resolved, &next); 127 128 if (lstat(resolved->buf, &st)) { 129 /* error out unless this was the last component */ 130 if (errno != ENOENT || 131 (!(flags & REALPATH_MANY_MISSING) && remaining.len)) { 132 if (flags & REALPATH_DIE_ON_ERROR) 133 die_errno("Invalid path '%s'", 134 resolved->buf); 135 else 136 goto error_out; 137 } 138 } else if (S_ISLNK(st.st_mode)) { 139 ssize_t len; 140 strbuf_reset(&symlink); 141 142 if (num_symlinks++ > MAXSYMLINKS) { 143 errno = ELOOP; 144 145 if (flags & REALPATH_DIE_ON_ERROR) 146 die("More than %d nested symlinks " 147 "on path '%s'", MAXSYMLINKS, path); 148 else 149 goto error_out; 150 } 151 152 len = strbuf_readlink(&symlink, resolved->buf, 153 st.st_size); 154 if (len < 0) { 155 if (flags & REALPATH_DIE_ON_ERROR) 156 die_errno("Invalid symlink '%s'", 157 resolved->buf); 158 else 159 goto error_out; 160 } 161 162 if (is_absolute_path(symlink.buf)) { 163 /* absolute symlink; set resolved to root */ 164 get_root_part(resolved, &symlink); 165 } else { 166 /* 167 * relative symlink 168 * strip off the last component since it will 169 * be replaced with the contents of the symlink 170 */ 171 strip_last_component(resolved); 172 } 173 174 /* 175 * if there are still remaining components to resolve 176 * then append them to symlink 177 */ 178 if (remaining.len) { 179 strbuf_addch(&symlink, '/'); 180 strbuf_addbuf(&symlink, &remaining); 181 } 182 183 /* 184 * use the symlink as the remaining components that 185 * need to be resolved 186 */ 187 strbuf_swap(&symlink, &remaining); 188 } 189 } 190 191 retval = resolved->buf; 192 193error_out: 194 strbuf_release(&remaining); 195 strbuf_release(&next); 196 strbuf_release(&symlink); 197 198 if (!retval) 199 strbuf_reset(resolved); 200 201 return retval; 202} 203 204/* 205 * Return the real path (i.e., absolute path, with symlinks resolved 206 * and extra slashes removed) equivalent to the specified path. (If 207 * you want an absolute path but don't mind links, use 208 * absolute_path().) Places the resolved realpath in the provided strbuf. 209 * 210 * The directory part of path (i.e., everything up to the last 211 * dir_sep) must denote a valid, existing directory, but the last 212 * component need not exist. If die_on_error is set, then die with an 213 * informative error message if there is a problem. Otherwise, return 214 * NULL on errors (without generating any output). 215 */ 216char *strbuf_realpath(struct strbuf *resolved, const char *path, 217 int die_on_error) 218{ 219 return strbuf_realpath_1(resolved, path, 220 die_on_error ? REALPATH_DIE_ON_ERROR : 0); 221} 222 223/* 224 * Just like strbuf_realpath, but allows an arbitrary number of path 225 * components to be missing. 226 */ 227char *strbuf_realpath_forgiving(struct strbuf *resolved, const char *path, 228 int die_on_error) 229{ 230 return strbuf_realpath_1(resolved, path, 231 ((die_on_error ? REALPATH_DIE_ON_ERROR : 0) | 232 REALPATH_MANY_MISSING)); 233} 234 235char *real_pathdup(const char *path, int die_on_error) 236{ 237 struct strbuf realpath = STRBUF_INIT; 238 char *retval = NULL; 239 240 if (strbuf_realpath(&realpath, path, die_on_error)) 241 retval = strbuf_detach(&realpath, NULL); 242 243 strbuf_release(&realpath); 244 245 return retval; 246} 247 248/* 249 * Use this to get an absolute path from a relative one. If you want 250 * to resolve links, you should use strbuf_realpath. 251 */ 252const char *absolute_path(const char *path) 253{ 254 static struct strbuf sb = STRBUF_INIT; 255 strbuf_reset(&sb); 256 strbuf_add_absolute_path(&sb, path); 257 return sb.buf; 258} 259 260char *absolute_pathdup(const char *path) 261{ 262 struct strbuf sb = STRBUF_INIT; 263 strbuf_add_absolute_path(&sb, path); 264 return strbuf_detach(&sb, NULL); 265} 266 267char *prefix_filename(const char *pfx, const char *arg) 268{ 269 struct strbuf path = STRBUF_INIT; 270 size_t pfx_len = pfx ? strlen(pfx) : 0; 271 272 if (!pfx_len) 273 ; /* nothing to prefix */ 274 else if (is_absolute_path(arg)) 275 pfx_len = 0; 276 else 277 strbuf_add(&path, pfx, pfx_len); 278 279 strbuf_addstr(&path, arg); 280#ifdef GIT_WINDOWS_NATIVE 281 convert_slashes(path.buf + pfx_len); 282#endif 283 return strbuf_detach(&path, NULL); 284} 285 286char *prefix_filename_except_for_dash(const char *pfx, const char *arg) 287{ 288 if (!strcmp(arg, "-")) 289 return xstrdup(arg); 290 return prefix_filename(pfx, arg); 291} 292 293void strbuf_add_absolute_path(struct strbuf *sb, const char *path) 294{ 295 if (!*path) 296 die("The empty string is not a valid path"); 297 if (!is_absolute_path(path)) { 298 struct stat cwd_stat, pwd_stat; 299 size_t orig_len = sb->len; 300 char *cwd = xgetcwd(); 301 char *pwd = getenv("PWD"); 302 if (pwd && strcmp(pwd, cwd) && 303 !stat(cwd, &cwd_stat) && 304 (cwd_stat.st_dev || cwd_stat.st_ino) && 305 !stat(pwd, &pwd_stat) && 306 pwd_stat.st_dev == cwd_stat.st_dev && 307 pwd_stat.st_ino == cwd_stat.st_ino) 308 strbuf_addstr(sb, pwd); 309 else 310 strbuf_addstr(sb, cwd); 311 if (sb->len > orig_len && !is_dir_sep(sb->buf[sb->len - 1])) 312 strbuf_addch(sb, '/'); 313 free(cwd); 314 } 315 strbuf_addstr(sb, path); 316} 317 318void strbuf_add_real_path(struct strbuf *sb, const char *path) 319{ 320 if (sb->len) { 321 struct strbuf resolved = STRBUF_INIT; 322 strbuf_realpath(&resolved, path, 1); 323 strbuf_addbuf(sb, &resolved); 324 strbuf_release(&resolved); 325 } else 326 strbuf_realpath(sb, path, 1); 327}