* Copyright (c) 1998,1999 Adrian Sun (asun@zoology.washington.edu)
* All Rights Reserved. See COPYRIGHT for more information.
*
- * Byte-range locks. This uses either whole-file flocks to fake byte
- * locks or fcntl-based actual byte locks. Because fcntl locks are
+ * Because fcntl locks are
* process-oriented, we need to keep around a list of file descriptors
- * that refer to the same file. Currently, this doesn't serialize access
- * to the locks. as a result, there's the potential for race conditions.
+ * that refer to the same file.
*
* TODO: fix the race when reading/writing.
* keep a pool of both locks and reference counters around so that
#endif /* HAVE_CONFIG_H */
#include <atalk/adouble.h>
+#include <atalk/logger.h>
+#include <atalk/compat.h>
+#include <atalk/errchk.h>
+#include <atalk/util.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
+#include <inttypes.h>
#include <string.h>
#include "ad_lock.h"
-/* translate between ADLOCK styles and specific locking mechanisms */
-#define XLATE_FLOCK(type) ((type) == ADLOCK_RD ? LOCK_SH : \
-((type) == ADLOCK_WR ? LOCK_EX : \
- ((type) == ADLOCK_CLR ? LOCK_UN : -1)))
-
-#ifdef DISABLE_LOCKING
-#define fcntl(a, b, c ) (0)
-#endif
+static const char *shmdstrfromoff(off_t off)
+{
+ switch (off) {
+ case AD_FILELOCK_OPEN_WR:
+ return "OPEN_WR_DATA";
+ case AD_FILELOCK_OPEN_RD:
+ return "OPEN_RD_DATA";
+ case AD_FILELOCK_RSRC_OPEN_WR:
+ return "OPEN_WR_RSRC";
+ case AD_FILELOCK_RSRC_OPEN_RD:
+ return "OPEN_RD_RSRC";
+ case AD_FILELOCK_DENY_WR:
+ return "DENY_WR_DATA";
+ case AD_FILELOCK_DENY_RD:
+ return "DENY_RD_DATA";
+ case AD_FILELOCK_RSRC_DENY_WR:
+ return "DENY_WR_RSRC";
+ case AD_FILELOCK_RSRC_DENY_RD:
+ return "DENY_RD_RSRC";
+ case AD_FILELOCK_OPEN_NONE:
+ return "OPEN_NONE_DATA";
+ case AD_FILELOCK_RSRC_OPEN_NONE:
+ return "OPEN_NONE_RSRC";
+ default:
+ return "-";
+ }
+}
/* ----------------------- */
static int set_lock(int fd, int cmd, struct flock *lock)
{
- if (fd == -2) {
- /* We assign fd = -2 for symlinks -> do nothing */
- if (cmd == F_GETLK)
- lock->l_type = F_UNLCK;
- return 0;
- }
- return fcntl(fd, cmd, lock);
+ EC_INIT;
+
+ LOG(log_debug, logtype_ad, "set_lock(fd: %d, %s, %s, off: %jd (%s), len: %jd): BEGIN",
+ fd, cmd == F_SETLK ? "F_SETLK" : "F_GETLK",
+ lock->l_type == F_RDLCK ? "F_RDLCK" : lock->l_type == F_WRLCK ? "F_WRLCK" : "F_UNLCK",
+ (intmax_t)lock->l_start,
+ shmdstrfromoff(lock->l_start),
+ (intmax_t)lock->l_len);
+
+ if (fd == AD_SYMLINK) {
+ if (cmd == F_GETLK)
+ lock->l_type = F_UNLCK;
+ return 0;
+ }
+
+ EC_NEG1( fcntl(fd, cmd, lock) );
+
+EC_CLEANUP:
+ EC_EXIT;
}
/* ----------------------- */
/* remove a lock and compact space if necessary */
static void adf_freelock(struct ad_fd *ad, const int i)
{
-#if 0
adf_lock_t *lock = ad->adf_lock + i;
if (--(*lock->refcount) < 1) {
free(lock->refcount);
- if (!ad->adf_excl) {
- lock->lock.l_type = F_UNLCK;
- set_lock(ad->adf_fd, F_SETLK, &lock->lock); /* unlock */
- }
+ lock->lock.l_type = F_UNLCK;
+ set_lock(ad->adf_fd, F_SETLK, &lock->lock); /* unlock */
}
ad->adf_lockcount--;
ad->adf_lockmax = ad->adf_lockcount + ARRAY_FREE_DELTA;
}
}
-#endif
}
/* this needs to deal with the following cases:
- * 1) fork is the only user of the lock
- * 2) fork shares a read lock with another open fork
+ * 1) free all UNIX byterange lock from any fork
+ * 2) free all locks of the requested fork
*
* i converted to using arrays of locks. everytime a lock
* gets removed, we shift all of the locks down.
*/
-static void adf_unlock(struct ad_fd *ad, const int fork)
+static void adf_unlock(struct adouble *ad, struct ad_fd *adf, const int fork, int unlckbrl)
{
-#if 0
- adf_lock_t *lock = ad->adf_lock;
+ adf_lock_t *lock = adf->adf_lock;
int i;
- for (i = 0; i < ad->adf_lockcount; i++) {
-
- if (lock[i].user == fork) {
- /* we're really going to delete this lock. note: read locks
- are the only ones that allow refcounts > 1 */
- adf_freelock(ad, i);
- i--; /* we shifted things down, so we need to backtrack */
- /* unlikely but realloc may have change adf_lock */
- lock = ad->adf_lock;
- }
- }
-#endif
+ for (i = 0; i < adf->adf_lockcount; i++) {
+ if ((unlckbrl && lock[i].lock.l_start < AD_FILELOCK_BASE)
+ || lock[i].user == fork) {
+ /* we're really going to delete this lock. note: read locks
+ are the only ones that allow refcounts > 1 */
+ adf_freelock(adf, i);
+ /* we shifted things down, so we need to backtrack */
+ i--;
+ /* unlikely but realloc may have change adf_lock */
+ lock = adf->adf_lock;
+ }
+ }
}
/* relock any byte lock that overlaps off/len. unlock everything
* else. */
static void adf_relockrange(struct ad_fd *ad, int fd, off_t off, off_t len)
{
-#if 0
adf_lock_t *lock = ad->adf_lock;
int i;
- if (!ad->adf_excl) for (i = 0; i < ad->adf_lockcount; i++) {
- if (OVERLAP(off, len, lock[i].lock.l_start, lock[i].lock.l_len))
- set_lock(fd, F_SETLK, &lock[i].lock);
+ for (i = 0; i < ad->adf_lockcount; i++) {
+ if (OVERLAP(off, len, lock[i].lock.l_start, lock[i].lock.l_len))
+ set_lock(fd, F_SETLK, &lock[i].lock);
}
-#endif
}
const off_t off,
const off_t len)
{
-#if 0
adf_lock_t *lock = ad->adf_lock;
int i;
return i;
}
}
-#endif
return -1;
}
const off_t off,
const off_t len)
{
-#if 0
- adf_lock_t *lock = ad->adf_lock;
- int i;
+ adf_lock_t *lock = ad->adf_lock;
+ int i;
- for (i = 0; i < ad->adf_lockcount; i++) {
- if ((((type & ADLOCK_RD) && (lock[i].lock.l_type == F_RDLCK)) ||
- ((type & ADLOCK_WR) && (lock[i].lock.l_type == F_WRLCK))) &&
- (lock[i].user != fork) &&
- OVERLAP(off, len, lock[i].lock.l_start, lock[i].lock.l_len))
- return i;
- }
-#endif
- return -1;
+ for (i = 0; i < ad->adf_lockcount; i++) {
+ if ((((type & ADLOCK_RD) && (lock[i].lock.l_type == F_RDLCK))
+ ||
+ ((type & ADLOCK_WR) && (lock[i].lock.l_type == F_WRLCK)))
+ &&
+ (lock[i].user != fork)
+ &&
+ OVERLAP(off, len, lock[i].lock.l_start, lock[i].lock.l_len))
+ return i;
+ }
+ return -1;
}
/* okay, this needs to do the following:
* 2) if the header file doesn't exist, we stick the locks
* in the locations specified by AD_FILELOCK_RD/WR.
*/
-#define LOCK_DATA_WR (0)
-#define LOCK_DATA_RD (1)
-#define LOCK_RSRC_WR (2)
-#define LOCK_RSRC_RD (3)
-
-#define LOCK_RSRC_DRD (4)
-#define LOCK_RSRC_DWR (5)
-#define LOCK_DATA_DRD (6)
-#define LOCK_DATA_DWR (7)
-
-#define LOCK_RSRC_NONE (8)
-#define LOCK_DATA_NONE (9)
/* --------------
- translate a data fork lock to an offset
+ translate a resource fork lock to an offset
*/
-
-static off_t df2off(int off)
+static off_t rf2off(off_t off)
{
-int start = off;
+ off_t start = off;
if (off == AD_FILELOCK_OPEN_WR)
- start = LOCK_DATA_WR;
+ start = AD_FILELOCK_RSRC_OPEN_WR;
else if (off == AD_FILELOCK_OPEN_RD)
- start = LOCK_DATA_RD;
+ start = AD_FILELOCK_RSRC_OPEN_RD;
else if (off == AD_FILELOCK_DENY_RD)
- start = LOCK_DATA_DRD;
+ start = AD_FILELOCK_RSRC_DENY_RD;
else if (off == AD_FILELOCK_DENY_WR)
- start = LOCK_DATA_DWR;
+ start = AD_FILELOCK_RSRC_DENY_WR;
else if (off == AD_FILELOCK_OPEN_NONE)
- start = LOCK_DATA_NONE;
+ start = AD_FILELOCK_RSRC_OPEN_NONE;
return start;
}
-/* --------------
- translate a resource fork lock to an offset
-*/
-
-static off_t hf2off(int off)
+/*!
+ * Test a lock
+ *
+ * (1) Test against our own locks array
+ * (2) Test fcntl lock, locks from other processes
+ *
+ * @param adf (r) handle
+ * @param off (r) offset
+ * @param len (r) lenght
+ *
+ * @returns 1 if there's an existing lock, 0 if there's no lock,
+ * -1 in case any error occured
+ */
+static int testlock(const struct ad_fd *adf, off_t off, off_t len)
{
-int start = off;
- if (off == AD_FILELOCK_OPEN_WR)
- start = LOCK_RSRC_WR;
- else if (off == AD_FILELOCK_OPEN_RD)
- start = LOCK_RSRC_RD;
- else if (off == AD_FILELOCK_DENY_RD)
- start = LOCK_RSRC_DRD;
- else if (off == AD_FILELOCK_DENY_WR)
- start = LOCK_RSRC_DWR;
- else if (off == AD_FILELOCK_OPEN_NONE)
- start = LOCK_RSRC_NONE;
- return start;
+ struct flock lock;
+ adf_lock_t *plock;
+ int i;
+
+ lock.l_start = off;
+
+ plock = adf->adf_lock;
+ lock.l_whence = SEEK_SET;
+ lock.l_len = len;
+
+ /* (1) Do we have a lock ? */
+ for (i = 0; i < adf->adf_lockcount; i++) {
+ if (OVERLAP(lock.l_start, 1, plock[i].lock.l_start, plock[i].lock.l_len))
+ return 1;
+ }
+
+ /* (2) Does another process have a lock? */
+ lock.l_type = (adf->adf_flags & O_RDWR) ? F_WRLCK : F_RDLCK;
+
+ if (set_lock(adf->adf_fd, F_GETLK, &lock) < 0) {
+ /* is that kind of error possible ?*/
+ return (errno == EACCES || errno == EAGAIN) ? 1 : -1;
+ }
+
+ if (lock.l_type == F_UNLCK) {
+ return 0;
+ }
+ return 1;
}
-/* ------------------ */
-static int ad_fcntl_lock(struct adouble *ad, const u_int32_t eid, const int locktype,
- const off_t off, const off_t len, const int fork)
+#define LTYPE2STRBUFSIZ 128
+static const char *locktypetostr(int type)
{
-#if 0
- struct flock lock;
- struct ad_fd *adf;
- adf_lock_t *adflock;
- int oldlock;
- int i;
- int type;
-
- lock.l_start = off;
- type = locktype;
- if (eid == ADEID_DFORK) {
- adf = &ad->ad_data_fork;
- if ((type & ADLOCK_FILELOCK)) {
- if (ad_meta_fileno(ad) != -1) { /* META */
- adf = ad->ad_md;
- lock.l_start = df2off(off);
- }
+ int first = 1;
+ static char buf[LTYPE2STRBUFSIZ];
+
+ buf[0] = 0;
+
+ if (type == 0) {
+ strlcat(buf, "CLR", LTYPE2STRBUFSIZ);
+ first = 0;
+ return buf;
}
- } else { /* rfork */
- if (ad_meta_fileno(ad) == -1 || ad_reso_fileno(ad) == -1) {
- /* there's no meta data. return a lock error
- * otherwise if a second process is able to create it
- * locks are a mess.
- */
- errno = EACCES;
- return -1;
+ if (type & ADLOCK_RD) {
+ if (!first)
+ strlcat(buf, "|", LTYPE2STRBUFSIZ);
+ strlcat(buf, "RD", LTYPE2STRBUFSIZ);
+ first = 0;
}
- if (type & ADLOCK_FILELOCK) {
- adf = ad->ad_md; /* either resource or meta data (set in ad_open) */
- lock.l_start = hf2off(off);
+ if (type & ADLOCK_WR) {
+ if (!first)
+ strlcat(buf, "|", LTYPE2STRBUFSIZ);
+ strlcat(buf, "WR", LTYPE2STRBUFSIZ);
+ first = 0;
}
- else {
- /* we really want the resource fork it's a byte lock */
- adf = &ad->ad_resource_fork;
- lock.l_start += ad_getentryoff(ad, eid);
+ if (type & ADLOCK_UPGRADE) {
+ if (!first)
+ strlcat(buf, "|", LTYPE2STRBUFSIZ);
+ strlcat(buf, "UPG", LTYPE2STRBUFSIZ);
+ first = 0;
+ }
+ if (type & ADLOCK_FILELOCK) {
+ if (!first)
+ strlcat(buf, "|", LTYPE2STRBUFSIZ);
+ strlcat(buf, "FILELOCK", LTYPE2STRBUFSIZ);
+ first = 0;
}
- }
- /* NOTE: we can't write lock a read-only file. on those, we just
- * make sure that we have a read lock set. that way, we at least prevent
- * someone else from really setting a deny read/write on the file.
- */
- if (!(adf->adf_flags & O_RDWR) && (type & ADLOCK_WR)) {
- type = (type & ~ADLOCK_WR) | ADLOCK_RD;
- }
-
- lock.l_type = XLATE_FCNTL_LOCK(type & ADLOCK_MASK);
- lock.l_whence = SEEK_SET;
- lock.l_len = len;
- /* byte_lock(len=-1) lock whole file */
- if (len == BYTELOCK_MAX) {
- lock.l_len -= lock.l_start; /* otherwise EOVERFLOW error */
- }
+ return buf;
+}
- /* see if it's locked by another fork.
- * NOTE: this guarantees that any existing locks must be at most
- * read locks. we use ADLOCK_WR/RD because F_RD/WRLCK aren't
- * guaranteed to be ORable. */
- if (adf_findxlock(adf, fork, ADLOCK_WR |
- ((type & ADLOCK_WR) ? ADLOCK_RD : 0),
- lock.l_start, lock.l_len) > -1) {
- errno = EACCES;
- return -1;
- }
-
- /* look for any existing lock that we may have */
- i = adf_findlock(adf, fork, ADLOCK_RD | ADLOCK_WR, lock.l_start, lock.l_len);
- adflock = (i < 0) ? NULL : adf->adf_lock + i;
-
- /* here's what we check for:
- 1) we're trying to re-lock a lock, but we didn't specify an update.
- 2) we're trying to free only part of a lock.
- 3) we're trying to free a non-existent lock. */
- if ( (!adflock && (lock.l_type == F_UNLCK))
- ||
- (adflock
- && !(type & ADLOCK_UPGRADE)
- && ((lock.l_type != F_UNLCK)
- || (adflock->lock.l_start != lock.l_start)
- || (adflock->lock.l_len != lock.l_len) ))
- ) {
- errno = EINVAL;
- return -1;
- }
+/******************************************************************************
+ * Public functions
+ ******************************************************************************/
+int ad_lock(struct adouble *ad, uint32_t eid, int locktype, off_t off, off_t len, int fork)
+{
+ struct flock lock;
+ struct ad_fd *adf;
+ adf_lock_t *adflock;
+ int oldlock;
+ int i;
+ int type;
+ int ret = 0, fcntl_lock_err = 0;
+
+ LOG(log_debug, logtype_ad, "ad_lock(%s, %s, off: %jd (%s), len: %jd): BEGIN",
+ eid == ADEID_DFORK ? "data" : "reso",
+ locktypetostr(locktype),
+ (intmax_t)off,
+ shmdstrfromoff(off),
+ (intmax_t)len);
+
+ if ((locktype & ADLOCK_FILELOCK) && (len != 1))
+ AFP_PANIC("lock API error");
+
+ type = locktype;
+
+ if (eid == ADEID_DFORK) {
+ adf = &ad->ad_data_fork;
+ lock.l_start = off;
+ } else { /* rfork */
+ if (type & ADLOCK_FILELOCK) {
+ adf = &ad->ad_data_fork;
+ lock.l_start = rf2off(off);
+ } else {
+ adf = ad->ad_rfp;
+ lock.l_start = off + ad_getentryoff(ad, ADEID_RFORK);
+ }
+ }
- /* now, update our list of locks */
- /* clear the lock */
- if (lock.l_type == F_UNLCK) {
- adf_freelock(adf, i);
- return 0;
- }
+ /* NOTE: we can't write lock a read-only file. on those, we just
+ * make sure that we have a read lock set. that way, we at least prevent
+ * someone else from really setting a deny read/write on the file.
+ */
+ if (!(adf->adf_flags & O_RDWR) && (type & ADLOCK_WR)) {
+ type = (type & ~ADLOCK_WR) | ADLOCK_RD;
+ }
+
+ lock.l_type = XLATE_FCNTL_LOCK(type & ADLOCK_MASK);
+ lock.l_whence = SEEK_SET;
+ lock.l_len = len;
- /* attempt to lock the file. */
- if (!adf->adf_excl && set_lock(adf->adf_fd, F_SETLK, &lock) < 0)
- return -1;
+ /* byte_lock(len=-1) lock whole file */
+ if (len == BYTELOCK_MAX) {
+ lock.l_len -= lock.l_start; /* otherwise EOVERFLOW error */
+ }
- /* we upgraded this lock. */
- if (adflock && (type & ADLOCK_UPGRADE)) {
- memcpy(&adflock->lock, &lock, sizeof(lock));
- return 0;
- }
-
- /* it wasn't an upgrade */
- oldlock = -1;
- if (lock.l_type == F_RDLCK) {
- oldlock = adf_findxlock(adf, fork, ADLOCK_RD, lock.l_start, lock.l_len);
- }
-
- /* no more space. this will also happen if lockmax == lockcount == 0 */
- if (adf->adf_lockmax == adf->adf_lockcount) {
- adf_lock_t *tmp = (adf_lock_t *)
- realloc(adf->adf_lock, sizeof(adf_lock_t)*
- (adf->adf_lockmax + ARRAY_BLOCK_SIZE));
- if (!tmp)
- goto fcntl_lock_err;
- adf->adf_lock = tmp;
- adf->adf_lockmax += ARRAY_BLOCK_SIZE;
- }
- adflock = adf->adf_lock + adf->adf_lockcount;
-
- /* fill in fields */
- memcpy(&adflock->lock, &lock, sizeof(lock));
- adflock->user = fork;
- if (oldlock > -1) {
- adflock->refcount = (adf->adf_lock + oldlock)->refcount;
- } else if ((adflock->refcount = calloc(1, sizeof(int))) == NULL) {
- goto fcntl_lock_err;
- }
+ /* see if it's locked by another fork.
+ * NOTE: this guarantees that any existing locks must be at most
+ * read locks. we use ADLOCK_WR/RD because F_RD/WRLCK aren't
+ * guaranteed to be ORable. */
+ if (adf_findxlock(adf, fork, ADLOCK_WR |
+ ((type & ADLOCK_WR) ? ADLOCK_RD : 0),
+ lock.l_start, lock.l_len) > -1) {
+ errno = EACCES;
+ ret = -1;
+ goto exit;
+ }
- (*adflock->refcount)++;
- adf->adf_lockcount++;
- return 0;
-
-fcntl_lock_err:
- lock.l_type = F_UNLCK;
- if (!adf->adf_excl) set_lock(adf->adf_fd, F_SETLK, &lock);
- return -1;
-#endif
- return 0;
-}
+ /* look for any existing lock that we may have */
+ i = adf_findlock(adf, fork, ADLOCK_RD | ADLOCK_WR, lock.l_start, lock.l_len);
+ adflock = (i < 0) ? NULL : adf->adf_lock + i;
+
+ /* here's what we check for:
+ 1) we're trying to re-lock a lock, but we didn't specify an update.
+ 2) we're trying to free only part of a lock.
+ 3) we're trying to free a non-existent lock. */
+ if ( (!adflock && (lock.l_type == F_UNLCK))
+ ||
+ (adflock
+ && !(type & ADLOCK_UPGRADE)
+ && ((lock.l_type != F_UNLCK)
+ || (adflock->lock.l_start != lock.l_start)
+ || (adflock->lock.l_len != lock.l_len) ))
+ ) {
+ errno = EINVAL;
+ ret = -1;
+ goto exit;
+ }
-/* -------------------------
- we are using lock as tristate variable
-
- we have a lock ==> 1
- no ==> 0
- error ==> -1
-
-*/
-static int testlock(struct ad_fd *adf, off_t off, off_t len)
-{
-#if 0
- struct flock lock;
- adf_lock_t *plock;
- int i;
- lock.l_start = off;
+ /* now, update our list of locks */
+ /* clear the lock */
+ if (lock.l_type == F_UNLCK) {
+ adf_freelock(adf, i);
+ goto exit;
+ }
- plock = adf->adf_lock;
- lock.l_whence = SEEK_SET;
- lock.l_len = len;
+ /* attempt to lock the file. */
+ if (set_lock(adf->adf_fd, F_SETLK, &lock) < 0) {
+ ret = -1;
+ goto exit;
+ }
- /* Do we have a lock? */
- for (i = 0; i < adf->adf_lockcount; i++) {
- if (OVERLAP(lock.l_start, 1, plock[i].lock.l_start, plock[i].lock.l_len))
- return 1; /* */
- }
- /* Does another process have a lock?
- */
- lock.l_type = (adf->adf_flags & O_RDWR) ?F_WRLCK : F_RDLCK;
+ /* we upgraded this lock. */
+ if (adflock && (type & ADLOCK_UPGRADE)) {
+ memcpy(&adflock->lock, &lock, sizeof(lock));
+ goto exit;
+ }
+
+ /* it wasn't an upgrade */
+ oldlock = -1;
+ if (lock.l_type == F_RDLCK) {
+ oldlock = adf_findxlock(adf, fork, ADLOCK_RD, lock.l_start, lock.l_len);
+ }
+
+ /* no more space. this will also happen if lockmax == lockcount == 0 */
+ if (adf->adf_lockmax == adf->adf_lockcount) {
+ adf_lock_t *tmp = (adf_lock_t *)
+ realloc(adf->adf_lock, sizeof(adf_lock_t)*
+ (adf->adf_lockmax + ARRAY_BLOCK_SIZE));
+ if (!tmp) {
+ ret = fcntl_lock_err = -1;
+ goto exit;
+ }
+ adf->adf_lock = tmp;
+ adf->adf_lockmax += ARRAY_BLOCK_SIZE;
+ }
+ adflock = adf->adf_lock + adf->adf_lockcount;
- if (set_lock(adf->adf_fd, F_GETLK, &lock) < 0) {
- /* is that kind of error possible ?*/
- return (errno == EACCES || errno == EAGAIN)?1:-1;
- }
+ /* fill in fields */
+ memcpy(&adflock->lock, &lock, sizeof(lock));
+ adflock->user = fork;
+ if (oldlock > -1) {
+ adflock->refcount = (adf->adf_lock + oldlock)->refcount;
+ } else if ((adflock->refcount = calloc(1, sizeof(int))) == NULL) {
+ ret = fcntl_lock_err = 1;
+ goto exit;
+ }
- if (lock.l_type == F_UNLCK) {
- return 0;
- }
- return 1;
-#endif
- return 0;
+ (*adflock->refcount)++;
+ adf->adf_lockcount++;
+
+exit:
+ if (ret != 0) {
+ if (fcntl_lock_err != 0) {
+ lock.l_type = F_UNLCK;
+ set_lock(adf->adf_fd, F_SETLK, &lock);
+ }
+ }
+ LOG(log_debug, logtype_ad, "ad_lock: END: %d", ret);
+ return ret;
}
-
-/* -------------------------
-*/
-static int ad_fcntl_tmplock(struct adouble *ad, const u_int32_t eid, const int locktype,
- const off_t off, const off_t len, const int fork)
+int ad_tmplock(struct adouble *ad, uint32_t eid, int locktype, off_t off, off_t len, int fork)
{
- struct flock lock;
- struct ad_fd *adf;
- int err;
- int type;
-
- lock.l_start = off;
- type = locktype;
- if (eid == ADEID_DFORK) {
- adf = &ad->ad_data_fork;
- } else {
- /* FIXME META */
- adf = &ad->ad_resource_fork;
- if (adf->adf_fd == -1) {
- /* there's no resource fork. return success */
- return 0;
+ struct flock lock;
+ struct ad_fd *adf;
+ int err;
+ int type;
+
+ LOG(log_debug, logtype_ad, "ad_tmplock(%s, %s, off: %jd (%s), len: %jd): BEGIN",
+ eid == ADEID_DFORK ? "data" : "reso",
+ locktypetostr(locktype),
+ (intmax_t)off,
+ shmdstrfromoff(off),
+ (intmax_t)len);
+
+ lock.l_start = off;
+ type = locktype;
+
+ if (eid == ADEID_DFORK) {
+ adf = &ad->ad_data_fork;
+ } else {
+ adf = &ad->ad_resource_fork;
+ if (adf->adf_fd == -1) {
+ /* there's no resource fork. return success */
+ err = 0;
+ goto exit;
+ }
+ /* if ADLOCK_FILELOCK we want a lock from offset 0
+ * it's used when deleting a file:
+ * in open we put read locks on meta datas
+ * in delete a write locks on the whole file
+ * so if the file is open by somebody else it fails
+ */
+ if (!(type & ADLOCK_FILELOCK))
+ lock.l_start += ad_getentryoff(ad, eid);
}
- /* if ADLOCK_FILELOCK we want a lock from offset 0
- * it's used when deleting a file:
- * in open we put read locks on meta datas
- * in delete a write locks on the whole file
- * so if the file is open by somebody else it fails
- */
- if (!(type & ADLOCK_FILELOCK))
- lock.l_start += ad_getentryoff(ad, eid);
- }
- if (!(adf->adf_flags & O_RDWR) && (type & ADLOCK_WR)) {
- type = (type & ~ADLOCK_WR) | ADLOCK_RD;
- }
+ if (!(adf->adf_flags & O_RDWR) && (type & ADLOCK_WR)) {
+ type = (type & ~ADLOCK_WR) | ADLOCK_RD;
+ }
- lock.l_type = XLATE_FCNTL_LOCK(type & ADLOCK_MASK);
- lock.l_whence = SEEK_SET;
- lock.l_len = len;
-
- /* see if it's locked by another fork. */
- if (fork && adf_findxlock(adf, fork, ADLOCK_WR |
- ((type & ADLOCK_WR) ? ADLOCK_RD : 0),
- lock.l_start, lock.l_len) > -1) {
- errno = EACCES;
- return -1;
- }
-
- /* okay, we might have ranges byte-locked. we need to make sure that
- * we restore the appropriate ranges once we're done. so, we check
- * for overlap on an unlock and relock.
- * XXX: in the future, all the byte locks will be sorted and contiguous.
- * we just want to upgrade all the locks and then downgrade them
- * here. */
- if (!adf->adf_excl) {
- err = set_lock(adf->adf_fd, F_SETLK, &lock);
- }
- else {
- err = 0;
- }
- if (!err && (lock.l_type == F_UNLCK))
- adf_relockrange(adf, adf->adf_fd, lock.l_start, len);
+ lock.l_type = XLATE_FCNTL_LOCK(type & ADLOCK_MASK);
+ lock.l_whence = SEEK_SET;
+ lock.l_len = len;
+
+ /* see if it's locked by another fork. */
+ if (fork && adf_findxlock(adf, fork,
+ ADLOCK_WR | ((type & ADLOCK_WR) ? ADLOCK_RD : 0),
+ lock.l_start, lock.l_len) > -1) {
+ errno = EACCES;
+ err = -1;
+ goto exit;
+ }
- return err;
+ /* okay, we might have ranges byte-locked. we need to make sure that
+ * we restore the appropriate ranges once we're done. so, we check
+ * for overlap on an unlock and relock.
+ * XXX: in the future, all the byte locks will be sorted and contiguous.
+ * we just want to upgrade all the locks and then downgrade them
+ * here. */
+ err = set_lock(adf->adf_fd, F_SETLK, &lock);
+ if (!err && (lock.l_type == F_UNLCK))
+ adf_relockrange(adf, adf->adf_fd, lock.l_start, len);
+
+exit:
+ LOG(log_debug, logtype_ad, "ad_tmplock: END: %d", err);
+ return err;
}
/* --------------------- */
-static void ad_fcntl_unlock(struct adouble *ad, const int fork)
+void ad_unlock(struct adouble *ad, const int fork, int unlckbrl)
{
- if (ad_data_fileno(ad) != -1) {
- adf_unlock(&ad->ad_data_fork, fork);
- }
- if (ad_reso_fileno(ad) != -1) {
- adf_unlock(&ad->ad_resource_fork, fork);
- }
+ LOG(log_debug, logtype_ad, "ad_unlock(unlckbrl: %d): BEGIN", unlckbrl);
- if (ad->ad_flags != AD_VERSION_EA) {
- return;
- }
- if (ad_meta_fileno(ad) != -1) {
- adf_unlock(&ad->ad_metadata_fork, fork);
- }
-}
+ if (ad_data_fileno(ad) != -1) {
+ adf_unlock(ad, &ad->ad_data_fork, fork, unlckbrl);
+ }
+ if (ad_reso_fileno(ad) != -1) {
+ adf_unlock(ad, &ad->ad_resource_fork, fork, unlckbrl);
+ }
-/******************************************************************************
- * Public functions
- ******************************************************************************/
+ LOG(log_debug, logtype_ad, "ad_unlock: END");
+}
-/* --------------- */
+/*!
+ * Test for a share mode lock
+ *
+ * @param ad (rw) handle
+ * @param eid (r) datafork or ressource fork
+ * @param off (r) sharemode lock to test
+ *
+ * @returns 1 if there's an existing lock, 0 if there's no lock,
+ * -1 in case any error occured
+ */
int ad_testlock(struct adouble *ad, int eid, const off_t off)
{
- return 0;
-#if 0
- struct ad_fd *adf;
- off_t lock_offset;
-
- lock_offset = off;
- if (eid == ADEID_DFORK) {
- adf = &ad->ad_data_fork;
- if (ad_meta_fileno(ad) != -1) {
- adf = ad->ad_md;
- lock_offset = df2off(off);
- }
- }
- else { /* rfork */
- if (ad_meta_fileno(ad) == -1) {
- /* there's no resource fork. return no lock */
- return 0;
+ int ret = 0;
+ off_t lock_offset;
+
+ LOG(log_debug, logtype_ad, "ad_testlock(%s, off: %jd (%s): BEGIN",
+ eid == ADEID_DFORK ? "data" : "reso",
+ (intmax_t)off,
+ shmdstrfromoff(off));
+
+ if (eid == ADEID_DFORK) {
+ lock_offset = off;
+ } else { /* rfork */
+ lock_offset = rf2off(off);
}
- adf = ad->ad_md;
- lock_offset = hf2off(off);
- }
- return testlock(adf, lock_offset, 1);
-#endif
+
+ ret = testlock(&ad->ad_data_fork, lock_offset, 1);
+
+ LOG(log_debug, logtype_ad, "ad_testlock: END: %d", ret);
+ return ret;
}
-/* -------------------------
- return if a file is open by another process.
- Optimized for the common case:
- - there's no locks held by another process (clients)
- - or we already know the answer and don't need to test.
-*/
-uint16_t ad_openforks(struct adouble *ad, u_int16_t attrbits)
+/*!
+ * Return if a file is open by another process.
+ *
+ * Optimized for the common case:
+ * - there's no locks held by another process (clients)
+ * - or we already know the answer and don't need to test (attrbits)
+ *
+ * @param ad (rw) handle
+ * @param attrbits (r) forks opened by us
+ * @returns bitflags ATTRBIT_DOPEN | ATTRBIT_ROPEN if
+ * other process has fork of file opened
+ */
+uint16_t ad_openforks(struct adouble *ad, uint16_t attrbits)
{
- return 0;
-#if 0
- u_int16_t ret = 0;
- struct ad_fd *adf;
- off_t off;
-
- if (!(attrbits & (ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
- off_t len;
- /* XXX know the locks layout:
- AD_FILELOCK_OPEN_WR is first
- and use it for merging requests
- */
- if (ad_meta_fileno(ad) != -1) {
- /* there's a resource fork test the four bytes for
- * data RW/RD and fork RW/RD locks in one request
- */
- adf = ad->ad_md;
- off = LOCK_DATA_WR;
- len = 4;
- }
- else {
- /* no resource fork, only data RD/RW may exist */
- adf = &ad->ad_data_fork;
- off = AD_FILELOCK_OPEN_WR;
- len = 2;
- }
- if (!testlock(adf, off, len))
- return ret;
- }
- /* either there's a lock or we already know one
- fork is open
- */
- if (!(attrbits & ATTRBIT_DOPEN)) {
- if (ad_meta_fileno(ad) != -1) {
- adf = ad->ad_md;
- off = LOCK_DATA_WR;
- }
- else {
- adf = &ad->ad_data_fork;
- off = AD_FILELOCK_OPEN_WR;
- }
- ret = testlock(adf, off, 2) > 0? ATTRBIT_DOPEN : 0;
- }
+ uint16_t ret = 0;
+ struct ad_fd *adf;
+ off_t off;
+ off_t len;
- if (!(attrbits & ATTRBIT_ROPEN)) {
- if (ad_meta_fileno(ad) != -1) {
- adf = ad->ad_md;
- off = LOCK_RSRC_WR;
- ret |= testlock(adf, off, 2) > 0? ATTRBIT_ROPEN : 0;
- }
- }
+ if (ad_data_fileno(ad) == -1)
+ return 0;
- return ret;
-#endif
-}
+ if (!(attrbits & (ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
+ /* Test all 4 locks at once */
+ off = AD_FILELOCK_OPEN_WR;
+ len = 4;
+ if (testlock(&ad->ad_data_fork, off, len) == 0)
+ return 0;
+ }
-/* -------------------------
- the fork is opened in Read Write, Deny Read, Deny Write mode
- lock the whole file once
-*/
-int ad_excl_lock(struct adouble *ad, const u_int32_t eid)
-{
- return 0;
-#if 0
- struct ad_fd *adf;
- struct flock lock;
- int err;
-
- lock.l_start = 0;
- lock.l_type = F_WRLCK;
- lock.l_whence = SEEK_SET;
- lock.l_len = 0;
-
- if (eid == ADEID_DFORK) {
- adf = &ad->ad_data_fork;
- } else {
- adf = &ad->ad_resource_fork;
- lock.l_start = ad_getentryoff(ad, eid);
- }
-
- err = set_lock(adf->adf_fd, F_SETLK, &lock);
- if (!err)
- adf->adf_excl = 1;
- return err;
-#endif
-}
+ /* either there's a lock or we already know one fork is open */
-int ad_lock(struct adouble *ad, uint32_t eid, int type, off_t off, off_t len, int user)
-{
- return 0;
-}
+ if (!(attrbits & ATTRBIT_DOPEN)) {
+ off = AD_FILELOCK_OPEN_WR;
+ ret = testlock(&ad->ad_data_fork, off, 2) > 0 ? ATTRBIT_DOPEN : 0;
+ }
-void ad_unlock(struct adouble *ad, int user)
-{
- return;
-}
+ if (!(attrbits & ATTRBIT_ROPEN)) {
+ off = AD_FILELOCK_RSRC_OPEN_WR;
+ ret |= testlock(&ad->ad_data_fork, off, 2) > 0? ATTRBIT_ROPEN : 0;
+ }
-int ad_tmplock(struct adouble *ad, uint32_t eid, int type, off_t off, off_t len, int user)
-{
- return 0;
+ return ret;
}