Logo Search packages:      
Sourcecode: darcs version File versions

compat.c

#include <sys/fcntl.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include <unistd.h>
#include <errno.h>
#include <sys/time.h>
#include <time.h>

int stdout_is_a_pipe() {
    struct stat s;
    int ret;
    ret = fstat(1, &s);
    if (ret == 0)
        return !S_ISFIFO(s.st_mode);
    else
        return 0;
}

#ifdef _WIN32
#include <windows.h>
#include <io.h>
#endif

static int sloppy_atomic_create(const char *p)
{
    int fd;
    fd = open(p, O_WRONLY | O_EXCL | O_CREAT, 0666);
    if(fd < 0)
        return -1;
    close(fd);
    return 1;
}

#ifdef _WIN32

int atomic_create(const char *p)
{
    return sloppy_atomic_create(p);
}

#else

/* memrchr is a GNU extension, and rindex is BSD.  Roll our own. */

static void *
my_memrchr(const void *s, int c, int n)
{
    const char *ss = (const char *)s;
    int i = n - 1;

    while(i >= 0) {
        if(ss[i] == c)
            return (void*)(ss + i);
        i--;
    }
    return NULL;
}

static int careful_atomic_create(const char *p)
{
    /* O_EXCL is not available over NFSv2, and even under NFSv3, it is
       broken on many systems.  The following protocol is provably
       safe assuming that:
       - creation of hard links is atomic;
       - stat hits the server rather than working from the cache.
    */

    static char hostname[65] = {'\0'};
    int fd, rc, saved_errno;
#define FILENAME_SIZE (11 + 15 + 8 + 1)
    char *filename;
    char *lastslash;
    int dirlen;
    struct timeval now;
    struct stat sb;

    if(hostname[0] == '\0') {
        char *c;
        /* POSIX guarantees 65 is enough. */
        rc = gethostname(hostname, 65);
        if(rc < 0 || rc >= 65) {
            fprintf(stderr, "Error reading hostname when locking.\n");
            strcpy(hostname, "kremvax");
        }
        c = strchr(hostname, '.');
        if(c != NULL)
            *c = '\0';
        hostname[15] = '\0';
    }

    lastslash = my_memrchr(p, '/', strlen(p));
    if(lastslash == NULL)
        dirlen = 0;
    else
        dirlen = lastslash - p + 1;

    filename = malloc(dirlen + FILENAME_SIZE);
    if(filename == NULL)
        return -1;

    if(dirlen > 0)
        memcpy(filename, p, dirlen);
    filename[dirlen] = '\0';

    gettimeofday(&now, NULL);

    rc = snprintf(filename + dirlen, FILENAME_SIZE, "darcs_lock_%s%04x%04x",
                  hostname, ((unsigned)getpid()) & 0xFFFF,
                  ((unsigned)(now.tv_usec ^ (now.tv_usec >> 16))) & 0xFFFF);
    if(rc < 0 || rc >= FILENAME_SIZE) {
        fprintf(stderr, "Error writing to lock filename (%d)\n", 
                rc < 0 ? errno : 0);
        goto fail2;
    }

    fd = open(filename, O_WRONLY | O_EXCL | O_CREAT, 0666);
    if(fd < 0)
        goto fail2;

    /* Paranoia: should cause the client to flush its metadata cache. */
    rc = close(fd);
    if(rc < 0) {
        fprintf(stderr, "Error closing file %s. (%d)\n", filename, errno);
        goto fail;
    }

    rc = link(filename, p);
    if(rc >= 0)
        goto success;
    else if(errno == EPERM || errno == EOPNOTSUPP) {
        /* Linux returns EPERM when making hard links on filesystems
           that don't support them. */
        /* It seems that MacOS returns EOPNOTSUPP on filesystems that
           don't support hard links. */
        unlink(filename);
        free(filename);
        return sloppy_atomic_create(p);
    } else if(errno != EEXIST && errno != EIO)
        goto fail;

    /* The link may still have been successful if we're running over
       UDP and got EEXIST or EIO.  Check the file's link count. */

    rc = stat(filename, &sb);
    if(rc < 0) {
        goto fail;
    }

    if(sb.st_nlink != 2) {
        errno = EEXIST;
        goto fail;
    }

 success:
    unlink(filename);
    free(filename);
    return 1;

 fail:
    saved_errno = errno;
    unlink(filename);
    errno = saved_errno;
 fail2:
    free(filename);
    return -1;
}

