Coverage Report

Created: 2023-11-19 07:08

/src/git/lockfile.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2005, Junio C Hamano
3
 */
4
5
#include "git-compat-util.h"
6
#include "abspath.h"
7
#include "gettext.h"
8
#include "lockfile.h"
9
10
/*
11
 * path = absolute or relative path name
12
 *
13
 * Remove the last path name element from path (leaving the preceding
14
 * "/", if any).  If path is empty or the root directory ("/"), set
15
 * path to the empty string.
16
 */
17
static void trim_last_path_component(struct strbuf *path)
18
0
{
19
0
  int i = path->len;
20
21
  /* back up past trailing slashes, if any */
22
0
  while (i && path->buf[i - 1] == '/')
23
0
    i--;
24
25
  /*
26
   * then go backwards until a slash, or the beginning of the
27
   * string
28
   */
29
0
  while (i && path->buf[i - 1] != '/')
30
0
    i--;
31
32
0
  strbuf_setlen(path, i);
33
0
}
34
35
36
/* We allow "recursive" symbolic links. Only within reason, though */
37
18.3k
#define MAXDEPTH 5
38
39
/*
40
 * path contains a path that might be a symlink.
41
 *
42
 * If path is a symlink, attempt to overwrite it with a path to the
43
 * real file or directory (which may or may not exist), following a
44
 * chain of symlinks if necessary.  Otherwise, leave path unmodified.
45
 *
46
 * This is a best-effort routine.  If an error occurs, path will
47
 * either be left unmodified or will name a different symlink in a
48
 * symlink chain that started with the original path.
49
 */
50
static void resolve_symlink(struct strbuf *path)
51
18.3k
{
52
18.3k
  int depth = MAXDEPTH;
53
18.3k
  static struct strbuf link = STRBUF_INIT;
54
55
18.3k
  while (depth--) {
56
18.3k
    if (strbuf_readlink(&link, path->buf, path->len) < 0)
57
18.3k
      break;
58
59
0
    if (is_absolute_path(link.buf))
60
      /* absolute path simply replaces p */
61
0
      strbuf_reset(path);
62
0
    else
63
      /*
64
       * link is a relative path, so replace the
65
       * last element of p with it.
66
       */
67
0
      trim_last_path_component(path);
68
69
0
    strbuf_addbuf(path, &link);
70
0
  }
71
18.3k
  strbuf_reset(&link);
72
18.3k
}
73
74
/* Make sure errno contains a meaningful value on error */
75
static int lock_file(struct lock_file *lk, const char *path, int flags,
76
         int mode)
77
35.1k
{
78
35.1k
  struct strbuf filename = STRBUF_INIT;
79
80
35.1k
  strbuf_addstr(&filename, path);
81
35.1k
  if (!(flags & LOCK_NO_DEREF))
82
18.3k
    resolve_symlink(&filename);
83
84
35.1k
  strbuf_addstr(&filename, LOCK_SUFFIX);
85
35.1k
  lk->tempfile = create_tempfile_mode(filename.buf, mode);
86
35.1k
  strbuf_release(&filename);
87
35.1k
  return lk->tempfile ? lk->tempfile->fd : -1;
88
35.1k
}
89
90
/*
91
 * Constants defining the gaps between attempts to lock a file. The
92
 * first backoff period is approximately INITIAL_BACKOFF_MS
93
 * milliseconds. The longest backoff period is approximately
94
 * (BACKOFF_MAX_MULTIPLIER * INITIAL_BACKOFF_MS) milliseconds.
95
 */
96
0
#define INITIAL_BACKOFF_MS 1L
97
0
#define BACKOFF_MAX_MULTIPLIER 1000
98
99
/*
100
 * Try locking path, retrying with quadratic backoff for at least
101
 * timeout_ms milliseconds. If timeout_ms is 0, try locking the file
102
 * exactly once. If timeout_ms is -1, try indefinitely.
103
 */
104
static int lock_file_timeout(struct lock_file *lk, const char *path,
105
           int flags, long timeout_ms, int mode)
