NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: PR/35619 CVS commit: src/libexec/makewhatis



The following reply was made to PR bin/35619; it has been noted by GNATS.

From: David Holland <dholland-bugs%netbsd.org@localhost>
To: gnats-bugs%netbsd.org@localhost, netbsd-bugs%netbsd.org@localhost, 
uwe%NetBSD.org@localhost
Cc: 
Subject: Re: PR/35619 CVS commit: src/libexec/makewhatis
Date: Tue, 27 May 2008 02:21:19 +0000

 On Tue, May 27, 2008 at 01:45:01AM +0000, David A. Holland wrote:
  > Urgh, part of another patch for PR 35619 (which is for after the freeze)
  > [...]
 
 Here's that patch, which I'll commit after the freeze is over. This
 should thoroughly take care of the race condition.
 
 diff -r f75028e978ef makewhatis.c
 --- a/makewhatis.c     Mon May 26 21:52:34 2008 -0400
 +++ b/makewhatis.c     Mon May 26 22:24:53 2008 -0400
 @@ -248,6 +248,8 @@
        whatis  *dest;
        FILE    *out;
        size_t  sdoff, sdlen;
 +      int     outfd;
 +      struct stat st_before, st_after;
  
        if ((fts = fts_open(manpath, FTS_LOGICAL, NULL)) == NULL)
                err(EXIT_FAILURE, "Cannot open `%s'", *manpath);
 @@ -328,15 +330,74 @@
        if (chdir(manpath[0]) == -1)
                err(EXIT_FAILURE, "Cannot change dir to `%s'", manpath[0]);
  
 -      (void)unlink(whatisdb_new);
 -      if ((out = fopen(whatisdb_new, "w")) == NULL)
 +      /*
 +       * makewhatis runs unattended, so it needs to be able to
 +       * recover if the last run crashed out. Therefore, if
 +       * whatisdb_new exists and is more than (arbitrarily) sixteen
 +       * hours old, nuke it. If it exists but is not so old, refuse
 +       * to run until it's cleaned up, in case another makewhatis is
 +       * already running. Also, open the output with O_EXCL to make
 +       * sure we get our own, in case two copies start exactly at
 +       * once. (Unlikely? Maybe, maybe not, if two copies of cron
 +       * end up running.)
 +       *
 +       * Similarly, before renaming the file after we finish writing
 +       * to it, make sure it's still the same file we opened. This
 +       * can't be completely race-free, but getting caught by it
 +       * would require an unexplained sixteen-hour-or-more lag
 +       * between the last mtime update when we wrote to it and when
 +       * we get to the stat call *and* another makewhatis starting
 +       * out to write at exactly the wrong moment. Not impossible,
 +       * but not likely enough to worry about.
 +       *
 +       * This is maybe unnecessarily elaborate, but generating
 +       * corrupted output isn't so good either.
 +       */
 +
 +      if (stat(whatisdb_new, &st_before) == 0) {
 +              if (st_before.st_mtime - time(NULL) > 16*60*60) {
 +                      /* Don't complain if someone else just removed it. */
 +                      if (unlink(whatisdb_new) == -1 && errno != ENOENT) {
 +                              err(EXIT_FAILURE, "Could not remove `%s'",
 +                                  whatisdb_new);
 +                      } else {
 +                              warnx("Removed stale `%s'", whatisdb_new);
 +                      }
 +              } else {
 +                      errx(EXIT_FAILURE, "The file `%s' already exists "
 +                          "-- am I already running?", whatisdb_new);
 +              }
 +      } else if (errno != ENOENT) {
 +              /* Something unexpected happened. */
 +              err(EXIT_FAILURE, "Cannot stat `%s'", whatisdb_new);
 +      }
 +
 +      outfd = open(whatisdb_new, O_WRONLY|O_CREAT|O_EXCL,
 +          S_IRUSR|S_IRGRP|S_IROTH);
 +      if (outfd < 0)
                err(EXIT_FAILURE, "Cannot open `%s'", whatisdb_new);
  
 +      if (fstat(outfd, &st_before) == -1)
 +              err(EXIT_FAILURE, "Cannot fstat `%s'", whatisdb_new);
 +
 +      if ((out = fdopen(outfd, "w")) == NULL)
 +              err(EXIT_FAILURE, "Cannot fdopen `%s'", whatisdb_new);
 +
        dumpwhatis(out, dest);
        if (fchmod(fileno(out), S_IRUSR|S_IRGRP|S_IROTH) == -1)
                err(EXIT_FAILURE, "Cannot chmod `%s'", whatisdb_new);
        if (fclose(out) != 0)
                err(EXIT_FAILURE, "Cannot close `%s'", whatisdb_new);
 +
 +      if (stat(whatisdb_new, &st_after) == -1)
 +              err(EXIT_FAILURE, "Cannot stat `%s' (after writing)",
 +                  whatisdb_new);
 +
 +      if (st_before.st_dev != st_after.st_dev ||
 +          st_before.st_ino != st_after.st_ino) {
 +              errx(EXIT_FAILURE, "The file `%s' changed under me; giving up",
 +                  whatisdb_new);
 +      }
  
        if (rename(whatisdb_new, whatisdb) == -1)
                err(EXIT_FAILURE, "Could not rename `%s' to `%s'",
 
 
 
 -- 
 David A. Holland
 dholland%netbsd.org@localhost
 


Home | Main Index | Thread Index | Old Index