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