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 
18 /* The node Concurrency in doc/cvs.texinfo has a brief introduction to
19    how CVS locks function, and some of the user-visible consequences of
20    their existence.  Here is a summary of why they exist (and therefore,
21    the consequences of hacking CVS to read a repository without creating
22    locks):
23 
24    There are two uses.  One is the ability to prevent there from being
25    two writers at the same time.  This is necessary for any number of
26    reasons (fileattr code, probably others).  Commit needs to lock the
27    whole tree so that nothing happens between the up-to-date check and
28    the actual checkin.
29 
30    The second use is the ability to ensure that there is not a writer
31    and a reader at the same time (several readers are allowed).  Reasons
32    for this are:
33 
34    * Readlocks ensure that once CVS has found a collection of rcs
35    files using Find_Names, the files will still exist when it reads
36    them (they may have moved in or out of the attic).
37 
38    * Readlocks provide some modicum of consistency, although this is
39    kind of limited--see the node Concurrency in cvs.texinfo.
40 
41    * Readlocks ensure that the RCS file does not change between
42    RCS_parse and RCS_reparsercsfile time.  This one strikes me as
43    important, although I haven't thought up what bad scenarios might
44    be.
45 
46    * Readlocks ensure that we won't find the file in the state in
47    which it is in between the calls to add_rcs_file and RCS_checkin in
48    commit.c (when a file is being added).  This state is a state in
49    which the RCS file parsing routines in rcs.c cannot parse the file.
50 
51    * Readlocks ensure that a reader won't try to look at a
52    half-written fileattr file (fileattr is not updated atomically).
53 
54    (see also the description of anonymous read-only access in
55    "Password authentication security" node in doc/cvs.texinfo).
56 
57    While I'm here, I'll try to summarize a few random suggestions
58    which periodically get made about how locks might be different:
59 
60    1.  Check for EROFS.  Maybe useful, although in the presence of NFS
61    EROFS does *not* mean that the file system is unchanging.
62 
63    2.  Provide an option to disable locks for operations which only
64    read (see above for some of the consequences).
65 
66    3.  Have a server internally do the locking.  Probably a good
67    long-term solution, and many people have been working hard on code
68    changes which would eventually make it possible to have a server
69    which can handle various connections in one process, but there is
70    much, much work still to be done before this is feasible.  */
71 
72 #include "cvs.h"
73 
74 __RCSID("$MirOS: src/gnu/usr.bin/cvs/src/lock.c,v 1.6 2010/09/19 19:43:05 tg Exp $");
75 
76 
77 struct lock {
78     /* This is the directory in which we may have a lock named by the
79        readlock variable, a lock named by the writelock variable, and/or
80        a lock named CVSLCK.  The storage is not allocated along with the
81        struct lock; it is allocated by the Reader_Lock caller or in the
82        case of promotablelocks, it is just a pointer to the storage allocated
83        for the ->key field.  */
84     const char *repository;
85 
86     /* The name of the lock files. */
87     char *file1;
88 #ifdef LOCK_COMPATIBILITY
89     char *file2;
90 #endif /* LOCK_COMPATIBILITY */
91 
92     /* The name of the master lock dir.  Usually CVSLCK.  */
93     const char *lockdirname;
94 
95     /* The full path to the lock dir, if we are currently holding it.
96      *
97      * This will be LOCKDIRNAME catted onto REPOSITORY.  We waste a little
98      * space by storing it, but save a later malloc/free.
99      */
100     char *lockdir;
101 
102     /* Note there is no way of knowing whether the readlock and writelock
103        exist.  The code which sets the locks doesn't use SIG_beginCrSect
104        to set a flag like we do for CVSLCK.  */
105     bool free_repository;
106 };
107 
108 static void remove_locks (void);
109 static int set_lock (struct lock *lock, int will_wait);
110 static void clear_lock (struct lock *lock);
111 static void set_lockers_name (struct stat *statp);
112 
113 /* Malloc'd array containing the username of the whoever has the lock.
114    Will always be non-NULL in the cases where it is needed.  */
115 static char *lockers_name;
116 /* Malloc'd array specifying name of a readlock within a directory.
117    Or NULL if none.  */
118 static char *readlock;
119 /* Malloc'd array specifying name of a writelock within a directory.
120    Or NULL if none.  */
121 static char *writelock;
122 /* Malloc'd array specifying name of a promotablelock within a directory.
123    Or NULL if none.  */
124 static char *promotablelock;
125 static List *locklist;
126 
127 #define L_OK		0		/* success */
128 #define L_ERROR		1		/* error condition */
129 #define L_LOCKED	2		/* lock owned by someone else */
130 
131 /* This is the (single) readlock which is set by Reader_Lock.  The
132    repository field is NULL if there is no such lock.  */
133 #ifdef LOCK_COMPATIBILITY
134 static struct lock global_readlock = {NULL, NULL, NULL, CVSLCK, NULL, false};
135 static struct lock global_writelock = {NULL, NULL, NULL, CVSLCK, NULL, false};
136 
137 static struct lock global_history_lock = {NULL, NULL, NULL, CVSHISTORYLCK,
138 					  NULL, false};
139 static struct lock global_val_tags_lock = {NULL, NULL, NULL, CVSVALTAGSLCK,
140 					   NULL, false};
141 #else
142 static struct lock global_readlock = {NULL, NULL, CVSLCK, NULL, false};
143 static struct lock global_writelock = {NULL, NULL, CVSLCK, NULL, false};
144 
145 static struct lock global_history_lock = {NULL, NULL, CVSHISTORYLCK, NULL,
146 					  false};
147 static struct lock global_val_tags_lock = {NULL, NULL, CVSVALTAGSLCK, NULL,
148 					   false};
149 #endif /* LOCK_COMPATIBILITY */
150 
151 /* List of locks set by lock_tree_for_write.  This is redundant
152    with locklist, sort of.  */
153 static List *lock_tree_list;
154 
155 
156 
157 /* Return a newly malloc'd string containing the name of the lock for the
158    repository REPOSITORY and the lock file name within that directory
159    NAME.  Also create the directories in which to put the lock file
160    if needed (if we need to, could save system call(s) by doing
161    that only if the actual operation fails.  But for now we'll keep
162    things simple).  */
163 static char *
lock_name(const char * repository,const char * name)164 lock_name (const char *repository, const char *name)
165 {
166     char *retval;
167     const char *p;
168     char *q;
169     const char *short_repos;
170     mode_t save_umask = 0000;
171     int saved_umask = 0;
172 
173     TRACE (TRACE_FLOW, "lock_name (%s, %s)",
174 	   repository  ? repository : "(null)", name ? name : "(null)");
175 
176     if (!config->lock_dir)
177     {
178 	/* This is the easy case.  Because the lock files go directly
179 	   in the repository, no need to create directories or anything.  */
180 	assert (name != NULL);
181 	assert (repository != NULL);
182 	retval = Xasprintf ("%s/%s", repository, name);
183     }
184     else
185     {
186 	struct stat sb;
187 	mode_t new_mode = 0;
188 
189 	/* The interesting part of the repository is the part relative
190 	   to CVSROOT.  */
191 	assert (current_parsed_root != NULL);
192 	assert (current_parsed_root->directory != NULL);
193 	assert (strncmp (repository, current_parsed_root->directory,
194 			 strlen (current_parsed_root->directory)) == 0);
195 	short_repos = repository + strlen (current_parsed_root->directory) + 1;
196 
197 	if (strcmp (repository, current_parsed_root->directory) == 0)
198 	    short_repos = ".";
199 	else
200 	    assert (short_repos[-1] == '/');
201 
202 	retval = xmalloc (strlen (config->lock_dir)
203 			  + strlen (short_repos)
204 			  + strlen (name)
205 			  + 10);
206 	strcpy (retval, config->lock_dir);
207 	q = retval + strlen (retval);
208 	*q++ = '/';
209 
210 	strcpy (q, short_repos);
211 
212 	/* In the common case, where the directory already exists, let's
213 	   keep it to one system call.  */
214 	if (stat (retval, &sb) < 0)
215 	{
216 	    /* If we need to be creating more than one directory, we'll
217 	       get the existence_error here.  */
218 	    if (!existence_error (errno))
219 		error (1, errno, "cannot stat directory %s", retval);
220 	}
221 	else
222 	{
223 	    if (S_ISDIR (sb.st_mode))
224 		goto created;
225 	    else
226 		error (1, 0, "%s is not a directory", retval);
227 	}
228 
229 	/* Now add the directories one at a time, so we can create
230 	   them if needed.
231 
232 	   The idea behind the new_mode stuff is that the directory we
233 	   end up creating will inherit permissions from its parent
234 	   directory (we re-set new_mode with each EEXIST).  CVSUMASK
235 	   isn't right, because typically the reason for LockDir is to
236 	   use a different set of permissions.  We probably want to
237 	   inherit group ownership also (but we don't try to deal with
238 	   that, some systems do it for us either always or when g+s is on).
239 
240 	   We don't try to do anything about the permissions on the lock
241 	   files themselves.  The permissions don't really matter so much
242 	   because the locks will generally be removed by the process
243 	   which created them.  */
244 
245 	if (stat (config->lock_dir, &sb) < 0)
246 	    error (1, errno, "cannot stat %s", config->lock_dir);
247 	new_mode = sb.st_mode;
248 	save_umask = umask (0000);
249 	saved_umask = 1;
250 
251 	p = short_repos;
252 	while (1)
253 	{
254 	    while (!ISSLASH (*p) && *p != '\0')
255 		++p;
256 	    if (ISSLASH (*p))
257 	    {
258 		strncpy (q, short_repos, p - short_repos);
259 		q[p - short_repos] = '\0';
260 		if (!ISSLASH (q[p - short_repos - 1])
261 		    && CVS_MKDIR (retval, new_mode) < 0)
262 		{
263 		    int saved_errno = errno;
264 		    if (saved_errno != EEXIST)
265 			error (1, errno, "cannot make directory %s", retval);
266 		    else
267 		    {
268 			if (stat (retval, &sb) < 0)
269 			    error (1, errno, "cannot stat %s", retval);
270 			new_mode = sb.st_mode;
271 		    }
272 		}
273 		++p;
274 	    }
275 	    else
276 	    {
277 		strcpy (q, short_repos);
278 		if (CVS_MKDIR (retval, new_mode) < 0
279 		    && errno != EEXIST)
280 		    error (1, errno, "cannot make directory %s", retval);
281 		goto created;
282 	    }
283 	}
284     created:;
285 
286 	strcat (retval, "/");
287 	strcat (retval, name);
288 
289 	if (saved_umask)
290 	{
291 	    assert (umask (save_umask) == 0000);
292 	    saved_umask = 0;
293 	}
294     }
295     return retval;
296 }
297 
298 
299 
300 /* Remove the lock files.  For interrupt purposes, it can be assumed that the
301  * first thing this function does is set lock->repository to NULL.
302  *
303  * INPUTS
304  *   lock	The lock to remove.
305  *   free	True if this lock directory will not be reused (free
306  *		lock->repository if necessary).
307  */
308 static void
remove_lock_files(struct lock * lock,bool free_repository)309 remove_lock_files (struct lock *lock, bool free_repository)
310 {
311     TRACE (TRACE_FLOW, "remove_lock_files (%s)", lock->repository);
312 
313     /* If lock->file is set, the lock *might* have been created, but since
314      * Reader_Lock & lock_dir_for_write don't use SIG_beginCrSect the way that
315      * set_lock does, we don't know that.  That is why we need to check for
316      * existence_error here.
317      */
318     if (lock->file1)
319     {
320 	char *tmp = lock->file1;
321 	lock->file1 = NULL;
322 	if (CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
323 	    error (0, errno, "failed to remove lock %s", tmp);
324 	free (tmp);
325     }
326 #ifdef LOCK_COMPATIBILITY
327     if (lock->file2)
328     {
329 	char *tmp = lock->file2;
330 	lock->file2 = NULL;
331 	if (CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
332 	    error (0, errno, "failed to remove lock %s", tmp);
333 	free (tmp);
334     }
335 #endif /* LOCK_COMPATIBILITY */
336 
337     clear_lock (lock);
338 
339     /* And free the repository string.  We don't really have to set the
340      * repository string to NULL first since there is no harm in running any of
341      * the above code twice.
342      *
343      * Use SIG_beginCrSect since otherwise we might be interrupted between
344      * checking whether free_repository is set and freeing stuff.
345      */
346     if (free_repository)
347     {
348 	SIG_beginCrSect ();
349 	if (lock->free_repository)
350 	{
351 	    free ((char *)lock->repository);
352 	    lock->free_repository = false;
353 	}
354 	lock->repository = NULL;
355 	SIG_endCrSect ();
356     }
357 }
358 
359 
360 
361 /*
362  * Clean up outstanding read and write locks and free their storage.
363  */
364 void
Simple_Lock_Cleanup(void)365 Simple_Lock_Cleanup (void)
366 {
367     TRACE (TRACE_FUNCTION, "Simple_Lock_Cleanup()");
368 
369     /* Avoid interrupts while accessing globals the interrupt handlers might
370      * make use of.
371      */
372     SIG_beginCrSect();
373 
374     /* clean up simple read locks (if any) */
375     if (global_readlock.repository != NULL)
376 	remove_lock_files (&global_readlock, true);
377     /* See note in Lock_Cleanup() below.  */
378     SIG_endCrSect();
379 
380     SIG_beginCrSect();
381 
382     /* clean up simple write locks (if any) */
383     if (global_writelock.repository != NULL)
384 	remove_lock_files (&global_writelock, true);
385     /* See note in Lock_Cleanup() below.  */
386     SIG_endCrSect();
387 
388     SIG_beginCrSect();
389 
390     /* clean up simple write locks (if any) */
391     if (global_history_lock.repository)
392 	remove_lock_files (&global_history_lock, true);
393     SIG_endCrSect();
394 
395     SIG_beginCrSect();
396 
397     if (global_val_tags_lock.repository)
398 	remove_lock_files (&global_val_tags_lock, true);
399     /* See note in Lock_Cleanup() below.  */
400     SIG_endCrSect();
401 }
402 
403 
404 
405 /*
406  * Clean up all outstanding locks and free their storage.
407  *
408  * NOTES
409  *   This function needs to be reentrant since a call to exit() can cause a
410  *   call to this function, which can then be interrupted by a signal, which
411  *   can cause a second call to this function.
412  *
413  * RETURNS
414  *   Nothing.
415  */
416 void
Lock_Cleanup(void)417 Lock_Cleanup (void)
418 {
419     TRACE (TRACE_FUNCTION, "Lock_Cleanup()");
420 
421     /* FIXME: Do not perform buffered I/O from an interrupt handler like
422      * this (via error).  However, I'm leaving the error-calling code there
423      * in the hope that on the rare occasion the error call is actually made
424      * (e.g., a fluky I/O error or permissions problem prevents the deletion
425      * of a just-created file) reentrancy won't be an issue.
426      */
427 
428     remove_locks ();
429 
430     /* Avoid being interrupted during calls which set globals to NULL.  This
431      * avoids having interrupt handlers attempt to use these global variables
432      * in inconsistent states.
433      *
434      * This isn't always necessary, because sometimes we are called via exit()
435      * or the interrupt handler, in which case signals will already be blocked,
436      * but sometimes we might be called from elsewhere.
437      */
438     SIG_beginCrSect();
439     dellist (&lock_tree_list);
440     /*  Unblocking allows any signal to be processed as soon as possible.  This
441      *  isn't really necessary, but since we know signals can cause us to be
442      *  called, why not avoid having blocks of code run twice.
443      */
444     SIG_endCrSect();
445 }
446 
447 
448 
449 /*
450  * walklist proc for removing a list of locks
451  */
452 static int
unlock_proc(Node * p,void * closure)453 unlock_proc (Node *p, void *closure)
454 {
455     remove_lock_files (p->data, false);
456     return 0;
457 }
458 
459 
460 
461 /*
462  * Remove locks without discarding the lock information.
463  */
464 static void
remove_locks(void)465 remove_locks (void)
466 {
467     TRACE (TRACE_FLOW, "remove_locks()");
468 
469     Simple_Lock_Cleanup ();
470 
471     /* clean up promotable locks (if any) */
472     SIG_beginCrSect();
473     if (locklist != NULL)
474     {
475 	/* Use a tmp var since any of these functions could call exit, causing
476 	 * us to be called a second time.
477 	 */
478 	List *tmp = locklist;
479 	locklist = NULL;
480 	walklist (tmp, unlock_proc, NULL);
481     }
482     SIG_endCrSect();
483 }
484 
485 
486 
487 /*
488  * Set the global readlock variable if it isn't already.
489  */
490 static void
set_readlock_name(void)491 set_readlock_name (void)
492 {
493     if (readlock == NULL)
494     {
495 	readlock = Xasprintf (
496 #ifdef HAVE_LONG_FILE_NAMES
497 			      "%s.%s.%ld", CVSRFL, hostname,
498 #else
499 			      "%s.%ld", CVSRFL,
500 #endif
501 			      (long) getpid ());
502     }
503 }
504 
505 
506 
507 /*
508  * Create a lock file for readers
509  */
510 int
Reader_Lock(char * xrepository)511 Reader_Lock (char *xrepository)
512 {
513     int err = 0;
514     FILE *fp;
515 
516     TRACE (TRACE_FUNCTION, "Reader_Lock(%s)", xrepository);
517 
518     if (noexec || readonlyfs)
519 	return 0;
520 
521     /* we only do one directory at a time for read locks!  */
522     if (global_readlock.repository != NULL)
523     {
524 	error (0, 0, "Reader_Lock called while read locks set - Help!");
525 	return 1;
526     }
527 
528     set_readlock_name ();
529 
530     /* remember what we're locking (for Lock_Cleanup) */
531     global_readlock.repository = xstrdup (xrepository);
532     global_readlock.free_repository = true;
533 
534     /* get the lock dir for our own */
535     if (set_lock (&global_readlock, 1) != L_OK)
536     {
537 	error (0, 0, "failed to obtain dir lock in repository `%s'",
538 	       xrepository);
539 	if (readlock != NULL)
540 	    free (readlock);
541 	readlock = NULL;
542 	/* We don't set global_readlock.repository to NULL.  I think this
543 	   only works because recurse.c will give a fatal error if we return
544 	   a nonzero value.  */
545 	return 1;
546     }
547 
548     /* write a read-lock */
549     global_readlock.file1 = lock_name (xrepository, readlock);
550     if ((fp = CVS_FOPEN (global_readlock.file1, "w+")) == NULL
551 	|| fclose (fp) == EOF)
552     {
553 	error (0, errno, "cannot create read lock in repository `%s'",
554 	       xrepository);
555 	err = 1;
556     }
557 
558     /* free the lock dir */
559     clear_lock (&global_readlock);
560 
561     return err;
562 }
563 
564 
565 
566 /*
567  * lock_exists() returns 0 if there is no lock file matching FILEPAT in
568  * the repository but not IGNORE; else 1 is returned, to indicate that the
569  * caller should sleep a while and try again.
570  *
571  * INPUTS
572  *   repository		The repository directory to search for locks.
573  *   filepat		The file name pattern to search for.
574  *   ignore		The name of a single file which can be ignored.
575  *
576  * GLOBALS
577  *   lockdir		The lock dir external to the repository, if any.
578  *
579  * RETURNS
580  *   0		No lock matching FILEPAT and not IGNORE exists.
581  *   1		Otherwise and on error.
582  *
583  * ERRORS
584  *  In the case where errors are encountered reading the directory, a warning
585  *  message is printed, 1 is is returned and ERRNO is left set.
586  */
587 static int
lock_exists(const char * repository,const char * filepat,const char * ignore)588 lock_exists (const char *repository, const char *filepat, const char *ignore)
589 {
590     char *lockdir;
591     char *line;
592     DIR *dirp;
593     struct dirent *dp;
594     struct stat sb;
595     int ret;
596 #ifdef CVS_FUDGELOCKS
597     time_t now;
598     (void)time (&now);
599 #endif
600 
601     TRACE (TRACE_FLOW, "lock_exists (%s, %s, %s)",
602 	   repository, filepat, ignore ? ignore : "(null)");
603 
604     lockdir = lock_name (repository, "");
605     lockdir[strlen (lockdir) - 1] = '\0';   /* remove trailing slash */
606 
607     do {
608 	if ((dirp = CVS_OPENDIR (lockdir)) == NULL)
609 	    error (1, 0, "cannot open directory %s", lockdir);
610 
611 	ret = 0;
612 	errno = 0;
613 	while ((dp = CVS_READDIR (dirp)) != NULL)
614 	{
615 	    if (CVS_FNMATCH (filepat, dp->d_name, 0) == 0)
616 	    {
617 		/* FIXME: the basename conversion below should be replaced with
618 		 * a call to the GNULIB basename function once it is imported.
619 		 */
620 		/* ignore our plock, if any */
621 		if (ignore && !fncmp (ignore, dp->d_name))
622 		    continue;
623 
624 		line = Xasprintf ("%s/%s", lockdir, dp->d_name);
625 		if (stat (line, &sb) != -1)
626 		{
627 #ifdef CVS_FUDGELOCKS
628 		    /*
629 		     * If the create time of the file is more than CVSLCKAGE
630 		     * seconds ago, try to clean-up the lock file, and if
631 		     * successful, re-open the directory and try again.
632 		     */
633 		    if (now >= (sb.st_ctime + CVSLCKAGE) &&
634                         CVS_UNLINK (line) != -1)
635 		    {
636 			free (line);
637 			ret = -1;
638 			break;
639 		    }
640 #endif
641 		    set_lockers_name (&sb);
642 		}
643 		else
644 		{
645 		    /* If the file doesn't exist, it just means that it
646 		     * disappeared between the time we did the readdir and the
647 		     * time we did the stat.
648 		     */
649 		    if (!existence_error (errno))
650 			error (0, errno, "cannot stat %s", line);
651 		}
652 		errno = 0;
653 		free (line);
654 		ret = 1;
655 		break;
656 	    }
657 	    errno = 0;
658 	}
659 	if (errno != 0)
660 	    error (0, errno, "error reading directory %s", repository);
661 
662 	CVS_CLOSEDIR (dirp);
663     } while (ret < 0);
664 
665     if (lockdir != NULL)
666 	free (lockdir);
667     return ret;
668 }
669 
670 
671 
672 /*
673  * readers_exist() returns 0 if there are no reader lock files remaining in
674  * the repository; else 1 is returned, to indicate that the caller should
675  * sleep a while and try again.
676  *
677  * See lock_exists() for argument detail.
678  */
679 static int
readers_exist(const char * repository)680 readers_exist (const char *repository)
681 {
682     TRACE (TRACE_FLOW, "readers_exist (%s)", repository);
683 
684     /* It is only safe to ignore a readlock set by our process if it was set as
685      * a safety measure to prevent older CVS processes from ignoring our
686      * promotable locks.  The code to ignore these readlocks can be removed
687      * once it is deemed unlikely that anyone will be using CVS servers earlier
688      * than version 1.12.4.
689      */
690     return lock_exists (repository, CVSRFLPAT,
691 #ifdef LOCK_COMPATIBILITY
692                          findnode (locklist, repository) ? readlock :
693 #endif /* LOCK_COMPATIBILITY */
694 			 NULL);
695 }
696 
697 
698 
699 /*
700  * promotable_exists() returns 0 if there is no promotable lock file in
701  * the repository; else 1 is returned, to indicate that the caller should
702  * sleep a while and try again.
703  *
704  * See lock_exists() for argument detail.
705  */
706 static int
promotable_exists(const char * repository)707 promotable_exists (const char *repository)
708 {
709     TRACE (TRACE_FLOW, "promotable_exists (%s)", repository);
710     return lock_exists (repository, CVSPFLPAT, promotablelock);
711 }
712 
713 
714 
715 /*
716  * Lock a list of directories for writing
717  */
718 static char *lock_error_repos;
719 static int lock_error;
720 
721 
722 
723 /*
724  * Create a lock file for potential writers returns L_OK if lock set ok,
725  * L_LOCKED if lock held by someone else or L_ERROR if an error occurred.
726  */
727 static int
set_promotable_lock(struct lock * lock)728 set_promotable_lock (struct lock *lock)
729 {
730     int status;
731     FILE *fp;
732 
733     TRACE (TRACE_FUNCTION, "set_promotable_lock(%s)",
734 	   lock->repository ? lock->repository : "(null)");
735 
736     if (promotablelock == NULL)
737     {
738 	promotablelock = Xasprintf (
739 #ifdef HAVE_LONG_FILE_NAMES
740 				    "%s.%s.%ld", CVSPFL, hostname,
741 #else
742 				    "%s.%ld", CVSPFL,
743 #endif
744 				    (long) getpid());
745     }
746 
747     /* make sure the lock dir is ours (not necessarily unique to us!) */
748     status = set_lock (lock, 0);
749     if (status == L_OK)
750     {
751 	/* we now own a promotable lock - make sure there are no others */
752 	if (promotable_exists (lock->repository))
753 	{
754 	    /* clean up the lock dir */
755 	    clear_lock (lock);
756 
757 	    /* indicate we failed due to read locks instead of error */
758 	    return L_LOCKED;
759 	}
760 
761 	/* write the promotable-lock file */
762 	lock->file1 = lock_name (lock->repository, promotablelock);
763 	if ((fp = CVS_FOPEN (lock->file1, "w+")) == NULL || fclose (fp) == EOF)
764 	{
765 	    int xerrno = errno;
766 
767 	    if (CVS_UNLINK (lock->file1) < 0 && ! existence_error (errno))
768 		error (0, errno, "failed to remove lock %s", lock->file1);
769 
770 	    /* free the lock dir */
771 	    clear_lock (lock);
772 
773 	    /* return the error */
774 	    error (0, xerrno,
775 		   "cannot create promotable lock in repository `%s'",
776 		   lock->repository);
777 	    return L_ERROR;
778 	}
779 
780 #ifdef LOCK_COMPATIBILITY
781 	/* write the read-lock file.  We only do this so that older versions of
782 	 * CVS will not think it is okay to create a write lock.  When it is
783 	 * decided that versions of CVS earlier than 1.12.4 are not likely to
784 	 * be used, this code can be removed.
785 	 */
786 	set_readlock_name ();
787 	lock->file2 = lock_name (lock->repository, readlock);
788 	if ((fp = CVS_FOPEN (lock->file2, "w+")) == NULL || fclose (fp) == EOF)
789 	{
790 	    int xerrno = errno;
791 
792 	    if ( CVS_UNLINK (lock->file2) < 0 && ! existence_error (errno))
793 		error (0, errno, "failed to remove lock %s", lock->file2);
794 
795 	    /* free the lock dir */
796 	    clear_lock (lock);
797 
798 	    /* Remove the promotable lock.  */
799 	    lock->file2 = NULL;
800 	    remove_lock_files (lock, false);
801 
802 	    /* return the error */
803 	    error (0, xerrno,
804 		   "cannot create read lock in repository `%s'",
805 		   lock->repository);
806 	    return L_ERROR;
807 	}
808 #endif /* LOCK_COMPATIBILITY */
809 
810 	clear_lock (lock);
811 
812 	return L_OK;
813     }
814     else
815 	return status;
816 }
817 
818 
819 
820 /*
821  * walklist proc for setting write locks.  Mostly just a wrapper for the
822  * set_promotable_lock function, which has a prettier API, but no other good
823  * reason for existing separately.
824  *
825  * INPUTS
826  *   p		The current node, as determined by walklist().
827  *   closure	Not used.
828  *
829  * GLOBAL INPUTS
830  *   lock_error		Any previous error encountered while attempting to get
831  *                      a lock.
832  *
833  * GLOBAL OUTPUTS
834  *   lock_error		Set if we encounter an error attempting to get axi
835  *			promotable lock.
836  *   lock_error_repos	Set so that if we set lock_error later functions will
837  *			be able to report where the other process's lock was
838  *			encountered.
839  *
840  * RETURNS
841  *   0 for no error.
842  */
843 static int
set_promotablelock_proc(Node * p,void * closure)844 set_promotablelock_proc (Node *p, void *closure)
845 {
846     /* if some lock was not OK, just skip this one */
847     if (lock_error != L_OK)
848 	return 0;
849 
850     /* apply the write lock */
851     lock_error_repos = p->key;
852     lock_error = set_promotable_lock ((struct lock *)p->data);
853     return 0;
854 }
855 
856 
857 
858 /*
859  * Print out a message that the lock is still held, then sleep a while.
860  */
861 static void
lock_wait(const char * repos)862 lock_wait (const char *repos)
863 {
864     time_t now;
865     char *msg;
866     struct tm *tm_p;
867 
868     (void) time (&now);
869     tm_p = gmtime (&now);
870     msg = Xasprintf ("[%8.8s] waiting for %s's lock in %s",
871 		     (tm_p ? asctime (tm_p) : ctime (&now)) + 11,
872 		     lockers_name, repos);
873     error (0, 0, "%s", msg);
874     /* Call cvs_flusherr to ensure that the user sees this message as
875        soon as possible.  */
876     cvs_flusherr ();
877     free (msg);
878     (void)sleep (CVSLCKSLEEP);
879 }
880 
881 
882 
883 /*
884  * Print out a message when we obtain a lock.
885  */
886 static void
lock_obtained(const char * repos)887 lock_obtained (const char *repos)
888 {
889     time_t now;
890     char *msg;
891     struct tm *tm_p;
892 
893     (void) time (&now);
894     tm_p = gmtime (&now);
895     msg = Xasprintf ("[%8.8s] obtained lock in %s",
896 		     (tm_p ? asctime (tm_p) : ctime (&now)) + 11, repos);
897     error (0, 0, "%s", msg);
898     /* Call cvs_flusherr to ensure that the user sees this message as
899        soon as possible.  */
900     cvs_flusherr ();
901     free (msg);
902 }
903 
904 
905 
906 static int
lock_list_promotably(List * list)907 lock_list_promotably (List *list)
908 {
909     char *wait_repos;
910 
911     TRACE (TRACE_FLOW, "lock_list_promotably ()");
912 
913     if (noexec)
914 	return 0;
915 
916     if (readonlyfs) {
917 	error (0, 0,
918 	       "promotable lock failed.\n\
919 WARNING: Read-only repository access mode selected via `cvs -R'.\n\
920 Attempting to write to a read-only filesystem is not allowed.");
921 	return 1;
922     }
923 
924     /* We only know how to do one list at a time */
925     if (locklist != NULL)
926     {
927 	error (0, 0,
928 	       "lock_list_promotably called while promotable locks set - Help!");
929 	return 1;
930     }
931 
932     wait_repos = NULL;
933     for (;;)
934     {
935 	/* try to lock everything on the list */
936 	lock_error = L_OK;		/* init for set_promotablelock_proc */
937 	lock_error_repos = NULL;	/* init for set_promotablelock_proc */
938 	locklist = list;		/* init for Lock_Cleanup */
939 	if (lockers_name != NULL)
940 	    free (lockers_name);
941 	lockers_name = xstrdup ("unknown");
942 
943 	(void) walklist (list, set_promotablelock_proc, NULL);
944 
945 	switch (lock_error)
946 	{
947 	    case L_ERROR:		/* Real Error */
948 		if (wait_repos != NULL)
949 		    free (wait_repos);
950 		Lock_Cleanup ();	/* clean up any locks we set */
951 		error (0, 0, "lock failed - giving up");
952 		return 1;
953 
954 	    case L_LOCKED:		/* Someone already had a lock */
955 		remove_locks ();	/* clean up any locks we set */
956 		lock_wait (lock_error_repos); /* sleep a while and try again */
957 		wait_repos = xstrdup (lock_error_repos);
958 		continue;
959 
960 	    case L_OK:			/* we got the locks set */
961 	        if (wait_repos != NULL)
962 		{
963 		    lock_obtained (wait_repos);
964 		    free (wait_repos);
965 		}
966 		return 0;
967 
968 	    default:
969 		if (wait_repos != NULL)
970 		    free (wait_repos);
971 		error (0, 0, "unknown lock status %d in lock_list_promotably",
972 		       lock_error);
973 		return 1;
974 	}
975     }
976 }
977 
978 
979 
980 /*
981  * Set the static variable lockers_name appropriately, based on the stat
982  * structure passed in.
983  */
984 static void
set_lockers_name(struct stat * statp)985 set_lockers_name (struct stat *statp)
986 {
987     struct passwd *pw;
988 
989     if (lockers_name != NULL)
990 	free (lockers_name);
991     pw = (struct passwd *) getpwuid (statp->st_uid);
992     if (pw != NULL)
993 	lockers_name = xstrdup (pw->pw_name);
994     else
995 	lockers_name = Xasprintf ("uid%lu", (unsigned long) statp->st_uid);
996 }
997 
998 
999 
1000 /*
1001  * Persistently tries to make the directory "lckdir", which serves as a
1002  * lock.
1003  *
1004  * #ifdef CVS_FUDGELOCKS
1005  * If the create time on the directory is greater than CVSLCKAGE
1006  * seconds old, just try to remove the directory.
1007  * #endif
1008  *
1009  */
1010 static int
set_lock(struct lock * lock,int will_wait)1011 set_lock (struct lock *lock, int will_wait)
1012 {
1013     int waited;
1014     long us;
1015     struct stat sb;
1016     mode_t omask;
1017     char *masterlock;
1018     int status;
1019 #ifdef CVS_FUDGELOCKS
1020     time_t now;
1021 #endif
1022 
1023     TRACE (TRACE_FLOW, "set_lock (%s, %d)",
1024 	   lock->repository ? lock->repository : "(null)", will_wait);
1025 
1026     masterlock = lock_name (lock->repository, lock->lockdirname);
1027 
1028     /*
1029      * Note that it is up to the callers of set_lock() to arrange for signal
1030      * handlers that do the appropriate things, like remove the lock
1031      * directory before they exit.
1032      */
1033     waited = 0;
1034     us = 1;
1035     for (;;)
1036     {
1037 	status = -1;
1038 	omask = umask (cvsumask);
1039 	SIG_beginCrSect ();
1040 	if (CVS_MKDIR (masterlock, 0777) == 0)
1041 	{
1042 	    lock->lockdir = masterlock;
1043 	    SIG_endCrSect ();
1044 	    status = L_OK;
1045 	    if (waited)
1046 	        lock_obtained (lock->repository);
1047 	    goto after_sig_unblock;
1048 	}
1049 	SIG_endCrSect ();
1050     after_sig_unblock:
1051 	(void) umask (omask);
1052 	if (status != -1)
1053 	    goto done;
1054 
1055 	if (errno != EEXIST)
1056 	{
1057 	    error (0, errno,
1058 		   "failed to create lock directory for `%s' (%s)",
1059 		   lock->repository, masterlock);
1060 	    status = L_ERROR;
1061 	    goto done;
1062 	}
1063 
1064 	/* Find out who owns the lock.  If the lock directory is
1065 	   non-existent, re-try the loop since someone probably just
1066 	   removed it (thus releasing the lock).  */
1067 	if (stat (masterlock, &sb) < 0)
1068 	{
1069 	    if (existence_error (errno))
1070 		continue;
1071 
1072 	    error (0, errno, "couldn't stat lock directory `%s'", masterlock);
1073 	    status = L_ERROR;
1074 	    goto done;
1075 	}
1076 
1077 #ifdef CVS_FUDGELOCKS
1078 	/*
1079 	 * If the create time of the directory is more than CVSLCKAGE seconds
1080 	 * ago, try to clean-up the lock directory, and if successful, just
1081 	 * quietly retry to make it.
1082 	 */
1083 	(void) time (&now);
1084 	if (now >= (sb.st_ctime + CVSLCKAGE))
1085 	{
1086 	    if (CVS_RMDIR (masterlock) >= 0)
1087 		continue;
1088 	}
1089 #endif
1090 
1091 	/* set the lockers name */
1092 	set_lockers_name (&sb);
1093 
1094 	/* if he wasn't willing to wait, return an error */
1095 	if (!will_wait)
1096 	{
1097 	    status = L_LOCKED;
1098 	    goto done;
1099 	}
1100 
1101 	/* if possible, try a very short sleep without a message */
1102 	if (!waited && us < 1000)
1103 	{
1104 	    us += us;
1105 	    {
1106 		struct timespec ts;
1107 		ts.tv_sec = 0;
1108 		ts.tv_nsec = us * 1000;
1109 		(void)nanosleep (&ts, NULL);
1110 		continue;
1111 	    }
1112 	}
1113 
1114 	lock_wait (lock->repository);
1115 	waited = 1;
1116     }
1117 
1118 done:
1119     if (!lock->lockdir)
1120 	free (masterlock);
1121     return status;
1122 }
1123 
1124 
1125 
1126 /*
1127  * Clear master lock.
1128  *
1129  * INPUTS
1130  *   lock	The lock information.
1131  *
1132  * OUTPUTS
1133  *   Sets LOCK->lockdir to NULL after removing the directory it names and
1134  *   freeing the storage.
1135  *
1136  * ASSUMPTIONS
1137  *   If we own the master lock directory, its name is stored in LOCK->lockdir.
1138  *   We may free LOCK->lockdir.
1139  */
1140 static void
clear_lock(struct lock * lock)1141 clear_lock (struct lock *lock)
1142 {
1143     SIG_beginCrSect ();
1144     if (lock->lockdir)
1145     {
1146 	if (CVS_RMDIR (lock->lockdir) < 0)
1147 	    error (0, errno, "failed to remove lock dir `%s'", lock->lockdir);
1148 	free (lock->lockdir);
1149 	lock->lockdir = NULL;
1150     }
1151     SIG_endCrSect ();
1152 }
1153 
1154 
1155 
1156 /*
1157  * Create a list of repositories to lock
1158  */
1159 /* ARGSUSED */
1160 static int
lock_filesdoneproc(void * callerdat,int err,const char * repository,const char * update_dir,List * entries)1161 lock_filesdoneproc (void *callerdat, int err, const char *repository,
1162                     const char *update_dir, List *entries)
1163 {
1164     Node *p;
1165 
1166     p = getnode ();
1167     p->type = LOCK;
1168     p->key = xstrdup (repository);
1169     p->data = xmalloc (sizeof (struct lock));
1170     ((struct lock *)p->data)->repository = p->key;
1171     ((struct lock *)p->data)->file1 = NULL;
1172 #ifdef LOCK_COMPATIBILITY
1173     ((struct lock *)p->data)->file2 = NULL;
1174 #endif /* LOCK_COMPATIBILITY */
1175     ((struct lock *)p->data)->lockdirname = CVSLCK;
1176     ((struct lock *)p->data)->lockdir = NULL;
1177     ((struct lock *)p->data)->free_repository = false;
1178 
1179     /* FIXME-KRP: this error condition should not simply be passed by. */
1180     if (p->key == NULL || addnode (lock_tree_list, p) != 0)
1181 	freenode (p);
1182     return err;
1183 }
1184 
1185 
1186 
1187 void
lock_tree_promotably(int argc,char ** argv,int local,int which,int aflag)1188 lock_tree_promotably (int argc, char **argv, int local, int which, int aflag)
1189 {
1190     TRACE (TRACE_FUNCTION, "lock_tree_promotably (%d, argv, %d, %d, %d)",
1191 	   argc, local, which, aflag);
1192 
1193     /*
1194      * Run the recursion processor to find all the dirs to lock and lock all
1195      * the dirs
1196      */
1197     lock_tree_list = getlist ();
1198     start_recursion
1199 	(NULL, lock_filesdoneproc,
1200 	 NULL, NULL, NULL, argc,
1201 	 argv, local, which, aflag, CVS_LOCK_NONE,
1202 	 NULL, 0, NULL );
1203     sortlist (lock_tree_list, fsortcmp);
1204     if (lock_list_promotably (lock_tree_list) != 0)
1205 	error (1, 0, "lock failed - giving up");
1206 }
1207 
1208 
1209 
1210 /* Lock a single directory in REPOSITORY.  It is OK to call this if
1211  * a lock has been set with lock_dir_for_write; the new lock will replace
1212  * the old one.  If REPOSITORY is NULL, don't do anything.
1213  *
1214  * We do not clear the dir lock after writing the lock file name since write
1215  * locks are exclusive to all other locks.
1216  */
1217 void
lock_dir_for_write(const char * repository)1218 lock_dir_for_write (const char *repository)
1219 {
1220     int waiting = 0;
1221 
1222     TRACE (TRACE_FLOW, "lock_dir_for_write (%s)", repository);
1223 
1224     if (repository != NULL
1225 	&& (global_writelock.repository == NULL
1226 	    || !strcmp (global_writelock.repository, repository)))
1227     {
1228 	if (writelock == NULL)
1229 	{
1230 	    writelock = Xasprintf (
1231 #ifdef HAVE_LONG_FILE_NAMES
1232 				   "%s.%s.%ld", CVSWFL, hostname,
1233 #else
1234 				   "%s.%ld", CVSWFL,
1235 #endif
1236 				   (long) getpid());
1237 	}
1238 
1239 	if (global_writelock.repository != NULL)
1240 	    remove_lock_files (&global_writelock, true);
1241 
1242 	global_writelock.repository = xstrdup (repository);
1243 	global_writelock.free_repository = true;
1244 
1245 	for (;;)
1246 	{
1247 	    FILE *fp;
1248 
1249 	    if (set_lock (&global_writelock, 1) != L_OK)
1250 		error (1, 0, "failed to obtain write lock in repository `%s'",
1251 		       repository);
1252 
1253 	    /* check if readers exist */
1254 	    if (readers_exist (repository)
1255 		|| promotable_exists (repository))
1256 	    {
1257 		clear_lock (&global_writelock);
1258 		lock_wait (repository); /* sleep a while and try again */
1259 		waiting = 1;
1260 		continue;
1261 	    }
1262 
1263 	    if (waiting)
1264 		lock_obtained (repository);
1265 
1266 	    /* write the write-lock file */
1267 	    global_writelock.file1 = lock_name (global_writelock.repository,
1268 	                                        writelock);
1269 	    if ((fp = CVS_FOPEN (global_writelock.file1, "w+")) == NULL
1270 		|| fclose (fp) == EOF)
1271 	    {
1272 		int xerrno = errno;
1273 
1274 		if (CVS_UNLINK (global_writelock.file1) < 0
1275 		    && !existence_error (errno))
1276 		{
1277 		    error (0, errno, "failed to remove write lock %s",
1278 			   global_writelock.file1);
1279 		}
1280 
1281 		/* free the lock dir */
1282 		clear_lock (&global_writelock);
1283 
1284 		/* return the error */
1285 		error (1, xerrno,
1286 		       "cannot create write lock in repository `%s'",
1287 		       global_writelock.repository);
1288 	    }
1289 
1290 	    /* If we upgraded from a promotable lock, remove it. */
1291 	    if (locklist)
1292 	    {
1293 		Node *p = findnode (locklist, repository);
1294 		if (p)
1295 		{
1296 		    remove_lock_files (p->data, true);
1297 		    delnode (p);
1298 		}
1299 	    }
1300 
1301 	    break;
1302 	}
1303     }
1304 }
1305 
1306 
1307 
1308 /* This is the internal implementation behind history_lock & val_tags_lock.  It
1309  * gets a write lock for the history or val-tags file.
1310  *
1311  * RETURNS
1312  *   true, on success
1313  *   false, on error
1314  */
1315 static inline int
internal_lock(struct lock * lock,const char * xrepository)1316 internal_lock (struct lock *lock, const char *xrepository)
1317 {
1318     /* remember what we're locking (for Lock_Cleanup) */
1319     assert (!lock->repository);
1320     lock->repository = Xasprintf ("%s/%s", xrepository, CVSROOTADM);
1321     lock->free_repository = true;
1322 
1323     /* do nothing if we know it fails anyway */
1324     if (readonlyfs)
1325       return 0;
1326 
1327     /* get the lock dir for our own */
1328     if (set_lock (lock, 1) != L_OK)
1329     {
1330 	if (!really_quiet)
1331 	    error (0, 0, "failed to obtain history lock in repository `%s'",
1332 		   xrepository);
1333 
1334 	return 0;
1335     }
1336 
1337     return 1;
1338 }
1339 
1340 
1341 
1342 /* Lock the CVSROOT/history file for write.
1343  */
1344 int
history_lock(const char * xrepository)1345 history_lock (const char *xrepository)
1346 {
1347     return internal_lock (&global_history_lock, xrepository);
1348 }
1349 
1350 
1351 
1352 /* Remove the CVSROOT/history lock, if it exists.
1353  */
1354 void
clear_history_lock()1355 clear_history_lock ()
1356 {
1357     remove_lock_files (&global_history_lock, true);
1358 }
1359 
1360 
1361 
1362 /* Lock the CVSROOT/val-tags file for write.
1363  */
1364 int
val_tags_lock(const char * xrepository)1365 val_tags_lock (const char *xrepository)
1366 {
1367     return internal_lock (&global_val_tags_lock, xrepository);
1368 }
1369 
1370 
1371 
1372 /* Remove the CVSROOT/val-tags lock, if it exists.
1373  */
1374 void
clear_val_tags_lock()1375 clear_val_tags_lock ()
1376 {
1377     remove_lock_files (&global_val_tags_lock, true);
1378 }
1379