Coverage Report

Created: 2023-11-19 07:08

/src/git/abspath.c
Line
Count
Source (jump to first uncovered line)
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
 */
10
int is_directory(const char *path)
11
7.22k
{
12
7.22k
  struct stat st;
13
7.22k
  return (!stat(path, &st) && S_ISDIR(st.st_mode));
14
7.22k
}
15
16
/* removes the last path component from 'path' except if 'path' is root */
17
static void strip_last_component(struct strbuf *path)
18
0
{
19
0
  size_t offset = offset_1st_component(path->buf);
20
0
  size_t len = path->len;
21
22
  /* Find start of the last component */
23
0
  while (offset < len && !is_dir_sep(path->buf[len - 1]))
24
0
    len--;
25
  /* Skip sequences of multiple path-separators */
26
0
  while (offset < len && is_dir_sep(path->buf[len - 1]))
27
0
    len--;
28
29
0
  strbuf_setlen(path, len);
30
0
}
31
32
/* get (and remove) the next component in 'remaining' and place it in 'next' */
33
static void get_next_component(struct strbuf *next, struct strbuf *remaining)
34
47.4k
{
35
47.4k
  char *start = NULL;
36
47.4k
  char *end = NULL;
37
38
47.4k
  strbuf_reset(next);
39
40
  /* look for the next component */
41
  /* Skip sequences of multiple path-separators */
42
79.9k
  for (start = remaining->buf; is_dir_sep(*start); start++)
43
32.4k
    ; /* nothing */
44
  /* Find end of the path component */
45
612k
  for (end = start; *end && !is_dir_sep(*end); end++)
46
564k
    ; /* nothing */
47
48
47.4k
  strbuf_add(next, start, end - start);
49
  /* remove the component from 'remaining' */
50
47.4k
  strbuf_remove(remaining, 0, end - remaining->buf);
51
47.4k
}
52
53
/* copies root part from remaining to resolved, canonicalizing it on the way */
54
static void get_root_part(struct strbuf *resolved, struct strbuf *remaining)
55
15.0k
{
56
15.0k
  int offset = offset_1st_component(remaining->buf);
57
58
15.0k
  strbuf_reset(resolved);
59
15.0k
  strbuf_add(resolved, remaining->buf, offset);
60
#ifdef GIT_WINDOWS_NATIVE
61
  convert_slashes(resolved->buf);
62
#endif
63
15.0k
  strbuf_remove(remaining, 0, offset);
64
15.0k
}
65
66
/* We allow "recursive" symbolic links. Only within reason, though. */
67
#ifndef MAXSYMLINKS
68
0
#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
2.44k
#define REALPATH_MANY_MISSING (1 << 0)
76
/* Should we die if there's an error? */
77
15.0k
#define REALPATH_DIE_ON_ERROR (1 << 1)
78
79
static char *strbuf_realpath_1(struct strbuf *resolved, const char *path,
80
             int flags)
