2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8 * Portions Copyright (C) 1989-1992, Brian Berliner
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.
15 * Lock file support for CVS.
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
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
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
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).
40 * Readlocks provide some modicum of consistency, although this is
41 kind of limited--see the node Concurrency in cvs.texinfo.
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
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.
53 * Readlocks ensure that a reader won't try to look at a
54 half-written fileattr file (fileattr is not updated atomically).
56 (see also the description of anonymous read-only access in
57 "Password authentication security" node in doc/cvs.texinfo).
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:
62 1. Check for EROFS. Maybe useful, although in the presence of NFS
63 EROFS does *not* mean that the file system is unchanging.
65 2. Provide an option to disable locks for operations which only
66 read (see above for some of the consequences).
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. */
79 #else /* HAVE_NANOSLEEP */
80 # if !defined HAVE_USLEEP && defined HAVE_SELECT
81 /* use select as a workaround */
83 # endif /* !defined HAVE_USLEEP && defined HAVE_SELECT */
84 #endif /* !HAVE_NANOSLEEP */
88 /* This is the directory in which we may have a lock named by the
89 readlock variable, a lock named by the writelock variable, and/or
90 a lock named CVSLCK. The storage is not allocated along with the
91 struct lock; it is allocated by the Reader_Lock caller or in the
92 case of writelocks, it is just a pointer to the storage allocated
93 for the ->key field. */
96 /* The name of the master lock dir. Usually CVSLCK. */
97 const char *lockdirname;
99 /* The full path to the lock dir, if we are currently holding it.
101 * This will be LOCKDIRNAME catted onto REPOSITORY. We waste a little
102 * space by storing it, but save a later malloc/free.
106 /* Note there is no way of knowing whether the readlock and writelock
107 exist. The code which sets the locks doesn't use SIG_beginCrSect
108 to set a flag like we do for CVSLCK. */
111 static void remove_locks PROTO((void));
112 static int readers_exist PROTO((char *repository));
113 static int set_lock PROTO ((struct lock *lock, int will_wait));
114 static void clear_lock PROTO ((struct lock *lock));
115 static void set_lockers_name PROTO((struct stat *statp));
116 static int set_writelock_proc PROTO((Node * p, void *closure));
117 static int unlock_proc PROTO((Node * p, void *closure));
118 static int write_lock PROTO ((struct lock *lock));
119 static void lock_simple_remove PROTO ((struct lock *lock));
120 static void lock_wait PROTO((char *repository));
121 static void lock_obtained PROTO((char *repository));
123 /* Malloc'd array containing the username of the whoever has the lock.
124 Will always be non-NULL in the cases where it is needed. */
125 static char *lockers_name;
126 /* Malloc'd array specifying name of a readlock within a directory.
128 static char *readlock;
129 /* Malloc'd array specifying name of a writelock within a directory.
131 static char *writelock;
132 /* Malloc'd array specifying the name of a CVSLCK file (absolute pathname).
133 Will always be non-NULL in the cases where it is used. */
134 static List *locklist;
136 #define L_OK 0 /* success */
137 #define L_ERROR 1 /* error condition */
138 #define L_LOCKED 2 /* lock owned by someone else */
140 /* This is the (single) readlock which is set by Reader_Lock. The
141 repository field is NULL if there is no such lock. */
142 static struct lock global_readlock = {NULL, CVSLCK, NULL};
144 static struct lock global_history_lock = {NULL, CVSHISTORYLCK, NULL};
145 static struct lock global_val_tags_lock = {NULL, CVSVALTAGSLCK, NULL};
147 /* List of locks set by lock_tree_for_write. This is redundant
148 with locklist, sort of. */
149 static List *lock_tree_list;
151 /* If we set locks with lock_dir_for_write, then locked_dir contains
152 the malloc'd name of the repository directory which we have locked.
153 locked_list is the same thing packaged into a list and is redundant
154 with locklist the same way that lock_tree_list is. */
155 static char *locked_dir;
156 static List *locked_list;
158 /* LockDir from CVSROOT/config. */
161 static char *lock_name PROTO ((const char *repository, const char *name));
163 /* Return a newly malloc'd string containing the name of the lock for the
164 repository REPOSITORY and the lock file name within that directory
165 NAME. Also create the directories in which to put the lock file
166 if needed (if we need to, could save system call(s) by doing
167 that only if the actual operation fails. But for now we'll keep
170 lock_name (repository, name)
171 const char *repository;
177 const char *short_repos;
178 mode_t save_umask = 0;
181 if (lock_dir == NULL)
183 /* This is the easy case. Because the lock files go directly
184 in the repository, no need to create directories or anything. */
185 retval = xmalloc (strlen (repository) + strlen (name) + 10);
186 (void) sprintf (retval, "%s/%s", repository, name);
193 /* The interesting part of the repository is the part relative
195 assert (current_parsed_root != NULL);
196 assert (current_parsed_root->directory != NULL);
197 assert (strncmp (repository, current_parsed_root->directory,
198 strlen (current_parsed_root->directory)) == 0);
199 short_repos = repository + strlen (current_parsed_root->directory) + 1;
201 if (strcmp (repository, current_parsed_root->directory) == 0)
204 assert (short_repos[-1] == '/');
206 retval = xmalloc (strlen (lock_dir)
207 + strlen (short_repos)
210 strcpy (retval, lock_dir);
211 q = retval + strlen (retval);
214 strcpy (q, short_repos);
216 /* In the common case, where the directory already exists, let's
217 keep it to one system call. */
218 if (CVS_STAT (retval, &sb) < 0)
220 /* If we need to be creating more than one directory, we'll
221 get the existence_error here. */
222 if (!existence_error (errno))
223 error (1, errno, "cannot stat directory %s", retval);
227 if (S_ISDIR (sb.st_mode))
230 error (1, 0, "%s is not a directory", retval);
233 /* Now add the directories one at a time, so we can create
236 The idea behind the new_mode stuff is that the directory we
237 end up creating will inherit permissions from its parent
238 directory (we re-set new_mode with each EEXIST). CVSUMASK
239 isn't right, because typically the reason for LockDir is to
240 use a different set of permissions. We probably want to
241 inherit group ownership also (but we don't try to deal with
242 that, some systems do it for us either always or when g+s is on).
244 We don't try to do anything about the permissions on the lock
245 files themselves. The permissions don't really matter so much
246 because the locks will generally be removed by the process
247 which created them. */
249 if (CVS_STAT (lock_dir, &sb) < 0)
250 error (1, errno, "cannot stat %s", lock_dir);
251 new_mode = sb.st_mode;
252 save_umask = umask (0000);
258 while (!ISDIRSEP (*p) && *p != '\0')
262 strncpy (q, short_repos, p - short_repos);
263 q[p - short_repos] = '\0';
264 if (!ISDIRSEP (q[p - short_repos - 1])
265 && CVS_MKDIR (retval, new_mode) < 0)
267 int saved_errno = errno;
268 if (saved_errno != EEXIST)
269 error (1, errno, "cannot make directory %s", retval);
272 if (CVS_STAT (retval, &sb) < 0)
273 error (1, errno, "cannot stat %s", retval);
274 new_mode = sb.st_mode;
281 strcpy (q, short_repos);
282 if (CVS_MKDIR (retval, new_mode) < 0
284 error (1, errno, "cannot make directory %s", retval);
290 strcat (retval, "/");
291 strcat (retval, name);
295 assert (umask (save_umask) == 0000);
303 * Clean up all outstanding locks
308 /* FIXME: error handling here is kind of bogus; we sometimes will call
309 error, which in turn can call us again. For the moment work around
310 this by refusing to reenter this function (this is a kludge). */
311 /* FIXME-reentrancy: the workaround isn't reentrant. */
312 static int in_lock_cleanup = 0;
315 (void) fprintf (stderr, "%s-> Lock_Cleanup()\n", CLIENT_SERVER_STR);
323 dellist (&lock_tree_list);
325 if (locked_dir != NULL)
327 dellist (&locked_list);
333 if (global_history_lock.repository) clear_history_lock ();
334 if (global_val_tags_lock.repository) clear_val_tags_lock ();
340 * Remove locks without discarding the lock information
345 /* clean up simple locks (if any) */
346 if (global_readlock.repository != NULL)
348 lock_simple_remove (&global_readlock);
349 global_readlock.repository = NULL;
352 /* clean up multiple locks (if any) */
353 if (locklist != (List *) NULL)
355 (void) walklist (locklist, unlock_proc, NULL);
356 locklist = (List *) NULL;
361 * walklist proc for removing a list of locks
364 unlock_proc (p, closure)
368 lock_simple_remove (p->data);
374 /* Remove the lock files. */
376 lock_simple_remove (lock)
381 /* If readlock is set, the lock directory *might* have been created, but
382 since Reader_Lock doesn't use SIG_beginCrSect the way that set_lock
383 does, we don't know that. That is why we need to check for
384 existence_error here. */
385 if (readlock != NULL)
387 tmp = lock_name (lock->repository, readlock);
388 if (CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
389 error (0, errno, "failed to remove lock %s", tmp);
393 /* If writelock is set, the lock directory *might* have been created, but
394 since write_lock doesn't use SIG_beginCrSect the way that set_lock
395 does, we don't know that. That is why we need to check for
396 existence_error here. */
397 if (writelock != NULL)
399 tmp = lock_name (lock->repository, writelock);
400 if (CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
401 error (0, errno, "failed to remove lock %s", tmp);
411 * Create a lock file for readers
414 Reader_Lock (xrepository)
422 (void) fprintf (stderr, "%s-> Reader_Lock(%s)\n", CLIENT_SERVER_STR,
425 if (noexec || readonlyfs)
428 /* we only do one directory at a time for read locks! */
429 if (global_readlock.repository != NULL)
431 error (0, 0, "Reader_Lock called while read locks set - Help!");
435 if (readlock == NULL)
437 readlock = xmalloc (strlen (hostname) + sizeof (CVSRFL) + 40);
438 (void) sprintf (readlock,
439 #ifdef HAVE_LONG_FILE_NAMES
440 "%s.%s.%ld", CVSRFL, hostname,
447 /* remember what we're locking (for Lock_Cleanup) */
448 global_readlock.repository = xrepository;
450 /* get the lock dir for our own */
451 if (set_lock (&global_readlock, 1) != L_OK)
453 error (0, 0, "failed to obtain dir lock in repository `%s'",
455 if (readlock != NULL)
458 /* We don't set global_readlock.repository to NULL. I think this
459 only works because recurse.c will give a fatal error if we return
464 /* write a read-lock */
465 tmp = lock_name (xrepository, readlock);
466 if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF)
468 error (0, errno, "cannot create read lock in repository `%s'",
470 if (readlock != NULL)
477 /* free the lock dir */
478 clear_lock (&global_readlock);
486 * Lock a list of directories for writing
488 static char *lock_error_repos;
489 static int lock_error;
491 static int Writer_Lock PROTO ((List * list));
503 error (0, 0, "write lock failed - read-only repository");
507 /* We only know how to do one list at a time */
508 if (locklist != (List *) NULL)
510 error (0, 0, "Writer_Lock called while write locks set - Help!");
517 /* try to lock everything on the list */
518 lock_error = L_OK; /* init for set_writelock_proc */
519 lock_error_repos = (char *) NULL; /* init for set_writelock_proc */
520 locklist = list; /* init for Lock_Cleanup */
521 if (lockers_name != NULL)
523 lockers_name = xstrdup ("unknown");
525 (void) walklist (list, set_writelock_proc, NULL);
529 case L_ERROR: /* Real Error */
530 if (wait_repos != NULL)
532 Lock_Cleanup (); /* clean up any locks we set */
533 error (0, 0, "lock failed - giving up");
536 case L_LOCKED: /* Someone already had a lock */
537 remove_locks (); /* clean up any locks we set */
538 lock_wait (lock_error_repos); /* sleep a while and try again */
539 wait_repos = xstrdup (lock_error_repos);
542 case L_OK: /* we got the locks set */
543 if (wait_repos != NULL)
545 lock_obtained (wait_repos);
551 if (wait_repos != NULL)
553 error (0, 0, "unknown lock status %d in Writer_Lock",
563 * walklist proc for setting write locks
566 set_writelock_proc (p, closure)
570 /* if some lock was not OK, just skip this one */
571 if (lock_error != L_OK)
574 /* apply the write lock */
575 lock_error_repos = p->key;
576 lock_error = write_lock (p->data);
583 * Create a lock file for writers returns L_OK if lock set ok, L_LOCKED if
584 * lock held by someone else or L_ERROR if an error occurred
595 (void) fprintf (stderr, "%s-> write_lock(%s)\n",
596 CLIENT_SERVER_STR, lock->repository);
598 if (writelock == NULL)
600 writelock = xmalloc (strlen (hostname) + sizeof (CVSWFL) + 40);
601 (void) sprintf (writelock,
602 #ifdef HAVE_LONG_FILE_NAMES
603 "%s.%s.%ld", CVSWFL, hostname,
610 /* make sure the lock dir is ours (not necessarily unique to us!) */
611 status = set_lock (lock, 0);
614 /* we now own a writer - make sure there are no readers */
615 if (readers_exist (lock->repository))
617 /* clean up the lock dir if we created it */
623 /* indicate we failed due to read locks instead of error */
627 /* write the write-lock file */
628 tmp = lock_name (lock->repository, writelock);
629 if ((fp = CVS_FOPEN (tmp, "w+")) == NULL || fclose (fp) == EOF)
633 if ( CVS_UNLINK (tmp) < 0 && ! existence_error (errno))
634 error (0, errno, "failed to remove lock %s", tmp);
636 /* free the lock dir if we created it */
642 /* return the error */
643 error (0, xerrno, "cannot create write lock in repository `%s'",
658 * readers_exist() returns 0 if there are no reader lock files remaining in
659 * the repository; else 1 is returned, to indicate that the caller should
660 * sleep a while and try again.
663 readers_exist (repository)
672 #ifdef CVS_FUDGELOCKS
677 lockdir = lock_name (repository, "");
679 assert (lockdir != NULL);
681 lockdir[strlen (lockdir) - 1] = '\0'; /* remove trailing slash */
684 if ((dirp = CVS_OPENDIR (lockdir)) == NULL)
685 error (1, 0, "cannot open directory %s", lockdir);
689 while ((dp = CVS_READDIR (dirp)) != NULL)
691 if (CVS_FNMATCH (CVSRFLPAT, dp->d_name, 0) == 0)
693 line = xmalloc (strlen (lockdir) + 1 + strlen (dp->d_name) + 1);
694 (void)sprintf (line, "%s/%s", lockdir, dp->d_name);
695 if (CVS_STAT (line, &sb) != -1)
697 #ifdef CVS_FUDGELOCKS
699 * If the create time of the file is more than CVSLCKAGE
700 * seconds ago, try to clean-up the lock file, and if
701 * successful, re-open the directory and try again.
703 if (now >= (sb.st_ctime + CVSLCKAGE) &&
704 CVS_UNLINK (line) != -1)
711 set_lockers_name (&sb);
715 /* If the file doesn't exist, it just means that it
716 * disappeared between the time we did the readdir and the
717 * time we did the stat.
719 if (!existence_error (errno))
720 error (0, errno, "cannot stat %s", line);
730 error (0, errno, "error reading directory %s", repository);
743 * Set the static variable lockers_name appropriately, based on the stat
744 * structure passed in.
747 set_lockers_name (statp)
752 if (lockers_name != NULL)
754 if ((pw = (struct passwd *)getpwuid (statp->st_uid)) !=
755 (struct passwd *)NULL)
757 lockers_name = xstrdup (pw->pw_name);
761 lockers_name = xmalloc (20);
762 (void)sprintf (lockers_name, "uid%lu", (unsigned long) statp->st_uid);
769 * Persistently tries to make the directory "lckdir", which serves as a
772 * #ifdef CVS_FUDGELOCKS
773 * If the create time on the directory is greater than CVSLCKAGE
774 * seconds old, just try to remove the directory.
779 set_lock (lock, will_wait)
789 #ifdef CVS_FUDGELOCKS
793 masterlock = lock_name (lock->repository, lock->lockdirname);
796 * Note that it is up to the callers of set_lock() to arrange for signal
797 * handlers that do the appropriate things, like remove the lock
798 * directory before they exit.
805 omask = umask (cvsumask);
807 if (CVS_MKDIR (masterlock, 0777) == 0)
809 lock->lockdir = masterlock;
813 lock_obtained (lock->repository);
814 goto after_sig_unblock;
818 (void) umask (omask);
825 "failed to create lock directory for `%s' (%s)",
826 lock->repository, masterlock);
831 /* Find out who owns the lock. If the lock directory is
832 non-existent, re-try the loop since someone probably just
833 removed it (thus releasing the lock). */
834 if (CVS_STAT (masterlock, &sb) < 0)
836 if (existence_error (errno))
839 error (0, errno, "couldn't stat lock directory `%s'", masterlock);
844 #ifdef CVS_FUDGELOCKS
846 * If the create time of the directory is more than CVSLCKAGE seconds
847 * ago, try to clean-up the lock directory, and if successful, just
848 * quietly retry to make it.
851 if (now >= (sb.st_ctime + CVSLCKAGE))
853 if (CVS_RMDIR (masterlock) >= 0)
858 /* set the lockers name */
859 set_lockers_name (&sb);
861 /* if he wasn't willing to wait, return an error */
868 /* if possible, try a very short sleep without a message */
869 if (!waited && us < 1000)
872 #if defined HAVE_NANOSLEEP
876 ts.tv_nsec = us * 1000;
877 (void)nanosleep (&ts, NULL);
880 #elif defined HAVE_USLEEP
883 #elif defined HAVE_SELECT
888 (void)select (0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL, &tv);
894 lock_wait (lock->repository);
898 if (!lock->lockdir) free (masterlock);
908 * lock The lock information.
911 * Sets LOCK->lockdir to NULL after removing the directory it names and
912 * freeing the storage.
915 * If we own the master lock directory, its name is stored in LOCK->lockdir.
916 * We may free LOCK->lockdir.
926 if (CVS_RMDIR (lock->lockdir) < 0)
927 error (0, errno, "failed to remove lock dir `%s'", lock->lockdir);
928 free (lock->lockdir);
929 lock->lockdir = NULL;
937 * Print out a message that the lock is still held, then sleep a while.
948 tm_p = gmtime (&now);
949 msg = xmalloc (100 + strlen (lockers_name) + strlen (repos));
950 sprintf (msg, "[%8.8s] waiting for %s's lock in %s",
951 (tm_p ? asctime (tm_p) : ctime (&now)) + 11,
952 lockers_name, repos);
953 error (0, 0, "%s", msg);
954 /* Call cvs_flusherr to ensure that the user sees this message as
958 (void) sleep (CVSLCKSLEEP);
962 * Print out a message when we obtain a lock.
965 lock_obtained (repos)
973 tm_p = gmtime (&now);
974 msg = xmalloc (100 + strlen (repos));
975 sprintf (msg, "[%8.8s] obtained lock in %s",
976 (tm_p ? asctime (tm_p) : ctime (&now)) + 11, repos);
977 error (0, 0, "%s", msg);
978 /* Call cvs_flusherr to ensure that the user sees this message as
986 static int lock_filesdoneproc PROTO ((void *callerdat, int err,
987 const char *repository,
988 const char *update_dir,
992 * Create a list of repositories to lock
996 lock_filesdoneproc (callerdat, err, repository, update_dir, entries)
999 const char *repository;
1000 const char *update_dir;
1007 p->key = xstrdup (repository);
1008 p->data = xmalloc (sizeof (struct lock));
1009 ((struct lock *)p->data)->repository = p->key;
1010 ((struct lock *)p->data)->lockdirname = CVSLCK;
1011 ((struct lock *)p->data)->lockdir = NULL;
1013 /* FIXME-KRP: this error condition should not simply be passed by. */
1014 if (p->key == NULL || addnode (lock_tree_list, p) != 0)
1020 lock_tree_for_write (argc, argv, local, which, aflag)
1028 * Run the recursion processor to find all the dirs to lock and lock all
1031 lock_tree_list = getlist ();
1032 start_recursion ((FILEPROC) NULL, lock_filesdoneproc,
1033 (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL, argc,
1034 argv, local, which, aflag, CVS_LOCK_NONE,
1035 (char *) NULL, 0, (char *) NULL);
1036 sortlist (lock_tree_list, fsortcmp);
1037 if (Writer_Lock (lock_tree_list) != 0)
1038 error (1, 0, "lock failed - giving up");
1041 /* Lock a single directory in REPOSITORY. It is OK to call this if
1042 a lock has been set with lock_dir_for_write; the new lock will replace
1043 the old one. If REPOSITORY is NULL, don't do anything. */
1045 lock_dir_for_write (repository)
1048 if (repository != NULL
1049 && (locked_dir == NULL
1050 || strcmp (locked_dir, repository) != 0))
1054 if (locked_dir != NULL)
1057 locked_dir = xstrdup (repository);
1058 locked_list = getlist ();
1061 node->key = xstrdup (repository);
1062 node->data = xmalloc (sizeof (struct lock));
1063 ((struct lock *)node->data)->repository = node->key;
1064 ((struct lock *)node->data)->lockdirname = CVSLCK;
1065 ((struct lock *)node->data)->lockdir = NULL;
1067 (void) addnode (locked_list, node);
1068 Writer_Lock (locked_list);
1074 /* This is the internal implementation behind history_lock & val_tags_lock. It
1075 * gets a write lock for the history or val-tags file.
1081 static int internal_lock PROTO ((struct lock *lock, const char *xrepository));
1083 internal_lock (lock, xrepository)
1085 const char *xrepository;
1087 /* remember what we're locking (for Lock_Cleanup) */
1088 assert (!lock->repository);
1089 lock->repository = xmalloc (strlen (xrepository) + sizeof (CVSROOTADM) + 2);
1090 sprintf (lock->repository, "%s/%s", xrepository, CVSROOTADM);
1092 /* get the lock dir for our own */
1093 if (set_lock (lock, 1) != L_OK)
1096 error (0, 0, "failed to obtain history lock in repository `%s'",
1107 /* This is the internal implementation behind history_lock & val_tags_lock. It
1108 * removes the write lock for the history or val-tags file, when it exists.
1110 static void internal_clear_lock PROTO((struct lock *lock));
1112 internal_clear_lock (lock)
1116 if (lock->repository)
1118 free (lock->repository);
1119 lock->repository = NULL;
1128 /* Lock the CVSROOT/history file for write.
1131 history_lock (xrepository)
1132 const char *xrepository;
1134 return internal_lock (&global_history_lock, xrepository);
1139 /* Remove the CVSROOT/history lock, if it exists.
1142 clear_history_lock ()
1144 internal_clear_lock (&global_history_lock);
1149 /* Lock the CVSROOT/val-tags file for write.
1152 val_tags_lock (xrepository)
1153 const char *xrepository;
1155 return internal_lock (&global_val_tags_lock, xrepository);
1160 /* Remove the CVSROOT/val-tags lock, if it exists.
1163 clear_val_tags_lock ()
1165 internal_clear_lock (&global_val_tags_lock);