X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=common-src%2Famflock.c;h=177266dd07c52bd4f5e03bac41fa23689b59d00e;hb=fd48f3e498442f0cbff5f3606c7c403d0566150e;hp=2a00b38835e33cf4ec3b819b62d41fed8725caf0;hpb=12179dea039515c06168c0037d048566a3f623de;p=debian%2Famanda diff --git a/common-src/amflock.c b/common-src/amflock.c index 2a00b38..177266d 100644 --- a/common-src/amflock.c +++ b/common-src/amflock.c @@ -24,325 +24,246 @@ * file named AUTHORS, in the root directory of this distribution. */ /* - * $Id: amflock.c,v 1.28 2006/05/25 01:47:11 johnfranks Exp $ + * $Id: amflock.c 7161 2007-07-03 16:27:26Z dustin $ * * file locking routines, put here to hide the system dependant stuff * from the rest of the code */ -/* -** -** Notes: -** - These are "best effort" routines. -** - "configure" has four variables that are used to determine which type of -** locking to use: -** USE_POSIX_FCNTL - use fcntl(). The full job. -** USE_FLOCK - use flock(). Does just as well. -** USE_LOCKF - use lockf(). Only handles advisory, exclusive, -** blocking file locks as used by Amanda. -** USE_LNLOCK - Home brew exclusive, blocking file lock. -** - No locking available. User beware! -** - "configure" compiles this with -DCONFIGURE_TEST to try and determine -** whether a particular type of locking works. -*/ #include "amanda.h" -#if defined(USE_POSIX_FCNTL) - static struct flock lock; /* zero-initialized */ -#endif - -#if !defined(USE_POSIX_FCNTL) && defined(USE_FLOCK) -# ifdef HAVE_SYS_FILE_H -# include -# endif - -# if !defined(HAVE_FLOCK_DECL) && !defined(CONFIGURE_TEST) - extern int flock(int fd, int operation); -# endif -#endif - - -#if !defined(USE_POSIX_FCNTL) && !defined(USE_FLOCK) && defined(USE_LOCKF) +/* + * New Implementation + */ -/* XPG4-UNIX (eg, SGI IRIX, DEC DU) has F_ULOCK instead of F_UNLOCK */ -#if defined(F_ULOCK) && !defined(F_UNLOCK) -# define F_UNLOCK F_ULOCK -#endif +static GStaticMutex lock_lock = G_STATIC_MUTEX_INIT; +static GHashTable *locally_locked_files = NULL; -/* Lock a file using lockf(). -** Notes: -** - returns errors for some non-files like pipes. -** - probably only works for files open for writing. -*/ -int -use_lockf( - int fd, /* fd of file to operate on */ - int op) /* true to lock; false to unlock */ +file_lock * +file_lock_new( + const char *filename) { - off_t pos; + file_lock *lock = g_new0(file_lock, 1); + lock->filename = g_strdup(filename); + lock->fd = -1; - if (op) { - /* lock from here on */ - if (lockf(fd, F_LOCK, (off_t)0) == -1) return -1; - } - else { - /* unlock from here on */ - if (lockf(fd, F_UNLOCK, (off_t)0) == -1) return -1; - - /* unlock from bof to here */ - pos = lseek(fd, (off_t)0, SEEK_CUR); - if (pos == (off_t)-1) { - if (errno == ESPIPE) pos = (off_t)0; - else return -1; - } - - if (pos > (off_t)0 && - lockf(fd, F_UNLOCK, -pos) == -1) return -1; - } - - return 0; + return lock; } -#endif - -#if !defined(USE_POSIX_FCNTL) && !defined(USE_FLOCK) && !defined(USE_LOCKF) && defined(USE_LNLOCK) -/* XXX - error checking in this section needs to be tightened up */ - -/* Delete a lock file. -*/ -int -delete_lock( - char *fn) +void +file_lock_free( + file_lock *lock) { - int rc; + g_static_mutex_lock(&lock_lock); + if (locally_locked_files) { + g_hash_table_remove(locally_locked_files, + lock->filename); + } - rc = unlink(fn); - if (rc != 0 && errno == ENOENT) rc = 0; + if (lock->data) + g_free(lock->data); + if (lock->filename) + g_free(lock->filename); - return rc; + if (lock->fd != -1) + close(lock->fd); + + g_static_mutex_unlock(&lock_lock); } -/* Create a lock file. -*/ int -create_lock( - char *fn, - pid_t pid) +file_lock_lock( + file_lock *lock) { - int fd; - FILE *f; - int mask; - - (void)delete_lock(fn); /* that's MY file! */ - - mask = umask(0027); - fd = open(fn, O_WRONLY | O_CREAT | O_EXCL, 0640); - umask(mask); - if (fd == -1) return -1; + int rv = -2; + int fd = -1; + int saved_errno; + struct flock lock_buf; + struct stat stat_buf; + + g_assert(!lock->locked); + + /* protect from overlapping lock operations within a process */ + g_static_mutex_lock(&lock_lock); + if (!locally_locked_files) { + locally_locked_files = g_hash_table_new(g_str_hash, g_str_equal); + } - if((f = fdopen(fd, "w")) == NULL) { - aclose(fd); - return -1; - } - fprintf(f, "%ld\n", pid); - if (fclose(f) == EOF) - return -1; - return 0; -} + /* if this filename is in the hash table, then some other thread in this + * process has locked it */ + if (g_hash_table_lookup(locally_locked_files, lock->filename)) { + rv = 1; + goto done; + } -/* Read the pid out of a lock file. -** -1=error, otherwise pid. -*/ -long -read_lock( - char * fn) /* name of lock file */ -{ - int save_errno; - FILE *f; - long pid; + /* The locks are advisory, so an error here never means the lock is already + * taken. */ + lock->fd = fd = open(lock->filename, O_CREAT|O_RDWR, 0666); + if (fd < 0) { + rv = -1; + goto done; + } - if ((f = fopen(fn, "r")) == NULL) { - return -1; - } - if (fscanf(f, "%ld", &pid) != 1) { - save_errno = errno; - afclose(f); - errno = save_errno; - return -1; - } - if (fclose(f) != 0) { - return -1; - } - return pid; -} + /* now try locking it */ + lock_buf.l_type = F_WRLCK; + lock_buf.l_start = 0; + lock_buf.l_whence = SEEK_SET; + lock_buf.l_len = 0; /* to EOF */ + if (fcntl(fd, F_SETLK, &lock_buf) < 0) { + if (errno == EACCES || errno == EAGAIN) + rv = 1; + else + rv = -1; + goto done; + } -/* Link a lock if we can. -** 0=done, 1=already locked, -1=error. -*/ -int -link_lock( - char * lk, /* real lock file */ - char * tlk) /* temp lock file */ -{ - int rc; - int serrno; /* saved errno */ - struct stat lkstat, tlkstat; + /* and read the file in its entirety */ + if (fstat(fd, &stat_buf) < 0) { + rv = -1; + goto done; + } - /* an atomic check and set operation */ - rc = link(tlk, lk); - if (rc == 0) return 0; /* XXX do we trust it? */ + if (!(stat_buf.st_mode & S_IFREG)) { + rv = -1; + errno = EINVAL; + goto done; + } - /* link() says it failed - don't beleive it */ - serrno = errno; + if (stat_buf.st_size) { + lock->data = g_malloc(stat_buf.st_size); + lock->len = stat_buf.st_size; + if (full_read(fd, lock->data, lock->len) < lock->len) { + rv = -1; + goto done; + } + } - if (stat(lk, &lkstat) == 0 && - stat(tlk, &tlkstat) == 0 && - lkstat.st_ino == tlkstat.st_ino) - return 0; /* it did work! */ + fd = -1; /* we'll keep the file now */ + lock->locked = TRUE; - errno = serrno; + /* the lock is acquired; record this in the hash table */ + g_hash_table_insert(locally_locked_files, lock->filename, lock->filename); - if (errno == EEXIST) rc = 1; + rv = 0; - return rc; +done: + saved_errno = errno; + g_static_mutex_unlock(&lock_lock); + if (fd >= 0) /* close and unlock if an error occurred */ + close(fd); + errno = saved_errno; + return rv; } -/* Steal a lock if we can. -** 0=done; 1=still in use; -1 = error. -*/ int -steal_lock( - char * fn, /* name of lock file to steal */ - pid_t mypid, /* my process id */ - char * sres) /* name of steal-resource to lock */ +file_lock_write( + file_lock *lock, + const char *data, + size_t len) { - int fd; - char buff[64]; - long pid; - int rc; - - /* prevent a race with another stealer */ - rc = ln_lock(sres, 1); - if (rc != 0) goto error; - - pid = read_lock(fn); - if (pid == -1) { - if (errno == ENOENT) goto done; - goto error; - } - - if (pid == mypid) goto steal; /* i'm the locker! */ - - /* are they still there ? */ - rc = kill((pid_t)pid, 0); - if (rc != 0) { - if (errno == ESRCH) goto steal; /* locker has gone */ - goto error; - } - -inuse: - rc = ln_lock(sres, 0); - if (rc != 0) goto error; + int fd = lock->fd; - return 1; + g_assert(lock->locked); -steal: - rc = delete_lock(fn); - if (rc != 0) goto error; + /* seek to position 0, rewrite, and truncate */ + if (lseek(fd, 0, SEEK_SET) < 0) + return -1; -done: - rc = ln_lock(sres, 0); - if (rc != 0) goto error; + /* from here on out, any errors have corrupted the datafile.. */ + if (full_write(fd, data, len) < len) + return -1; - return 0; + if (lock->len > len) { + if (ftruncate(fd, len) < 0) + return -1; + } -error: - rc = ln_lock(sres, 0); + if (lock->data) + g_free(lock->data); + lock->data = g_strdup(data); + lock->len = len; - return -1; + return 0; } -/* Locking using existance of a file. -*/ int -ln_lock( - char * res, /* name of resource to lock */ - int op) /* true to lock; false to unlock */ +file_lock_unlock( + file_lock *lock) { - long mypid; - char *lockfile = NULL; - char *tlockfile = NULL; - char *mres = NULL; - int rc; - char pid_str[NUM_STR_SIZE]; + g_assert(lock->locked); - mypid = (long)getpid(); + g_static_mutex_lock(&lock_lock); - lockfile = vstralloc(AMANDA_TMPDIR, "/am", res, ".lock", NULL); + /* relase the filesystem-level lock */ + close(lock->fd); - if (!op) { - /* unlock the resource */ - assert(read_lock(lockfile) == mypid); - - (void)delete_lock(lockfile); - amfree(lockfile); - return 0; - } + /* and the hash table entry */ + g_hash_table_remove(locally_locked_files, lock->filename); - /* lock the resource */ + g_static_mutex_unlock(&lock_lock); - snprintf(pid_str, SIZEOF(pid_str), "%ld", mypid); - tlockfile = vstralloc(AMANDA_TMPDIR, "am", res, ".", pid_str, NULL); + if (lock->data) + g_free(lock->data); + lock->data = NULL; + lock->len = 0; + lock->fd = -1; + lock->locked = FALSE; - (void)create_lock(tlockfile, mypid); - - mres = stralloc2(res, "."); - - while(1) { - rc = link_lock(lockfile, tlockfile); - if (rc == -1) break; - if (rc == 0) break; + return 0; +} - rc = steal_lock(lockfile, mypid, mres); - if (rc == -1) break; - if (rc == 0) continue; - sleep(1); - } +/* + * Old Implementation + */ - (void) delete_lock(tlockfile); +/* +** +** Notes: +** - These are "best effort" routines. +** - "configure" has four variables that are used to determine which type of +** locking to use: +** USE_POSIX_FCNTL - use fcntl(). The full job. +** USE_FLOCK - use flock(). Does just as well. +** USE_LOCKF - use lockf(). Only handles advisory, exclusive, +** blocking file locks as used by Amanda. +** USE_LNLOCK - Home brew exclusive, blocking file lock. +** - No locking available. User beware! +*/ - amfree(mres); - amfree(tlockfile); - amfree(lockfile); +/* Interface to the implementations in common-src/amflock-*.c */ - return rc; -} +#ifdef WANT_AMFLOCK_POSIX +extern amflock_impl_t amflock_posix_impl; #endif - - -/* - * Get a file lock (for read-only files). - */ -int -amroflock( - int fd, - char * resource) -{ - int r; - -#ifdef USE_POSIX_FCNTL - (void)resource; /* Quiet unused paramater warning */ - lock.l_type = F_RDLCK; - lock.l_whence = SEEK_SET; - r = fcntl(fd, F_SETLKW, &lock); -#else - (void)fd; /* Quiet unused paramater warning */ - r = amflock(fd, resource); +#ifdef WANT_AMFLOCK_FLOCK +extern amflock_impl_t amflock_flock_impl; +#endif +#ifdef WANT_AMFLOCK_LOCKF +extern amflock_impl_t amflock_lockf_impl; +#endif +#ifdef WANT_AMFLOCK_LNLOCK +extern amflock_impl_t amflock_lnlock_impl; #endif - return r; -} +amflock_impl_t *amflock_impls[] = { +#ifdef WANT_AMFLOCK_POSIX + &amflock_posix_impl, +#endif +#ifdef WANT_AMFLOCK_FLOCK + &amflock_flock_impl, +#endif +#ifdef WANT_AMFLOCK_LOCKF + &amflock_lockf_impl, +#endif +#ifdef WANT_AMFLOCK_LNLOCK + &amflock_lnlock_impl, +#endif + NULL +}; +/* Interface functions */ +/* FIXME: for now, these just use the first non-NULL implementation + */ /* Get a file lock (for read/write files). */ @@ -351,139 +272,30 @@ amflock( int fd, char * resource) { - int r; - -#ifdef USE_POSIX_FCNTL - (void)resource; /* Quiet unused paramater warning */ - lock.l_type = F_WRLCK; - lock.l_whence = SEEK_SET; - r = fcntl(fd, F_SETLKW, &lock); -#else -#ifdef USE_FLOCK - (void)resource; /* Quiet unused paramater warning */ - r = flock(fd, LOCK_EX); -#else -#ifdef USE_LOCKF - (void)resource; /* Quiet unused paramater warning */ - r = use_lockf(fd, 1); -#else -#ifdef USE_LNLOCK - (void)fd; /* Quiet unused paramater warning */ - r = ln_lock(resource, 1); -#else - (void)fd; /* Quiet unused paramater warning */ - (void)resource; /* Quiet unused paramater warning */ - r = 0; -#endif -#endif -#endif -#endif - - return r; + if (!amflock_impls[0]) return 0; /* no locking */ + return amflock_impls[0]->amflock_impl(fd, resource); } - -/* Release a file lock. -*/ +/* + * Get a file lock (for read-only files). + */ int -amfunlock( +amroflock( int fd, char * resource) { - int r; - -#ifdef USE_POSIX_FCNTL - (void)resource; /* Quiet unused paramater warning */ - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - r = fcntl(fd, F_SETLK, &lock); -#else -#ifdef USE_FLOCK - (void)resource; /* Quiet unused paramater warning */ - r = flock(fd, LOCK_UN); -#else -#ifdef USE_LOCKF - (void)fd; /* Quiet unused paramater warning */ - r = use_lockf(fd, 0); -#else -#ifdef USE_LNLOCK - (void)fd; /* Quiet unused paramater warning */ - r = ln_lock(resource, 0); -#else - (void)fd; /* Quiet unused paramater warning */ - (void)resource; /* Quiet unused paramater warning */ - r = 0; -#endif -#endif -#endif -#endif - - return r; + if (!amflock_impls[0]) return 0; /* no locking */ + return amflock_impls[0]->amroflock_impl(fd, resource); } - -/* Test routine for use by configure. -** (I'm not sure why we use both return and exit!) -** XXX the testing here should be a lot more comprehensive. -** - lock the file and then try and lock it from another process -** - lock the file from another process and check that process -** termination unlocks it. -** The hard part is to find a system independent way to not block -** for ever. -*/ -#ifdef CONFIGURE_TEST +/* + * Release a file lock. + */ int -main( - int argc, - char **argv) +amfunlock( + int fd, + char * resource) { - int lockfd; - char *filen = "/tmp/conftest.lock"; - char *resn = "test"; - int fd; - - (void)argc; /* Quiet compiler warning */ - (void)argv; /* Quiet compiler warning */ - - unlink(filen); - if ((lockfd = open(filen, O_RDONLY | O_CREAT | O_EXCL, 0600)) == -1) { - perror (filen); - exit(10); - } - - if (amroflock(lockfd, resn) != 0) { - perror ("amroflock"); - exit(1); - } - if (amfunlock(lockfd, resn) != 0) { - perror ("amfunlock/2"); - exit(2); - } - - /* - * Do not use aclose() here. During configure we do not have - * areads_relbuf() available and it makes configure think all - * the tests have failed. - */ - close(lockfd); - - unlink(filen); - if ((lockfd = open(filen, O_WRONLY | O_CREAT | O_EXCL, 0600)) == -1) { - perror (filen); - exit(20); - } - - if (amflock(lockfd, resn) != 0) { - perror ("amflock"); - exit(3); - } - if (amfunlock(lockfd, resn) != 0) { - perror ("amfunlock/4"); - exit(4); - } - - close(lockfd); - - exit(0); + if (!amflock_impls[0]) return 0; /* no locking */ + return amflock_impls[0]->amfunlock_impl(fd, resource); } -#endif