Merge rev 1.3 (catch write-lock attempts immediately if running in

read-only mode) & rev 1.2 (check out from read-only repository support)
into version 1.11.22.
This commit is contained in:
David E. O'Brien 2008-01-13 06:08:17 +00:00
parent cf7a6ed752
commit fff17d416c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=175272

View File

@ -1,6 +1,11 @@
/*
* Copyright (c) 1992, Brian Berliner and Jeff Polk
* Copyright (c) 1989-1992, Brian Berliner
* Copyright (C) 1986-2005 The Free Software Foundation, Inc.
*
* Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
* and others.
*
* Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
* Portions Copyright (C) 1989-1992, Brian Berliner
*
* You may distribute under the terms of the GNU General Public License as
* specified in the README file that comes with the CVS source distribution.
@ -87,8 +92,17 @@ struct lock {
case of writelocks, it is just a pointer to the storage allocated
for the ->key field. */
char *repository;
/* Do we have a lock named CVSLCK? */
int have_lckdir;
/* The name of the master lock dir. Usually CVSLCK. */
const char *lockdirname;
/* The full path to the lock dir, if we are currently holding it.
*
* This will be LOCKDIRNAME catted onto REPOSITORY. We waste a little
* space by storing it, but save a later malloc/free.
*/
char *lockdir;
/* Note there is no way of knowing whether the readlock and writelock
exist. The code which sets the locks doesn't use SIG_beginCrSect
to set a flag like we do for CVSLCK. */
@ -117,7 +131,6 @@ static char *readlock;
static char *writelock;
/* Malloc'd array specifying the name of a CVSLCK file (absolute pathname).
Will always be non-NULL in the cases where it is used. */
static char *masterlock;
static List *locklist;
#define L_OK 0 /* success */
@ -126,7 +139,10 @@ static List *locklist;
/* This is the (single) readlock which is set by Reader_Lock. The
repository field is NULL if there is no such lock. */
static struct lock global_readlock;
static struct lock global_readlock = {NULL, CVSLCK, NULL};
static struct lock global_history_lock = {NULL, CVSHISTORYLCK, NULL};
static struct lock global_val_tags_lock = {NULL, CVSVALTAGSLCK, NULL};
/* List of locks set by lock_tree_for_write. This is redundant
with locklist, sort of. */
@ -142,7 +158,7 @@ static List *locked_list;
/* LockDir from CVSROOT/config. */
char *lock_dir;
static char *lock_name PROTO ((char *repository, char *name));
static char *lock_name PROTO ((const char *repository, const char *name));
/* Return a newly malloc'd string containing the name of the lock for the
repository REPOSITORY and the lock file name within that directory
@ -152,13 +168,13 @@ static char *lock_name PROTO ((char *repository, char *name));
things simple). */
static char *
lock_name (repository, name)
char *repository;
char *name;
const char *repository;
const char *name;
{
char *retval;
char *p;
const char *p;
char *q;
char *short_repos;
const char *short_repos;
mode_t save_umask;
int saved_umask = 0;
@ -313,6 +329,10 @@ Lock_Cleanup ()
locked_dir = NULL;
locked_list = NULL;
}
if (global_history_lock.repository) clear_history_lock ();
if (global_val_tags_lock.repository) clear_val_tags_lock ();
in_lock_cleanup = 0;
}
@ -349,6 +369,8 @@ unlock_proc (p, closure)
return (0);
}
/* Remove the lock files. */
static void
lock_simple_remove (lock)
@ -363,7 +385,7 @@ lock_simple_remove (lock)
if (readlock != NULL)
{
tmp = lock_name (lock->repository, readlock);
if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
if (CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
error (0, errno, "failed to remove lock %s", tmp);
free (tmp);
}
@ -375,21 +397,12 @@ lock_simple_remove (lock)
if (writelock != NULL)
{
tmp = lock_name (lock->repository, writelock);
if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
if (CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
error (0, errno, "failed to remove lock %s", tmp);
free (tmp);
}
if (lock->have_lckdir)
{
tmp = lock_name (lock->repository, CVSLCK);
SIG_beginCrSect ();
if (CVS_RMDIR (tmp) < 0)
error (0, errno, "failed to remove lock dir %s", tmp);
lock->have_lckdir = 0;
SIG_endCrSect ();
free (tmp);
}
clear_lock (lock);
}
@ -662,6 +675,9 @@ readers_exist (repository)
#endif
lockdir = lock_name (repository, "");
assert (lockdir != NULL);
lockdir[strlen (lockdir) - 1] = '\0'; /* remove trailing slash */
do {
@ -768,13 +784,13 @@ set_lock (lock, will_wait)
long us;
struct stat sb;
mode_t omask;
char *masterlock;
int status;
#ifdef CVS_FUDGELOCKS
time_t now;
#endif
if (masterlock != NULL)
free (masterlock);
masterlock = lock_name (lock->repository, CVSLCK);
masterlock = lock_name (lock->repository, lock->lockdirname);
/*
* Note that it is up to the callers of set_lock() to arrange for signal
@ -783,33 +799,33 @@ set_lock (lock, will_wait)
*/
waited = 0;
us = 1;
lock->have_lckdir = 0;
for (;;)
{
int status = -1;
status = -1;
omask = umask (cvsumask);
SIG_beginCrSect ();
if (CVS_MKDIR (masterlock, 0777) == 0)
{
lock->have_lckdir = 1;
lock->lockdir = masterlock;
SIG_endCrSect ();
status = L_OK;
if (waited)
lock_obtained (lock->repository);
goto out;
goto after_sig_unblock;
}
SIG_endCrSect ();
out:
after_sig_unblock:
(void) umask (omask);
if (status != -1)
return status;
goto done;
if (errno != EEXIST)
{
error (0, errno,
"failed to create lock directory for `%s' (%s)",
lock->repository, masterlock);
return (L_ERROR);
status = L_ERROR;
goto done;
}
/* Find out who owns the lock. If the lock directory is
@ -821,7 +837,8 @@ set_lock (lock, will_wait)
continue;
error (0, errno, "couldn't stat lock directory `%s'", masterlock);
return (L_ERROR);
status = L_ERROR;
goto done;
}
#ifdef CVS_FUDGELOCKS
@ -843,7 +860,10 @@ set_lock (lock, will_wait)
/* if he wasn't willing to wait, return an error */
if (!will_wait)
return (L_LOCKED);
{
status = L_LOCKED;
goto done;
}
/* if possible, try a very short sleep without a message */
if (!waited && us < 1000)
@ -874,23 +894,45 @@ set_lock (lock, will_wait)
lock_wait (lock->repository);
waited = 1;
}
done:
if (!lock->lockdir) free (masterlock);
return status;
}
/*
* Clear master lock. We don't have to recompute the lock name since
* clear_lock is never called except after a successful set_lock().
* Clear master lock.
*
* INPUTS
* lock The lock information.
*
* OUTPUTS
* Sets LOCK->lockdir to NULL after removing the directory it names and
* freeing the storage.
*
* ASSUMPTIONS
* If we own the master lock directory, its name is stored in LOCK->lockdir.
* We may free LOCK->lockdir.
*
*/
static void
clear_lock (lock)
struct lock *lock;
{
SIG_beginCrSect ();
if (CVS_RMDIR (masterlock) < 0)
error (0, errno, "failed to remove lock dir `%s'", masterlock);
lock->have_lckdir = 0;
if (lock->lockdir)
{
if (CVS_RMDIR (lock->lockdir) < 0)
error (0, errno, "failed to remove lock dir `%s'", lock->lockdir);
free (lock->lockdir);
lock->lockdir = NULL;
}
SIG_endCrSect ();
}
/*
* Print out a message that the lock is still held, then sleep a while.
*/
@ -965,7 +1007,8 @@ lock_filesdoneproc (callerdat, err, repository, update_dir, entries)
p->key = xstrdup (repository);
p->data = xmalloc (sizeof (struct lock));
((struct lock *)p->data)->repository = p->key;
((struct lock *)p->data)->have_lckdir = 0;
((struct lock *)p->data)->lockdirname = CVSLCK;
((struct lock *)p->data)->lockdir = NULL;
/* FIXME-KRP: this error condition should not simply be passed by. */
if (p->key == NULL || addnode (lock_tree_list, p) != 0)
@ -1018,9 +1061,106 @@ lock_dir_for_write (repository)
node->key = xstrdup (repository);
node->data = xmalloc (sizeof (struct lock));
((struct lock *)node->data)->repository = node->key;
((struct lock *)node->data)->have_lckdir = 0;
((struct lock *)node->data)->lockdirname = CVSLCK;
((struct lock *)node->data)->lockdir = NULL;
(void) addnode (locked_list, node);
Writer_Lock (locked_list);
}
}
/* This is the internal implementation behind history_lock & val_tags_lock. It
* gets a write lock for the history or val-tags file.
*
* RETURNS
* true, on success
* false, on error
*/
static int internal_lock PROTO ((struct lock *lock, const char *xrepository));
static int
internal_lock (lock, xrepository)
struct lock *lock;
const char *xrepository;
{
/* remember what we're locking (for Lock_Cleanup) */
assert (!lock->repository);
lock->repository = xmalloc (strlen (xrepository) + sizeof (CVSROOTADM) + 2);
sprintf (lock->repository, "%s/%s", xrepository, CVSROOTADM);
/* get the lock dir for our own */
if (set_lock (lock, 1) != L_OK)
{
if (!really_quiet)
error (0, 0, "failed to obtain history lock in repository `%s'",
xrepository);
return 0;
}
return 1;
}
/* This is the internal implementation behind history_lock & val_tags_lock. It
* removes the write lock for the history or val-tags file, when it exists.
*/
static void internal_clear_lock PROTO((struct lock *lock));
static void
internal_clear_lock (lock)
struct lock *lock;
{
SIG_beginCrSect ();
if (lock->repository)
{
free (lock->repository);
lock->repository = NULL;
}
SIG_endCrSect ();
clear_lock (lock);
}
/* Lock the CVSROOT/history file for write.
*/
int
history_lock (xrepository)
const char *xrepository;
{
return internal_lock (&global_history_lock, xrepository);
}
/* Remove the CVSROOT/history lock, if it exists.
*/
void
clear_history_lock ()
{
internal_clear_lock (&global_history_lock);
}
/* Lock the CVSROOT/val-tags file for write.
*/
int
val_tags_lock (xrepository)
const char *xrepository;
{
return internal_lock (&global_val_tags_lock, xrepository);
}
/* Remove the CVSROOT/val-tags lock, if it exists.
*/
void
clear_val_tags_lock ()
{
internal_clear_lock (&global_val_tags_lock);
}