int atomic_create(const char *p)
{
    static int sloppy = -1;

    if(sloppy < 0) {
        char *s = getenv("DARCS_SLOPPY_LOCKS");
        sloppy = (s != NULL);
    }

    if(sloppy)
        return sloppy_atomic_create(p);
    else
        return careful_atomic_create(p);
}

#endif

#ifdef _WIN32
static int inited_rand = 0;
int mkstemp(char *p)
{
    char* p2;
    int fd;
    int len = strlen(p);
    if (len < 6)
        return -1;
    if (!inited_rand) {
      srand(time(0));
      inited_rand = 1;
    }
    snprintf(p+len-6, 6, "%06d", rand()+rand()<<16);
    fd = open(p, O_CREAT | O_EXCL | O_RDWR, 0666);
    return fd;
}

int pipe( int fildes[2] ) {
return _pipe( fildes, 8 * (2<<10), O_BINARY );
}

int isnt_symlink(const char *file) {
  return 1; /* FIXME: should ignore windows shortcuts */
}

static DWORD get_mode()
{
    DWORD console_mode;
    HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
    GetConsoleMode(stdin_handle, &console_mode);
    return console_mode;
}

int get_raw_mode()
{
    return (get_mode() & ENABLE_LINE_INPUT) == 0;
}

void set_raw_mode(int raw)
{
    HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE);
    DWORD console_mode = get_mode();
    if (raw)
        console_mode &= ~(ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT);
    else
        console_mode |= ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT;
    if (!SetConsoleMode(stdin_handle, console_mode))
        fprintf(stderr, "SetConsoleMode error: %x\n", GetLastError());
}


#else

int get_raw_mode()
{
    return 0;
}

void set_raw_mode(int raw)
{
}

int open_read(const char *fname) {
  return open(fname, O_RDONLY);
}

int open_write(const char *fname) {
  return open(fname, O_WRONLY | O_TRUNC | O_CREAT,
              S_IWUSR | S_IRUSR | S_IROTH | S_IRGRP);
}

#include <sys/wait.h>
#include <signal.h>

/* This waits and then returns the exit status of the process that was
   waited for. */
int smart_wait(int pid) {
  int stat;
  pid_t rc;

  do {
      rc = waitpid(pid, &stat, 0);
  } while(rc < 0 && errno == EINTR);

  if(rc < 0) {
    perror("waitpid");
    return -138;
  } else if (WIFEXITED(stat)) {
    return WEXITSTATUS(stat);
  } else if (WIFSIGNALED(stat)) {
    psignal(WTERMSIG(stat), "Error in subprocess");
    return - WTERMSIG(stat);
  } else {
    return -137;
  }
}

#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#include <stdio.h>

int execvp_no_vtalarm(const char *file, char *const argv[]) {
  /* Reset the itimers in the child, so it doesn't get plagued
   * by SIGVTALRM interrupts.
   */
  struct timeval tv_null = { 0, 0 };
  struct itimerval itv;
  itv.it_interval = tv_null;
  itv.it_value = tv_null;
  setitimer(ITIMER_REAL, &itv, NULL);
  setitimer(ITIMER_VIRTUAL, &itv, NULL);
  setitimer(ITIMER_PROF, &itv, NULL);
  execvp(file, argv);
  perror("Error in execvp");
  return errno;
}

#include <sys/types.h>
#include <sys/stat.h>

int isnt_symlink(const char *file) {
  struct stat buf;
  if (lstat(file, &buf)) return 0; /* treat error as symlink */
  return !S_ISLNK(buf.st_mode);
}

#endif

#ifdef _WIN32
int
maybe_relink(const char *src, const char *dst, int careful)
{
    return 0;
}

#else

