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

lock_open.c

/* Distributed Checksum Clearinghouse server database functions
 *
 * 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.37 $Revision$
 */

#include "dcc_defs.h"
#include "dcc_paths.h"


#ifndef DCC_WIN32
static uid_t dcc_real_uid, dcc_effective_uid;
#endif

/* We must be SUID to read and write the system's common
 *    connection parameter memory mapped file.
 *    If the real UID is 0, then forget any SUID stuff and run as root.
 *    Otherwise remember the powerful UID and the real UID, and
 *    release the privilege of using the powerful UID */
void
dcc_init_priv(void)
{
#ifndef DCC_WIN32
      dcc_real_uid = getuid();
      if (dcc_real_uid == 0) {
            dcc_effective_uid = 0;
      } else {
            dcc_effective_uid = geteuid();
      }

      if (0 > seteuid(dcc_real_uid))
            dcc_error_msg("seteuid(%d): %s",
                        (int)dcc_real_uid, ERROR_STR());
#endif
}



#ifndef DCC_WIN32
static void
dcc_get_priv(void)
{
      if (dcc_real_uid != dcc_effective_uid
          && 0 > seteuid(dcc_effective_uid))
            dcc_error_msg("seteuid(%d): %s",
                        (int)dcc_effective_uid, ERROR_STR());
}



/* get set-UID privilege if the file is in the DCC home directory */
u_char                              /* 0=bad idea, 1=have privilege */
dcc_get_priv_home(const char *nm)
{
      DCC_PATH abs_nm;

      if (dcc_real_uid == dcc_effective_uid)
            return 0;

      dcc_fnm2path(abs_nm, nm);
      if (strncmp(abs_nm, dcc_homedir, strlen(dcc_homedir))
          || strstr(abs_nm+strlen(dcc_homedir)-1, "/../"))
            return 0;

      dcc_get_priv();
      return 1;
}
#endif



void
dcc_rel_priv(void)
{
#ifndef DCC_WIN32
      int serrno;

      if (dcc_real_uid != dcc_effective_uid) {
            serrno = errno;
            if (0 > seteuid(dcc_real_uid))
                  dcc_error_msg("seteuid(%d): %s",
                              (int)dcc_real_uid, ERROR_STR());
            errno = serrno;
      }
#endif
}



/* see if a file is private */
u_char
dcc_ck_private(DCC_EMSG emsg, struct stat *sb, const char *nm, int fd)
{
      struct stat sb0;
      int i;

      if (!sb)
            sb = &sb0;

      if (fd == -1) {
            i = stat(nm, sb);
      } else {
            i = fstat(fd, sb);
      }
      if (i < 0) {
            dcc_pemsg(EX_IOERR, emsg, "stat(%s): %s",
                    DCC_NM2PATH(nm), ERROR_STR());
            return 0;
      }
#ifdef HAVE_PRIVATE_FILES
      /* even on systems like Windows without private files,
       * some callers want the results of the stat() */
      if ((sb->st_mode & (S_IRGRP|S_IWGRP|S_IWOTH|S_IXOTH)) != 0) {
            dcc_pemsg(EX_NOPERM, emsg,
                    "%s is not private", DCC_NM2PATH(nm));
            return 0;
      }
#endif
      return 1;
}



#ifndef DCC_WIN32
/* #define DEBUG_FLOCK */
#ifdef DEBUG_FLOCK
#define FSTAT_NM(nm) {char cmd[200];                              \
      snprintf(cmd, sizeof(cmd), "fstat %s > /dev/console", nm);  \
      system(cmd);                                          \
}
#else
#define FSTAT_NM(nm)
#endif


static void
set_fl(struct flock *fl, int type, int byte_num)
{
      fl->l_type = type;
      fl->l_whence = SEEK_SET;
      if (byte_num == DCC_LOCK_ALL_FILE) {
            fl->l_start = 0;
            fl->l_len = 0;
      } else {
            fl->l_start = byte_num;
            fl->l_len = 1;
      }
}
#endif /* DCC_WIN32 */



/* open a file with a (possibly pretend) lock
 *    If flags does not include O_RDWR, then lock_mode must be 2 */
