xref: /openbsd/gnu/usr.bin/cvs/src/lock.c (revision 7b36286a)
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 (current_parsed_root != NULL);
176 	assert (current_parsed_root->directory != NULL);
177 	assert (strncmp (repository, current_parsed_root->directory,
178 			 strlen (current_parsed_root->directory)) == 0);
179 	short_repos = repository + strlen (current_parsed_root->directory) + 1;
180 
181 	if (strcmp (repository, current_parsed_root->directory) == 0)
182 	    short_repos = ".";
183 	else
184 	    assert (short_repos[-1] == '/');
185 
186 	retval = xmalloc (strlen (lock_dir)
187 			  + strlen (short_repos)
188 			  + strlen (name)
189 			  + 10);
190 	strcpy (retval, lock_dir);
191 	q = retval + strlen (retval);
192 	*q++ = '/';
193 
194 	strcpy (q, short_repos);
195 
196 	/* In the common case, where the directory already exists, let's
197 	   keep it to one system call.  */
198 	if (CVS_STAT (retval, &sb) < 0)
199 	{
200 	    /* If we need to be creating more than one directory, we'll
201 	       get the existence_error here.  */
202 	    if (!existence_error (errno))
203 		error (1, errno, "cannot stat directory %s", retval);
204 	}
205 	else
206 	{
207 	    if (S_ISDIR (sb.st_mode))
208 		goto created;
209 	    else
210 		error (1, 0, "%s is not a directory", retval);
211 	}
212 
213 	/* Now add the directories one at a time, so we can create
214 	   them if needed.
215 
216 	   The idea behind the new_mode stuff is that the directory we
217 	   end up creating will inherit permissions from its parent
218 	   directory (we re-set new_mode with each EEXIST).  CVSUMASK
219 	   isn't right, because typically the reason for LockDir is to
220 	   use a different set of permissions.  We probably want to
221 	   inherit group ownership also (but we don't try to deal with
222 	   that, some systems do it for us either always or when g+s is on).
223 
224 	   We don't try to do anything about the permissions on the lock
225 	   files themselves.  The permissions don't really matter so much
226 	   because the locks will generally be removed by the process
227 	   which created them.  */
228 
229 	if (CVS_STAT (lock_dir, &sb) < 0)
230 	    error (1, errno, "cannot stat %s", lock_dir);
231 	new_mode = sb.st_mode;
232 	save_umask = umask (0000);
233 	saved_umask = 1;
234 
235 	p = short_repos;
236 	while (1)
237 	{
238 	    while (!ISDIRSEP (*p) && *p != '\0')
239 		++p;
240 	    if (ISDIRSEP (*p))
241 	    {
242 		strncpy (q, short_repos, p - short_repos);
243 		q[p - short_repos] = '\0';
244 		if (!ISDIRSEP (q[p - short_repos - 1])
245 		    && CVS_MKDIR (retval, new_mode) < 0)
246 		{
247 		    int saved_errno = errno;
248 		    if (saved_errno != EEXIST)
249 			error (1, errno, "cannot make directory %s", retval);
250 		    else
251 		    {
252 			if (CVS_STAT (retval, &sb) < 0)
253 			    error (1, errno, "cannot stat %s", retval);
254 			new_mode = sb.st_mode;
255 		    }
256 		}
257 		++p;
258 	    }
259 	    else
260 	    {
261 		strcpy (q, short_repos);
262 		if (CVS_MKDIR (retval, new_mode) < 0
263 		    && errno != EEXIST)
264 		    error (1, errno, "cannot make directory %s", retval);
265 		goto created;
266 	    }
267 	}
268     created:;
269 
270 	strcat (retval, "/");
271 	strcat (retval, name);
272 
273 	if (saved_umask)
274 	{
275 	    assert (umask (save_umask) == 0000);
276 	    saved_umask = 0;
277 	}
278     }
279     return retval;
280 }
281 
282 /*
283  * Clean up all outstanding locks
284  */
285 void
286 Lock_Cleanup ()
287 {
288     /* FIXME: error handling here is kind of bogus; we sometimes will call
289        error, which in turn can call us again.  For the moment work around
290        this by refusing to reenter this function (this is a kludge).  */
291     /* FIXME-reentrancy: the workaround isn't reentrant.  */
292     static int in_lock_cleanup = 0;
293 
294     if (in_lock_cleanup)
295 	return;
296     in_lock_cleanup = 1;
297 
298     remove_locks ();
299 
300     dellist (&lock_tree_list);
301 
302     if (locked_dir != NULL)
303     {
304 	dellist (&locked_list);
305 	free (locked_dir);
306 	locked_dir = NULL;
307 	locked_list = NULL;
308     }
309     in_lock_cleanup = 0;
310 }
311 
312 /*
313  * Remove locks without discarding the lock information
314  */
315 static void
316 remove_locks ()
317 {
318     /* clean up simple locks (if any) */
319     if (global_readlock.repository != NULL)
320     {
321 	lock_simple_remove (&global_readlock);
322 	global_readlock.repository = NULL;
323     }
324 
325     /* clean up multiple locks (if any) */
326     if (locklist != (List *) NULL)
327     {
328 	(void) walklist (locklist, unlock_proc, NULL);
329 	locklist = (List *) NULL;
330     }
331 }
332 
333 /*
334  * walklist proc for removing a list of locks
335  */
336 static int
337 unlock_proc (p, closure)
338     Node *p;
339     void *closure;
340 {
341     lock_simple_remove ((struct lock *)p->data);
342     return (0);
343 }
344 
345 /* Remove the lock files.  */
346 static void
347 lock_simple_remove (lock)
348     struct lock *lock;
349 {
350     char *tmp;
351 
352     /* If readlock is set, the lock directory *might* have been created, but
353        since Reader_Lock doesn't use SIG_beginCrSect the way that set_lock
354        does, we don't know that.  That is why we need to check for
355        existence_error here.  */
356     if (readlock != NULL)
357     {
358 	tmp = lock_name (lock->repository, readlock);
359 	if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
360 	    error (0, errno, "failed to remove lock %s", tmp);
361 	free (tmp);
362     }
363 
364     /* If writelock is set, the lock directory *might* have been created, but
365        since write_lock doesn't use SIG_beginCrSect the way that set_lock
366        does, we don't know that.  That is why we need to check for
367        existence_error here.  */
368     if (writelock != NULL)
369     {
370 	tmp = lock_name (lock->repository, writelock);
371 	if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
372 	    error (0, errno, "failed to remove lock %s", tmp);
373 	free (tmp);
374     }
375 
376     if (lock->have_lckdir)
377     {
378 	tmp = lock_name (lock->repository, CVSLCK);
379 	SIG_beginCrSect ();
380 	if (CVS_RMDIR (tmp) < 0)
381 	    error (0, errno, "failed to remove lock dir %s", tmp);
382 	lock->have_lckdir = 0;
383 	SIG_endCrSect ();
384 	free (tmp);
385     }
386 }
387 
388 /*
389  * Create a lock file for readers
390  */
391 int
392 Reader_Lock (xrepository)
393     char *xrepository;
394 {
395     int err = 0;
396     FILE *fp;
397     char *tmp;
398 
399     if (noexec || readonlyfs)
400 	return (0);
401 
402     /* we only do one directory at a time for read locks! */
403     if (global_readlock.repository != NULL)
404     {
405 	error (0, 0, "Reader_Lock called while read locks set - Help!");
406 	return (1);
407     }
408 
409     if (readlock == NULL)
410     {
411 	readlock = xmalloc (strlen (hostname) + sizeof (CVSRFL) + 40);
412 	(void) sprintf (readlock,
413 #ifdef HAVE_LONG_FILE_NAMES
414 			"%s.%s.%ld", CVSRFL, hostname,
415 #else
416 			"%s.%ld", CVSRFL,
417 #endif
418 			(long) getpid ());
419     }
420 
421     /* remember what we're locking (for Lock_Cleanup) */
422     global_readlock.repository = xrepository;
423 
424     /* get the lock dir for our own */
425     if (set_lock (&global_readlock, 1) != L_OK)
426     {
427 	error (0, 0, "failed to obtain dir lock in repository `%s'",
428 	       xrepository);
429 	if (readlock != NULL)
430 	    free (readlock);
431 	readlock = NULL;
432 	/* We don't set global_readlock.repository to NULL.  I think this
433 	   only works because recurse.c will give a fatal error if we return
434 	   a nonzero value.  */
435 	return (1);
436     }
437 
438     /* write a read-lock */
439     tmp = lock_name (xrepository, readlock);
440     if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF)
441     {
442 	error (0, errno, "cannot create read lock in repository `%s'",
443 	       xrepository);
444 	if (readlock != NULL)
445 	    free (readlock);
446 	readlock = NULL;
447 	err = 1;
448     }
449     free (tmp);
450 
451     /* free the lock dir */
452     clear_lock (&global_readlock);
453 
454     return (err);
455 }
456 
457 /*
458  * Lock a list of directories for writing
459  */
460 static char *lock_error_repos;
461 static int lock_error;
462 
463 static int Writer_Lock PROTO ((List * list));
464 
465 static int
466 Writer_Lock (list)
467     List *list;
468 {
469     char *wait_repos;
470 
471     if (noexec)
472 	return (0);
473 
474     /* We only know how to do one list at a time */
475     if (locklist != (List *) NULL)
476     {
477 	error (0, 0, "Writer_Lock called while write locks set - Help!");
478 	return (1);
479     }
480 
481     wait_repos = NULL;
482     for (;;)
483     {
484 	/* try to lock everything on the list */
485 	lock_error = L_OK;		/* init for set_writelock_proc */
486 	lock_error_repos = (char *) NULL; /* init for set_writelock_proc */
487 	locklist = list;		/* init for Lock_Cleanup */
488 	if (lockers_name != NULL)
489 	    free (lockers_name);
490 	lockers_name = xstrdup ("unknown");
491 
492 	(void) walklist (list, set_writelock_proc, NULL);
493 
494 	switch (lock_error)
495 	{
496 	    case L_ERROR:		/* Real Error */
497 		if (wait_repos != NULL)
498 		    free (wait_repos);
499 		Lock_Cleanup ();	/* clean up any locks we set */
500 		error (0, 0, "lock failed - giving up");
501 		return (1);
502 
503 	    case L_LOCKED:		/* Someone already had a lock */
504 		remove_locks ();	/* clean up any locks we set */
505 		lock_wait (lock_error_repos); /* sleep a while and try again */
506 		wait_repos = xstrdup (lock_error_repos);
507 		continue;
508 
509 	    case L_OK:			/* we got the locks set */
510 	        if (wait_repos != NULL)
511 		{
512 		    lock_obtained (wait_repos);
513 		    free (wait_repos);
514 		}
515 		return (0);
516 
517 	    default:
518 		if (wait_repos != NULL)
519 		    free (wait_repos);
520 		error (0, 0, "unknown lock status %d in Writer_Lock",
521 		       lock_error);
522 		return (1);
523 	}
524     }
525 }
526 
527 /*
528  * walklist proc for setting write locks
529  */
530 static int
531 set_writelock_proc (p, closure)
532     Node *p;
533     void *closure;
534 {
535     /* if some lock was not OK, just skip this one */
536     if (lock_error != L_OK)
537 	return (0);
538 
539     /* apply the write lock */
540     lock_error_repos = p->key;
541     lock_error = write_lock ((struct lock *)p->data);
542     return (0);
543 }
544 
545 /*
546  * Create a lock file for writers returns L_OK if lock set ok, L_LOCKED if
547  * lock held by someone else or L_ERROR if an error occurred
548  */
549 static int
550 write_lock (lock)
551     struct lock *lock;
552 {
553     int status;
554     FILE *fp;
555     char *tmp;
556 
557     if (writelock == NULL)
558     {
559 	writelock = xmalloc (strlen (hostname) + sizeof (CVSWFL) + 40);
560 	(void) sprintf (writelock,
561 #ifdef HAVE_LONG_FILE_NAMES
562 			"%s.%s.%ld", CVSWFL, hostname,
563 #else
564 			"%s.%ld", CVSWFL,
565 #endif
566 			(long) getpid());
567     }
568 
569     /* make sure the lock dir is ours (not necessarily unique to us!) */
570     status = set_lock (lock, 0);
571     if (status == L_OK)
572     {
573 	/* we now own a writer - make sure there are no readers */
574 	if (readers_exist (lock->repository))
575 	{
576 	    /* clean up the lock dir if we created it */
577 	    if (status == L_OK)
578 	    {
579 		clear_lock (lock);
580 	    }
581 
582 	    /* indicate we failed due to read locks instead of error */
583 	    return (L_LOCKED);
584 	}
585 
586 	/* write the write-lock file */
587 	tmp = lock_name (lock->repository, writelock);
588 	if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF)
589 	{
590 	    int xerrno = errno;
591 
592 	    if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
593 		error (0, errno, "failed to remove lock %s", tmp);
594 
595 	    /* free the lock dir if we created it */
596 	    if (status == L_OK)
597 	    {
598 		clear_lock (lock);
599 	    }
600 
601 	    /* return the error */
602 	    error (0, xerrno, "cannot create write lock in repository `%s'",
603 		   lock->repository);
604 	    free (tmp);
605 	    return (L_ERROR);
606 	}
607 	free (tmp);
608 	return (L_OK);
609     }
610     else
611 	return (status);
612 }
613 
614 /*
615  * readers_exist() returns 0 if there are no reader lock files remaining in
616  * the repository; else 1 is returned, to indicate that the caller should
617  * sleep a while and try again.
618  */
619 static int
620 readers_exist (repository)
621     char *repository;
622 {
623     char *line;
624     DIR *dirp;
625     struct dirent *dp;
626     struct stat sb;
627     int ret = 0;
628 
629 #ifdef CVS_FUDGELOCKS
630 again:
631 #endif
632 
633     if ((dirp = CVS_OPENDIR (repository)) == NULL)
634 	error (1, 0, "cannot open directory %s", repository);
635 
636     errno = 0;
637     while ((dp = CVS_READDIR (dirp)) != NULL)
638     {
639 	if (CVS_FNMATCH (CVSRFLPAT, dp->d_name, 0) == 0)
640 	{
641 #ifdef CVS_FUDGELOCKS
642 	    time_t now;
643 	    (void) time (&now);
644 #endif
645 
646 	    line = xmalloc (strlen (repository) + strlen (dp->d_name) + 5);
647 	    (void) sprintf (line, "%s/%s", repository, dp->d_name);
648 	    if ( CVS_STAT (line, &sb) != -1)
649 	    {
650 #ifdef CVS_FUDGELOCKS
651 		/*
652 		 * If the create time of the file is more than CVSLCKAGE
653 		 * seconds ago, try to clean-up the lock file, and if
654 		 * successful, re-open the directory and try again.
655 		 */
656 		if (now >= (sb.st_ctime + CVSLCKAGE) && CVS_UNLINK (line) != -1)
657 		{
658 		    (void) CVS_CLOSEDIR (dirp);
659 		    free (line);
660 		    goto again;
661 		}
662 #endif
663 		set_lockers_name (&sb);
664 	    }
665 	    else
666 	    {
667 		/* If the file doesn't exist, it just means that it disappeared
668 		   between the time we did the readdir and the time we did
669 		   the stat.  */
670 		if (!existence_error (errno))
671 		    error (0, errno, "cannot stat %s", line);
672 	    }
673 	    errno = 0;
674 	    free (line);
675 
676 	    ret = 1;
677 	    break;
678 	}
679 	errno = 0;
680     }
681     if (errno != 0)
682 	error (0, errno, "error reading directory %s", repository);
683 
684     CVS_CLOSEDIR (dirp);
685     return (ret);
686 }
687 
688 /*
689  * Set the static variable lockers_name appropriately, based on the stat
690  * structure passed in.
691  */
692 static void
693 set_lockers_name (statp)
694     struct stat *statp;
695 {
696     struct passwd *pw;
697 
698     if (lockers_name != NULL)
699 	free (lockers_name);
700     if ((pw = (struct passwd *) getpwuid (statp->st_uid)) !=
701 	(struct passwd *) NULL)
702     {
703 	lockers_name = xstrdup (pw->pw_name);
704     }
705     else
706     {
707 	lockers_name = xmalloc (20);
708 	(void) sprintf (lockers_name, "uid%lu", (unsigned long) statp->st_uid);
709     }
710 }
711 
712 /*
713  * Persistently tries to make the directory "lckdir",, which serves as a
714  * lock. If the create time on the directory is greater than CVSLCKAGE
715  * seconds old, just try to remove the directory.
716  */
717 static int
718 set_lock (lock, will_wait)
719     struct lock *lock;
720     int will_wait;
721 {
722     int waited;
723     struct stat sb;
724     mode_t omask;
725 #ifdef CVS_FUDGELOCKS
726     time_t now;
727 #endif
728 
729     if (masterlock != NULL)
730 	free (masterlock);
731     masterlock = lock_name (lock->repository, CVSLCK);
732 
733     /*
734      * Note that it is up to the callers of set_lock() to arrange for signal
735      * handlers that do the appropriate things, like remove the lock
736      * directory before they exit.
737      */
738     waited = 0;
739     lock->have_lckdir = 0;
740     for (;;)
741     {
742 	int status = -1;
743 	omask = umask (cvsumask);
744 	SIG_beginCrSect ();
745 	if (CVS_MKDIR (masterlock, 0777) == 0)
746 	{
747 	    lock->have_lckdir = 1;
748 	    SIG_endCrSect ();
749 	    status = L_OK;
750 	    if (waited)
751 	        lock_obtained (lock->repository);
752 	    goto out;
753 	}
754 	SIG_endCrSect ();
755       out:
756 	(void) umask (omask);
757 	if (status != -1)
758 	    return status;
759 
760 	if (errno != EEXIST)
761 	{
762 	    error (0, errno,
763 		   "failed to create lock directory for `%s' (%s)",
764 		   lock->repository, masterlock);
765 	    return (L_ERROR);
766 	}
767 
768 	/* Find out who owns the lock.  If the lock directory is
769 	   non-existent, re-try the loop since someone probably just
770 	   removed it (thus releasing the lock).  */
771 	if (CVS_STAT (masterlock, &sb) < 0)
772 	{
773 	    if (existence_error (errno))
774 		continue;
775 
776 	    error (0, errno, "couldn't stat lock directory `%s'", masterlock);
777 	    return (L_ERROR);
778 	}
779 
780 #ifdef CVS_FUDGELOCKS
781 	/*
782 	 * If the create time of the directory is more than CVSLCKAGE seconds
783 	 * ago, try to clean-up the lock directory, and if successful, just
784 	 * quietly retry to make it.
785 	 */
786 	(void) time (&now);
787 	if (now >= (sb.st_ctime + CVSLCKAGE))
788 	{
789 	    if (CVS_RMDIR (masterlock) >= 0)
790 		continue;
791 	}
792 #endif
793 
794 	/* set the lockers name */
795 	set_lockers_name (&sb);
796 
797 	/* if he wasn't willing to wait, return an error */
798 	if (!will_wait)
799 	    return (L_LOCKED);
800 	lock_wait (lock->repository);
801 	waited = 1;
802     }
803 }
804 
805 /*
806  * Clear master lock.  We don't have to recompute the lock name since
807  * clear_lock is never called except after a successful set_lock().
808  */
809 static void
810 clear_lock (lock)
811     struct lock *lock;
812 {
813     SIG_beginCrSect ();
814     if (CVS_RMDIR (masterlock) < 0)
815 	error (0, errno, "failed to remove lock dir `%s'", masterlock);
816     lock->have_lckdir = 0;
817     SIG_endCrSect ();
818 }
819 
820 /*
821  * Print out a message that the lock is still held, then sleep a while.
822  */
823 static void
824 lock_wait (repos)
825     char *repos;
826 {
827     time_t now;
828     char *msg;
829 
830     (void) time (&now);
831     msg = xmalloc (100 + strlen (lockers_name) + strlen (repos));
832     sprintf (msg, "[%8.8s] waiting for %s's lock in %s", ctime (&now) + 11,
833 	     lockers_name, repos);
834     error (0, 0, "%s", msg);
835     /* Call cvs_flusherr to ensure that the user sees this message as
836        soon as possible.  */
837     cvs_flusherr ();
838     free (msg);
839     (void) sleep (CVSLCKSLEEP);
840 }
841 
842 /*
843  * Print out a message when we obtain a lock.
844  */
845 static void
846 lock_obtained (repos)
847      char *repos;
848 {
849     time_t now;
850     char *msg;
851 
852     (void) time (&now);
853     msg = xmalloc (100 + strlen (repos));
854     sprintf (msg, "[%8.8s] obtained lock in %s", ctime (&now) + 11, repos);
855     error (0, 0, "%s", msg);
856     /* Call cvs_flusherr to ensure that the user sees this message as
857        soon as possible.  */
858     cvs_flusherr ();
859     free (msg);
860 }
861 
862 static int lock_filesdoneproc PROTO ((void *callerdat, int err,
863 				      char *repository, char *update_dir,
864 				      List *entries));
865 
866 /*
867  * Create a list of repositories to lock
868  */
869 /* ARGSUSED */
870 static int
871 lock_filesdoneproc (callerdat, err, repository, update_dir, entries)
872     void *callerdat;
873     int err;
874     char *repository;
875     char *update_dir;
876     List *entries;
877 {
878     Node *p;
879 
880     p = getnode ();
881     p->type = LOCK;
882     p->key = xstrdup (repository);
883     p->data = xmalloc (sizeof (struct lock));
884     ((struct lock *)p->data)->repository = p->key;
885     ((struct lock *)p->data)->have_lckdir = 0;
886 
887     /* FIXME-KRP: this error condition should not simply be passed by. */
888     if (p->key == NULL || addnode (lock_tree_list, p) != 0)
889 	freenode (p);
890     return (err);
891 }
892 
893 void
894 lock_tree_for_write (argc, argv, local, which, aflag)
895     int argc;
896     char **argv;
897     int local;
898     int which;
899     int aflag;
900 {
901     int err;
902     /*
903      * Run the recursion processor to find all the dirs to lock and lock all
904      * the dirs
905      */
906     lock_tree_list = getlist ();
907     err = start_recursion ((FILEPROC) NULL, lock_filesdoneproc,
908 			   (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc,
909 			   argv, local, which, aflag, 0, (char *) NULL, 0);
910     sortlist (lock_tree_list, fsortcmp);
911     if (Writer_Lock (lock_tree_list) != 0)
912 	error (1, 0, "lock failed - giving up");
913 }
914 
915 /* Lock a single directory in REPOSITORY.  It is OK to call this if
916    a lock has been set with lock_dir_for_write; the new lock will replace
917    the old one.  If REPOSITORY is NULL, don't do anything.  */
918 void
919 lock_dir_for_write (repository)
920      char *repository;
921 {
922     if (repository != NULL
923 	&& (locked_dir == NULL
924 	    || strcmp (locked_dir, repository) != 0))
925     {
926 	Node *node;
927 
928 	if (locked_dir != NULL)
929 	    Lock_Cleanup ();
930 
931 	locked_dir = xstrdup (repository);
932 	locked_list = getlist ();
933 	node = getnode ();
934 	node->type = LOCK;
935 	node->key = xstrdup (repository);
936 	node->data = xmalloc (sizeof (struct lock));
937 	((struct lock *)node->data)->repository = node->key;
938 	((struct lock *)node->data)->have_lckdir = 0;
939 
940 	(void) addnode (locked_list, node);
941 	Writer_Lock (locked_list);
942     }
943 }
944