Coverage Report

Created: 2026-05-30 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tor/src/lib/fs/lockfile.c
Line
Count
Source
1
/* Copyright (c) 2003-2004, Roger Dingledine
2
 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3
 * Copyright (c) 2007-2021, The Tor Project, Inc. */
4
/* See LICENSE for licensing information */
5
6
/**
7
 * \file lockfile.c
8
 *
9
 * \brief Implements lock files to prevent two Tor processes from using the
10
 * same data directory at the same time.
11
 **/
12
13
#include "orconfig.h"
14
#include "lib/fs/files.h"
15
#include "lib/fs/lockfile.h"
16
#include "lib/log/log.h"
17
#include "lib/log/util_bug.h"
18
#include "lib/malloc/malloc.h"
19
20
#ifdef HAVE_SYS_FILE_H
21
#include <sys/file.h>
22
#endif
23
#ifdef HAVE_FCNTL_H
24
#include <fcntl.h>
25
#endif
26
#ifdef HAVE_UNISTD_H
27
#include <unistd.h>
28
#endif
29
#ifdef _WIN32
30
#include <windows.h>
31
#include <sys/locking.h>
32
#endif
33
34
#include <errno.h>
35
#include <string.h>
36
37
/** Represents a lockfile on which we hold the lock. */
38
struct tor_lockfile_t {
39
  /** Name of the file */
40
  char *filename;
41
  /** File descriptor used to hold the file open */
42
  int fd;
43
};
44
45
/** Try to get a lock on the lockfile <b>filename</b>, creating it as
46
 * necessary.  If someone else has the lock and <b>blocking</b> is true,
47
 * wait until the lock is available.  Otherwise return immediately whether
48
 * we succeeded or not.
49
 *
50
 * Set *<b>locked_out</b> to true if somebody else had the lock, and to false
51
 * otherwise.
52
 *
53
 * Return a <b>tor_lockfile_t</b> on success, NULL on failure.
54
 *
55
 * (Implementation note: because we need to fall back to fcntl on some
56
 *  platforms, these locks are per-process, not per-thread.  If you want
57
 *  to do in-process locking, use tor_mutex_t like a normal person.
58
 *  On Windows, when <b>blocking</b> is true, the maximum time that
59
 *  is actually waited is 10 seconds, after which NULL is returned
60
 *  and <b>locked_out</b> is set to 1.)
61
 */
62
tor_lockfile_t *
63
tor_lockfile_lock(const char *filename, int blocking, int *locked_out)
64
0
{
65
0
  tor_lockfile_t *result;
66
0
  int fd;
67
0
  *locked_out = 0;
68
69
0
  log_info(LD_FS, "Locking \"%s\"", filename);
70
0
  fd = tor_open_cloexec(filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
71
0
  if (fd < 0) {
72
0
    log_warn(LD_FS,"Couldn't open \"%s\" for locking: %s", filename,
73
0
             strerror(errno));
74
0
    return NULL;
75
0
  }
76
77
#ifdef _WIN32
78
  _lseek(fd, 0, SEEK_SET);
79
  if (_locking(fd, blocking ? _LK_LOCK : _LK_NBLCK, 1) < 0) {
80
    if (errno != EACCES && errno != EDEADLOCK)
81
      log_warn(LD_FS,"Couldn't lock \"%s\": %s", filename, strerror(errno));
82
    else
83
      *locked_out = 1;
84
    close(fd);
85
    return NULL;
86
  }
87
#elif defined(HAVE_FLOCK)
88
0
  if (flock(fd, LOCK_EX|(blocking ? 0 : LOCK_NB)) < 0) {
89
0
    if (errno != EWOULDBLOCK)
90
0
      log_warn(LD_FS,"Couldn't lock \"%s\": %s", filename, strerror(errno));
91
0
    else
92
0
      *locked_out = 1;
93
0
    close(fd);
94
0
    return NULL;
95
0
  }
96
#else
97
  {
98
    struct flock lock;
99
    memset(&lock, 0, sizeof(lock));
100
    lock.l_type = F_WRLCK;
101
    lock.l_whence = SEEK_SET;
102
    if (fcntl(fd, blocking ? F_SETLKW : F_SETLK, &lock) < 0) {
103
      if (errno != EACCES && errno != EAGAIN)
104
        log_warn(LD_FS, "Couldn't lock \"%s\": %s", filename, strerror(errno));
105
      else
106
        *locked_out = 1;
107
      close(fd);
108
      return NULL;
109
    }
110
  }
111
#endif /* defined(_WIN32) || ... */
112
113
0
  result = tor_malloc(sizeof(tor_lockfile_t));
114
0
  result->filename = tor_strdup(filename);
115
0
  result->fd = fd;
116
0
  return result;
117
0
}
118
119
/** Release the lock held as <b>lockfile</b>. */
120
void
121
tor_lockfile_unlock(tor_lockfile_t *lockfile)
122
0
{
123
0
  tor_assert(lockfile);
124
125
0
  log_info(LD_FS, "Unlocking \"%s\"", lockfile->filename);
126
#ifdef _WIN32
127
  _lseek(lockfile->fd, 0, SEEK_SET);
128
  if (_locking(lockfile->fd, _LK_UNLCK, 1) < 0) {
129
    log_warn(LD_FS,"Error unlocking \"%s\": %s", lockfile->filename,
130
             strerror(errno));
131
  }
132
#elif defined(HAVE_FLOCK)
133
0
  if (flock(lockfile->fd, LOCK_UN) < 0) {
134
0
    log_warn(LD_FS, "Error unlocking \"%s\": %s", lockfile->filename,
135
0
             strerror(errno));
136
0
  }
137
#else
138
  /* Closing the lockfile is sufficient. */
139
#endif /* defined(_WIN32) || ... */
140
141
0
  close(lockfile->fd);
142
0
  lockfile->fd = -1;
143
0
  tor_free(lockfile->filename);
144
  tor_free(lockfile);
145
0
}