tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

lockfile.c (4023B)


      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 {
     65  tor_lockfile_t *result;
     66  int fd;
     67  *locked_out = 0;
     68 
     69  log_info(LD_FS, "Locking \"%s\"", filename);
     70  fd = tor_open_cloexec(filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
     71  if (fd < 0) {
     72    log_warn(LD_FS,"Couldn't open \"%s\" for locking: %s", filename,
     73             strerror(errno));
     74    return NULL;
     75  }
     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  if (flock(fd, LOCK_EX|(blocking ? 0 : LOCK_NB)) < 0) {
     89    if (errno != EWOULDBLOCK)
     90      log_warn(LD_FS,"Couldn't lock \"%s\": %s", filename, strerror(errno));
     91    else
     92      *locked_out = 1;
     93    close(fd);
     94    return NULL;
     95  }
     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  result = tor_malloc(sizeof(tor_lockfile_t));
    114  result->filename = tor_strdup(filename);
    115  result->fd = fd;
    116  return result;
    117 }
    118 
    119 /** Release the lock held as <b>lockfile</b>. */
    120 void
    121 tor_lockfile_unlock(tor_lockfile_t *lockfile)
    122 {
    123  tor_assert(lockfile);
    124 
    125  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  if (flock(lockfile->fd, LOCK_UN) < 0) {
    134    log_warn(LD_FS, "Error unlocking \"%s\": %s", lockfile->filename,
    135             strerror(errno));
    136  }
    137 #else
    138  /* Closing the lockfile is sufficient. */
    139 #endif /* defined(_WIN32) || ... */
    140 
    141  close(lockfile->fd);
    142  lockfile->fd = -1;
    143  tor_free(lockfile->filename);
    144  tor_free(lockfile);
    145 }