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