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