Coverage Report

Created: 2025-12-31 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/git/symlinks.c
Line
Count
Source
1
#define DISABLE_SIGN_COMPARE_WARNINGS
2
3
#include "git-compat-util.h"
4
#include "gettext.h"
5
#include "setup.h"
6
#include "symlinks.h"
7
8
static int threaded_check_leading_path(struct cache_def *cache, const char *name,
9
               int len, int warn_on_lstat_err);
10
static int threaded_has_dirs_only_path(struct cache_def *cache, const char *name, int len, int prefix_len);
11
12
/*
13
 * Returns the length (on a path component basis) of the longest
14
 * common prefix match of 'name_a' and 'name_b'.
15
 */
16
static int longest_path_match(const char *name_a, int len_a,
17
            const char *name_b, int len_b,
18
            int *previous_slash)
19
0
{
20
0
  int max_len, match_len = 0, match_len_prev = 0, i = 0;
21
22
0
  max_len = len_a < len_b ? len_a : len_b;
23
0
  while (i < max_len && name_a[i] == name_b[i]) {
24
0
    if (name_a[i] == '/') {
25
0
      match_len_prev = match_len;
26
0
      match_len = i;
27
0
    }
28
0
    i++;
29
0
  }
30
  /*
31
   * Is 'name_b' a substring of 'name_a', the other way around,
32
   * or is 'name_a' and 'name_b' the exact same string?
33
   */
34
0
  if (i >= max_len && ((len_a > len_b && name_a[len_b] == '/') ||
35
0
           (len_a < len_b && name_b[len_a] == '/') ||
36
0
           (len_a == len_b))) {
37
0
    match_len_prev = match_len;
38
0
    match_len = i;
39
0
  }
40
0
  *previous_slash = match_len_prev;
41
0
  return match_len;
42
0
}
43
44
static struct cache_def default_cache = CACHE_DEF_INIT;
45
46
static inline void reset_lstat_cache(struct cache_def *cache)
47
0
{
48
0
  strbuf_reset(&cache->path);
49
0
  cache->flags = 0;
50
  /*
51
   * The track_flags and prefix_len_stat_func members is only
52
   * set by the safeguard rule inside lstat_cache()
53
   */
54
0
}
55
56
0
#define FL_DIR      (1 << 0)
57
0
#define FL_NOENT    (1 << 1)
58
0
#define FL_SYMLINK  (1 << 2)
59
0
#define FL_LSTATERR (1 << 3)
60
0
#define FL_ERR      (1 << 4)
61
0
#define FL_FULLPATH (1 << 5)
62
63
/*
64
 * Check if name 'name' of length 'len' has a symlink leading
65
 * component, or if the directory exists and is real, or not.
66
 *
67
 * To speed up the check, some information is allowed to be cached.
68
 * This can be indicated by the 'track_flags' argument, which also can
69
 * be used to indicate that we should check the full path.
70
 *
71
 * The 'prefix_len_stat_func' parameter can be used to set the length
72
 * of the prefix, where the cache should use the stat() function
73
 * instead of the lstat() function to test each path component.
74
 */
75
static int lstat_cache_matchlen(struct cache_def *cache,
76
        const char *name, int len,
77
        int *ret_flags, int track_flags,
78
        int prefix_len_stat_func)