81
15.0k
{
82
15.0k
  struct strbuf remaining = STRBUF_INIT;
83
15.0k
  struct strbuf next = STRBUF_INIT;
84
15.0k
  struct strbuf symlink = STRBUF_INIT;
85
15.0k
  char *retval = NULL;
86
15.0k
  int num_symlinks = 0;
87
15.0k
  struct stat st;
88
89
15.0k
  if (!*path) {
90
0
    if (flags & REALPATH_DIE_ON_ERROR)
91
0
      die("The empty string is not a valid path");
92
0
    else
93
0
      goto error_out;
94
0
  }
95
96
15.0k
  strbuf_addstr(&remaining, path);
97
15.0k
  get_root_part(resolved, &remaining);
98
99
15.0k
  if (!resolved->len) {
100
    /* relative path; can use CWD as the initial resolved path */
101
2
    if (strbuf_getcwd(resolved)) {
102
0
      if (flags & REALPATH_DIE_ON_ERROR)
103
0
        die_errno("unable to get current working directory");
104
0
      else
105
0
        goto error_out;
106
0
    }
107
2
  }
108
109
  /* Iterate over the remaining path components */
110
62.4k
  while (remaining.len > 0) {
111
47.4k
    get_next_component(&next, &remaining);
112
113
47.4k
    if (next.len == 0) {
114
0
      continue; /* empty component */
115
47.4k
    } else if (next.len == 1 && !strcmp(next.buf, ".")) {
116
0
      continue; /* '.' component */
117
47.4k
    } else if (next.len == 2 && !strcmp(next.buf, "..")) {
118
      /* '..' component; strip the last path component */
119
0
      strip_last_component(resolved);
120
0
      continue;
121
0
    }
122
123
    /* append the next component and resolve resultant path */
124
47.4k
    if (!is_dir_sep(resolved->buf[resolved->len - 1]))
125
32.4k
      strbuf_addch(resolved, '/');
126
47.4k
    strbuf_addbuf(resolved, &next);
127
128
47.4k
    if (lstat(resolved->buf, &st)) {
129
      /* error out unless this was the last component */
130
2.44k
      if (errno != ENOENT ||
131
2.44k
         (!(flags & REALPATH_MANY_MISSING) && remaining.len)) {
132
0
        if (flags & REALPATH_DIE_ON_ERROR)
133
0
          die_errno("Invalid path '%s'",
134
0
              resolved->buf);
135
0
        else
136
0
          goto error_out;
137
0
      }
138
45.0k
    } else if (S_ISLNK(st.st_mode)) {
139
0
      ssize_t len;
140
0
      strbuf_reset(&symlink);
141
142
0
      if (num_symlinks++ > MAXSYMLINKS) {
143
0
        errno = ELOOP;
144
145
0
        if (flags & REALPATH_DIE_ON_ERROR)
146
0
          die("More than %d nested symlinks "
147
0
              "on path '%s'", MAXSYMLINKS, path);
148
0
        else
149
0
          goto error_out;
150
0
      }
151
152
0
      len = strbuf_readlink(&symlink, resolved->buf,
153
0
                st.st_size);
154
0
      if (len < 0) {
155
0
        if (flags & REALPATH_DIE_ON_ERROR)
156
0
          die_errno("Invalid symlink '%s'",
157
0
              resolved->buf);
158
0
        else
159
0
          goto error_out;
160
0
      }
161
162
0
      if (is_absolute_path(symlink.buf)) {
163
        /* absolute symlink; set resolved to root */
164
0
        get_root_part(resolved, &symlink);
165
0
      } 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
0
        strip_last_component(resolved);
172
0
      }
173
174
      /*
175
       * if there are still remaining components to resolve
176
       * then append them to symlink
177
       */
178
0
      if (remaining.len) {
179
0
        strbuf_addch(&symlink, '/');
180
0
        strbuf_addbuf(&symlink, &remaining);
181
0
      }
182
183
      /*
184
       * use the symlink as the remaining components that
185
       * need to be resolved
186
       */
187
0
      strbuf_swap(&symlink, &remaining);
188
0
    }
189
47.4k
  }
190
191
15.0k
  retval = resolved->buf;
192
193
15.0k
error_out:
194
15.0k
  strbuf_release(&remaining);
195
15.0k
  strbuf_release(&next);
196
15.0k
  strbuf_release(&symlink);
197
198
15.0k
  if (!retval)
199
0
    strbuf_reset(resolved);
200
201
15.0k
  return retval;
202
15.0k
}
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
 */
216
char *strbuf_realpath(struct strbuf *resolved, const char *path,
217
          int die_on_error)
218
15.0k
{
219
15.0k
  return strbuf_realpath_1(resolved, path,
220
15.0k
         die_on_error ? REALPATH_DIE_ON_ERROR : 0);
221
15.0k
}
222
223
/*
224
 * Just like strbuf_realpath, but allows an arbitrary number of path
225
 * components to be missing.
226
 */
227
char *strbuf_realpath_forgiving(struct strbuf *resolved, const char *path,
228
        int die_on_error)
