X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=libatalk%2Fadouble%2Fad_lock.c;h=bdae8dc473ad11c99fef7913c11a5503dde1cb7f;hb=080aff015fa627cab032d6009ff3b27e7c2a46d6;hp=49006e132d163f44b589a0a1da90aff739ab75b9;hpb=22b6ad9df2e4d9608e47b262d69927bf5958bd44;p=netatalk.git diff --git a/libatalk/adouble/ad_lock.c b/libatalk/adouble/ad_lock.c index 49006e13..bdae8dc4 100644 --- a/libatalk/adouble/ad_lock.c +++ b/libatalk/adouble/ad_lock.c @@ -2,11 +2,9 @@ * 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 @@ -19,34 +17,71 @@ #endif /* HAVE_CONFIG_H */ #include +#include +#include +#include +#include #include #include #include +#include #include #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_default, "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 == -2) { + /* We assign fd = -2 for symlinks -> do nothing */ + if (cmd == F_GETLK) + lock->l_type = F_UNLCK; + return 0; + } + + EC_NEG1( fcntl(fd, cmd, lock) ); + +EC_CLEANUP: + EC_EXIT; } /* ----------------------- */ @@ -79,15 +114,12 @@ static int OVERLAP(off_t a, off_t alen, off_t b, off_t blen) /* 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--; @@ -112,50 +144,46 @@ static void adf_freelock(struct ad_fd *ad, const int i) 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 } @@ -165,7 +193,6 @@ static int adf_findlock(struct ad_fd *ad, const off_t off, const off_t len) { -#if 0 adf_lock_t *lock = ad->adf_lock; int i; @@ -177,7 +204,6 @@ static int adf_findlock(struct ad_fd *ad, return i; } } -#endif return -1; } @@ -188,19 +214,20 @@ static int adf_findxlock(struct ad_fd *ad, 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: @@ -215,442 +242,421 @@ static int adf_findxlock(struct ad_fd *ad, * 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; } -/* ------------------ */ -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_default, "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_default, "ad_lock: END: %d", ret); + return ret; } -/* --------------- */ -int ad_testlock(struct adouble *ad, int eid, const off_t off) +int ad_tmplock(struct adouble *ad, uint32_t eid, int locktype, off_t off, off_t len, int fork) { - 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; + struct flock lock; + struct ad_fd *adf; + int err; + int type; + + LOG(log_debug, logtype_default, "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); } - adf = ad->ad_md; - lock_offset = hf2off(off); - } - return testlock(adf, lock_offset, 1); + + 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; + err = -1; + goto exit; + } + + /* 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_default, "ad_tmplock: END: %d", err); + return err; } -/* ------------------------- - 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. -*/ -u_int16_t ad_openforks(struct adouble *ad, u_int16_t attrbits) +/* --------------------- */ +void ad_unlock(struct adouble *ad, const int fork, int unlckbrl) { - 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; - } + LOG(log_debug, logtype_default, "ad_unlock(unlckbrl: %d): BEGIN", unlckbrl); - 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) { + adf_unlock(ad, &ad->ad_data_fork, fork, unlckbrl); + } + if (ad_reso_fileno(ad) != -1) { + adf_unlock(ad, &ad->ad_resource_fork, fork, unlckbrl); + } - return ret; + LOG(log_debug, logtype_default, "ad_unlock: END"); } -/* ------------------------- -*/ -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) +/*! + * 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) { - 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; + int ret = 0; + off_t lock_offset; + + LOG(log_debug, logtype_default, "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); } - /* 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; - } - - 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; - } + ret = testlock(&ad->ad_data_fork, lock_offset, 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); - - return err; + LOG(log_debug, logtype_default, "ad_testlock: END: %d", ret); + return ret; } -/* ------------------------- - 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 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) { - 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; -} + uint16_t ret = 0; + struct ad_fd *adf; + off_t off; + off_t len; -/* --------------------- */ -void ad_fcntl_unlock(struct adouble *ad, const int fork) -{ - 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); - } + if (ad_data_fileno(ad) == -1) + return 0; - if (ad->ad_flags != AD_VERSION_EA) { - return; - } - if (ad_meta_fileno(ad) != -1) { - adf_unlock(&ad->ad_metadata_fork, fork); - } + 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; + } + + /* either there's a lock or we already know one fork is open */ + + if (!(attrbits & ATTRBIT_DOPEN)) { + off = AD_FILELOCK_OPEN_WR; + ret = testlock(&ad->ad_data_fork, off, 2) > 0 ? ATTRBIT_DOPEN : 0; + } + + if (!(attrbits & ATTRBIT_ROPEN)) { + off = AD_FILELOCK_RSRC_OPEN_WR; + ret |= testlock(&ad->ad_data_fork, off, 2) > 0? ATTRBIT_ROPEN : 0; + } + return ret; }