/* Tries to link src to dst if both files exist and have the same
   contents.  If careful is false only the file sizes are compared; if
   it is true, the full contents are compared.

   This code assumes that dst cannot change behind our back -- the
   caller is supposed to protect it by a lock.  On the other hand, it
   does handle simultaneous access to src, but only if src is never
   modified in place.  It should also be safe over NFS.

   Assumes that rename cannot fail mid-way on a single filesystem.

   Returns 1 on success, 0 if the files are already linked, -1 for an
   error in errno, -2 if the files cannot be linked because they are not
   the same, on different devices, or on a filesystem with no support for
   hard links, -3 if there was a race condition, -4 if something unexpected
   happened. */

int
maybe_relink(char *src, char *dst, int careful)
{
#define RELINK_BUFFER_SIZE 8192

    int len, rc, saved_errno;
    char *tempname;
    struct stat srcstat, dststat, tempstat;
    struct timeval now;

    rc = stat(src, &srcstat);
    if(rc < 0) {
        if(errno == ENOENT)
            return -2;
        else
            return -1;
    }

    rc = stat(dst, &dststat);
    if(rc < 0) return -1;

    if(!S_ISREG(srcstat.st_mode) || !S_ISREG(dststat.st_mode)) {
        return -4;
    }

    if(srcstat.st_dev != dststat.st_dev) {
        return -2;
    }

    if(srcstat.st_ino == dststat.st_ino)
        /* Files are already linked */
        return 0;

    if(srcstat.st_size != dststat.st_size)
        return -2;

    /* link is atomic even on NFS, we will fail gracefully if the name
       is not unique. */
    gettimeofday(&now, NULL);
    rc = strlen(dst) + 6;
    tempname = malloc(rc);
    if(tempname == NULL) return -1;
    len = snprintf(tempname, rc, "%s-%04x", dst,
                   ((unsigned)(now.tv_usec ^ (now.tv_usec >> 16))) & 0xFFFF);
    if(len < 0 || len >= rc) {
        free(tempname);
        return -4;
    }

    rc = link(src, tempname);
    if(rc < 0) {
        /* We need to try to remove the link in case this was a
           problem with NFS over an unreliable transport. */
        goto fail;
    }

    rc = stat(tempname, &tempstat);
    if(rc < 0) goto fail;

    /* Check for a race condition.  The size and mtime checks are
       gratuitious, but they don't cost much, and might save your data
       if you're on a filesystem without i-nodes. */
    if(tempstat.st_ino != srcstat.st_ino ||
       tempstat.st_size != srcstat.st_size ||
       tempstat.st_mtime != srcstat.st_mtime) {
        unlink(tempname);
        free(tempname);
        return -3;
    }
    if(careful) {
        int fd1, fd2, i, rc1, rc2;
        char buf1[RELINK_BUFFER_SIZE], buf2[RELINK_BUFFER_SIZE];

        fd1 = open(tempname, O_RDONLY);
        if(fd1 < 0) goto fail;
        fd2 = open(dst, O_RDONLY);
        if(fd2 < 0) { close(fd1); goto fail; }

        i = 0;
        /* This comparison is approximate: it doesn't deal with short
           reads and EINTR.  It's okay, as these cases are rare and if
           they happen, we're still safe. */
        while(i < tempstat.st_size) {
            rc1 = read(fd1, buf1, RELINK_BUFFER_SIZE);
            if(rc1 < 0) { close(fd1); close(fd2); goto fail; }
            rc2 = read(fd2, buf2, RELINK_BUFFER_SIZE);
            if(rc2 < 0) { close(fd1); close(fd2); goto fail; }
            if(rc1 == 0 || rc1 != rc2 || memcmp(buf1, buf2, rc1) != 0) {
                close(fd1); close(fd2);
                unlink(tempname);
                free(tempname);
                return -2;
            }
            i += rc1;
        }
        close(fd1); close(fd2);
    }

    rc = rename(tempname, dst);
    if(rc < 0) goto fail;

    free(tempname);
    return 1;

 fail:
    saved_errno = errno;
    unlink(tempname);
    free(tempname);
    errno = saved_errno;
    if(errno == EPERM || errno == EOPNOTSUPP)
        return -2;
    return -1;

#undef RELINK_BUFFER_SIZE
}

#endif

Generated by  Doxygen 1.6.0   Back to index