Logo Search packages:      
Sourcecode: dcc version File versions  Download package

mkstemp.c

/* Distributed Checksum Clearinghouse
 *
 * Copyright (c) 2005 by Rhyolite Software
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE
 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Rhyolite Software DCC 1.2.74-1.23 $Revision$
 */

#include "dcc_defs.h"
#ifdef DCC_WIN32
#include <direct.h>
#define MKDIR(nm,perm) mkdir(nm)
#else
#include <dirent.h>
#define MKDIR(nm,perm) mkdir(nm,perm)
#endif

static u_int32_t gen;


static char *
mkstr(char str[DCC_MKSTEMP_LEN+1])
{
      static const char digits[] = "0123456789"
                            "abcdefghijklmnopqrstuvwxyz"
                            "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
      u_int32_t n;
      int i;

      i = DCC_MKSTEMP_LEN;
      str[i] = '\0';
      n = ++gen;
      do {
            str[--i] = digits[n % (sizeof(digits)-1)];
            n /= (sizeof(digits)-1);
      } while (i > 0);
      return str;
}



#define MKSEQ() (gen += (getpid() << 16) + time(0))



/* Some platforms have broken implementations of mkstemp() that generate
 * only 32 different names.
 * Given the main uses for mkstemp() in the DCC for log files in directories
 * used only by the DCC, it is nice to try to make the names sort of
 * sequential for a given dccm process.  That means nothing more than putting
 * the random bits in the most significant bits of the seed and using
 * a small constant for the addition in the random number
 * generator that is commonly used, and remembering the generator. */
int                           /* -1 or FD of temporary file */
dcc_mkstemp(DCC_EMSG emsg,
          char *nm, int nm_len,     /* put the name here */
          const char *p1,           /* concatenate these strings */
          const char *p2,           /*    to generate the name */
          u_char close_del,         /* 1=delete on close */
          int gen_delta,            /* adjust generator by this */
          int mode)                 /* add these mode bits */
{
      char str[DCC_MKSTEMP_LEN+1];
      int fd, i;
#ifdef DCC_WIN32
      HANDLE h;
      DWORD flags;
#endif

      if (!gen) {
            MKSEQ();
      } else {
            gen += gen_delta;
      }

      /* this loop should almost always need only a single pass */
      for (;;) {
            i = snprintf(nm, nm_len, "%s%s%s",
                       p1 ? p1 : "", p2 ? p2 : "", mkstr(str));
            if (i >= nm_len) {
                  dcc_pemsg(EX_SOFTWARE, emsg,
                          "temporary file name \"%s\" too big", nm);
                  return -1;
            }

#ifdef DCC_WIN32
            /* Given the uses of this function, if the temporary
             * file is open, then it has been abandoned.  All valid
             * uses have the file renamed.  Windows does not allow
             * open files to be unlinked.  FILE_FLAGS_DELETE_ON_CLOSE
             * does not seem to be supported everywhere or it is
             * unreliable.  So open existing files without sharing
             * but truncated.
             * Use CreateFile() because the Borland and Microsoft
             * open(), _open(), and _sopen() functions differ */
            flags = (close_del
                   ? FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE
                   : FILE_ATTRIBUTE_NORMAL);
            h = CreateFile(nm, GENERIC_READ | GENERIC_WRITE, 0,
                         0, CREATE_NEW, flags, 0);
            if (h == INVALID_HANDLE_VALUE)
                  h = CreateFile(nm, GENERIC_READ | GENERIC_WRITE, 0,
                               0, TRUNCATE_EXISTING, flags, 0);
            if (h != INVALID_HANDLE_VALUE) {
                  fd = _open_osfhandle((long)h, 0);
                  if (fd >= 0)
                        return fd;
                  dcc_pemsg(EX_SOFTWARE, emsg,
                          "_open_osfhandle(%s): %s",
                          nm, ERROR_STR());
                  CloseHandle(h);
                  return -1;
            }
#else
            fd = open(nm, O_RDWR | O_CREAT | O_EXCL, (mode & 0777) | 0600);
            if (fd >= 0) {
                  if (close_del
                      && 0 > unlink(nm)) {
                        dcc_pemsg(EX_SOFTWARE, emsg, "unlink(%s): %s",
                                nm, ERROR_STR());
                        close(fd);
                        return -1;
                  }
                  return fd;
            }
#endif

            if (errno != EEXIST) {
                  dcc_pemsg(EX_IOERR, emsg, "open(%s): %s",
                          nm, ERROR_STR());
                  return -1;
            }

            /* there is already a file of that name,
             * so look for another sequence of names */
            MKSEQ();
      }
}


