lockfile.con commit Merge branch 'jk/fix-alias-pager-config-key-warnings' (7b7c10b)
   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        int fd;
  76        struct strbuf filename = STRBUF_INIT;
  77
  78        strbuf_addstr(&filename, path);
  79        if (!(flags & LOCK_NO_DEREF))
  80                resolve_symlink(&filename);
  81
  82        strbuf_addstr(&filename, LOCK_SUFFIX);
  83        fd = create_tempfile(&lk->tempfile, filename.buf);
  84        strbuf_release(&filename);
  85        return fd;
  86}
  87
  88/*
  89 * Constants defining the gaps between attempts to lock a file. The
  90 * first backoff period is approximately INITIAL_BACKOFF_MS
  91 * milliseconds. The longest backoff period is approximately
  92 * (BACKOFF_MAX_MULTIPLIER * INITIAL_BACKOFF_MS) milliseconds.
  93 */
  94#define INITIAL_BACKOFF_MS 1L
  95#define BACKOFF_MAX_MULTIPLIER 1000
  96
  97/*
  98 * Try locking path, retrying with quadratic backoff for at least
  99 * timeout_ms milliseconds. If timeout_ms is 0, try locking the file
 100 * exactly once. If timeout_ms is -1, try indefinitely.
 101 */
 102static int lock_file_timeout(struct lock_file *lk, const char *path,
 103                             int flags, long timeout_ms)
 104{
 105        int n = 1;
 106        int multiplier = 1;
 107        long remaining_ms = 0;
 108        static int random_initialized = 0;
 109
 110        if (timeout_ms == 0)
 111                return lock_file(lk, path, flags);
 112
 113        if (!random_initialized) {
 114                srand((unsigned int)getpid());
 115                random_initialized = 1;
 116        }
 117
 118        if (timeout_ms > 0)
 119                remaining_ms = timeout_ms;
 120
 121        while (1) {
 122                long backoff_ms, wait_ms;
 123                int fd;
 124
 125                fd = lock_file(lk, path, flags);
 126
 127                if (fd >= 0)
 128                        return fd; /* success */
 129                else if (errno != EEXIST)
 130                        return -1; /* failure other than lock held */
 131                else if (timeout_ms > 0 && remaining_ms <= 0)
 132                        return -1; /* failure due to timeout */
 133
 134                backoff_ms = multiplier * INITIAL_BACKOFF_MS;
 135                /* back off for between 0.75*backoff_ms and 1.25*backoff_ms */
 136                wait_ms = (750 + rand() % 500) * backoff_ms / 1000;
 137                sleep_millisec(wait_ms);
 138                remaining_ms -= wait_ms;
 139
 140                /* Recursion: (n+1)^2 = n^2 + 2n + 1 */
 141                multiplier += 2*n + 1;
 142                if (multiplier > BACKOFF_MAX_MULTIPLIER)
 143                        multiplier = BACKOFF_MAX_MULTIPLIER;
 144                else
 145                        n++;
 146        }
 147}
 148
 149void unable_to_lock_message(const char *path, int err, struct strbuf *buf)
 150{
 151        if (err == EEXIST) {
 152                strbuf_addf(buf, "Unable to create '%s.lock': %s.\n\n"
 153                    "If no other git process is currently running, this probably means a\n"
 154                    "git process crashed in this repository earlier. Make sure no other git\n"
 155                    "process is running and remove the file manually to continue.",
 156                            absolute_path(path), strerror(err));
 157        } else
 158                strbuf_addf(buf, "Unable to create '%s.lock': %s",
 159                            absolute_path(path), strerror(err));
 160}
 161
 162NORETURN void unable_to_lock_die(const char *path, int err)
 163{
 164        struct strbuf buf = STRBUF_INIT;
 165
 166        unable_to_lock_message(path, err, &buf);
 167        die("%s", buf.buf);
 168}
 169
 170/* This should return a meaningful errno on failure */
 171int hold_lock_file_for_update_timeout(struct lock_file *lk, const char *path,
 172                                      int flags, long timeout_ms)
 173{
 174        int fd = lock_file_timeout(lk, path, flags, timeout_ms);
 175        if (fd < 0 && (flags & LOCK_DIE_ON_ERROR))
 176                unable_to_lock_die(path, errno);
 177        return fd;
 178}
 179
 180int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags)
 181{
 182        int fd, orig_fd;
 183
 184        fd = lock_file(lk, path, flags);
 185        if (fd < 0) {
 186                if (flags & LOCK_DIE_ON_ERROR)
 187                        unable_to_lock_die(path, errno);
 188                return fd;
 189        }
 190
 191        orig_fd = open(path, O_RDONLY);
 192        if (orig_fd < 0) {
 193                if (errno != ENOENT) {
 194                        int save_errno = errno;
 195
 196                        if (flags & LOCK_DIE_ON_ERROR)
 197                                die("cannot open '%s' for copying", path);
 198                        rollback_lock_file(lk);
 199                        error("cannot open '%s' for copying", path);
 200                        errno = save_errno;
 201                        return -1;
 202                }
 203        } else if (copy_fd(orig_fd, fd)) {
 204                int save_errno = errno;
 205
 206                if (flags & LOCK_DIE_ON_ERROR)
 207                        die("failed to prepare '%s' for appending", path);
 208                close(orig_fd);
 209                rollback_lock_file(lk);
 210                errno = save_errno;
 211                return -1;
 212        } else {
 213                close(orig_fd);
 214        }
 215        return fd;
 216}
 217
 218char *get_locked_file_path(struct lock_file *lk)
 219{
 220        struct strbuf ret = STRBUF_INIT;
 221
 222        strbuf_addstr(&ret, get_tempfile_path(&lk->tempfile));
 223        if (ret.len <= LOCK_SUFFIX_LEN ||
 224            strcmp(ret.buf + ret.len - LOCK_SUFFIX_LEN, LOCK_SUFFIX))
 225                die("BUG: get_locked_file_path() called for malformed lock object");
 226        /* remove ".lock": */
 227        strbuf_setlen(&ret, ret.len - LOCK_SUFFIX_LEN);
 228        return strbuf_detach(&ret, NULL);
 229}
 230
 231int commit_lock_file(struct lock_file *lk)
 232{
 233        char *result_path = get_locked_file_path(lk);
 234
 235        if (commit_lock_file_to(lk, result_path)) {
 236                int save_errno = errno;
 237                free(result_path);
 238                errno = save_errno;
 239                return -1;
 240        }
 241        free(result_path);
 242        return 0;
 243}