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