79
0
{
80
0
  int match_len, last_slash, last_slash_dir, previous_slash;
81
0
  int save_flags, ret, saved_errno = 0;
82
0
  struct stat st;
83
84
0
  if (cache->track_flags != track_flags ||
85
0
      cache->prefix_len_stat_func != prefix_len_stat_func) {
86
    /*
87
     * As a safeguard rule we clear the cache if the
88
     * values of track_flags and/or prefix_len_stat_func
89
     * does not match with the last supplied values.
90
     */
91
0
    reset_lstat_cache(cache);
92
0
    cache->track_flags = track_flags;
93
0
    cache->prefix_len_stat_func = prefix_len_stat_func;
94
0
    match_len = last_slash = 0;
95
0
  } else {
96
    /*
97
     * Check to see if we have a match from the cache for
98
     * the 2 "excluding" path types.
99
     */
100
0
    match_len = last_slash =
101
0
      longest_path_match(name, len, cache->path.buf,
102
0
             cache->path.len, &previous_slash);
103
0
    *ret_flags = cache->flags & track_flags & (FL_NOENT|FL_SYMLINK);
104
105
0
    if (!(track_flags & FL_FULLPATH) && match_len == len)
106
0
      match_len = last_slash = previous_slash;
107
108
0
    if (*ret_flags && match_len == cache->path.len)
109
0
      return match_len;
110
    /*
111
     * If we now have match_len > 0, we would know that
112
     * the matched part will always be a directory.
113
     *
114
     * Also, if we are tracking directories and 'name' is
115
     * a substring of the cache on a path component basis,
116
     * we can return immediately.
117
     */
118
0
    *ret_flags = track_flags & FL_DIR;
119
0
    if (*ret_flags && len == match_len)
120
0
      return match_len;
121
0
  }
122
123
  /*
124
   * Okay, no match from the cache so far, so now we have to
125
   * check the rest of the path components.
126
   */
127
0
  *ret_flags = FL_DIR;
128
0
  last_slash_dir = last_slash;
129
0
  if (len > cache->path.len)
130
0
    strbuf_grow(&cache->path, len - cache->path.len);
131
0
  while (match_len < len) {
132
0
    do {
133
0
      cache->path.buf[match_len] = name[match_len];
134
0
      match_len++;
135
0
    } while (match_len < len && name[match_len] != '/');
136
0
    if (match_len >= len && !(track_flags & FL_FULLPATH))
137
0
      break;
138
0
    last_slash = match_len;
139
0
    cache->path.buf[last_slash] = '\0';
140
141
0
    if (last_slash <= prefix_len_stat_func)
142
0
      ret = stat(cache->path.buf, &st);
143
0
    else
144
0
      ret = lstat(cache->path.buf, &st);
145
146
0
    if (ret) {
147
0
      *ret_flags = FL_LSTATERR;
148
0
      saved_errno = errno;
149
0
      if (errno == ENOENT)
150
0
        *ret_flags |= FL_NOENT;
151
0
    } else if (S_ISDIR(st.st_mode)) {
152
0
      last_slash_dir = last_slash;
153
0
      continue;
154
0
    } else if (S_ISLNK(st.st_mode)) {
155
0
      *ret_flags = FL_SYMLINK;
156
0
    } else {
157
0
      *ret_flags = FL_ERR;
158
0
    }
159
0
    break;
160
0
  }
161
162
  /*
163
   * At the end update the cache.  Note that max 3 different
164
   * path types, FL_NOENT, FL_SYMLINK and FL_DIR, can be cached
165
   * for the moment!
166
   */
167
0
  save_flags = *ret_flags & track_flags & (FL_NOENT|FL_SYMLINK);
168
0
  if (save_flags && last_slash > 0) {
169
0
    cache->path.buf[last_slash] = '\0';
170
0
    cache->path.len = last_slash;
171
0
    cache->flags = save_flags;
172
0
  } else if ((track_flags & FL_DIR) && last_slash_dir > 0) {
173
    /*
174
     * We have a separate test for the directory case,
175
     * since it could be that we have found a symlink or a
176
     * non-existing directory and the track_flags says
177
     * that we cannot cache this fact, so the cache would
178
     * then have been left empty in this case.
179
     *
180
     * But if we are allowed to track real directories, we
181
     * can still cache the path components before the last
182
     * one (the found symlink or non-existing component).
183
     */
184
0
    cache->path.buf[last_slash_dir] = '\0';
185
0
    cache->path.len = last_slash_dir;
186
0
    cache->flags = FL_DIR;
187
0
  } else {
188
0
    reset_lstat_cache(cache);
189
0
  }
190
0
  if (saved_errno)
191
0
    errno = saved_errno;
192
0
  return match_len;
193
0
}
194
195
static int lstat_cache(struct cache_def *cache, const char *name, int len,
196
           int track_flags, int prefix_len_stat_func)
197
0
{
198
0
  int flags;
199
0
  (void)lstat_cache_matchlen(cache, name, len, &flags, track_flags,
200
0
      prefix_len_stat_func);
201
0
  return flags;
202
0
}
203
204
0
#define USE_ONLY_LSTAT  0
205
206
/*
207
 * Return non-zero if path 'name' has a leading symlink component
208
 */
