2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
5 * You may distribute under the terms of the GNU General Public License as
6 * specified in the README file that comes with the CVS source distribution.
10 * Lock file support for CVS.
13 /* The node Concurrency in doc/cvs.texinfo has a brief introduction to
14 how CVS locks function, and some of the user-visible consequences of
15 their existence. Here is a summary of why they exist (and therefore,
16 the consequences of hacking CVS to read a repository without creating
19 There are two uses. One is the ability to prevent there from being
20 two writers at the same time. This is necessary for any number of
21 reasons (fileattr code, probably others). Commit needs to lock the
22 whole tree so that nothing happens between the up-to-date check and
25 The second use is the ability to ensure that there is not a writer
26 and a reader at the same time (several readers are allowed). Reasons
29 * Readlocks ensure that once CVS has found a collection of rcs
30 files using Find_Names, the files will still exist when it reads
31 them (they may have moved in or out of the attic).
33 * Readlocks provide some modicum of consistency, although this is
34 kind of limited--see the node Concurrency in cvs.texinfo.
36 * Readlocks ensure that the RCS file does not change between
37 RCS_parse and RCS_reparsercsfile time. This one strikes me as
38 important, although I haven't thought up what bad scenarios might
41 * Readlocks ensure that we won't find the file in the state in
42 which it is in between the calls to add_rcs_file and RCS_checkin in
43 commit.c (when a file is being added). This state is a state in
44 which the RCS file parsing routines in rcs.c cannot parse the file.
46 * Readlocks ensure that a reader won't try to look at a
47 half-written fileattr file (fileattr is not updated atomically).
49 (see also the description of anonymous read-only access in
50 "Password authentication security" node in doc/cvs.texinfo).
52 While I'm here, I'll try to summarize a few random suggestions
53 which periodically get made about how locks might be different:
55 1. Check for EROFS. Maybe useful, although in the presence of NFS
56 EROFS does *not* mean that the file system is unchanging.
58 2. Provide an option to disable locks for operations which only
59 read (see above for some of the consequences).
61 3. Have a server internally do the locking. Probably a good
62 long-term solution, and many people have been working hard on code
63 changes which would eventually make it possible to have a server
64 which can handle various connections in one process, but there is
65 much, much work still to be done before this is feasible. */
72 /* This is the directory in which we may have a lock named by the
73 readlock variable, a lock named by the writelock variable, and/or
74 a lock named CVSLCK. The storage is not allocated along with the
75 struct lock; it is allocated by the Reader_Lock caller or in the
76 case of promotablelocks, it is just a pointer to the storage allocated
77 for the ->key field. */
78 const char *repository;
80 /* The name of the lock files. */
82 #ifdef LOCK_COMPATIBILITY
84 #endif /* LOCK_COMPATIBILITY */
86 /* Do we have a lock named CVSLCK? */
88 /* Note there is no way of knowing whether the readlock and writelock
89 exist. The code which sets the locks doesn't use SIG_beginCrSect
90 to set a flag like we do for CVSLCK. */
94 static void remove_locks (void);
95 static int set_lock (struct lock *lock, int will_wait);
96 static void clear_lock (struct lock *lock);
97 static void set_lockers_name (struct stat *statp);
99 /* Malloc'd array containing the username of the whoever has the lock.
100 Will always be non-NULL in the cases where it is needed. */
101 static char *lockers_name;
102 /* Malloc'd array specifying name of a readlock within a directory.
104 static char *readlock;
105 /* Malloc'd array specifying name of a writelock within a directory.
107 static char *writelock;
108 /* Malloc'd array specifying name of a promotablelock within a directory.
110 static char *promotablelock;
111 /* Malloc'd array specifying the name of a CVSLCK file (absolute pathname).
112 Will always be non-NULL in the cases where it is used. */
113 static char *masterlock;
114 static List *locklist;
116 #define L_OK 0 /* success */
117 #define L_ERROR 1 /* error condition */
118 #define L_LOCKED 2 /* lock owned by someone else */
120 /* This is the (single) readlock which is set by Reader_Lock. The
121 repository field is NULL if there is no such lock. */
122 static struct lock global_readlock;
123 static struct lock global_writelock;
125 /* List of locks set by lock_tree_for_write. This is redundant
126 with locklist, sort of. */
127 static List *lock_tree_list;
129 /* LockDir from CVSROOT/config. */
134 /* Return a newly malloc'd string containing the name of the lock for the
135 repository REPOSITORY and the lock file name within that directory
136 NAME. Also create the directories in which to put the lock file
137 if needed (if we need to, could save system call(s) by doing
138 that only if the actual operation fails. But for now we'll keep
141 lock_name (const char *repository, const char *name)
146 const char *short_repos;
147 mode_t save_umask = 0000;
150 TRACE (TRACE_FLOW, "lock_name (%s, %s)",
151 repository ? repository : "(null)", name ? name : "(null)");
153 if (lock_dir == NULL)
155 /* This is the easy case. Because the lock files go directly
156 in the repository, no need to create directories or anything. */
157 assert (name != NULL);
158 assert (repository != NULL);
159 retval = xmalloc (strlen (repository) + strlen (name) + 10);
160 (void) sprintf (retval, "%s/%s", repository, name);
167 /* The interesting part of the repository is the part relative
169 assert (current_parsed_root != NULL);
170 assert (current_parsed_root->directory != NULL);
171 assert (strncmp (repository, current_parsed_root->directory,
172 strlen (current_parsed_root->directory)) == 0);
173 short_repos = repository + strlen (current_parsed_root->directory) + 1;
175 if (strcmp (repository, current_parsed_root->directory) == 0)
178 assert (short_repos[-1] == '/');
180 retval = xmalloc (strlen (lock_dir)
181 + strlen (short_repos)
184 strcpy (retval, lock_dir);
185 q = retval + strlen (retval);
188 strcpy (q, short_repos);
190 /* In the common case, where the directory already exists, let's
191 keep it to one system call. */
192 if (CVS_STAT (retval, &sb) < 0)
194 /* If we need to be creating more than one directory, we'll
195 get the existence_error here. */
196 if (!existence_error (errno))
197 error (1, errno, "cannot stat directory %s", retval);
201 if (S_ISDIR (sb.st_mode))
204 error (1, 0, "%s is not a directory", retval);
207 /* Now add the directories one at a time, so we can create
210 The idea behind the new_mode stuff is that the directory we
211 end up creating will inherit permissions from its parent
212 directory (we re-set new_mode with each EEXIST). CVSUMASK
213 isn't right, because typically the reason for LockDir is to
214 use a different set of permissions. We probably want to
215 inherit group ownership also (but we don't try to deal with
216 that, some systems do it for us either always or when g+s is on).
218 We don't try to do anything about the permissions on the lock
219 files themselves. The permissions don't really matter so much
220 because the locks will generally be removed by the process
221 which created them. */
223 if (CVS_STAT (lock_dir, &sb) < 0)
224 error (1, errno, "cannot stat %s", lock_dir);
225 new_mode = sb.st_mode;
226 save_umask = umask (0000);
232 while (!ISSLASH (*p) && *p != '\0')
236 strncpy (q, short_repos, p - short_repos);
237 q[p - short_repos] = '\0';
238 if (!ISSLASH (q[p - short_repos - 1])
239 && CVS_MKDIR (retval, new_mode) < 0)
241 int saved_errno = errno;
242 if (saved_errno != EEXIST)
243 error (1, errno, "cannot make directory %s", retval);
246 if (CVS_STAT (retval, &sb) < 0)
247 error (1, errno, "cannot stat %s", retval);
248 new_mode = sb.st_mode;
255 strcpy (q, short_repos);
256 if (CVS_MKDIR (retval, new_mode) < 0
258 error (1, errno, "cannot make directory %s", retval);
264 strcat (retval, "/");
265 strcat (retval, name);
269 assert (umask (save_umask) == 0000);
278 /* Remove the lock files. For interrupt purposes, it can be assumed that the
279 * first thing this function does is set lock->repository to NULL.
282 * lock The lock to remove.
283 * free True if this lock directory will not5 be reused (free
284 * lock->repository if necessary).
287 remove_lock_files (struct lock *lock, int free_repository)
289 TRACE (TRACE_FLOW, "remove_lock_files(%s)", lock->repository);
291 /* If lock->file is set, the lock *might* have been created, but since
292 * Reader_Lock & lock_dir_for_write don't use SIG_beginCrSect the way that
293 * set_lock does, we don't know that. That is why we need to check for
294 * existence_error here.
298 char *tmp = lock->file1;
300 if (CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
301 error (0, errno, "failed to remove lock %s", tmp);
304 #ifdef LOCK_COMPATIBILITY
307 char *tmp = lock->file2;
309 if (CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
310 error (0, errno, "failed to remove lock %s", tmp);
313 #endif /* LOCK_COMPATIBILITY */
315 if (lock->have_lckdir)
317 char *tmp = lock_name (lock->repository, CVSLCK);
319 if (CVS_RMDIR (tmp) < 0)
320 error (0, errno, "failed to remove lock dir %s", tmp);
321 lock->have_lckdir = 0;
326 /* And free the repository string. We don't really have to set the
327 * repository string to NULL first since there is no harm in running any of
328 * the above code twice.
330 * Use SIG_beginCrSect since otherwise we might be interrupted between
331 * checking whether free_repository is set and freeing stuff.
336 if (lock->free_repository)
338 free ((char *)lock->repository);
339 lock->free_repository = 0;
341 lock->repository = NULL;
349 * Clean up outstanding read and write locks and free their storage.
352 Simple_Lock_Cleanup (void)
354 TRACE (TRACE_FUNCTION, "Simple_Lock_Cleanup()");
356 /* Avoid interrupts while accessing globals the interrupt handlers might
361 /* clean up simple read locks (if any) */
362 if (global_readlock.repository != NULL)
363 remove_lock_files (&global_readlock, 1);
364 /* See note in Lock_Cleanup() below. */
369 /* clean up simple write locks (if any) */
370 if (global_writelock.repository != NULL)
371 remove_lock_files (&global_writelock, 1);
372 /* See note in Lock_Cleanup() below. */
379 * Clean up all outstanding locks and free their storage.
382 * This function needs to be reentrant since a call to exit() can cause a
383 * call to this function, which can then be interrupted by a signal, which
384 * can cause a second call to this function.
392 static short int never_run_again = 0;
394 TRACE (TRACE_FUNCTION, "Lock_Cleanup()");
396 /* Since main_cleanup() always calls exit() (via error (1, ...)), we avoid
397 * allowing this function to be called twice as an optimization.
399 * If we are already in a signal critical section, assume we were called
400 * via the signal handler and set a flag which will prevent future calls.
401 * The only time that we should get into one of these functions otherwise
402 * while still in a critical section is if error(1,...) is called from a
403 * critical section, in which case we are exiting and interrupts are
404 * already being ignored.
406 * For Lock_Cleanup(), this is not a run_once variable since Lock_Cleanup()
407 * can be called to clean up the current lock set multiple times by the
408 * same run of a CVS command.
410 * For server_cleanup() and rcs_cleanup(), this is not a run_once variable
411 * since a call to the cleanup function from atexit() could be interrupted
412 * by the interrupt handler.
414 if (never_run_again) return;
415 if (SIG_inCrSect()) never_run_again = 1;
419 /* Avoid being interrupted during calls which set globals to NULL. This
420 * avoids having interrupt handlers attempt to use these global variables
421 * in inconsistent states.
424 dellist (&lock_tree_list);
425 /* Unblocking allows any signal to be processed as soon as possible. This
426 * isn't really necessary, but since we know signals can cause us to be
427 * called, why not avoid having blocks of code run twice.
435 * walklist proc for removing a list of locks
438 unlock_proc (Node *p, void *closure)
440 remove_lock_files (p->data, 0);
447 * Remove locks without discarding the lock information.
452 TRACE (TRACE_FLOW, "remove_locks()");
454 Simple_Lock_Cleanup ();
456 /* clean up promotable locks (if any) */
458 if (locklist != NULL)
460 /* Use a tmp var since any of these functions could call exit, causing
461 * us to be called a second time.
463 List *tmp = locklist;
465 walklist (tmp, unlock_proc, NULL);
473 * Set the global readlock variable if it isn't already.
476 set_readlock_name (void)
478 if (readlock == NULL)
480 readlock = xmalloc (strlen (hostname) + sizeof (CVSRFL) + 40);
481 (void) sprintf (readlock,
482 #ifdef HAVE_LONG_FILE_NAMES
483 "%s.%s.%ld", CVSRFL, hostname,
494 * Create a lock file for readers
497 Reader_Lock (char *xrepository)
502 TRACE (TRACE_FUNCTION, "Reader_Lock(%s)", xrepository);
504 if (noexec || readonlyfs)
507 /* we only do one directory at a time for read locks! */
508 if (global_readlock.repository != NULL)
510 error (0, 0, "Reader_Lock called while read locks set - Help!");
514 set_readlock_name ();
516 /* remember what we're locking (for Lock_Cleanup) */
517 global_readlock.repository = xstrdup (xrepository);
518 global_readlock.free_repository = 1;
520 /* get the lock dir for our own */
521 if (set_lock (&global_readlock, 1) != L_OK)
523 error (0, 0, "failed to obtain dir lock in repository `%s'",
525 if (readlock != NULL)
528 /* We don't set global_readlock.repository to NULL. I think this
529 only works because recurse.c will give a fatal error if we return
534 /* write a read-lock */
535 global_readlock.file1 = lock_name (xrepository, readlock);
536 if ((fp = CVS_FOPEN (global_readlock.file1, "w+")) == NULL
537 || fclose (fp) == EOF)
539 error (0, errno, "cannot create read lock in repository `%s'",
544 /* free the lock dir */
545 clear_lock (&global_readlock);
553 * lock_exists() returns 0 if there is no lock file matching FILEPAT in
554 * the repository but not IGNORE; else 1 is returned, to indicate that the
555 * caller should sleep a while and try again.
558 * repository The repository directory to search for locks.
559 * filepat The file name pattern to search for.
560 * ignore The name of a single file which can be ignored.
563 * lockdir The lock dir external to the repository, if any.
566 * 0 No lock matching FILEPAT and not IGNORE exists.
567 * 1 Otherwise and on error.
570 * In the case where errors are encountered reading the directory, a warning
571 * message is printed, 1 is is returned and ERRNO is left set.
574 lock_exists (const char *repository, const char *filepat, const char *ignore)
582 #ifdef CVS_FUDGELOCKS
587 TRACE (TRACE_FLOW, "lock_exists (%s, %s, %s)",
588 repository, filepat, ignore ? ignore : "(null)");
590 lockdir = lock_name (repository, "");
591 lockdir[strlen (lockdir) - 1] = '\0'; /* remove trailing slash */
594 if ((dirp = CVS_OPENDIR (lockdir)) == NULL)
595 error (1, 0, "cannot open directory %s", lockdir);
599 while ((dp = CVS_READDIR (dirp)) != NULL)
601 if (CVS_FNMATCH (filepat, dp->d_name, 0) == 0)
603 /* FIXME: the basename conversion below should be replaced with
604 * a call to the GNULIB basename function once it is imported.
606 /* ignore our plock, if any */
607 if (ignore && !fncmp (ignore, dp->d_name))
610 line = xmalloc (strlen (lockdir) + 1 + strlen (dp->d_name) + 1);
611 (void)sprintf (line, "%s/%s", lockdir, dp->d_name);
612 if (CVS_STAT (line, &sb) != -1)
614 #ifdef CVS_FUDGELOCKS
616 * If the create time of the file is more than CVSLCKAGE
617 * seconds ago, try to clean-up the lock file, and if
618 * successful, re-open the directory and try again.
620 if (now >= (sb.st_ctime + CVSLCKAGE) &&
621 CVS_UNLINK (line) != -1)
628 set_lockers_name (&sb);
632 /* If the file doesn't exist, it just means that it
633 * disappeared between the time we did the readdir and the
634 * time we did the stat.
636 if (!existence_error (errno))
637 error (0, errno, "cannot stat %s", line);
647 error (0, errno, "error reading directory %s", repository);
660 * readers_exist() returns 0 if there are no reader lock files remaining in
661 * the repository; else 1 is returned, to indicate that the caller should
662 * sleep a while and try again.
664 * See lock_exists() for argument detail.
667 readers_exist (const char *repository)
669 TRACE (TRACE_FLOW, "readers_exist (%s)", repository);
671 /* It is only safe to ignore a readlock set by our process if it was set as
672 * a safety measure to prevent older CVS processes from ignoring our
673 * promotable locks. The code to ignore these readlocks can be removed
674 * once it is deemed unlikely that anyone will be using CVS servers earlier
675 * than version 1.12.4.
677 return lock_exists (repository, CVSRFLPAT,
678 #ifdef LOCK_COMPATIBILITY
679 findnode (locklist, repository) ? readlock :
680 #endif /* LOCK_COMPATIBILITY */
687 * promotable_exists() returns 0 if there is no promotable lock file in
688 * the repository; else 1 is returned, to indicate that the caller should
689 * sleep a while and try again.
691 * See lock_exists() for argument detail.
694 promotable_exists (const char *repository)
696 TRACE (TRACE_FLOW, "promotable_exists (%s)", repository);
697 return lock_exists (repository, CVSPFLPAT, promotablelock);
703 * Lock a list of directories for writing
705 static char *lock_error_repos;
706 static int lock_error;
711 * Create a lock file for potential writers returns L_OK if lock set ok,
712 * L_LOCKED if lock held by someone else or L_ERROR if an error occurred.
715 set_promotable_lock (struct lock *lock)
720 TRACE (TRACE_FUNCTION, "set_promotable_lock(%s)",
721 lock->repository ? lock->repository : "(null)");
723 if (promotablelock == NULL)
725 promotablelock = xmalloc (strlen (hostname) + sizeof (CVSPFL) + 40);
726 (void) sprintf (promotablelock,
727 #ifdef HAVE_LONG_FILE_NAMES
728 "%s.%s.%ld", CVSPFL, hostname,
735 /* make sure the lock dir is ours (not necessarily unique to us!) */
736 status = set_lock (lock, 0);
739 /* we now own a promotable lock - make sure there are no others */
740 if (promotable_exists (lock->repository))
742 /* clean up the lock dir */
745 /* indicate we failed due to read locks instead of error */
749 /* write the promotable-lock file */
750 lock->file1 = lock_name (lock->repository, promotablelock);
751 if ((fp = CVS_FOPEN (lock->file1, "w+")) == NULL || fclose (fp) == EOF)
755 if (CVS_UNLINK (lock->file1) < 0 && ! existence_error (errno))
756 error (0, errno, "failed to remove lock %s", lock->file1);
758 /* free the lock dir */
761 /* return the error */
763 "cannot create promotable lock in repository `%s'",
768 #ifdef LOCK_COMPATIBILITY
769 /* write the read-lock file. We only do this so that older versions of
770 * CVS will not think it is okay to create a write lock. When it is
771 * decided that versions of CVS earlier than 1.12.4 are not likely to
772 * be used, this code can be removed.
774 set_readlock_name ();
775 lock->file2 = lock_name (lock->repository, readlock);
776 if ((fp = CVS_FOPEN (lock->file2, "w+")) == NULL || fclose (fp) == EOF)
780 if ( CVS_UNLINK (lock->file2) < 0 && ! existence_error (errno))
781 error (0, errno, "failed to remove lock %s", lock->file2);
783 /* free the lock dir */
786 /* Remove the promotable lock. */
788 remove_lock_files (lock, 0);
790 /* return the error */
792 "cannot create read lock in repository `%s'",
796 #endif /* LOCK_COMPATIBILITY */
809 * walklist proc for setting write locks. Mostly just a wrapper for the
810 * set_promotable_lock function, which has a prettier API, but no other good
811 * reason for existing separately.
814 * p The current node, as determined by walklist().
818 * lock_error Any previous error encountered while attempting to get
822 * lock_error Set if we encounter an error attempting to get axi
824 * lock_error_repos Set so that if we set lock_error later functions will
825 * be able to report where the other process's lock was
832 set_promotablelock_proc (Node *p, void *closure)
834 /* if some lock was not OK, just skip this one */
835 if (lock_error != L_OK)
838 /* apply the write lock */
839 lock_error_repos = p->key;
840 lock_error = set_promotable_lock ((struct lock *)p->data);
847 * Print out a message that the lock is still held, then sleep a while.
850 lock_wait (const char *repos)
857 tm_p = gmtime (&now);
858 msg = xmalloc (100 + strlen (lockers_name) + strlen (repos));
859 sprintf (msg, "[%8.8s] waiting for %s's lock in %s",
860 (tm_p ? asctime (tm_p) : ctime (&now)) + 11,
861 lockers_name, repos);
862 error (0, 0, "%s", msg);
863 /* Call cvs_flusherr to ensure that the user sees this message as
867 (void)sleep (CVSLCKSLEEP);
873 * Print out a message when we obtain a lock.
876 lock_obtained (const char *repos)
883 tm_p = gmtime (&now);
884 msg = xmalloc (100 + strlen (repos));
885 sprintf (msg, "[%8.8s] obtained lock in %s",
886 (tm_p ? asctime (tm_p) : ctime (&now)) + 11, repos);
887 error (0, 0, "%s", msg);
888 /* Call cvs_flusherr to ensure that the user sees this message as
897 lock_list_promotably (List *list)
901 TRACE (TRACE_FLOW, "lock_list_promotably ()");
908 "promotable lock failed.\n\
909 WARNING: Read-only repository access mode selected via `cvs -R'.\n\
910 Attempting to write to a read-only filesystem is not allowed.");
914 /* We only know how to do one list at a time */
915 if (locklist != (List *) NULL)
918 "lock_list_promotably called while promotable locks set - Help!");
925 /* try to lock everything on the list */
926 lock_error = L_OK; /* init for set_promotablelock_proc */
927 lock_error_repos = (char *) NULL; /* init for set_promotablelock_proc */
928 locklist = list; /* init for Lock_Cleanup */
929 if (lockers_name != NULL)
931 lockers_name = xstrdup ("unknown");
933 (void) walklist (list, set_promotablelock_proc, NULL);
937 case L_ERROR: /* Real Error */
938 if (wait_repos != NULL)
940 Lock_Cleanup (); /* clean up any locks we set */
941 error (0, 0, "lock failed - giving up");
944 case L_LOCKED: /* Someone already had a lock */
945 remove_locks (); /* clean up any locks we set */
946 lock_wait (lock_error_repos); /* sleep a while and try again */
947 wait_repos = xstrdup (lock_error_repos);
950 case L_OK: /* we got the locks set */
951 if (wait_repos != NULL)
953 lock_obtained (wait_repos);
959 if (wait_repos != NULL)
961 error (0, 0, "unknown lock status %d in lock_list_promotably",
971 * Set the static variable lockers_name appropriately, based on the stat
972 * structure passed in.
975 set_lockers_name (struct stat *statp)
979 if (lockers_name != NULL)
981 if ((pw = (struct passwd *)getpwuid (statp->st_uid)) !=
982 (struct passwd *)NULL)
984 lockers_name = xstrdup (pw->pw_name);
988 lockers_name = xmalloc (20);
989 (void)sprintf (lockers_name, "uid%lu", (unsigned long) statp->st_uid);
996 * Persistently tries to make the directory "lckdir", which serves as a
999 * #ifdef CVS_FUDGELOCKS
1000 * If the create time on the directory is greater than CVSLCKAGE
1001 * seconds old, just try to remove the directory.
1006 set_lock (struct lock *lock, int will_wait)
1012 #ifdef CVS_FUDGELOCKS
1016 TRACE (TRACE_FLOW, "set_lock (%s, %d)",
1017 lock->repository ? lock->repository : "(null)", will_wait);
1019 if (masterlock != NULL)
1021 masterlock = lock_name (lock->repository, CVSLCK);
1024 * Note that it is up to the callers of set_lock() to arrange for signal
1025 * handlers that do the appropriate things, like remove the lock
1026 * directory before they exit.
1030 lock->have_lckdir = 0;
1034 omask = umask (cvsumask);
1036 if (CVS_MKDIR (masterlock, 0777) == 0)
1038 lock->have_lckdir = 1;
1042 lock_obtained (lock->repository);
1047 (void) umask (omask);
1051 if (errno != EEXIST)
1054 "failed to create lock directory for `%s' (%s)",
1055 lock->repository, masterlock);
1059 /* Find out who owns the lock. If the lock directory is
1060 non-existent, re-try the loop since someone probably just
1061 removed it (thus releasing the lock). */
1062 if (CVS_STAT (masterlock, &sb) < 0)
1064 if (existence_error (errno))
1067 error (0, errno, "couldn't stat lock directory `%s'", masterlock);
1071 #ifdef CVS_FUDGELOCKS
1073 * If the create time of the directory is more than CVSLCKAGE seconds
1074 * ago, try to clean-up the lock directory, and if successful, just
1075 * quietly retry to make it.
1078 if (now >= (sb.st_ctime + CVSLCKAGE))
1080 if (CVS_RMDIR (masterlock) >= 0)
1085 /* set the lockers name */
1086 set_lockers_name (&sb);
1088 /* if he wasn't willing to wait, return an error */
1092 /* if possible, try a very short sleep without a message */
1093 if (!waited && us < 1000)
1099 ts.tv_nsec = us * 1000;
1100 (void)nanosleep (&ts, NULL);
1105 lock_wait (lock->repository);
1113 * Clear master lock. We don't have to recompute the lock name since
1114 * clear_lock is never called except after a successful set_lock().
1117 clear_lock (struct lock *lock)
1120 if (CVS_RMDIR (masterlock) < 0)
1121 error (0, errno, "failed to remove lock dir `%s'", masterlock);
1122 lock->have_lckdir = 0;
1129 * Create a list of repositories to lock
1133 lock_filesdoneproc (void *callerdat, int err, const char *repository,
1134 const char *update_dir, List *entries)
1140 p->key = xstrdup (repository);
1141 p->data = xmalloc (sizeof (struct lock));
1142 ((struct lock *)p->data)->repository = p->key;
1143 ((struct lock *)p->data)->file1 = NULL;
1144 #ifdef LOCK_COMPATIBILITY
1145 ((struct lock *)p->data)->file2 = NULL;
1146 #endif /* LOCK_COMPATIBILITY */
1147 ((struct lock *)p->data)->have_lckdir = 0;
1148 ((struct lock *)p->data)->free_repository = 0;
1150 /* FIXME-KRP: this error condition should not simply be passed by. */
1151 if (p->key == NULL || addnode (lock_tree_list, p) != 0)
1159 lock_tree_promotably (int argc, char **argv, int local, int which, int aflag)
1161 TRACE (TRACE_FUNCTION, "lock_tree_promotably (%d, argv, %d, %d, %d)",
1162 argc, local, which, aflag);
1165 * Run the recursion processor to find all the dirs to lock and lock all
1168 lock_tree_list = getlist ();
1170 (NULL, lock_filesdoneproc,
1171 NULL, NULL, NULL, argc,
1172 argv, local, which, aflag, CVS_LOCK_NONE,
1174 sortlist (lock_tree_list, fsortcmp);
1175 if (lock_list_promotably (lock_tree_list) != 0)
1176 error (1, 0, "lock failed - giving up");
1181 /* Lock a single directory in REPOSITORY. It is OK to call this if
1182 * a lock has been set with lock_dir_for_write; the new lock will replace
1183 * the old one. If REPOSITORY is NULL, don't do anything.
1185 * We do not clear the dir lock after writing the lock file name since write
1186 * locks are exclusive to all other locks.
1189 lock_dir_for_write (const char *repository)
1193 TRACE (TRACE_FLOW, "lock_dir_for_write (%s)", repository);
1195 if (repository != NULL
1196 && (global_writelock.repository == NULL
1197 || !strcmp (global_writelock.repository, repository)))
1199 if (writelock == NULL)
1201 writelock = xmalloc (strlen (hostname) + sizeof (CVSWFL) + 40);
1202 (void) sprintf (writelock,
1203 #ifdef HAVE_LONG_FILE_NAMES
1204 "%s.%s.%ld", CVSWFL, hostname,
1211 if (global_writelock.repository != NULL)
1212 remove_lock_files (&global_writelock, 1);
1214 global_writelock.repository = xstrdup (repository);
1215 global_writelock.free_repository = 1;
1221 if (set_lock (&global_writelock, 1) != L_OK)
1222 error (1, 0, "failed to obtain write lock in repository `%s'",
1225 /* check if readers exist */
1226 if (readers_exist (repository)
1227 || promotable_exists (repository))
1229 clear_lock (&global_writelock);
1230 lock_wait (repository); /* sleep a while and try again */
1236 lock_obtained (repository);
1238 /* write the write-lock file */
1239 global_writelock.file1 = lock_name (global_writelock.repository,
1241 if ((fp = CVS_FOPEN (global_writelock.file1, "w+")) == NULL
1242 || fclose (fp) == EOF)
1246 if (CVS_UNLINK (global_writelock.file1) < 0
1247 && !existence_error (errno))
1249 error (0, errno, "failed to remove write lock %s",
1250 global_writelock.file1);
1253 /* free the lock dir */
1254 clear_lock (&global_writelock);
1256 /* return the error */
1258 "cannot create write lock in repository `%s'",
1259 global_writelock.repository);
1262 /* If we upgraded from a promotable lock, remove it. */
1265 Node *p = findnode (locklist, repository);
1268 remove_lock_files ((struct lock *)p->data, 1);