static enum {
      LOG_FLAT,               /* use logdir/ */
      LOG_DAY,                /* use logdir/ddd/ */
      LOG_HOUR,               /* use logdir/ddd/hh/ */
      LOG_MIN                       /* use logdir/ddd/hh/mm/ */
} log_mode;

DCC_PATH dcc_logdir;
static DCC_PATH log_daydir_pat;
static DCC_PATH log_hourdir_pat;
static DCC_PATH log_mindir_pat;
static int dcc_logdir_len;          /* strlen(dcc_logdir)+subdirectory */
char dcc_have_logdir;               /* create log files here */



int                           /* 1=ok, 0=non-existent, -1=bad */
dcc_logdir_ck(DCC_EMSG emsg,
            const char *dir,        /* check this name */
            int *modep)       /* put file mode bits here */
{
      struct stat sb;

      if (0 > stat(dir, &sb)) {
            if (errno == ENOTDIR || errno == ENOENT) {
                  dcc_pemsg(EX_IOERR, emsg, "stat(log directory %s): %s",
                          DCC_NM2PATH(dir), ERROR_STR());
                  return 0;
            } else {
                  dcc_pemsg(EX_IOERR, emsg, "stat(log directory %s): %s",
                          DCC_NM2PATH(dir), ERROR_STR());
                  return -1;
            }
      }

      if (!S_ISDIR(sb.st_mode)) {
            dcc_pemsg(EX_IOERR, emsg, "log directory %s is %s",
                    DCC_NM2PATH(dir), ERROR_STR1(ENOTDIR));
            return -1;
      }

#ifndef DCC_WIN32
      if (0 > access(dir, R_OK|W_OK|X_OK)) {
            dcc_pemsg(EX_IOERR, emsg, "access(log directory %s): %s",
                    DCC_NM2PATH(dir), ERROR_STR());
            return -1;
      }
#endif

      if (modep)
            *modep = sb.st_mode & 0666;
      return 1;
}



/* check and initialize main DCC client log directory */
u_char
dcc_log_init(DCC_EMSG emsg, const char *arg)
{
      /* if the directory name starts with "D?", "H?", or "M?",
       * automatically add subdirectories */
      if (!CSTRCMP(arg, "D?")) {
            log_mode = LOG_DAY;
            dcc_logdir_len = 4;
            arg += 2;

      } else if (!CSTRCMP(arg, "H?")) {
            log_mode = LOG_HOUR;
            dcc_logdir_len = 4+3;
            arg += 2;

      } else if (!CSTRCMP(arg, "M?")) {
            log_mode = LOG_MIN;
            dcc_logdir_len = 4+3+3;
            arg += 2;

      } else {
            log_mode = LOG_FLAT;
            dcc_logdir_len = 0;
      }

      dcc_logdir_len += strlen(arg);
      snprintf(dcc_logdir, sizeof(dcc_logdir), "%s", arg);
      snprintf(log_daydir_pat, sizeof(log_daydir_pat), "%s/%%j", arg);
      snprintf(log_hourdir_pat, sizeof(log_hourdir_pat), "%s/%%j/%%H", arg);
      snprintf(log_mindir_pat, sizeof(log_mindir_pat), "%s/%%j/%%H/%%M", arg);

      if (dcc_logdir_ck(emsg, dcc_logdir, 0) <= 0) {
            dcc_logdir_len = 0;
            dcc_have_logdir = 0;
            return 0;
      }

      dcc_have_logdir = 1;
      return 1;
}