229
0
{
230
0
  return strbuf_realpath_1(resolved, path,
231
0
         ((die_on_error ? REALPATH_DIE_ON_ERROR : 0) |
232
0
          REALPATH_MANY_MISSING));
233
0
}
234
235
char *real_pathdup(const char *path, int die_on_error)
236
13.7k
{
237
13.7k
  struct strbuf realpath = STRBUF_INIT;
238
13.7k
  char *retval = NULL;
239
240
13.7k
  if (strbuf_realpath(&realpath, path, die_on_error))
241
13.7k
    retval = strbuf_detach(&realpath, NULL);
242
243
13.7k
  strbuf_release(&realpath);
244
245
13.7k
  return retval;
246
13.7k
}
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
 */
252
const char *absolute_path(const char *path)
253
0
{
254
0
  static struct strbuf sb = STRBUF_INIT;
255
0
  strbuf_reset(&sb);
256
0
  strbuf_add_absolute_path(&sb, path);
257
0
  return sb.buf;
258
0
}
259
260
char *absolute_pathdup(const char *path)
261
0
{
262
0
  struct strbuf sb = STRBUF_INIT;
263
0
  strbuf_add_absolute_path(&sb, path);
264
0
  return strbuf_detach(&sb, NULL);
265
0
}
266
267
char *prefix_filename(const char *pfx, const char *arg)
268
3.37k
{
269
3.37k
  struct strbuf path = STRBUF_INIT;
270
3.37k
  size_t pfx_len = pfx ? strlen(pfx) : 0;
271
272
3.37k
  if (!pfx_len)
273
3.37k
    ; /* nothing to prefix */
274
0
  else if (is_absolute_path(arg))
275
0
    pfx_len = 0;
276
0
  else
277
0
    strbuf_add(&path, pfx, pfx_len);
278
279
3.37k
  strbuf_addstr(&path, arg);
280
#ifdef GIT_WINDOWS_NATIVE
281
  convert_slashes(path.buf + pfx_len);
282
#endif
283
3.37k
  return strbuf_detach(&path, NULL);
284
3.37k
}
285
286
char *prefix_filename_except_for_dash(const char *pfx, const char *arg)
287
0
{
288
0
  if (!strcmp(arg, "-"))
289
0
    return xstrdup(arg);
290
0
  return prefix_filename(pfx, arg);
291
0
}
292
293
void strbuf_add_absolute_path(struct strbuf *sb, const char *path)
294
35.1k
{
295
35.1k
  if (!*path)
296
0
    die("The empty string is not a valid path");
297
35.1k
  if (!is_absolute_path(path)) {
298
0
    struct stat cwd_stat, pwd_stat;
299
0
    size_t orig_len = sb->len;
300
0
    char *cwd = xgetcwd();
301
0
    char *pwd = getenv("PWD");
302
0
    if (pwd && strcmp(pwd, cwd) &&
303
0
        !stat(cwd, &cwd_stat) &&
304
0
        (cwd_stat.st_dev || cwd_stat.st_ino) &&
305
0
        !stat(pwd, &pwd_stat) &&
306
0
        pwd_stat.st_dev == cwd_stat.st_dev &&
307
0
        pwd_stat.st_ino == cwd_stat.st_ino)
308
0
      strbuf_addstr(sb, pwd);
309
0
    else
310
0
      strbuf_addstr(sb, cwd);
311
0
    if (sb->len > orig_len && !is_dir_sep(sb->buf[sb->len - 1]))
312
0
      strbuf_addch(sb, '/');
313
0
    free(cwd);
314
0
  }
315
35.1k
  strbuf_addstr(sb, path);
316
35.1k
}
317
318
void strbuf_add_real_path(struct strbuf *sb, const char *path)
319
0
{
320
0
  if (sb->len) {
321
0
    struct strbuf resolved = STRBUF_INIT;
322
0
    strbuf_realpath(&resolved, path, 1);
323
0
    strbuf_addbuf(sb, &resolved);
324
0
    strbuf_release(&resolved);
325
0
  } else
326
0
    strbuf_realpath(sb, path, 1);
327
0
}