Commit e8674181 e86741813eb537dc19491a9f69f8ebc12815ac0e by Sergey Poznyakoff

Rewrite

1 parent 525ec621
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2000, 2001, 2005 Free Software Foundation, Inc.
Copyright (C) 1999, 2000, 2001, 2005, 2007 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
......@@ -47,34 +47,53 @@ enum mu_locker_set_mode
/* mu_locker_create() flags */
#define MU_LOCKER_SIMPLE 0x00
/* Locker types */
#define MU_LOCKER_TYPE_DOTLOCK 0
#define MU_LOCKER_TYPE_EXTERNAL 1
/* Use an external program to lock the file. This is necessary
for programs having permission to access a file, but do not
have write permission on the directory that contains that file. */
#define MU_LOCKER_TYPE_KERNEL 2
/* Use kernel locking (flock, lockf or ioctl) */
#define MU_LOCKER_TYPE_NULL 3
/* Special locker type: means no lock. This is to be used with
temporary mailboxes stored in memory. */
#define MU_LOCKER_TYPE_TO_FLAG(t) ((t) << 8)
#define MU_LOCKER_FLAG_TO_TYPE(f) ((f) >> 8)
#define MU_LOCKER_IS_TYPE(f,t) (MU_LOCKER_FLAG_TO_TYPE(f) == (t))
#define MU_LOCKER_SET_TYPE(f,t) ((f) = MU_LOCKER_TYPE_TO_FLAG(t) | MU_LOCKER_OPTIONS(f))
#define MU_LOCKER_TYPE_MASK 0xff00
#define MU_LOCKER_OPTION_MASK 0x00ff
#define MU_LOCKER_OPTIONS(f) ((f) & MU_LOCKER_OPTION_MASK)
#define MU_LOCKER_NULL MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_NULL)
#define MU_LOCKER_DOTLOCK MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_DOTLOCK)
#define MU_LOCKER_EXTERNAL MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_EXTERNAL)
#define MU_LOCKER_KERNEL MU_LOCKER_TYPE_TO_FLAG(MU_LOCKER_TYPE_KERNEL)
/* Options */
#define MU_LOCKER_SIMPLE 0x0000
/* Just try and dotlock the file, not the default because its usually
better to retry. */
#define MU_LOCKER_RETRY 0x01
#define MU_LOCKER_RETRY 0x0001
/* This requests that we loop retries times, sleeping retry_sleep
seconds in between trying to obtain the lock before failing with
MU_LOCK_CONFLICT. */
#define MU_LOCKER_TIME 0x02
#define MU_LOCKER_TIME 0x0002
/* This mode checks the last update time of the lock, then removes
it if older than MU_LOCKER_EXPIRE_TIME. If a client uses this,
then the servers better periodically update the lock on the
file... do they? */
#define MU_LOCKER_PID 0x04
#define MU_LOCKER_PID 0x0004
/* PID locking is only useful for programs that aren't using
an external dotlocker, non-setgid programs will use a dotlocker,
which locks and exits imediately. This is a protection against
a server crashing, it's not generally useful. */
#define MU_LOCKER_EXTERNAL 0x08
/* Use an external program to lock the file. This is necessary
for programs having permission to access a file, but do not
have write permission on the directory that contains that file. */
#define MU_LOCKER_NULL 0x10
/* Special locker type: means no lock. This is to be used with
temporary mailboxes stored in memory. */
#define MU_LOCKER_KERNEL 0x20
/* Use kernel locking (flock, lockf or ioctl) */
#define MU_LOCKER_DEFAULT (MU_LOCKER_RETRY)
#define MU_LOCKER_DEFAULT (MU_LOCKER_DOTLOCK | MU_LOCKER_RETRY)
/* Use these flags for as the default locker flags (the default defaults
* to MU_LOCKER_DEFAULT). A flags of 0 resets the flags back to the
......@@ -104,6 +123,14 @@ extern int mu_locker_get_retries (mu_locker_t, int*);
extern int mu_locker_get_retry_sleep (mu_locker_t, int*);
extern int mu_locker_get_external (mu_locker_t, char**);
enum mu_locker_mode
{
mu_lck_shr, /* Shared (advisory) lock */
mu_lck_exc, /* Exclusive lock */
mu_lck_opt /* Optional lock = shared, if the locker supports it, no
locking otherwise */
};
extern int mu_locker_lock (mu_locker_t);
extern int mu_locker_touchlock (mu_locker_t);
extern int mu_locker_unlock (mu_locker_t);
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2000, 2001, 2005, 2006 Free Software Foundation, Inc.
Copyright (C) 1999, 2000, 2001, 2005, 2006,
2007 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
......@@ -49,7 +50,8 @@
struct _mu_locker
{
int refcnt;
unsigned refcnt; /* Number of times mu_locker_lock was called */
enum mu_locker_mode mode; /* Current locking mode (if refcnt > 0) */
char *file;
int flags;
......@@ -73,20 +75,121 @@ struct _mu_locker
} data;
};
/* Assert that we're managing the refcnt and fd correctly, either
* we have a lock, and the fd is valid, or refcnt is 0 and fd is -1.
* And refcnt can never be less than 0.
*/
#define INVARIANT(l) assert((l)->refcnt >= 0);
static void expire_stale_lock (mu_locker_t lock);
static int stat_check (const char *file, int fd, int links);
static int check_file_permissions (const char *file);
static int lock_external (mu_locker_t l, int lock);
static int _locker_lock_dotlock (mu_locker_t lock);
static int _locker_unlock_dotlock (mu_locker_t lock);
static int _locker_lock_kernel (mu_locker_t lock);
static int _locker_unlock_kernel (mu_locker_t lock);
#define MU_LOCKER_TYPE(l) MU_LOCKER_FLAG_TO_TYPE((l)->flags)
struct locker_tab
{
int (*init) (mu_locker_t);
void (*destroy) (mu_locker_t);
int (*prelock) (mu_locker_t);
int (*lock) (mu_locker_t, enum mu_locker_mode);
int (*unlock) (mu_locker_t);
};
static int init_dotlock (mu_locker_t);
static void destroy_dotlock (mu_locker_t);
static int lock_dotlock (mu_locker_t, enum mu_locker_mode);
static int unlock_dotlock (mu_locker_t);
static int init_external (mu_locker_t);
static void destroy_external (mu_locker_t);
static int lock_external (mu_locker_t, enum mu_locker_mode);
static int unlock_external (mu_locker_t);
static int init_kernel (mu_locker_t);
static int lock_kernel (mu_locker_t, enum mu_locker_mode);
static int unlock_kernel (mu_locker_t);
static int prelock_common (mu_locker_t);
static struct locker_tab locker_tab[] = {
{ init_dotlock, destroy_dotlock, prelock_common,
lock_dotlock, unlock_dotlock },
{ init_external, destroy_external, prelock_common,
lock_external, unlock_external },
{ init_kernel, NULL, NULL, lock_kernel, unlock_kernel },
{ NULL, NULL, NULL, NULL, NULL }
};
#define MU_LOCKER_NTYPES (sizeof (locker_tab) / sizeof (locker_tab[0]))
static int
stat_check (const char *file, int fd, int links)
{
struct stat fn_stat;
struct stat fd_stat;
int err = 0;
int localfd = -1;
if (fd == -1)
{
localfd = open (file, O_RDONLY);
if (localfd == -1)
return errno;
fd = localfd;
}
/* We should always be able to stat a valid fd, so this
is an error condition. */
if (lstat (file, &fn_stat) || fstat (fd, &fd_stat))
err = errno;
else
{
/* If the link and stat don't report the same info, or the
file is a symlink, fail the locking. */
#define CHK(X) if(X) err = EINVAL
CHK (!S_ISREG (fn_stat.st_mode));
CHK (!S_ISREG (fd_stat.st_mode));
CHK (fn_stat.st_nlink != links);
CHK (fn_stat.st_dev != fd_stat.st_dev);
CHK (fn_stat.st_ino != fd_stat.st_ino);
CHK (fn_stat.st_mode != fd_stat.st_mode);
CHK (fn_stat.st_nlink != fd_stat.st_nlink);
CHK (fn_stat.st_uid != fd_stat.st_uid);
CHK (fn_stat.st_gid != fd_stat.st_gid);
CHK (fn_stat.st_rdev != fd_stat.st_rdev);
#undef CHK
}
if (localfd != -1)
close (localfd);
return err;
}
static int
check_file_permissions (const char *file)
{
int fd = -1;
int err = 0;
if ((fd = open (file, O_RDONLY)) == -1)
return errno == ENOENT ? 0 : errno;
err = stat_check (file, fd, 1);
close (fd);
fd = -1;
if (err)
{
if (err == EINVAL)
err = MU_ERR_LOCK_BAD_FILE;
return err;
}
return 0;
}
static int
prelock_common (mu_locker_t locker)
{
/* Check if we are trying to lock a regular file, with a link count
of 1, that we have permission to read, etc., or don't lock it. */
return check_file_permissions (locker->file);
}
static int mu_locker_default_flags = MU_LOCKER_DEFAULT;
static time_t mu_locker_retry_timeout = MU_LOCKER_RETRY_SLEEP;
......@@ -143,115 +246,36 @@ mu_locker_set_default_external_program (char *path)
}
int
mu_locker_create (mu_locker_t *plocker, const char *filename_, int flags)
mu_locker_set_flags (mu_locker_t locker, int flags)
{
mu_locker_t l;
char filename[_POSIX_PATH_MAX];
int err = 0;
unsigned otype, ntype;
if (plocker == NULL)
return MU_ERR_OUT_PTR_NULL;
if (!locker)
return MU_ERR_LOCKER_NULL;
if (filename_ == NULL)
otype = MU_LOCKER_TYPE (locker);
if (otype >= MU_LOCKER_NTYPES)
return EINVAL;
ntype = MU_LOCKER_FLAG_TO_TYPE (flags);
if (ntype >= MU_LOCKER_NTYPES)
return EINVAL;
if((err = mu_unroll_symlink(filename, sizeof(filename), filename_)))
return err;
l = calloc (1, sizeof (*l));
if (l == NULL)
return ENOMEM;
l->file = strdup(filename);
if (l->file == NULL)
{
free (l);
return ENOMEM;
}
if (strcmp (filename, "/dev/null") == 0)
l->flags = MU_LOCKER_NULL;
else if (flags)
l->flags = flags;
else
l->flags = mu_locker_default_flags;
l->expire_time = mu_locker_expire_timeout;
l->retries = mu_locker_retry_count;
l->retry_sleep = mu_locker_retry_timeout;
/* Initialize locker-type-specific data */
if (l->flags & MU_LOCKER_EXTERNAL)
{
if (!(l->data.external.name = strdup (mu_locker_external_program ?
mu_locker_external_program :
MU_LOCKER_EXTERNAL_PROGRAM)))
if (ntype != otype)
{
mu_locker_destroy (&l);
return ENOMEM;
}
}
else if (!(l->flags & MU_LOCKER_KERNEL))
{
l->data.dot.dotlock = malloc (strlen (l->file)
+ 5 /*strlen(".lock")*/ + 1);
int rc;
if (!l->data.dot.dotlock)
if (locker_tab[otype].destroy)
locker_tab[otype].destroy (locker);
locker->flags = flags;
if (locker_tab[otype].init)
{
free (l->file);
free (l);
return ENOMEM;
rc = locker_tab[otype].init (locker);
if (rc)
locker->flags = MU_LOCKER_NULL;
return rc;
}
sprintf (l->data.dot.dotlock, "%s.lock", l->file);
}
INVARIANT(l);
*plocker = l;
return 0;
}
void
_locker_destroy_private (mu_locker_t locker)
{
if (locker)
{
if (locker->flags & MU_LOCKER_EXTERNAL)
free (locker->data.external.name);
else if (locker->flags & MU_LOCKER_KERNEL)
/* nothing */;
else
{
free (locker->data.dot.dotlock);
locker->data.dot.dotlock = NULL;
free (locker->data.dot.nfslock);
locker->data.dot.nfslock = NULL;
}
}
}
void
mu_locker_destroy (mu_locker_t *plocker)
{
if (plocker && *plocker)
{
_locker_destroy_private (*plocker);
free ((*plocker)->file);
free (*plocker);
*plocker = NULL;
}
}
int
mu_locker_set_flags (mu_locker_t locker, int flags)
{
if (!locker)
return MU_ERR_LOCKER_NULL;
locker->flags = flags;
return 0;
......@@ -306,7 +330,7 @@ mu_locker_set_external (mu_locker_t locker, const char* program)
if (!locker)
return MU_ERR_LOCKER_NULL;
if (!(locker->flags & MU_LOCKER_EXTERNAL))
if (MU_LOCKER_TYPE (locker) != MU_LOCKER_TYPE_EXTERNAL)
return EINVAL;
/* program can be NULL */
......@@ -329,7 +353,7 @@ mu_locker_get_flags (mu_locker_t locker, int *flags)
if (!locker)
return MU_ERR_LOCKER_NULL;
if(!flags)
if (!flags)
return EINVAL;
*flags = locker->flags;
......@@ -380,144 +404,162 @@ mu_locker_get_retry_sleep (mu_locker_t locker, int *retry_sleep)
}
int
mu_locker_lock (mu_locker_t lock)
mu_locker_create (mu_locker_t *plocker, const char *fname, int flags)
{
int rc;
int retries = 1;
unsigned type;
mu_locker_t l;
char filename[_POSIX_PATH_MAX];
int err = 0;
if (lock == NULL)
if (plocker == NULL)
return MU_ERR_OUT_PTR_NULL;
if (fname == NULL)
return EINVAL;
if (lock->flags == MU_LOCKER_NULL)
return 0;
if ((err = mu_unroll_symlink (filename, sizeof (filename), fname)))
return err;
INVARIANT (lock)
/* Is the lock already applied? */
if (lock->refcnt > 0)
l = calloc (1, sizeof (*l));
if (l == NULL)
return ENOMEM;
l->file = strdup (filename);
if (l->file == NULL)
{
lock->refcnt++;
return 0;
free (l);
return ENOMEM;
}
if (access (lock->file, W_OK))
if (strcmp (filename, "/dev/null") == 0)
l->flags = MU_LOCKER_NULL;
else if (flags)
l->flags = flags;
else
l->flags = mu_locker_default_flags;
l->expire_time = mu_locker_expire_timeout;
l->retries = mu_locker_retry_count;
l->retry_sleep = mu_locker_retry_timeout;
type = MU_LOCKER_TYPE (l);
if (type >= MU_LOCKER_NTYPES)
{
/* There is no use trying to lock the file if we are not
allowed to write to it */
_locker_destroy_private (lock);
lock->flags |= MU_LOCKER_NULL;
return 0;
free (l->file);
return EINVAL;
}
/* Check we are trying to lock a regular file, with a link count
of 1, that we have permission to read, etc., or don't lock it. */
if ((rc = check_file_permissions(lock->file)))
return rc;
/* Do the lock with an external program, if requested. */
if (lock->flags & MU_LOCKER_EXTERNAL)
return lock_external (lock, 1);
else if (!(lock->flags & MU_LOCKER_KERNEL))
/* Initialize locker-type-specific data */
err = locker_tab[type].init ? locker_tab[type].init (l) : 0;
if (err)
{
char *tmp, *p;
mu_locker_destroy (&l);
return err;
}
tmp = strdup (lock->file);
if (!tmp)
return ENOMEM;
*plocker = l;
strcpy (tmp, lock->file);
p = strrchr (tmp, '/');
if (!p)
return 0;
}
void
mu_locker_destroy (mu_locker_t *plocker)
{
if (plocker && *plocker)
{
free (tmp);
tmp = strdup (".");
if (!tmp)
return ENOMEM;
unsigned type = MU_LOCKER_TYPE (*plocker);
if (type < MU_LOCKER_NTYPES)
{
if (locker_tab[type].destroy)
locker_tab[type].destroy (*plocker);
free ((*plocker)->file);
free (*plocker);
*plocker = NULL;
}
else
*p = 0;
}
}
if (access (tmp, W_OK))
int
_mu_locker_lock (mu_locker_t lock, enum mu_locker_mode mode)
{
int rc;
unsigned type;
unsigned retries = 1;
if (lock == NULL || (type = MU_LOCKER_TYPE (lock)) >= MU_LOCKER_NTYPES)
return EINVAL;
if (locker_tab[type].prelock && (rc = locker_tab[type].prelock (lock)))
return rc;
/* Is the lock already applied? */
if (lock->refcnt > 0)
{
/* Fallback to kernel locking */
_locker_destroy_private (lock);
lock->flags |= MU_LOCKER_KERNEL;
}
free (tmp);
lock->refcnt++;
if (mode == lock->mode)
return 0;
}
lock->mode = mode;
if (lock->flags & MU_LOCKER_RETRY)
{
retries = lock->retries;
}
if (locker_tab[type].lock)
{
while (retries--)
{
if (lock->flags & MU_LOCKER_KERNEL)
rc = _locker_lock_kernel (lock);
else
rc = _locker_lock_dotlock (lock);
rc = locker_tab[type].lock (lock, mode);
if (rc == EAGAIN && retries)
{
sleep (lock->retry_sleep);
else if (rc)
return rc;
else /* rc == 0 */
break;
continue;
}
lock->refcnt = 1;
if (rc == 0)
lock->refcnt++;
return 0;
break;
}
}
else
rc = 0;
return rc;
}
int
mu_locker_touchlock (mu_locker_t lock)
mu_locker_lock (mu_locker_t lock)
{
if (!lock)
return MU_ERR_LOCKER_NULL;
if (lock->flags == MU_LOCKER_NULL)
return 0;
if (lock->refcnt > 0)
return utime (lock->data.dot.dotlock, NULL);
return MU_ERR_LOCK_NOT_HELD;
return _mu_locker_lock (lock, mu_lck_exc);
}
int
mu_locker_unlock (mu_locker_t lock)
{
int rc = 0;
unsigned type;
if (!lock)
return MU_ERR_LOCKER_NULL;
if (lock->flags == MU_LOCKER_NULL)
return 0;
if (lock->refcnt == 0)
return MU_ERR_LOCK_NOT_HELD;
/* Do the lock with an external program, if requested. */
if (lock->flags & MU_LOCKER_EXTERNAL)
return lock_external (lock, 0);
if (lock->refcnt > 1)
{
lock->refcnt--;
return 0;
}
if ((rc = check_file_permissions (lock->file)))
return rc;
if (lock->flags & MU_LOCKER_KERNEL)
rc = _locker_unlock_kernel (lock);
else
rc = _locker_unlock_dotlock (lock);
if (--lock->refcnt > 0)
return 0;
if (rc == 0)
lock->refcnt = 0;
type = MU_LOCKER_TYPE (lock);
if (locker_tab[type].unlock)
rc = locker_tab[type].unlock (lock);
else
rc = 0;
return rc;
}
......@@ -525,21 +567,17 @@ mu_locker_unlock (mu_locker_t lock)
int
mu_locker_remove_lock (mu_locker_t lock)
{
int err;
if (!lock)
return MU_ERR_LOCKER_NULL;
if (lock->flags == MU_LOCKER_NULL)
return 0;
/* Force the reference count to 1 to unlock the file. */
lock->refcnt = 1;
err = mu_locker_unlock (lock);
return err;
return mu_locker_unlock (lock);
}
#define DOTLOCK_SUFFIX ".lock"
/* expire a stale lock (if MU_LOCKER_PID or MU_LOCKER_TIME) */
static void
expire_stale_lock (mu_locker_t lock)
......@@ -587,80 +625,60 @@ expire_stale_lock (mu_locker_t lock)
}
static int
stat_check (const char *file, int fd, int links)
init_dotlock (mu_locker_t locker)
{
struct stat fn_stat;
struct stat fd_stat;
int err = 0;
int localfd = -1;
char *tmp, *p;
if (fd == -1)
{
localfd = open(file, O_RDONLY);
/* Make sure the spool directory is writable */
tmp = strdup (locker->file);
if (!tmp)
return ENOMEM;
if (localfd == -1)
return errno;
fd = localfd;
strcpy (tmp, locker->file);
p = strrchr (tmp, '/');
if (!p)
{
free (tmp);
tmp = strdup (".");
if (!tmp)
return ENOMEM;
}
/* We should always be able to stat a valid fd, so this
is an error condition. */
if (lstat (file, &fn_stat) || fstat (fd, &fd_stat))
err = errno;
else
{
/* If the link and stat don't report the same info, or the
file is a symlink, fail the locking. */
#define CHK(X) if(X) err = EINVAL
CHK (!S_ISREG (fn_stat.st_mode));
CHK (!S_ISREG (fd_stat.st_mode));
CHK (fn_stat.st_nlink != links);
CHK (fn_stat.st_dev != fd_stat.st_dev);
CHK (fn_stat.st_ino != fd_stat.st_ino);
CHK (fn_stat.st_mode != fd_stat.st_mode);
CHK (fn_stat.st_nlink != fd_stat.st_nlink);
CHK (fn_stat.st_uid != fd_stat.st_uid);
CHK (fn_stat.st_gid != fd_stat.st_gid);
CHK (fn_stat.st_rdev != fd_stat.st_rdev);
*p = 0;
#undef CHK
if (access (tmp, W_OK))
{
/* Fallback to kernel locking */
free (tmp);
return mu_locker_set_flags (locker,
MU_LOCKER_KERNEL|MU_LOCKER_OPTIONS(locker->flags));
}
if (localfd != -1)
close (localfd);
return err;
}
static int
check_file_permissions (const char *file)
{
int fd = -1;
int err = 0;
free (tmp);
if ((fd = open (file, O_RDONLY)) == -1)
return errno == ENOENT ? 0 : errno;
locker->data.dot.dotlock = malloc (strlen (locker->file)
+ sizeof (DOTLOCK_SUFFIX));
err = stat_check (file, fd, 1);
close (fd);
fd = -1;
if (err)
{
if (err == EINVAL)
err = MU_ERR_LOCK_BAD_FILE;
return err;
}
if (!locker->data.dot.dotlock)
return ENOMEM;
strcpy (locker->data.dot.dotlock, locker->file);
strcat (locker->data.dot.dotlock, DOTLOCK_SUFFIX);
return 0;
}
static void
destroy_dotlock (mu_locker_t locker)
{
free (locker->data.dot.dotlock);
}
#ifndef MAXHOSTNAMELEN
# define MAXHOSTNAMELEN 256
#endif
/* Locker-specific lock/unlock functions */
int
_locker_lock_dotlock (mu_locker_t lock)
static int
lock_dotlock (mu_locker_t locker, enum mu_locker_mode mode)
{
char host[MAXHOSTNAMELEN + 1] = "localhost";
char pid[11]; /* 10 is strlen(2^32 = 4294967296) */
......@@ -669,14 +687,14 @@ _locker_lock_dotlock (mu_locker_t lock)
int err = 0;
int fd;
if (lock->data.dot.nfslock)
if (locker->data.dot.nfslock)
{
unlink (lock->data.dot.nfslock);
free (lock->data.dot.nfslock);
lock->data.dot.nfslock = 0;
unlink (locker->data.dot.nfslock);
free (locker->data.dot.nfslock);
locker->data.dot.nfslock = 0;
}
expire_stale_lock (lock);
expire_stale_lock (locker);
/* build the NFS hitching-post to the lock file */
......@@ -689,20 +707,20 @@ _locker_lock_dotlock (mu_locker_t lock)
snprintf (pid, sizeof (pid), "%lu", (unsigned long) getpid ());
pid[sizeof (pid) - 1] = 0;
sz = strlen (lock->file) + 1 /* "." */
sz = strlen (locker->file) + 1 /* "." */
+ strlen (pid) + 1 /* "." */
+ strlen (now) + 1 /* "." */
+ strlen (host) + 1;
lock->data.dot.nfslock = malloc (sz);
locker->data.dot.nfslock = malloc (sz);
if (!lock->data.dot.nfslock)
if (!locker->data.dot.nfslock)
return ENOMEM;
snprintf (lock->data.dot.nfslock, sz, "%s.%s.%s.%s", lock->file, pid, now,
host);
snprintf (locker->data.dot.nfslock, sz, "%s.%s.%s.%s",
locker->file, pid, now, host);
fd = open (lock->data.dot.nfslock,
fd = open (locker->data.dot.nfslock,
O_WRONLY | O_CREAT | O_EXCL, LOCKFILE_ATTR);
if (fd == -1)
{
......@@ -714,53 +732,53 @@ _locker_lock_dotlock (mu_locker_t lock)
close (fd);
/* Try to link to the lockfile. */
if (link (lock->data.dot.nfslock, lock->data.dot.dotlock) == -1)
if (link (locker->data.dot.nfslock, locker->data.dot.dotlock) == -1)
{
unlink (lock->data.dot.nfslock);
unlink (locker->data.dot.nfslock);
if (errno == EEXIST)
return MU_ERR_LOCK_CONFLICT;
return errno;
}
if ((fd = open (lock->data.dot.dotlock, O_RDWR)) == -1)
if ((fd = open (locker->data.dot.dotlock, O_RDWR)) == -1)
{
unlink (lock->data.dot.nfslock);
unlink (locker->data.dot.nfslock);
return errno;
}
err = stat_check (lock->data.dot.nfslock, fd, 2);
err = stat_check (locker->data.dot.nfslock, fd, 2);
if (err)
{
unlink (lock->data.dot.nfslock);
unlink (locker->data.dot.nfslock);
if (err == EINVAL)
return MU_ERR_LOCK_BAD_LOCK;
return errno;
}
unlink (lock->data.dot.nfslock);
unlink (locker->data.dot.nfslock);
/* If no errors, we have the lock. */
assert (lock->refcnt == 0);
/* FIXME: If no errors, we have the lock. */
assert (locker->refcnt == 0);
if (lock->flags & MU_LOCKER_PID)
if (locker->flags & MU_LOCKER_PID)
{
char buf[16];
sprintf (buf, "%ld", (long) getpid ());
write (fd, buf, strlen (buf));
}
close(fd);
close (fd);
return 0;
}
int
_locker_unlock_dotlock (mu_locker_t lock)
static int
unlock_dotlock (mu_locker_t locker)
{
if (unlink (lock->data.dot.dotlock) == -1)
if (unlink (locker->data.dot.dotlock) == -1)
{
int err = errno;
if (err == ENOENT)
{
lock->refcnt = 0;
locker->refcnt = 0; /*FIXME?*/
err = MU_ERR_LOCK_NOT_HELD;
return err;
}
......@@ -770,16 +788,56 @@ _locker_unlock_dotlock (mu_locker_t lock)
}
int
_locker_lock_kernel (mu_locker_t lock)
mu_locker_touchlock (mu_locker_t lock)
{
if (!lock)
return MU_ERR_LOCKER_NULL;
if (MU_LOCKER_TYPE (lock) != MU_LOCKER_TYPE_DOTLOCK)
return 0;
if (lock->refcnt > 0)
return utime (lock->data.dot.dotlock, NULL);
return MU_ERR_LOCK_NOT_HELD;
}
/* Kernel locking */
static int
init_kernel (mu_locker_t locker)
{
return 0;
}
static int
lock_kernel (mu_locker_t locker, enum mu_locker_mode mode)
{
int fd;
struct flock fl;
fd = open (lock->file, O_RDWR);
switch (mode)
{
case mu_lck_shr:
case mu_lck_opt:
mode = O_RDONLY;
fl.l_type = F_RDLCK;
break;
case mu_lck_exc:
mode = O_RDWR;
fl.l_type = F_WRLCK;
break;
default:
return EINVAL;
}
fd = open (locker->file, O_RDWR);
if (fd == -1)
return errno;
lock->data.kernel.fd = fd;
fl.l_type = F_WRLCK;
locker->data.kernel.fd = fd;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0; /* Lock entire file */
......@@ -796,8 +854,8 @@ _locker_lock_kernel (mu_locker_t lock)
return 0;
}
int
_locker_unlock_kernel (mu_locker_t lock)
static int
unlock_kernel (mu_locker_t locker)
{
struct flock fl;
......@@ -805,7 +863,7 @@ _locker_unlock_kernel (mu_locker_t lock)
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0; /* Unlock entire file */
if (fcntl (lock->data.kernel.fd, F_SETLK, &fl))
if (fcntl (locker->data.kernel.fd, F_SETLK, &fl))
{
#ifdef EACCESS
if (errno == EACCESS)
......@@ -815,17 +873,33 @@ _locker_unlock_kernel (mu_locker_t lock)
return EAGAIN;
return errno;
}
close (lock->data.kernel.fd);
close (locker->data.kernel.fd);
return 0;
}
static int
init_external (mu_locker_t locker)
{
if (!(locker->data.external.name = strdup (mu_locker_external_program ?
mu_locker_external_program :
MU_LOCKER_EXTERNAL_PROGRAM)))
return ENOMEM;
return 0;
}
static void
destroy_external (mu_locker_t locker)
{
free (locker->data.external.name);
}
/*
Estimate 1 decimal digit per 3 bits, + 1 for round off.
*/
#define DEC_DIGS_PER_INT (sizeof(int) * 8 / 3 + 1)
static int
lock_external (mu_locker_t l, int lock)
external_locker (mu_locker_t l, int lock)
{
int err = 0;
char *av[6];
......@@ -836,6 +910,7 @@ lock_external (mu_locker_t l, int lock)
assert (l);
assert (l->flags & MU_LOCKER_EXTERNAL);
/* FIXME */
assert (lock == !l->refcnt);
/* lock is true, refcnt is 0 or lock is false and refcnt is 1 */
......@@ -856,10 +931,8 @@ lock_external (mu_locker_t l, int lock)
av[ac++] = aretry;
}
if (lock == 0)
{
if (!lock)
av[ac++] = "-u";
}
av[ac++] = l->file;
......@@ -879,19 +952,24 @@ lock_external (mu_locker_t l, int lock)
case 127:
err = MU_ERR_LOCK_EXT_FAIL;
break;
case MU_DL_EX_OK:
err = 0;
l->refcnt = lock;
break;
case MU_DL_EX_NEXIST:
err = MU_ERR_LOCK_NOT_HELD;
break;
case MU_DL_EX_EXIST:
err = MU_ERR_LOCK_CONFLICT;
break;
case MU_DL_EX_PERM:
err = EPERM;
break;
default:
case MU_DL_EX_ERROR:
err = MU_ERR_LOCK_EXT_ERR;
......@@ -902,3 +980,15 @@ lock_external (mu_locker_t l, int lock)
return err;
}
static int
lock_external (mu_locker_t locker, enum mu_locker_mode mode)
{
return external_locker (locker, 1);
}
static int
unlock_external (mu_locker_t locker)
{
return external_locker (locker, 0);
}
......