int                           /* -1=failed & emsg set, >=0 if done */
dcc_lock_open(DCC_EMSG emsg,
            const char *nm,
            int open_flags,         /* eg. O_CREAT */
            u_char lock_mode,       /* DCC_LOCK_OPEN_* */
            int lock_num,           /* which byte of the file to lock */
            u_char *busy)           /* 1=file is busy */
{
#ifdef DCC_WIN32
/* Win95 and Win98 does not include UNIX style file locking,
 * including non-blocking locks and upgrading locks from shared to exclusive.
 * In principle they could be constructed from the WIN32 synchronization
 * primitives and a few bytes of shared memory.  Win98 does not even
 * have a blocking file lock function.
 */
      HANDLE h;
      int fd;

      h = CreateFile(nm,
                   ((open_flags & O_RDWR)
                  ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE)),
                   FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
                   ((open_flags & O_EXCL)
                  ? CREATE_NEW
                  : (open_flags & O_CREAT)
                  ? CREATE_ALWAYS
                  : OPEN_EXISTING),
                   FILE_ATTRIBUTE_NORMAL, 0);
      if (h == INVALID_HANDLE_VALUE) {
            dcc_pemsg(EX_IOERR, emsg, "CreateFile(%s): %s",
                    DCC_NM2PATH(nm), ERROR_STR());
            if (busy)
                  *busy = 0;
            return -1;
      }
      fd = _open_osfhandle((long)h, 0);
      if (fd < 0) {
            dcc_pemsg(EX_SOFTWARE, emsg, "_open_osfhandle(%s): %s",
                    nm, ERROR_STR());
            CloseHandle(h);
            if (busy)
                  *busy = 0;
            return -1;
      }
      if (lock_mode != DCC_LOCK_OPEN_EXT) {
            if (!win32_lock(h, (lock_mode == DCC_LOCK_OPEN_WAIT
                            ? LOCKFILE_EXCLUSIVE_LOCK
                            : (LOCKFILE_FAIL_IMMEDIATELY
                               | LOCKFILE_EXCLUSIVE_LOCK)))) {
                  if (!busy
                      || lock_mode != DCC_LOCK_OPEN_WAIT
                      || (GetLastError() != ERROR_LOCK_VIOLATION
                        && GetLastError() != ERROR_LOCK_FAILED))
                        dcc_pemsg(EX_IOERR, emsg,
                                "open LockFileEx(%s): %s",
                                DCC_NM2PATH(nm), ERROR_STR());
                  if (busy)
                        *busy = (GetLastError() == ERROR_LOCK_VIOLATION
                               || GetLastError() ==ERROR_LOCK_FAILED);
                  close(fd);
                  return -1;
            }
      }

#else /* !DCC_WIN32 */
      static u_char checked_stdio = 0;
      int fd;
      struct flock fl;

      /* ensure 0, 1, and 2 are open so none of our real files get
       * those file descriptors and are used as stdin, stdout, or stderr */
      if (!checked_stdio) {
            for (;;) {
                  fd = open(_PATH_DEVNULL, O_RDWR, 0666);
                  if (fd < 0)
                        break;
                  if (fd > 2) {
                        close(fd);
                        break;
                  }
            }
            checked_stdio = 1;
      }

      fd = open(nm, open_flags, 0666);
      if (fd < 0 && dcc_get_priv_home(nm)) {
            fd = open(nm, open_flags, 0666);
            dcc_rel_priv();
      }
      if (fd < 0) {
            /* generate message only if necessary */
            if (!busy
                || lock_mode != DCC_LOCK_OPEN_NOWAIT
                || !DCC_BLOCK_ERROR()) {
                  if (errno == EACCES)
                        dcc_pemsg(EX_NOINPUT, emsg,
                                "lock open(%s): %s;"
                                " file not writeable for locking",
                                DCC_NM2PATH(nm), ERROR_STR());
                  else
                        dcc_pemsg(EX_NOINPUT, emsg,
                                "lock open(%s): %s",
                                DCC_NM2PATH(nm), ERROR_STR());
            }
            if (busy)
                  *busy = DCC_BLOCK_ERROR();
            FSTAT_NM(nm);
            return -1;
      }

      if (0 > fcntl(fd, F_SETFD, FD_CLOEXEC)) {
            if (busy)
                  *busy = 0;
            dcc_pemsg(EX_IOERR, emsg,
                    "fcntl(F_SETFD FD_CLOEXEC %s %d): %s",
                    DCC_NM2PATH(nm), fd, ERROR_STR());
            close(fd);
            return -1;
      }

      /* We may already have a lock on the file under a different file
       * descriptor.  If not, try to get a lock */
      if (lock_mode != DCC_LOCK_OPEN_EXT) {
            set_fl(&fl, F_WRLCK, lock_num);
            if (0 > fcntl(fd, (lock_mode == DCC_LOCK_OPEN_WAIT
                           ? F_SETLKW : F_SETLK),
                        &fl)) {
                  /* generate message only if necessary */
                  if (!busy
                      || lock_mode != DCC_LOCK_OPEN_WAIT
                      || (!DCC_BLOCK_ERROR()
                        && errno != EACCES))    /* for SunOS */
                        dcc_pemsg(EX_NOINPUT, emsg,
                                "open fcntl(%s F_WRLCK %s %d): %s",
                                (lock_mode == DCC_LOCK_OPEN_WAIT
                                 ? "F_SETLKW" : "F_SETLK"),
                                DCC_NM2PATH(nm), fd, ERROR_STR());
                  if (busy)
                        *busy = (DCC_BLOCK_ERROR()
                               || errno == EACCES);   /* for SunOS */
                  FSTAT_NM(nm);
                  close(fd);
                  return -1;
            }
      }
