#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 "ad_lock.h"
+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 == AD_SYMLINK) {
+ if (cmd == F_GETLK)
+ lock->l_type = F_UNLCK;
+ return 0;
+ }
+
+ EC_NEG1( fcntl(fd, cmd, lock) );
+
+EC_CLEANUP:
+ EC_EXIT;
}
/* ----------------------- */
/* 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)
{
- 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;
- }
+ 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;
+ }
}
}
* 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
-*/
-
-static off_t df2off(off_t off)
-{
- off_t start = off;
- if (off == AD_FILELOCK_OPEN_WR)
- start = LOCK_DATA_WR;
- else if (off == AD_FILELOCK_OPEN_RD)
- start = LOCK_DATA_RD;
- else if (off == AD_FILELOCK_DENY_RD)
- start = LOCK_DATA_DRD;
- else if (off == AD_FILELOCK_DENY_WR)
- start = LOCK_DATA_DWR;
- else if (off == AD_FILELOCK_OPEN_NONE)
- start = LOCK_DATA_NONE;
- return start;
-}
-
-/* --------------
- translate a resource fork lock to an offset
-*/
-
-static off_t hf2off(off_t off)
-{
- off_t 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;
-}
/* --------------
translate a resource fork lock to an offset
return 1;
}
-static uint16_t ad_openforks_v2(struct adouble *ad, uint16_t attrbits)
+#define LTYPE2STRBUFSIZ 128
+static const char *locktypetostr(int type)
{
- uint16_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_mdp;
- 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_mdp;
- off = LOCK_DATA_WR;
- }
- else {
- adf = &ad->ad_data_fork;
- off = AD_FILELOCK_OPEN_WR;
- }
- ret = testlock(adf, off, 2) > 0? ATTRBIT_DOPEN : 0;
- }
+ int first = 1;
+ static char buf[LTYPE2STRBUFSIZ];
- if (!(attrbits & ATTRBIT_ROPEN)) {
- if (ad_meta_fileno(ad) != -1) {
- adf = ad->ad_mdp;
- off = LOCK_RSRC_WR;
- ret |= testlock(adf, off, 2) > 0? ATTRBIT_ROPEN : 0;
- }
- }
+ buf[0] = 0;
- return ret;
-}
-
-/* test for sharemode locks, adouble:ea stores them on the datafork */
-static uint16_t ad_openforks_ea(struct adouble *ad, uint16_t attrbits)
-{
- uint16_t ret = 0;
- struct ad_fd *adf;
- off_t off;
- off_t len;
-
- if (ad_data_fileno(ad) == -1)
- return 0;
-
- 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;
+ if (type == 0) {
+ strlcat(buf, "CLR", LTYPE2STRBUFSIZ);
+ first = 0;
+ return buf;
}
-
- /* 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 (type & ADLOCK_RD) {
+ if (!first)
+ strlcat(buf, "|", LTYPE2STRBUFSIZ);
+ strlcat(buf, "RD", LTYPE2STRBUFSIZ);
+ first = 0;
}
-
- if (!(attrbits & ATTRBIT_ROPEN)) {
- off = AD_FILELOCK_RSRC_OPEN_WR;
- ret |= testlock(&ad->ad_data_fork, off, 2) > 0? ATTRBIT_ROPEN : 0;
+ if (type & ADLOCK_WR) {
+ if (!first)
+ strlcat(buf, "|", LTYPE2STRBUFSIZ);
+ strlcat(buf, "WR", LTYPE2STRBUFSIZ);
+ first = 0;
}
-
- return ret;
-}
-
-static int ad_testlock_v2(struct adouble *ad, int eid, const off_t off)
-{
- 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_mdp;
- lock_offset = df2off(off);
- }
- } else { /* rfork */
- if (ad_meta_fileno(ad) == -1) {
- /* there's no resource fork. return no lock */
- return 0;
- }
- adf = ad->ad_mdp;
- lock_offset = hf2off(off);
+ if (type & ADLOCK_UPGRADE) {
+ if (!first)
+ strlcat(buf, "|", LTYPE2STRBUFSIZ);
+ strlcat(buf, "UPG", LTYPE2STRBUFSIZ);
+ first = 0;
}
- return testlock(adf, lock_offset, 1);
-}
-
-static int ad_testlock_ea(struct adouble *ad, int eid, const off_t off)
-{
- off_t lock_offset;
-
- if (eid == ADEID_DFORK) {
- lock_offset = off;
- } else { /* rfork */
- lock_offset = rf2off(off);
+ if (type & ADLOCK_FILELOCK) {
+ if (!first)
+ strlcat(buf, "|", LTYPE2STRBUFSIZ);
+ strlcat(buf, "FILELOCK", LTYPE2STRBUFSIZ);
+ first = 0;
}
- return testlock(&ad->ad_data_fork, lock_offset, 1);
+
+ return buf;
}
/******************************************************************************
int oldlock;
int i;
int type;
+ int ret = 0, fcntl_lock_err = 0;
- if ((type & ADLOCK_FILELOCK) && (len != 1))
- /* safety check */
- return -1;
+ 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");
- lock.l_start = off;
type = locktype;
+
if (eid == ADEID_DFORK) {
adf = &ad->ad_data_fork;
- if ((ad->ad_vers == AD_VERSION2) && (type & ADLOCK_FILELOCK)) {
- if (ad_meta_fileno(ad) != -1) { /* META */
- adf = ad->ad_mdp;
- lock.l_start = df2off(off);
- }
- }
+ lock.l_start = off;
} else { /* rfork */
- switch (ad->ad_vers) {
- case AD_VERSION2:
- 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_FILELOCK) {
- adf = ad->ad_mdp; /* either resource or meta data (set in ad_open) */
- lock.l_start = hf2off(off);
- } else {
- /* we really want the resource fork it's a byte lock */
- adf = &ad->ad_resource_fork;
- lock.l_start += ad_getentryoff(ad, eid);
- }
- break;
-
- case AD_VERSION_EA:
- if (type & ADLOCK_FILELOCK) {
- lock.l_start = rf2off(off);
- } else {
- /* it's a byterange lock on the rsrcfork -> discard it */
- return 0;
- }
+ if (type & ADLOCK_FILELOCK) {
adf = &ad->ad_data_fork;
- break;
-
- default:
- return -1;
+ lock.l_start = rf2off(off);
+ } else {
+ adf = ad->ad_rfp;
+ lock.l_start = off + ad_getentryoff(ad, ADEID_RFORK);
}
}
((type & ADLOCK_WR) ? ADLOCK_RD : 0),
lock.l_start, lock.l_len) > -1) {
errno = EACCES;
- return -1;
+ ret = -1;
+ goto exit;
}
/* look for any existing lock that we may have */
|| (adflock->lock.l_len != lock.l_len) ))
) {
errno = EINVAL;
- return -1;
+ ret = -1;
+ goto exit;
}
/* clear the lock */
if (lock.l_type == F_UNLCK) {
adf_freelock(adf, i);
- return 0;
+ goto exit;
}
/* attempt to lock the file. */
- if (set_lock(adf->adf_fd, F_SETLK, &lock) < 0)
- return -1;
+ if (set_lock(adf->adf_fd, F_SETLK, &lock) < 0) {
+ ret = -1;
+ goto exit;
+ }
/* we upgraded this lock. */
if (adflock && (type & ADLOCK_UPGRADE)) {
memcpy(&adflock->lock, &lock, sizeof(lock));
- return 0;
+ goto exit;
}
/* it wasn't an upgrade */
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;
+ if (!tmp) {
+ ret = fcntl_lock_err = -1;
+ goto exit;
+ }
adf->adf_lock = tmp;
adf->adf_lockmax += ARRAY_BLOCK_SIZE;
}
if (oldlock > -1) {
adflock->refcount = (adf->adf_lock + oldlock)->refcount;
} else if ((adflock->refcount = calloc(1, sizeof(int))) == NULL) {
- goto fcntl_lock_err;
+ ret = fcntl_lock_err = 1;
+ goto exit;
}
(*adflock->refcount)++;
adf->adf_lockcount++;
- return 0;
-fcntl_lock_err:
- lock.l_type = F_UNLCK;
- set_lock(adf->adf_fd, F_SETLK, &lock);
- return -1;
+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_tmplock(struct adouble *ad, const uint32_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;
+ 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 {
- /* FIXME META */
adf = &ad->ad_resource_fork;
if (adf->adf_fd == -1) {
/* there's no resource fork. return success */
- return 0;
+ err = 0;
+ goto exit;
}
/* if ADLOCK_FILELOCK we want a lock from offset 0
* it's used when deleting a file:
ADLOCK_WR | ((type & ADLOCK_WR) ? ADLOCK_RD : 0),
lock.l_start, lock.l_len) > -1) {
errno = EACCES;
- return -1;
+ err = -1;
+ goto exit;
}
/* okay, we might have ranges byte-locked. we need to make sure that
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;
}
/* --------------------- */
-void ad_unlock(struct adouble *ad, const int fork)
+void ad_unlock(struct adouble *ad, const int fork, int unlckbrl)
{
+ LOG(log_debug, logtype_default, "ad_unlock(unlckbrl: %d): BEGIN", unlckbrl);
+
if (ad_data_fileno(ad) != -1) {
- adf_unlock(&ad->ad_data_fork, fork);
+ adf_unlock(ad, &ad->ad_data_fork, fork, unlckbrl);
}
if (ad_reso_fileno(ad) != -1) {
- adf_unlock(&ad->ad_resource_fork, fork);
+ adf_unlock(ad, &ad->ad_resource_fork, fork, unlckbrl);
}
+
+ LOG(log_debug, logtype_default, "ad_unlock: END");
}
/*!
*/
int ad_testlock(struct adouble *ad, int eid, const off_t off)
{
- switch (ad->ad_vers) {
- case AD_VERSION2:
- return ad_testlock_v2(ad, eid, off);
- case AD_VERSION_EA:
- return ad_testlock_ea(ad, eid, off);
- default:
- return -1;
+ 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);
}
+
+ ret = testlock(&ad->ad_data_fork, lock_offset, 1);
+
+ LOG(log_debug, logtype_default, "ad_testlock: END: %d", ret);
+ return ret;
}
/*!
*/
uint16_t ad_openforks(struct adouble *ad, uint16_t attrbits)
{
- switch (ad->ad_vers) {
- case AD_VERSION2:
- return ad_openforks_v2(ad, attrbits);
- case AD_VERSION_EA:
- return ad_openforks_ea(ad, attrbits);
- default:
- return -1;
+ uint16_t ret = 0;
+ struct ad_fd *adf;
+ off_t off;
+ off_t len;
+
+ if (ad_data_fileno(ad) == -1)
+ return 0;
+
+ 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;
}