/* create and open a DCC client log file */
int
dcc_log_open(DCC_EMSG emsg,
           char *nm, int nm_len)    /* put the name here */
{
      time_t now;
      struct tm tm;
      DCC_PATH dir;
      const char *p1;

      if (nm_len < dcc_logdir_len + DCC_MKSTEMP_LEN + 1) {
            dcc_pemsg(EX_SOFTWARE, emsg,
                    "directory path buffer too small");
            return -1;
      }


      if (log_mode == LOG_FLAT) {
            p1 = dcc_logdir;
      } else {
            now = time(0);
            strftime(dir, sizeof(dir), log_daydir_pat,
                   dcc_localtime(now, &tm));
            MKDIR(dir, 0755);

            if (log_mode == LOG_HOUR
                || log_mode == LOG_MIN) {
                  strftime(dir, sizeof(dir), log_hourdir_pat, &tm);
                  MKDIR(dir, 0755);
                  if (log_mode == LOG_MIN) {
                        strftime(dir, sizeof(dir), log_mindir_pat, &tm);
                        MKDIR(dir, 0755);
                  }
            }
            p1 = dir;
      }

      return dcc_mkstemp(emsg, nm, nm_len, p1, DCC_TMP_LOG_PAT, 0, 0, 0);
}



/* rename a DCC client log file to a unique name */
u_char
dcc_log_keep(DCC_EMSG emsg, char *cur_nm, int cur_nm_len)
{
      DCC_PATH new_nm;
      char str[DCC_MKSTEMP_LEN+1];

      if (cur_nm_len <= (dcc_logdir_len
                     + ISZ(DCC_FIN_LOG_PAT) + DCC_MKSTEMP_LEN)
          || cur_nm_len <= (dcc_logdir_len
                        + ISZ(DCC_TMP_LOG_PAT) + DCC_MKSTEMP_LEN)) {
            dcc_pemsg(EX_SOFTWARE, emsg,
                    "dcc_log_keep(): target path buffer too small");
            return 0;
      }
      memcpy(new_nm, cur_nm, dcc_logdir_len);
      memcpy(new_nm+dcc_logdir_len, DCC_FIN_LOG_PAT,
             sizeof(DCC_FIN_LOG_PAT)-1);
      strcpy(new_nm+dcc_logdir_len+sizeof(DCC_FIN_LOG_PAT)-1,
             cur_nm+dcc_logdir_len+sizeof(DCC_TMP_LOG_PAT)-1);

      for (;;) {
#ifdef DCC_WIN32
            /* Windows does not have hard links */
            if (!rename(cur_nm, new_nm)) {
                  strncpy(cur_nm, new_nm, cur_nm_len);
                  return 1;
            }
            if (errno != EACCES) {
                  dcc_pemsg(EX_IOERR, emsg, "rename(%s,%s): %s",
                          DCC_NM2PATH(cur_nm), new_nm, ERROR_STR());
                  return 0;
            }
#else
            /* rename() on some UNIX systems deletes existing new_nm */
            if (link(cur_nm, new_nm) >= 0) {
                  if (unlink(cur_nm) < 0) {
                        strncpy(cur_nm, new_nm, cur_nm_len);
                        dcc_pemsg(EX_IOERR, emsg, "unlink(%s): %s",
                                DCC_NM2PATH(cur_nm), ERROR_STR());
                        return 0;
                  }
                  strncpy(cur_nm, new_nm, cur_nm_len);
                  return 1;
            }
            if (errno != EEXIST) {
                  dcc_pemsg(EX_IOERR, emsg, "link(%s,%s): %s",
                          DCC_NM2PATH(cur_nm), new_nm, ERROR_STR());
                  return 0;
            }
#endif

            /* if our name was taken,
             * look for another sequence of names */
            MKSEQ();
            strcpy(new_nm+dcc_logdir_len+sizeof(DCC_FIN_LOG_PAT)-1,
                   mkstr(str));
      }
}

Generated by  Doxygen 1.6.0   Back to index