summaryrefslogtreecommitdiffstats
path: root/contrib/cvs/src/lock.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/cvs/src/lock.c')
-rw-r--r--contrib/cvs/src/lock.c497
1 files changed, 327 insertions, 170 deletions
diff --git a/contrib/cvs/src/lock.c b/contrib/cvs/src/lock.c
index 7e35aed..f84eccf 100644
--- a/contrib/cvs/src/lock.c
+++ b/contrib/cvs/src/lock.c
@@ -10,40 +10,160 @@
* Lock file support for CVS.
*/
+/* The node Concurrency in doc/cvs.texinfo has a brief introduction to
+ how CVS locks function, and some of the user-visible consequences of
+ their existence. Here is a summary of why they exist (and therefore,
+ the consequences of hacking CVS to read a repository without creating
+ locks):
+
+ There are two uses. One is the ability to prevent there from being
+ two writers at the same time. This is necessary for any number of
+ reasons (fileattr code, probably others). Commit needs to lock the
+ whole tree so that nothing happens between the up-to-date check and
+ the actual checkin.
+
+ The second use is the ability to ensure that there is not a writer
+ and a reader at the same time (several readers are allowed). Reasons
+ for this are:
+
+ * Readlocks ensure that once CVS has found a collection of rcs
+ files using Find_Names, the files will still exist when it reads
+ them (they may have moved in or out of the attic).
+
+ * Readlocks provide some modicum of consistency, although this is
+ kind of limited--see the node Concurrency in cvs.texinfo.
+
+ * Readlocks ensure that the RCS file does not change between
+ RCS_parse and RCS_reparsercsfile time. This one strikes me as
+ important, although I haven't thought up what bad scenarios might
+ be.
+
+ * Readlocks ensure that we won't find the file in the state in
+ which it is in between the "rcs -i" and the RCS_checkin in commit.c
+ (when a file is being added). This state is a state in which the
+ RCS file parsing routines in rcs.c cannot parse the file.
+
+ * Readlocks ensure that a reader won't try to look at a
+ half-written fileattr file (fileattr is not updated atomically).
+
+ (see also the description of anonymous read-only access in
+ "Password authentication security" node in doc/cvs.texinfo).
+
+ While I'm here, I'll try to summarize a few random suggestions
+ which periodically get made about how locks might be different:
+
+ 1. Check for EROFS. Maybe useful, although in the presence of NFS
+ EROFS does *not* mean that the file system is unchanging.
+
+ 2. Provide a means to put the cvs locks in some directory apart from
+ the repository (CVSROOT/locks; a -l option in modules; etc.).
+
+ 3. Provide an option to disable locks for operations which only
+ read (see above for some of the consequences).
+
+ 4. Have a server internally do the locking. Probably a good
+ long-term solution, and many people have been working hard on code
+ changes which would eventually make it possible to have a server
+ which can handle various connections in one process, but there is
+ much, much work still to be done before this is feasible.
+
+ 5. Like #4 but use shared memory or something so that the servers
+ merely need to all be on the same machine. This is a much smaller
+ change to CVS (it functions much like #2; shared memory might be an
+ unneeded complication although it presumably would be faster). */
+
#include "cvs.h"
+struct lock {
+ /* This is the directory in which we may have a lock named by the
+ readlock variable, a lock named by the writelock variable, and/or
+ a lock named CVSLCK. The storage is not allocated along with the
+ struct lock; it is allocated by the Reader_Lock caller or in the
+ 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;
+ /* 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. */
+};
+
+static void remove_locks PROTO((void));
static int readers_exist PROTO((char *repository));
-static int set_lock PROTO((char *repository, int will_wait));
-static void clear_lock PROTO((void));
+static int set_lock PROTO ((struct lock *lock, int will_wait));
+static void clear_lock PROTO ((struct lock *lock));
static void set_lockers_name PROTO((struct stat *statp));
static int set_writelock_proc PROTO((Node * p, void *closure));
static int unlock_proc PROTO((Node * p, void *closure));
-static int write_lock PROTO((char *repository));
-static void lock_simple_remove PROTO((char *repository));
+static int write_lock PROTO ((struct lock *lock));
+static void lock_simple_remove PROTO ((struct lock *lock));
static void lock_wait PROTO((char *repository));
-static int Check_Owner PROTO((char *lockdir));
-
-static char lockers_name[20];
-static char *repository;
-static char readlock[PATH_MAX], writelock[PATH_MAX], masterlock[PATH_MAX];
-static int cleanup_lckdir;
+static void lock_obtained PROTO((char *repository));
+
+/* Malloc'd array containing the username of the whoever has the lock.
+ Will always be non-NULL in the cases where it is needed. */
+static char *lockers_name;
+/* Malloc'd array specifying name of a readlock within a directory.
+ Or NULL if none. */
+static char *readlock;
+/* Malloc'd array specifying name of a writelock within a directory.
+ Or NULL if none. */
+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 */
#define L_ERROR 1 /* error condition */
#define L_LOCKED 2 /* lock owned by someone else */
+/* 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;
+
+/* List of locks set by lock_tree_for_write. This is redundant
+ with locklist, sort of. */
+static List *lock_tree_list;
+
+/* If we set locks with lock_dir_for_write, then locked_dir contains
+ the malloc'd name of the repository directory which we have locked.
+ locked_list is the same thing packaged into a list and is redundant
+ with locklist the same way that lock_tree_list is. */
+static char *locked_dir;
+static List *locked_list;
+
/*
* Clean up all outstanding locks
*/
void
Lock_Cleanup ()
{
+ remove_locks ();
+
+ dellist (&lock_tree_list);
+
+ if (locked_dir != NULL)
+ {
+ dellist (&locked_list);
+ free (locked_dir);
+ locked_dir = NULL;
+ locked_list = NULL;
+ }
+}
+
+/*
+ * Remove locks without discarding the lock information
+ */
+static void
+remove_locks ()
+{
/* clean up simple locks (if any) */
- if (repository != NULL)
+ if (global_readlock.repository != NULL)
{
- lock_simple_remove (repository);
- repository = (char *) NULL;
+ lock_simple_remove (&global_readlock);
+ global_readlock.repository = NULL;
}
/* clean up multiple locks (if any) */
@@ -62,87 +182,57 @@ unlock_proc (p, closure)
Node *p;
void *closure;
{
- lock_simple_remove (p->key);
+ lock_simple_remove ((struct lock *)p->data);
return (0);
}
-/*
- * Remove the lock files (without complaining if they are not there),
- */
+/* Remove the lock files. */
static void
-lock_simple_remove (repository)
- char *repository;
+lock_simple_remove (lock)
+ struct lock *lock;
{
- char tmp[PATH_MAX];
+ char *tmp;
- if (readlock[0] != '\0')
+ /* If readlock is set, the lock directory *might* have been created, but
+ since Reader_Lock doesn't use SIG_beginCrSect the way that set_lock
+ does, we don't know that. That is why we need to check for
+ existence_error here. */
+ if (readlock != NULL)
{
- (void) sprintf (tmp, "%s/%s", repository, readlock);
- if (unlink (tmp) < 0 && ! existence_error (errno))
+ tmp = xmalloc (strlen (lock->repository) + strlen (readlock) + 10);
+ (void) sprintf (tmp, "%s/%s", lock->repository, readlock);
+ if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
error (0, errno, "failed to remove lock %s", tmp);
+ free (tmp);
}
- if (writelock[0] != '\0')
+ /* If writelock is set, the lock directory *might* have been created, but
+ since write_lock doesn't use SIG_beginCrSect the way that set_lock
+ does, we don't know that. That is why we need to check for
+ existence_error here. */
+ if (writelock != NULL)
{
- (void) sprintf (tmp, "%s/%s", repository, writelock);
- if (unlink (tmp) < 0 && ! existence_error (errno))
+ tmp = xmalloc (strlen (lock->repository) + strlen (writelock) + 10);
+ (void) sprintf (tmp, "%s/%s", lock->repository, writelock);
+ if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
error (0, errno, "failed to remove lock %s", tmp);
+ free (tmp);
}
- /*
- * Only remove the lock directory if it is ours, note that this does
- * lead to the limitation that one user ID should not be committing
- * files into the same Repository directory at the same time. Oh well.
- */
- if (writelock[0] != '\0' || (readlock[0] != '\0' && cleanup_lckdir))
+ if (lock->have_lckdir)
{
- (void) sprintf (tmp, "%s/%s", repository, CVSLCK);
- if (Check_Owner(tmp))
- {
-#ifdef AFSCVS
- char rmuidlock[PATH_MAX];
- sprintf(rmuidlock, "rm -f %s/uidlock%d", tmp, geteuid() );
- system(rmuidlock);
-#endif
- (void) rmdir (tmp);
- }
+ tmp = xmalloc (strlen (lock->repository) + sizeof (CVSLCK) + 10);
+ (void) sprintf (tmp, "%s/%s", 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);
}
- cleanup_lckdir = 0;
}
/*
- * Check the owner of a lock. Returns 1 if we own it, 0 otherwise.
- */
-static int
-Check_Owner(lockdir)
- char *lockdir;
-{
- struct stat sb;
-
-#ifdef AFSCVS
- /* In the Andrew File System (AFS), user ids from stat don't match
- those from geteuid(). The AFSCVS code can deal with either AFS or
- non-AFS repositories; the non-AFSCVS code is faster. */
- char uidlock[PATH_MAX];
-
- /* Check if the uidlock is in the lock directory */
- sprintf(uidlock, "%s/uidlock%d", lockdir, geteuid() );
- if( stat(uidlock, &sb) != -1)
- return 1; /* The file exists, therefore we own the lock */
- else
- return 0; /* The file didn't exist or some other error.
- * Assume that we don't own it.
- */
-#else
- if (stat (lockdir, &sb) != -1 && sb.st_uid == geteuid ())
- return 1;
- else
- return 0;
-#endif
-} /* end Check_Owner() */
-
-
-/*
* Create a lock file for readers
*/
int
@@ -151,51 +241,63 @@ Reader_Lock (xrepository)
{
int err = 0;
FILE *fp;
- char tmp[PATH_MAX];
+ char *tmp;
if (noexec)
return (0);
/* we only do one directory at a time for read locks! */
- if (repository != NULL)
+ if (global_readlock.repository != NULL)
{
error (0, 0, "Reader_Lock called while read locks set - Help!");
return (1);
}
- if (readlock[0] == '\0')
- (void) sprintf (readlock,
+ if (readlock == NULL)
+ {
+ readlock = xmalloc (strlen (hostname) + sizeof (CVSRFL) + 40);
+ (void) sprintf (readlock,
#ifdef HAVE_LONG_FILE_NAMES
- "%s.%s.%ld", CVSRFL, hostname,
+ "%s.%s.%ld", CVSRFL, hostname,
#else
- "%s.%ld", CVSRFL,
+ "%s.%ld", CVSRFL,
#endif
- (long) getpid ());
+ (long) getpid ());
+ }
- /* remember what we're locking (for lock_cleanup) */
- repository = xrepository;
+ /* remember what we're locking (for Lock_Cleanup) */
+ global_readlock.repository = xrepository;
/* get the lock dir for our own */
- if (set_lock (xrepository, 1) != L_OK)
+ if (set_lock (&global_readlock, 1) != L_OK)
{
error (0, 0, "failed to obtain dir lock in repository `%s'",
xrepository);
- readlock[0] = '\0';
+ if (readlock != NULL)
+ free (readlock);
+ readlock = NULL;
+ /* We don't set global_readlock.repository to NULL. I think this
+ only works because recurse.c will give a fatal error if we return
+ a nonzero value. */
return (1);
}
/* write a read-lock */
+ tmp = xmalloc (strlen (xrepository) + strlen (readlock) + 10);
(void) sprintf (tmp, "%s/%s", xrepository, readlock);
- if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
+ if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF)
{
error (0, errno, "cannot create read lock in repository `%s'",
xrepository);
- readlock[0] = '\0';
+ if (readlock != NULL)
+ free (readlock);
+ readlock = NULL;
err = 1;
}
+ free (tmp);
/* free the lock dir */
- clear_lock();
+ clear_lock (&global_readlock);
return (err);
}
@@ -205,10 +307,15 @@ Reader_Lock (xrepository)
*/
static char *lock_error_repos;
static int lock_error;
-int
+
+static int Writer_Lock PROTO ((List * list));
+
+static int
Writer_Lock (list)
List *list;
{
+ char *wait_repos;
+
if (noexec)
return (0);
@@ -219,32 +326,45 @@ Writer_Lock (list)
return (1);
}
+ wait_repos = NULL;
for (;;)
{
/* try to lock everything on the list */
lock_error = L_OK; /* init for set_writelock_proc */
lock_error_repos = (char *) NULL; /* init for set_writelock_proc */
locklist = list; /* init for Lock_Cleanup */
- (void) strcpy (lockers_name, "unknown");
+ if (lockers_name != NULL)
+ free (lockers_name);
+ lockers_name = xstrdup ("unknown");
(void) walklist (list, set_writelock_proc, NULL);
switch (lock_error)
{
case L_ERROR: /* Real Error */
+ if (wait_repos != NULL)
+ free (wait_repos);
Lock_Cleanup (); /* clean up any locks we set */
error (0, 0, "lock failed - giving up");
return (1);
case L_LOCKED: /* Someone already had a lock */
- Lock_Cleanup (); /* clean up any locks we set */
+ remove_locks (); /* clean up any locks we set */
lock_wait (lock_error_repos); /* sleep a while and try again */
+ wait_repos = xstrdup (lock_error_repos);
continue;
case L_OK: /* we got the locks set */
+ if (wait_repos != NULL)
+ {
+ lock_obtained (wait_repos);
+ free (wait_repos);
+ }
return (0);
default:
+ if (wait_repos != NULL)
+ free (wait_repos);
error (0, 0, "unknown lock status %d in Writer_Lock",
lock_error);
return (1);
@@ -266,7 +386,7 @@ set_writelock_proc (p, closure)
/* apply the write lock */
lock_error_repos = p->key;
- lock_error = write_lock (p->key);
+ lock_error = write_lock ((struct lock *)p->data);
return (0);
}
@@ -275,33 +395,36 @@ set_writelock_proc (p, closure)
* lock held by someone else or L_ERROR if an error occurred
*/
static int
-write_lock (repository)
- char *repository;
+write_lock (lock)
+ struct lock *lock;
{
int status;
FILE *fp;
- char tmp[PATH_MAX];
+ char *tmp;
- if (writelock[0] == '\0')
+ if (writelock == NULL)
+ {
+ writelock = xmalloc (strlen (hostname) + sizeof (CVSWFL) + 40);
(void) sprintf (writelock,
#ifdef HAVE_LONG_FILE_NAMES
- "%s.%s.%ld", CVSWFL, hostname,
+ "%s.%s.%ld", CVSWFL, hostname,
#else
- "%s.%ld", CVSWFL,
+ "%s.%ld", CVSWFL,
#endif
- (long) getpid());
+ (long) getpid());
+ }
/* make sure the lock dir is ours (not necessarily unique to us!) */
- status = set_lock (repository, 0);
+ status = set_lock (lock, 0);
if (status == L_OK)
{
/* we now own a writer - make sure there are no readers */
- if (readers_exist (repository))
+ if (readers_exist (lock->repository))
{
/* clean up the lock dir if we created it */
if (status == L_OK)
{
- clear_lock();
+ clear_lock (lock);
}
/* indicate we failed due to read locks instead of error */
@@ -309,25 +432,28 @@ write_lock (repository)
}
/* write the write-lock file */
- (void) sprintf (tmp, "%s/%s", repository, writelock);
- if ((fp = fopen (tmp, "w+")) == NULL || fclose (fp) == EOF)
+ tmp = xmalloc (strlen (lock->repository) + strlen (writelock) + 10);
+ (void) sprintf (tmp, "%s/%s", lock->repository, writelock);
+ if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF)
{
int xerrno = errno;
- if (unlink (tmp) < 0 && ! existence_error (errno))
+ if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
error (0, errno, "failed to remove lock %s", tmp);
/* free the lock dir if we created it */
if (status == L_OK)
{
- clear_lock();
+ clear_lock (lock);
}
/* return the error */
error (0, xerrno, "cannot create write lock in repository `%s'",
- repository);
+ lock->repository);
+ free (tmp);
return (L_ERROR);
}
+ free (tmp);
return (L_OK);
}
else
@@ -353,7 +479,7 @@ readers_exist (repository)
again:
#endif
- if ((dirp = opendir (repository)) == NULL)
+ if ((dirp = CVS_OPENDIR (repository)) == NULL)
error (1, 0, "cannot open directory %s", repository);
errno = 0;
@@ -368,7 +494,7 @@ again:
line = xmalloc (strlen (repository) + strlen (dp->d_name) + 5);
(void) sprintf (line, "%s/%s", repository, dp->d_name);
- if (stat (line, &sb) != -1)
+ if ( CVS_STAT (line, &sb) != -1)
{
#ifdef CVS_FUDGELOCKS
/*
@@ -376,7 +502,7 @@ again:
* seconds ago, try to clean-up the lock file, and if
* successful, re-open the directory and try again.
*/
- if (now >= (sb.st_ctime + CVSLCKAGE) && unlink (line) != -1)
+ if (now >= (sb.st_ctime + CVSLCKAGE) && CVS_UNLINK (line) != -1)
{
(void) closedir (dirp);
free (line);
@@ -418,13 +544,18 @@ set_lockers_name (statp)
{
struct passwd *pw;
+ if (lockers_name != NULL)
+ free (lockers_name);
if ((pw = (struct passwd *) getpwuid (statp->st_uid)) !=
(struct passwd *) NULL)
{
- (void) strcpy (lockers_name, pw->pw_name);
+ lockers_name = xstrdup (pw->pw_name);
}
else
+ {
+ lockers_name = xmalloc (20);
(void) sprintf (lockers_name, "uid%lu", (unsigned long) statp->st_uid);
+ }
}
/*
@@ -433,24 +564,29 @@ set_lockers_name (statp)
* seconds old, just try to remove the directory.
*/
static int
-set_lock (repository, will_wait)
- char *repository;
+set_lock (lock, will_wait)
+ struct lock *lock;
int will_wait;
{
+ int waited;
struct stat sb;
mode_t omask;
#ifdef CVS_FUDGELOCKS
time_t now;
#endif
- (void) sprintf (masterlock, "%s/%s", repository, CVSLCK);
+ if (masterlock != NULL)
+ free (masterlock);
+ masterlock = xmalloc (strlen (lock->repository) + sizeof (CVSLCK) + 10);
+ (void) sprintf (masterlock, "%s/%s", lock->repository, CVSLCK);
/*
* Note that it is up to the callers of set_lock() to arrange for signal
* handlers that do the appropriate things, like remove the lock
* directory before they exit.
*/
- cleanup_lckdir = 0;
+ waited = 0;
+ lock->have_lckdir = 0;
for (;;)
{
int status = -1;
@@ -458,27 +594,11 @@ set_lock (repository, will_wait)
SIG_beginCrSect ();
if (CVS_MKDIR (masterlock, 0777) == 0)
{
-#ifdef AFSCVS
- char uidlock[PATH_MAX];
- FILE *fp;
-
- sprintf(uidlock, "%s/uidlock%d", masterlock, geteuid() );
- if ((fp = fopen(uidlock, "w+")) == NULL)
- {
- /* We failed to create the uidlock,
- so rm masterlock and leave */
- rmdir(masterlock);
- SIG_endCrSect ();
- status = L_ERROR;
- goto out;
- }
-
- /* We successfully created the uid lock, so close the file */
- fclose(fp);
-#endif
- cleanup_lckdir = 1;
+ lock->have_lckdir = 1;
SIG_endCrSect ();
status = L_OK;
+ if (waited)
+ lock_obtained (lock->repository);
goto out;
}
SIG_endCrSect ();
@@ -491,15 +611,14 @@ set_lock (repository, will_wait)
{
error (0, errno,
"failed to create lock directory in repository `%s'",
- repository);
+ lock->repository);
return (L_ERROR);
}
- /*
- * stat the dir - if it is non-existent, re-try the loop since
- * someone probably just removed it (thus releasing the lock)
- */
- if (stat (masterlock, &sb) < 0)
+ /* Find out who owns the lock. If the lock directory is
+ non-existent, re-try the loop since someone probably just
+ removed it (thus releasing the lock). */
+ if (CVS_STAT (masterlock, &sb) < 0)
{
if (existence_error (errno))
continue;
@@ -517,13 +636,7 @@ set_lock (repository, will_wait)
(void) time (&now);
if (now >= (sb.st_ctime + CVSLCKAGE))
{
-#ifdef AFSCVS
- /* Remove the uidlock first */
- char rmuidlock[PATH_MAX];
- sprintf(rmuidlock, "rm -f %s/uidlock%d", masterlock, geteuid() );
- system(rmuidlock);
-#endif
- if (rmdir (masterlock) >= 0)
+ if (CVS_RMDIR (masterlock) >= 0)
continue;
}
#endif
@@ -534,7 +647,8 @@ set_lock (repository, will_wait)
/* if he wasn't willing to wait, return an error */
if (!will_wait)
return (L_LOCKED);
- lock_wait (repository);
+ lock_wait (lock->repository);
+ waited = 1;
}
}
@@ -543,17 +657,14 @@ set_lock (repository, will_wait)
* clear_lock is never called except after a successful set_lock().
*/
static void
-clear_lock()
+clear_lock (lock)
+ struct lock *lock;
{
-#ifdef AFSCVS
- /* Remove the uidlock first */
- char rmuidlock[PATH_MAX];
- sprintf(rmuidlock, "rm -f %s/uidlock%d", masterlock, geteuid() );
- system(rmuidlock);
-#endif
- if (rmdir (masterlock) < 0)
+ SIG_beginCrSect ();
+ if (CVS_RMDIR (masterlock) < 0)
error (0, errno, "failed to remove lock dir `%s'", masterlock);
- cleanup_lckdir = 0;
+ lock->have_lckdir = 0;
+ SIG_endCrSect ();
}
/*
@@ -568,30 +679,54 @@ lock_wait (repos)
(void) time (&now);
error (0, 0, "[%8.8s] waiting for %s's lock in %s", ctime (&now) + 11,
lockers_name, repos);
+ /* Call cvs_flusherr to ensure that the user sees this message as
+ soon as possible. */
+ cvs_flusherr ();
(void) sleep (CVSLCKSLEEP);
}
+
+/*
+ * Print out a message when we obtain a lock.
+ */
+static void
+lock_obtained (repos)
+ char *repos;
+{
+ time_t now;
+
+ (void) time (&now);
+ error (0, 0, "[%8.8s] obtained lock in %s", ctime (&now) + 11, repos);
+ /* Call cvs_flusherr to ensure that the user sees this message as
+ soon as possible. */
+ cvs_flusherr ();
+}
-static int lock_filesdoneproc PROTO ((int err, char *repository,
- char *update_dir));
+static int lock_filesdoneproc PROTO ((void *callerdat, int err,
+ char *repository, char *update_dir,
+ List *entries));
static int fsortcmp PROTO((const Node * p, const Node * q));
-static List *lock_tree_list;
-
/*
* Create a list of repositories to lock
*/
/* ARGSUSED */
static int
-lock_filesdoneproc (err, repository, update_dir)
+lock_filesdoneproc (callerdat, err, repository, update_dir, entries)
+ void *callerdat;
int err;
char *repository;
char *update_dir;
+ List *entries;
{
Node *p;
p = getnode ();
p->type = LOCK;
p->key = xstrdup (repository);
+ p->data = xmalloc (sizeof (struct lock));
+ ((struct lock *)p->data)->repository = p->key;
+ ((struct lock *)p->data)->have_lckdir = 0;
+
/* FIXME-KRP: this error condition should not simply be passed by. */
if (p->key == NULL || addnode (lock_tree_list, p) != 0)
freenode (p);
@@ -623,17 +758,39 @@ lock_tree_for_write (argc, argv, local, aflag)
*/
lock_tree_list = getlist ();
err = start_recursion ((FILEPROC) NULL, lock_filesdoneproc,
- (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, argc,
- argv, local, W_LOCAL, aflag, 0, (char *) NULL, 0,
- 0);
+ (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc,
+ argv, local, W_LOCAL, aflag, 0, (char *) NULL, 0);
sortlist (lock_tree_list, fsortcmp);
if (Writer_Lock (lock_tree_list) != 0)
error (1, 0, "lock failed - giving up");
}
-
+
+/* Lock a single directory in REPOSITORY. It is OK to call this if
+ a lock has been set with lock_dir_for_write; the new lock will replace
+ the old one. If REPOSITORY is NULL, don't do anything. */
void
-lock_tree_cleanup ()
+lock_dir_for_write (repository)
+ char *repository;
{
- Lock_Cleanup ();
- dellist (&lock_tree_list);
+ if (repository != NULL
+ && (locked_dir == NULL
+ || strcmp (locked_dir, repository) != 0))
+ {
+ Node *node;
+
+ if (locked_dir != NULL)
+ Lock_Cleanup ();
+
+ locked_dir = xstrdup (repository);
+ locked_list = getlist ();
+ node = getnode ();
+ node->type = LOCK;
+ node->key = xstrdup (repository);
+ node->data = xmalloc (sizeof (struct lock));
+ ((struct lock *)node->data)->repository = node->key;
+ ((struct lock *)node->data)->have_lckdir = 0;
+
+ (void) addnode (locked_list, node);
+ Writer_Lock (locked_list);
+ }
}
OpenPOWER on IntegriCloud