xref: /openbsd-src/gnu/usr.bin/cvs/src/lock.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*
2  * Copyright (c) 1992, Brian Berliner and Jeff Polk
3  * Copyright (c) 1989-1992, Brian Berliner
4  *
5  * You may distribute under the terms of the GNU General Public License as
6  * specified in the README file that comes with the CVS source distribution.
7  *
8  * Set Lock
9  *
10  * Lock file support for CVS.
11  */
12 
13 /* The node Concurrency in doc/cvs.texinfo has a brief introduction to
14    how CVS locks function, and some of the user-visible consequences of
15    their existence.  Here is a summary of why they exist (and therefore,
16    the consequences of hacking CVS to read a repository without creating
17    locks):
18 
19    There are two uses.  One is the ability to prevent there from being
20    two writers at the same time.  This is necessary for any number of
21    reasons (fileattr code, probably others).  Commit needs to lock the
22    whole tree so that nothing happens between the up-to-date check and
23    the actual checkin.
24 
25    The second use is the ability to ensure that there is not a writer
26    and a reader at the same time (several readers are allowed).  Reasons
27    for this are:
28 
29    * Readlocks ensure that once CVS has found a collection of rcs
30    files using Find_Names, the files will still exist when it reads
31    them (they may have moved in or out of the attic).
32 
33    * Readlocks provide some modicum of consistency, although this is
34    kind of limited--see the node Concurrency in cvs.texinfo.
35 
36    * Readlocks ensure that the RCS file does not change between
37    RCS_parse and RCS_reparsercsfile time.  This one strikes me as
38    important, although I haven't thought up what bad scenarios might
39    be.
40 
41    * Readlocks ensure that we won't find the file in the state in
42    which it is in between the calls to add_rcs_file and RCS_checkin in
43    commit.c (when a file is being added).  This state is a state in
44    which the RCS file parsing routines in rcs.c cannot parse the file.
45 
46    * Readlocks ensure that a reader won't try to look at a
47    half-written fileattr file (fileattr is not updated atomically).
48 
49    (see also the description of anonymous read-only access in
50    "Password authentication security" node in doc/cvs.texinfo).
51 
52    While I'm here, I'll try to summarize a few random suggestions
53    which periodically get made about how locks might be different:
54 
55    1.  Check for EROFS.  Maybe useful, although in the presence of NFS
56    EROFS does *not* mean that the file system is unchanging.
57 
58    2.  Provide a means to put the cvs locks in some directory apart from
59    the repository (CVSROOT/locks; a -l option in modules; etc.).
60 
61    3.  Provide an option to disable locks for operations which only
62    read (see above for some of the consequences).
63 
64    4.  Have a server internally do the locking.  Probably a good
65    long-term solution, and many people have been working hard on code
66    changes which would eventually make it possible to have a server
67    which can handle various connections in one process, but there is
68    much, much work still to be done before this is feasible.
69 
70    5.  Like #4 but use shared memory or something so that the servers
71    merely need to all be on the same machine.  This is a much smaller
72    change to CVS (it functions much like #2; shared memory might be an
73    unneeded complication although it presumably would be faster).  */
74 
75 #include "cvs.h"
76 #include <assert.h>
77 
78 struct lock {
79     /* This is the directory in which we may have a lock named by the
80        readlock variable, a lock named by the writelock variable, and/or
81        a lock named CVSLCK.  The storage is not allocated along with the
82        struct lock; it is allocated by the Reader_Lock caller or in the
83        case of writelocks, it is just a pointer to the storage allocated
84        for the ->key field.  */
85     char *repository;
86     /* Do we have a lock named CVSLCK?  */
87     int have_lckdir;
88     /* Note there is no way of knowing whether the readlock and writelock
89        exist.  The code which sets the locks doesn't use SIG_beginCrSect
90        to set a flag like we do for CVSLCK.  */
91 };
92 
93 static void remove_locks PROTO((void));
94 static int readers_exist PROTO((char *repository));
95 static int set_lock PROTO ((struct lock *lock, int will_wait));
96 static void clear_lock PROTO ((struct lock *lock));
97 static void set_lockers_name PROTO((struct stat *statp));
98 static int set_writelock_proc PROTO((Node * p, void *closure));
99 static int unlock_proc PROTO((Node * p, void *closure));
100 static int write_lock PROTO ((struct lock *lock));
101 static void lock_simple_remove PROTO ((struct lock *lock));
102 static void lock_wait PROTO((char *repository));
103 static void lock_obtained PROTO((char *repository));
104 
105 /* Malloc'd array containing the username of the whoever has the lock.
106    Will always be non-NULL in the cases where it is needed.  */
107 static char *lockers_name;
108 /* Malloc'd array specifying name of a readlock within a directory.
109    Or NULL if none.  */
110 static char *readlock;
111 /* Malloc'd array specifying name of a writelock within a directory.
112    Or NULL if none.  */
113 static char *writelock;
114 /* Malloc'd array specifying the name of a CVSLCK file (absolute pathname).
115    Will always be non-NULL in the cases where it is used.  */
116 static char *masterlock;
117 static List *locklist;
118 
119 #define L_OK		0		/* success */
120 #define L_ERROR		1		/* error condition */
121 #define L_LOCKED	2		/* lock owned by someone else */
122 
123 /* This is the (single) readlock which is set by Reader_Lock.  The
124    repository field is NULL if there is no such lock.  */
125 static struct lock global_readlock;
126 
127 /* List of locks set by lock_tree_for_write.  This is redundant
128    with locklist, sort of.  */
129 static List *lock_tree_list;
130 
131 /* If we set locks with lock_dir_for_write, then locked_dir contains
132    the malloc'd name of the repository directory which we have locked.
133    locked_list is the same thing packaged into a list and is redundant
134    with locklist the same way that lock_tree_list is.  */
135 static char *locked_dir;
136 static List *locked_list;
137 
138 /* LockDir from CVSROOT/config.  */
139 char *lock_dir;
140 
141 static char *lock_name PROTO ((char *repository, char *name));
142 
143 /* Return a newly malloc'd string containing the name of the lock for the
144    repository REPOSITORY and the lock file name within that directory
145    NAME.  Also create the directories in which to put the lock file
146    if needed (if we need to, could save system call(s) by doing
147    that only if the actual operation fails.  But for now we'll keep
148    things simple).  */
149 static char *
150 lock_name (repository, name)
151     char *repository;
152     char *name;
153 {
154     char *retval;
155     char *p;
156     char *q;
157     char *short_repos;
158     mode_t save_umask;
159     int saved_umask = 0;
160 
161     if (lock_dir == NULL)
162     {
163 	/* This is the easy case.  Because the lock files go directly
164 	   in the repository, no need to create directories or anything.  */
165 	retval = xmalloc (strlen (repository) + strlen (name) + 10);
166 	(void) sprintf (retval, "%s/%s", repository, name);
167     }
168     else
169     {
170 	struct stat sb;
171 	mode_t new_mode = 0;
172 
173 	/* The interesting part of the repository is the part relative
174 	   to CVSROOT.  */
175 	assert (CVSroot_directory != NULL);
176 	assert (strncmp (repository, CVSroot_directory,
177 			 strlen (CVSroot_directory)) == 0);
178 	short_repos = repository + strlen (CVSroot_directory) + 1;
179 
180 	if (strcmp (repository, CVSroot_directory) == 0)
181 	    short_repos = ".";
182 	else
183 	    assert (short_repos[-1] == '/');
184 
185 	retval = xmalloc (strlen (lock_dir)
186 			  + strlen (short_repos)
187 			  + strlen (name)
188 			  + 10);
189 	strcpy (retval, lock_dir);
190 	q = retval + strlen (retval);
191 	*q++ = '/';
192 
193 	strcpy (q, short_repos);
194 
195 	/* In the common case, where the directory already exists, let's
196 	   keep it to one system call.  */
197 	if (CVS_STAT (retval, &sb) < 0)
198 	{
199 	    /* If we need to be creating more than one directory, we'll
200 	       get the existence_error here.  */
201 	    if (!existence_error (errno))
202 		error (1, errno, "cannot stat directory %s", retval);
203 	}
204 	else
205 	{
206 	    if (S_ISDIR (sb.st_mode))
207 		goto created;
208 	    else
209 		error (1, 0, "%s is not a directory", retval);
210 	}
211 
212 	/* Now add the directories one at a time, so we can create
213 	   them if needed.
214 
215 	   The idea behind the new_mode stuff is that the directory we
216 	   end up creating will inherit permissions from its parent
217 	   directory (we re-set new_mode with each EEXIST).  CVSUMASK
218 	   isn't right, because typically the reason for LockDir is to
219 	   use a different set of permissions.  We probably want to
220 	   inherit group ownership also (but we don't try to deal with
221 	   that, some systems do it for us either always or when g+s is on).
222 
223 	   We don't try to do anything about the permissions on the lock
224 	   files themselves.  The permissions don't really matter so much
225 	   because the locks will generally be removed by the process
226 	   which created them.  */
227 
228 	if (CVS_STAT (lock_dir, &sb) < 0)
229 	    error (1, errno, "cannot stat %s", lock_dir);
230 	new_mode = sb.st_mode;
231 	save_umask = umask (0000);
232 	saved_umask = 1;
233 
234 	p = short_repos;
235 	while (1)
236 	{
237 	    while (!ISDIRSEP (*p) && *p != '\0')
238 		++p;
239 	    if (ISDIRSEP (*p))
240 	    {
241 		strncpy (q, short_repos, p - short_repos);
242 		q[p - short_repos] = '\0';
243 		if (!ISDIRSEP (q[p - short_repos - 1])
244 		    && CVS_MKDIR (retval, new_mode) < 0)
245 		{
246 		    int saved_errno = errno;
247 		    if (saved_errno != EEXIST)
248 			error (1, errno, "cannot make directory %s", retval);
249 		    else
250 		    {
251 			if (CVS_STAT (retval, &sb) < 0)
252 			    error (1, errno, "cannot stat %s", retval);
253 			new_mode = sb.st_mode;
254 		    }
255 		}
256 		++p;
257 	    }
258 	    else
259 	    {
260 		strcpy (q, short_repos);
261 		if (CVS_MKDIR (retval, new_mode) < 0
262 		    && errno != EEXIST)
263 		    error (1, errno, "cannot make directory %s", retval);
264 		goto created;
265 	    }
266 	}
267     created:;
268 
269 	strcat (retval, "/");
270 	strcat (retval, name);
271 
272 	if (saved_umask)
273 	{
274 	    assert (umask (save_umask) == 0000);
275 	    saved_umask = 0;
276 	}
277     }
278     return retval;
279 }
280 
281 /*
282  * Clean up all outstanding locks
283  */
284 void
285 Lock_Cleanup ()
286 {
287     /* FIXME: error handling here is kind of bogus; we sometimes will call
288        error, which in turn can call us again.  For the moment work around
289        this by refusing to reenter this function (this is a kludge).  */
290     /* FIXME-reentrancy: the workaround isn't reentrant.  */
291     static int in_lock_cleanup = 0;
292 
293     if (in_lock_cleanup)
294 	return;
295     in_lock_cleanup = 1;
296 
297     remove_locks ();
298 
299     dellist (&lock_tree_list);
300 
301     if (locked_dir != NULL)
302     {
303 	dellist (&locked_list);
304 	free (locked_dir);
305 	locked_dir = NULL;
306 	locked_list = NULL;
307     }
308     in_lock_cleanup = 0;
309 }
310 
311 /*
312  * Remove locks without discarding the lock information
313  */
314 static void
315 remove_locks ()
316 {
317     /* clean up simple locks (if any) */
318     if (global_readlock.repository != NULL)
319     {
320 	lock_simple_remove (&global_readlock);
321 	global_readlock.repository = NULL;
322     }
323 
324     /* clean up multiple locks (if any) */
325     if (locklist != (List *) NULL)
326     {
327 	(void) walklist (locklist, unlock_proc, NULL);
328 	locklist = (List *) NULL;
329     }
330 }
331 
332 /*
333  * walklist proc for removing a list of locks
334  */
335 static int
336 unlock_proc (p, closure)
337     Node *p;
338     void *closure;
339 {
340     lock_simple_remove ((struct lock *)p->data);
341     return (0);
342 }
343 
344 /* Remove the lock files.  */
345 static void
346 lock_simple_remove (lock)
347     struct lock *lock;
348 {
349     char *tmp;
350 
351     /* If readlock is set, the lock directory *might* have been created, but
352        since Reader_Lock doesn't use SIG_beginCrSect the way that set_lock
353        does, we don't know that.  That is why we need to check for
354        existence_error here.  */
355     if (readlock != NULL)
356     {
357 	tmp = lock_name (lock->repository, readlock);
358 	if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
359 	    error (0, errno, "failed to remove lock %s", tmp);
360 	free (tmp);
361     }
362 
363     /* If writelock is set, the lock directory *might* have been created, but
364        since write_lock doesn't use SIG_beginCrSect the way that set_lock
365        does, we don't know that.  That is why we need to check for
366        existence_error here.  */
367     if (writelock != NULL)
368     {
369 	tmp = lock_name (lock->repository, writelock);
370 	if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
371 	    error (0, errno, "failed to remove lock %s", tmp);
372 	free (tmp);
373     }
374 
375     if (lock->have_lckdir)
376     {
377 	tmp = lock_name (lock->repository, CVSLCK);
378 	SIG_beginCrSect ();
379 	if (CVS_RMDIR (tmp) < 0)
380 	    error (0, errno, "failed to remove lock dir %s", tmp);
381 	lock->have_lckdir = 0;
382 	SIG_endCrSect ();
383 	free (tmp);
384     }
385 }
386 
387 /*
388  * Create a lock file for readers
389  */
390 int
391 Reader_Lock (xrepository)
392     char *xrepository;
393 {
394     int err = 0;
395     FILE *fp;
396     char *tmp;
397 
398     if (noexec || readonlyfs)
399 	return (0);
400 
401     /* we only do one directory at a time for read locks! */
402     if (global_readlock.repository != NULL)
403     {
404 	error (0, 0, "Reader_Lock called while read locks set - Help!");
405 	return (1);
406     }
407 
408     if (readlock == NULL)
409     {
410 	readlock = xmalloc (strlen (hostname) + sizeof (CVSRFL) + 40);
411 	(void) sprintf (readlock,
412 #ifdef HAVE_LONG_FILE_NAMES
413 			"%s.%s.%ld", CVSRFL, hostname,
414 #else
415 			"%s.%ld", CVSRFL,
416 #endif
417 			(long) getpid ());
418     }
419 
420     /* remember what we're locking (for Lock_Cleanup) */
421     global_readlock.repository = xrepository;
422 
423     /* get the lock dir for our own */
424     if (set_lock (&global_readlock, 1) != L_OK)
425     {
426 	error (0, 0, "failed to obtain dir lock in repository `%s'",
427 	       xrepository);
428 	if (readlock != NULL)
429 	    free (readlock);
430 	readlock = NULL;
431 	/* We don't set global_readlock.repository to NULL.  I think this
432 	   only works because recurse.c will give a fatal error if we return
433 	   a nonzero value.  */
434 	return (1);
435     }
436 
437     /* write a read-lock */
438     tmp = lock_name (xrepository, readlock);
439     if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF)
440     {
441 	error (0, errno, "cannot create read lock in repository `%s'",
442 	       xrepository);
443 	if (readlock != NULL)
444 	    free (readlock);
445 	readlock = NULL;
446 	err = 1;
447     }
448     free (tmp);
449 
450     /* free the lock dir */
451     clear_lock (&global_readlock);
452 
453     return (err);
454 }
455 
456 /*
457  * Lock a list of directories for writing
458  */
459 static char *lock_error_repos;
460 static int lock_error;
461 
462 static int Writer_Lock PROTO ((List * list));
463 
464 static int
465 Writer_Lock (list)
466     List *list;
467 {
468     char *wait_repos;
469 
470     if (noexec)
471 	return (0);
472 
473     /* We only know how to do one list at a time */
474     if (locklist != (List *) NULL)
475     {
476 	error (0, 0, "Writer_Lock called while write locks set - Help!");
477 	return (1);
478     }
479 
480     wait_repos = NULL;
481     for (;;)
482     {
483 	/* try to lock everything on the list */
484 	lock_error = L_OK;		/* init for set_writelock_proc */
485 	lock_error_repos = (char *) NULL; /* init for set_writelock_proc */
486 	locklist = list;		/* init for Lock_Cleanup */
487 	if (lockers_name != NULL)
488 	    free (lockers_name);
489 	lockers_name = xstrdup ("unknown");
490 
491 	(void) walklist (list, set_writelock_proc, NULL);
492 
493 	switch (lock_error)
494 	{
495 	    case L_ERROR:		/* Real Error */
496 		if (wait_repos != NULL)
497 		    free (wait_repos);
498 		Lock_Cleanup ();	/* clean up any locks we set */
499 		error (0, 0, "lock failed - giving up");
500 		return (1);
501 
502 	    case L_LOCKED:		/* Someone already had a lock */
503 		remove_locks ();	/* clean up any locks we set */
504 		lock_wait (lock_error_repos); /* sleep a while and try again */
505 		wait_repos = xstrdup (lock_error_repos);
506 		continue;
507 
508 	    case L_OK:			/* we got the locks set */
509 	        if (wait_repos != NULL)
510 		{
511 		    lock_obtained (wait_repos);
512 		    free (wait_repos);
513 		}
514 		return (0);
515 
516 	    default:
517 		if (wait_repos != NULL)
518 		    free (wait_repos);
519 		error (0, 0, "unknown lock status %d in Writer_Lock",
520 		       lock_error);
521 		return (1);
522 	}
523     }
524 }
525 
526 /*
527  * walklist proc for setting write locks
528  */
529 static int
530 set_writelock_proc (p, closure)
531     Node *p;
532     void *closure;
533 {
534     /* if some lock was not OK, just skip this one */
535     if (lock_error != L_OK)
536 	return (0);
537 
538     /* apply the write lock */
539     lock_error_repos = p->key;
540     lock_error = write_lock ((struct lock *)p->data);
541     return (0);
542 }
543 
544 /*
545  * Create a lock file for writers returns L_OK if lock set ok, L_LOCKED if
546  * lock held by someone else or L_ERROR if an error occurred
547  */
548 static int
549 write_lock (lock)
550     struct lock *lock;
551 {
552     int status;
553     FILE *fp;
554     char *tmp;
555 
556     if (writelock == NULL)
557     {
558 	writelock = xmalloc (strlen (hostname) + sizeof (CVSWFL) + 40);
559 	(void) sprintf (writelock,
560 #ifdef HAVE_LONG_FILE_NAMES
561 			"%s.%s.%ld", CVSWFL, hostname,
562 #else
563 			"%s.%ld", CVSWFL,
564 #endif
565 			(long) getpid());
566     }
567 
568     /* make sure the lock dir is ours (not necessarily unique to us!) */
569     status = set_lock (lock, 0);
570     if (status == L_OK)
571     {
572 	/* we now own a writer - make sure there are no readers */
573 	if (readers_exist (lock->repository))
574 	{
575 	    /* clean up the lock dir if we created it */
576 	    if (status == L_OK)
577 	    {
578 		clear_lock (lock);
579 	    }
580 
581 	    /* indicate we failed due to read locks instead of error */
582 	    return (L_LOCKED);
583 	}
584 
585 	/* write the write-lock file */
586 	tmp = lock_name (lock->repository, writelock);
587 	if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF)
588 	{
589 	    int xerrno = errno;
590 
591 	    if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
592 		error (0, errno, "failed to remove lock %s", tmp);
593 
594 	    /* free the lock dir if we created it */
595 	    if (status == L_OK)
596 	    {
597 		clear_lock (lock);
598 	    }
599 
600 	    /* return the error */
601 	    error (0, xerrno, "cannot create write lock in repository `%s'",
602 		   lock->repository);
603 	    free (tmp);
604 	    return (L_ERROR);
605 	}
606 	free (tmp);
607 	return (L_OK);
608     }
609     else
610 	return (status);
611 }
612 
613 /*
614  * readers_exist() returns 0 if there are no reader lock files remaining in
615  * the repository; else 1 is returned, to indicate that the caller should
616  * sleep a while and try again.
617  */
618 static int
619 readers_exist (repository)
620     char *repository;
621 {
622     char *line;
623     DIR *dirp;
624     struct dirent *dp;
625     struct stat sb;
626     int ret = 0;
627 
628 #ifdef CVS_FUDGELOCKS
629 again:
630 #endif
631 
632     if ((dirp = CVS_OPENDIR (repository)) == NULL)
633 	error (1, 0, "cannot open directory %s", repository);
634 
635     errno = 0;
636     while ((dp = readdir (dirp)) != NULL)
637     {
638 	if (CVS_FNMATCH (CVSRFLPAT, dp->d_name, 0) == 0)
639 	{
640 #ifdef CVS_FUDGELOCKS
641 	    time_t now;
642 	    (void) time (&now);
643 #endif
644 
645 	    line = xmalloc (strlen (repository) + strlen (dp->d_name) + 5);
646 	    (void) sprintf (line, "%s/%s", repository, dp->d_name);
647 	    if ( CVS_STAT (line, &sb) != -1)
648 	    {
649 #ifdef CVS_FUDGELOCKS
650 		/*
651 		 * If the create time of the file is more than CVSLCKAGE
652 		 * seconds ago, try to clean-up the lock file, and if
653 		 * successful, re-open the directory and try again.
654 		 */
655 		if (now >= (sb.st_ctime + CVSLCKAGE) && CVS_UNLINK (line) != -1)
656 		{
657 		    (void) closedir (dirp);
658 		    free (line);
659 		    goto again;
660 		}
661 #endif
662 		set_lockers_name (&sb);
663 	    }
664 	    else
665 	    {
666 		/* If the file doesn't exist, it just means that it disappeared
667 		   between the time we did the readdir and the time we did
668 		   the stat.  */
669 		if (!existence_error (errno))
670 		    error (0, errno, "cannot stat %s", line);
671 	    }
672 	    errno = 0;
673 	    free (line);
674 
675 	    ret = 1;
676 	    break;
677 	}
678 	errno = 0;
679     }
680     if (errno != 0)
681 	error (0, errno, "error reading directory %s", repository);
682 
683     closedir (dirp);
684     return (ret);
685 }
686 
687 /*
688  * Set the static variable lockers_name appropriately, based on the stat
689  * structure passed in.
690  */
691 static void
692 set_lockers_name (statp)
693     struct stat *statp;
694 {
695     struct passwd *pw;
696 
697     if (lockers_name != NULL)
698 	free (lockers_name);
699     if ((pw = (struct passwd *) getpwuid (statp->st_uid)) !=
700 	(struct passwd *) NULL)
701     {
702 	lockers_name = xstrdup (pw->pw_name);
703     }
704     else
705     {
706 	lockers_name = xmalloc (20);
707 	(void) sprintf (lockers_name, "uid%lu", (unsigned long) statp->st_uid);
708     }
709 }
710 
711 /*
712  * Persistently tries to make the directory "lckdir",, which serves as a
713  * lock. If the create time on the directory is greater than CVSLCKAGE
714  * seconds old, just try to remove the directory.
715  */
716 static int
717 set_lock (lock, will_wait)
718     struct lock *lock;
719     int will_wait;
720 {
721     int waited;
722     struct stat sb;
723     mode_t omask;
724 #ifdef CVS_FUDGELOCKS
725     time_t now;
726 #endif
727 
728     if (masterlock != NULL)
729 	free (masterlock);
730     masterlock = lock_name (lock->repository, CVSLCK);
731 
732     /*
733      * Note that it is up to the callers of set_lock() to arrange for signal
734      * handlers that do the appropriate things, like remove the lock
735      * directory before they exit.
736      */
737     waited = 0;
738     lock->have_lckdir = 0;
739     for (;;)
740     {
741 	int status = -1;
742 	omask = umask (cvsumask);
743 	SIG_beginCrSect ();
744 	if (CVS_MKDIR (masterlock, 0777) == 0)
745 	{
746 	    lock->have_lckdir = 1;
747 	    SIG_endCrSect ();
748 	    status = L_OK;
749 	    if (waited)
750 	        lock_obtained (lock->repository);
751 	    goto out;
752 	}
753 	SIG_endCrSect ();
754       out:
755 	(void) umask (omask);
756 	if (status != -1)
757 	    return status;
758 
759 	if (errno != EEXIST)
760 	{
761 	    error (0, errno,
762 		   "failed to create lock directory for `%s' (%s)",
763 		   lock->repository, masterlock);
764 	    return (L_ERROR);
765 	}
766 
767 	/* Find out who owns the lock.  If the lock directory is
768 	   non-existent, re-try the loop since someone probably just
769 	   removed it (thus releasing the lock).  */
770 	if (CVS_STAT (masterlock, &sb) < 0)
771 	{
772 	    if (existence_error (errno))
773 		continue;
774 
775 	    error (0, errno, "couldn't stat lock directory `%s'", masterlock);
776 	    return (L_ERROR);
777 	}
778 
779 #ifdef CVS_FUDGELOCKS
780 	/*
781 	 * If the create time of the directory is more than CVSLCKAGE seconds
782 	 * ago, try to clean-up the lock directory, and if successful, just
783 	 * quietly retry to make it.
784 	 */
785 	(void) time (&now);
786 	if (now >= (sb.st_ctime + CVSLCKAGE))
787 	{
788 	    if (CVS_RMDIR (masterlock) >= 0)
789 		continue;
790 	}
791 #endif
792 
793 	/* set the lockers name */
794 	set_lockers_name (&sb);
795 
796 	/* if he wasn't willing to wait, return an error */
797 	if (!will_wait)
798 	    return (L_LOCKED);
799 	lock_wait (lock->repository);
800 	waited = 1;
801     }
802 }
803 
804 /*
805  * Clear master lock.  We don't have to recompute the lock name since
806  * clear_lock is never called except after a successful set_lock().
807  */
808 static void
809 clear_lock (lock)
810     struct lock *lock;
811 {
812     SIG_beginCrSect ();
813     if (CVS_RMDIR (masterlock) < 0)
814 	error (0, errno, "failed to remove lock dir `%s'", masterlock);
815     lock->have_lckdir = 0;
816     SIG_endCrSect ();
817 }
818 
819 /*
820  * Print out a message that the lock is still held, then sleep a while.
821  */
822 static void
823 lock_wait (repos)
824     char *repos;
825 {
826     time_t now;
827     char *msg;
828 
829     (void) time (&now);
830     msg = xmalloc (100 + strlen (lockers_name) + strlen (repos));
831     sprintf (msg, "[%8.8s] waiting for %s's lock in %s", ctime (&now) + 11,
832 	     lockers_name, repos);
833     error (0, 0, "%s", msg);
834     /* Call cvs_flusherr to ensure that the user sees this message as
835        soon as possible.  */
836     cvs_flusherr ();
837     free (msg);
838     (void) sleep (CVSLCKSLEEP);
839 }
840 
841 /*
842  * Print out a message when we obtain a lock.
843  */
844 static void
845 lock_obtained (repos)
846      char *repos;
847 {
848     time_t now;
849     char *msg;
850 
851     (void) time (&now);
852     msg = xmalloc (100 + strlen (repos));
853     sprintf (msg, "[%8.8s] obtained lock in %s", ctime (&now) + 11, repos);
854     error (0, 0, "%s", msg);
855     /* Call cvs_flusherr to ensure that the user sees this message as
856        soon as possible.  */
857     cvs_flusherr ();
858     free (msg);
859 }
860 
861 static int lock_filesdoneproc PROTO ((void *callerdat, int err,
862 				      char *repository, char *update_dir,
863 				      List *entries));
864 
865 /*
866  * Create a list of repositories to lock
867  */
868 /* ARGSUSED */
869 static int
870 lock_filesdoneproc (callerdat, err, repository, update_dir, entries)
871     void *callerdat;
872     int err;
873     char *repository;
874     char *update_dir;
875     List *entries;
876 {
877     Node *p;
878 
879     p = getnode ();
880     p->type = LOCK;
881     p->key = xstrdup (repository);
882     p->data = xmalloc (sizeof (struct lock));
883     ((struct lock *)p->data)->repository = p->key;
884     ((struct lock *)p->data)->have_lckdir = 0;
885 
886     /* FIXME-KRP: this error condition should not simply be passed by. */
887     if (p->key == NULL || addnode (lock_tree_list, p) != 0)
888 	freenode (p);
889     return (err);
890 }
891 
892 void
893 lock_tree_for_write (argc, argv, local, aflag)
894     int argc;
895     char **argv;
896     int local;
897     int aflag;
898 {
899     int err;
900     /*
901      * Run the recursion processor to find all the dirs to lock and lock all
902      * the dirs
903      */
904     lock_tree_list = getlist ();
905     err = start_recursion ((FILEPROC) NULL, lock_filesdoneproc,
906 			   (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc,
907 			   argv, local, W_LOCAL, aflag, 0, (char *) NULL, 0);
908     sortlist (lock_tree_list, fsortcmp);
909     if (Writer_Lock (lock_tree_list) != 0)
910 	error (1, 0, "lock failed - giving up");
911 }
912 
913 /* Lock a single directory in REPOSITORY.  It is OK to call this if
914    a lock has been set with lock_dir_for_write; the new lock will replace
915    the old one.  If REPOSITORY is NULL, don't do anything.  */
916 void
917 lock_dir_for_write (repository)
918      char *repository;
919 {
920     if (repository != NULL
921 	&& (locked_dir == NULL
922 	    || strcmp (locked_dir, repository) != 0))
923     {
924 	Node *node;
925 
926 	if (locked_dir != NULL)
927 	    Lock_Cleanup ();
928 
929 	locked_dir = xstrdup (repository);
930 	locked_list = getlist ();
931 	node = getnode ();
932 	node->type = LOCK;
933 	node->key = xstrdup (repository);
934 	node->data = xmalloc (sizeof (struct lock));
935 	((struct lock *)node->data)->repository = node->key;
936 	((struct lock *)node->data)->have_lckdir = 0;
937 
938 	(void) addnode (locked_list, node);
939 	Writer_Lock (locked_list);
940     }
941 }
942