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