#endif /* !DCC_WIN32 or UNIX */

      if (busy)
            *busy = 0;
      return fd;
}



u_char                              /* 1=done 0=failed */
dcc_unlock_fd(DCC_EMSG emsg, int fd,
            int lock_num,           /* which byte of the file to unlock */
            const char *str, const char *nm)
{
#ifdef DCC_WIN32
      if (!win32_unlock((HANDLE)_get_osfhandle(fd))) {
            dcc_pemsg(EX_NOINPUT, emsg,
                    "UnlockFileEx(%s%s): %s",
                    str, DCC_NM2PATH(nm), ERROR_STR());
            return 0;
      }
      return 1;
#else
      struct flock fl;

      set_fl(&fl, F_UNLCK, lock_num);
      if (0 > fcntl(fd, F_SETLK, &fl)) {
            dcc_pemsg(EX_NOINPUT, emsg,
                    "fcntl(F_SETLK F_UNLCK %s%s %d): %s",
                    str, DCC_NM2PATH(nm), fd, ERROR_STR());
            return 0;
      }
      return 1;
#endif /* DCC_WIN32 */
}



u_char                              /* 1=done 0=failed */
dcc_shlock_fd(DCC_EMSG emsg, int fd,
            int lock_num,           /* which byte of the file to lock */
            const char *str, const char *nm)
{
#ifdef DCC_WIN32
      if (!win32_lock((HANDLE)_get_osfhandle(fd), 0)) {
            dcc_pemsg(EX_IOERR, emsg, "LockFileEx(%s %s): %s",
                    str, DCC_NM2PATH(nm), ERROR_STR());
            return 0;
      }
      return 1;
#else
      struct flock fl;

      set_fl(&fl, F_RDLCK, lock_num);
      if (0 > fcntl(fd, F_SETLKW, &fl)) {
            dcc_pemsg(EX_NOINPUT, emsg,
                    "fcntl(F_SETLKW F_RDLCK %s%s %d): %s",
                    str, DCC_NM2PATH(nm), fd, ERROR_STR());
            return 0;
      }
      return 1;
#endif /* DCC_WIN32 */
}



u_char                              /* 1=done 0=failed */
dcc_exlock_fd(DCC_EMSG emsg, int fd,
            int lock_num,           /* which byte of the file to lock */
            const char *str, const char *nm)
{
#ifdef DCC_WIN32
      if (!win32_lock((HANDLE)_get_osfhandle(fd), LOCKFILE_EXCLUSIVE_LOCK)) {
            dcc_pemsg(EX_IOERR, emsg, "LockFileEx(%s %s): %s",
                    str, DCC_NM2PATH(nm), ERROR_STR());
            return 0;
      }
      return 1;
#else
      struct flock fl;

      set_fl(&fl, F_WRLCK, lock_num);
      if (0 > fcntl(fd, F_SETLKW, &fl)) {
            dcc_pemsg(EX_NOINPUT, emsg,
                    "fcntl(F_SETLKW F_WRLCK %s%s %d): %s",
                    str, DCC_NM2PATH(nm), fd, ERROR_STR());
            return 0;
      }
      return 1;
#endif /* DCC_WIN32 */
}



/* Several systems do not update the mtimes of files modified with mmap().
 * Some like BSD/OS delay changing the mtime until the file accessed with
 * read().  Others including filesystems on some versions of Linux
 * apparently never change the mtime. */
void
dcc_mmap_utime(const char *nm, u_char now)
{
#ifdef HAVE_UTIME_H
      static const struct utimbuf never = {0, 0};
      int result;

      result = utime(nm, now ? 0 : &never);
      if (result < 0
          && dcc_real_uid != dcc_effective_uid) {
            dcc_get_priv();
            result = utime(nm, now ? 0 : &never);
            dcc_rel_priv();
      }
      if (result < 0)
            dcc_error_msg("utime(%s): %s",
                        DCC_NM2PATH(nm), ERROR_STR());

#endif
}

Generated by  Doxygen 1.6.0   Back to index