209
int threaded_has_symlink_leading_path(struct cache_def *cache, const char *name, int len)
210
0
{
211
0
  return lstat_cache(cache, name, len, FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & FL_SYMLINK;
212
0
}
213
214
int has_symlink_leading_path(const char *name, int len)
215
0
{
216
0
  return threaded_has_symlink_leading_path(&default_cache, name, len);
217
0
}
218
219
int check_leading_path(const char *name, int len, int warn_on_lstat_err)
220
0
{
221
0
  return threaded_check_leading_path(&default_cache, name, len,
222
0
             warn_on_lstat_err);
223
0
}
224
225
/*
226
 * Return zero if some leading path component of 'name' does not exist.
227
 *
228
 * Return -1 if leading path exists and is a directory.
229
 *
230
 * Return the length of a leading component if it either exists but it's not a
231
 * directory, or if we were unable to lstat() it. If warn_on_lstat_err is true,
232
 * also emit a warning for this error.
233
 */
234
static int threaded_check_leading_path(struct cache_def *cache, const char *name,
235
               int len, int warn_on_lstat_err)
236
0
{
237
0
  int flags;
238
0
  int match_len = lstat_cache_matchlen(cache, name, len, &flags,
239
0
         FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT);
240
0
  int saved_errno = errno;
241
242
0
  if (flags & FL_NOENT)
243
0
    return 0;
244
0
  else if (flags & FL_DIR)
245
0
    return -1;
246
0
  if (warn_on_lstat_err && (flags & FL_LSTATERR)) {
247
0
    char *path = xmemdupz(name, match_len);
248
0
    errno = saved_errno;
249
0
    warning_errno(_("failed to lstat '%s'"), path);
250
0
    free(path);
251
0
  }
252
0
  return match_len;
253
0
}
254
255
int has_dirs_only_path(const char *name, int len, int prefix_len)
256
0
{
257
0
  return threaded_has_dirs_only_path(&default_cache, name, len, prefix_len);
258
0
}
259
260
/*
261
 * Return non-zero if all path components of 'name' exists as a
262
 * directory.  If prefix_len > 0, we will test with the stat()
263
 * function instead of the lstat() function for a prefix length of
264
 * 'prefix_len', thus we then allow for symlinks in the prefix part as
265
 * long as those points to real existing directories.
266
 */
267
static int threaded_has_dirs_only_path(struct cache_def *cache, const char *name, int len, int prefix_len)
268
0
{
269
  /*
270
   * Note: this function is used by the checkout machinery, which also
271
   * takes care to properly reset the cache when it performs an operation
272
   * that would leave the cache outdated. If this function starts caching
273
   * anything else besides FL_DIR, remember to also invalidate the cache
274
   * when creating or deleting paths that might be in the cache.
275
   */
276
0
  return lstat_cache(cache, name, len,
277
0
         FL_DIR|FL_FULLPATH, prefix_len) &
278
0
    FL_DIR;
279
0
}
280
281
static struct strbuf removal = STRBUF_INIT;
282
283
static void do_remove_scheduled_dirs(int new_len)
284
0
{
285
0
  while (removal.len > new_len) {
286
0
    removal.buf[removal.len] = '\0';
287
0
    if ((startup_info->original_cwd &&
288
0
         !strcmp(removal.buf, startup_info->original_cwd)) ||
289
0
        rmdir(removal.buf))
290
0
      break;
291
0
    do {
292
0
      removal.len--;
293
0
    } while (removal.len > new_len &&
294
0
       removal.buf[removal.len] != '/');
295
0
  }
296
0
  removal.len = new_len;
297
0
}
298
299
void schedule_dir_for_removal(const char *name, int len)
300
0
{
301
0
  int match_len, last_slash, i, previous_slash;
302
303
0
  if (startup_info->original_cwd &&
304
0
      !strcmp(name, startup_info->original_cwd))
305
0
    return; /* Do not remove the current working directory */
306
307
0
  match_len = last_slash = i =
308
0
    longest_path_match(name, len, removal.buf, removal.len,
309
0
           &previous_slash);
310
  /* Find last slash inside 'name' */
311
0
  while (i < len) {
312
0
    if (name[i] == '/')
313
0
      last_slash = i;
314
0
    i++;
315
0
  }
316
317
  /*
318
   * If we are about to go down the directory tree, we check if
319
   * we must first go upwards the tree, such that we then can
320
   * remove possible empty directories as we go upwards.
321
   */
322
0
  if (match_len < last_slash && match_len < removal.len)
323
0
    do_remove_scheduled_dirs(match_len);
324
  /*
325
   * If we go deeper down the directory tree, we only need to
326
   * save the new path components as we go down.
327
   */
328
0
  if (match_len < last_slash)
329
0
    strbuf_add(&removal, &name[match_len], last_slash - match_len);
330
0
}
331
332
void remove_scheduled_dirs(void)
333
0
{
334
0
  do_remove_scheduled_dirs(0);
335
0
}
336
337
void invalidate_lstat_cache(void)
338
0
{
339
0
  reset_lstat_cache(&default_cache);
340
0
}
341
342
#undef rmdir
343
int lstat_cache_aware_rmdir(const char *path)
344
0
{
345
  /* Any change in this function must be made also in `mingw_rmdir()` */
346
0
  int ret = rmdir(path);
347
348
0
  if (!ret)
349
0
    invalidate_lstat_cache();
350
351
0
  return ret;
352
0
}