106
35.1k
{
107
35.1k
  int n = 1;
108
35.1k
  int multiplier = 1;
109
35.1k
  long remaining_ms = 0;
110
35.1k
  static int random_initialized = 0;
111
112
35.1k
  if (timeout_ms == 0)
113
0
    return lock_file(lk, path, flags, mode);
114
115
35.1k
  if (!random_initialized) {
116
1
    srand((unsigned int)getpid());
117
1
    random_initialized = 1;
118
1
  }
119
120
35.1k
  if (timeout_ms > 0)
121
35.1k
    remaining_ms = timeout_ms;
122
123
35.1k
  while (1) {
124
35.1k
    long backoff_ms, wait_ms;
125
35.1k
    int fd;
126
127
35.1k
    fd = lock_file(lk, path, flags, mode);
128
129
35.1k
    if (fd >= 0)
130
35.1k
      return fd; /* success */
131
0
    else if (errno != EEXIST)
132
0
      return -1; /* failure other than lock held */
133
0
    else if (timeout_ms > 0 && remaining_ms <= 0)
134
0
      return -1; /* failure due to timeout */
135
136
0
    backoff_ms = multiplier * INITIAL_BACKOFF_MS;
137
    /* back off for between 0.75*backoff_ms and 1.25*backoff_ms */
138
0
    wait_ms = (750 + rand() % 500) * backoff_ms / 1000;
139
0
    sleep_millisec(wait_ms);
140
0
    remaining_ms -= wait_ms;
141
142
    /* Recursion: (n+1)^2 = n^2 + 2n + 1 */
143
0
    multiplier += 2*n + 1;
144
0
    if (multiplier > BACKOFF_MAX_MULTIPLIER)
145
0
      multiplier = BACKOFF_MAX_MULTIPLIER;
146
0
    else
147
0
      n++;
148
0
  }
149
35.1k
}
150
151
void unable_to_lock_message(const char *path, int err, struct strbuf *buf)
152
0
{
153
0
  if (err == EEXIST) {
154
0
    strbuf_addf(buf, _("Unable to create '%s.lock': %s.\n\n"
155
0
        "Another git process seems to be running in this repository, e.g.\n"
156
0
        "an editor opened by 'git commit'. Please make sure all processes\n"
157
0
        "are terminated then try again. If it still fails, a git process\n"
158
0
        "may have crashed in this repository earlier:\n"
159
0
        "remove the file manually to continue."),
160
0
          absolute_path(path), strerror(err));
161
0
  } else
162
0
    strbuf_addf(buf, _("Unable to create '%s.lock': %s"),
163
0
          absolute_path(path), strerror(err));
164
0
}
165
166
NORETURN void unable_to_lock_die(const char *path, int err)
167
0
{
168
0
  struct strbuf buf = STRBUF_INIT;
169
170
0
  unable_to_lock_message(path, err, &buf);
171
0
  die("%s", buf.buf);
172
0
}
173
174
/* This should return a meaningful errno on failure */
175
int hold_lock_file_for_update_timeout_mode(struct lock_file *lk,
176
             const char *path, int flags,
177
             long timeout_ms, int mode)
178
35.1k
{
179
35.1k
  int fd = lock_file_timeout(lk, path, flags, timeout_ms, mode);
180
35.1k
  if (fd < 0) {
181
0
    if (flags & LOCK_DIE_ON_ERROR)
182
0
      unable_to_lock_die(path, errno);
183
0
    if (flags & LOCK_REPORT_ON_ERROR) {
184
0
      struct strbuf buf = STRBUF_INIT;
185
0
      unable_to_lock_message(path, errno, &buf);
186
0
      error("%s", buf.buf);
187
0
      strbuf_release(&buf);
188
0
    }
189
0
  }
190
35.1k
  return fd;
191
35.1k
}
192
193
char *get_locked_file_path(struct lock_file *lk)
194
37.4k
{
195
37.4k
  struct strbuf ret = STRBUF_INIT;
196
197
37.4k
  strbuf_addstr(&ret, get_tempfile_path(lk->tempfile));
198
37.4k
  if (ret.len <= LOCK_SUFFIX_LEN ||
199
37.4k
      strcmp(ret.buf + ret.len - LOCK_SUFFIX_LEN, LOCK_SUFFIX))
200
0
    BUG("get_locked_file_path() called for malformed lock object");
201
  /* remove ".lock": */
202
37.4k
  strbuf_setlen(&ret, ret.len - LOCK_SUFFIX_LEN);
203
37.4k
  return strbuf_detach(&ret, NULL);
204
37.4k
}
205
206
int commit_lock_file(struct lock_file *lk)
207
27.8k
{
208
27.8k
  char *result_path = get_locked_file_path(lk);
209
210
27.8k
  if (commit_lock_file_to(lk, result_path)) {
211
0
    int save_errno = errno;
212
0
    free(result_path);
213
0
    errno = save_errno;
214
0
    return -1;
215
0
  }
216
27.8k
  free(result_path);
217
27.8k
  return 0;
218
27.8k
}