[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: WC corruption after checkout on Mac

From: Eric Gillespie <epg_at_google.com>
Date: 2007-11-13 22:12:55 CET

Anyone with a Mac, please try this test program.

- save it as bug.c
- make bug
- cp -r $svn_trunk/build data
- while :; do rm -rf work; ./bug; done

Let us know what happens. On one particular Mac we have, we get
the random 0 byte files:

work/gen_base.py 0 != 39113, unlink_error=2
work/get-py-info.py 0 != 4255, unlink_error=2
work/vc6-build.bat.in 0 != 5630, unlink_error=2
work/gen_make.py 0 != 20457, unlink_error=2
work/svn_config.vcproj.ezt 0 != 2515, unlink_error=2
work/swig.m4 0 != 9023, unlink_error=2
work/serf.dsp.ezt 0 != 2912, unlink_error=2
...

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <fts.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Demonstrate the bug:
   Copy path to a tmp file in work_dir, rename the tmp file to
   basename(path), and unlink the tmpfile. rename works so the tmp does
   not exist, yet the target size is correct before the unlink call and
   incorrect (0) after. The unlink always raises ENOENT.
*/

static void
work(char *path, char *accpath, struct stat *st, char *work_dir)
{
    char *buf, *target, *tmp;
    FILE *out;
    int in;
    ssize_t bytes;
    struct stat target_st;
    int unlink_error;

    target = tmp = NULL;

    /* Setup target and tmp file names. */
    asprintf(&target, "%s/%s", work_dir, basename(path));
    asprintf(&tmp, "%s/%s", work_dir, "tmp");

    /* Open tmp for writing. */
    if ((out = fopen(tmp, "w")) == NULL) {
        fprintf(stderr, "fopen(%s, w): ", tmp);
        perror("");
        exit(errno);
    }

    /* Open path for reading. */
    if ((in = open(accpath, O_RDONLY)) == -1) {
        fprintf(stderr, "open(%s, O_RDONLY): ", path);
        perror("");
        exit(errno);
    }
    /* Allocate a buffer. */
    buf = malloc(st->st_size);
    if (buf == NULL) {
        perror("malloc");
        exit(errno);
    }
    /* And suck it all in. */
    if ((bytes = read(in, buf, st->st_size)) != st->st_size) {
        if (bytes == -1) {
            fprintf(stderr, "read(%s, %lld): ", path, (long long)st->st_size);
            perror("");
            exit(errno);
        }
        fprintf(stderr, "can't read all %lld of %s\n",
                (long long)st->st_size, path);
        exit(-1);
    }

    /* Write it all out to the tmp file. */
    if ((bytes = fwrite(buf, 1, st->st_size, out)) != st->st_size) {
        if (bytes == -1) {
            fprintf(stderr, "fwrite(%lld, %s): ",
                    (long long)st->st_size, tmp);
            perror("");
            exit(errno);
        }
        fprintf(stderr, "can't write all %lld of %s\n",
                (long long)st->st_size, tmp);
        exit(-1);
    }
    if (fclose(out) == EOF) {
        fprintf(stderr, "fclose(%s): ", tmp);
        perror("");
        exit(errno);
    }

    /* Rename it into place; this always works. */
    if (rename(tmp, target) == -1) {
        fprintf(stderr, "rename(%s, %s): ", tmp, target);
        perror("");
    }

    /* Ensure target has the correct size; it always does. */
    if (stat(target, &target_st) == -1) {
        fprintf(stderr, "stat(%s) before unlink: ", tmp);
        perror("");
    } else {
        if (target_st.st_size != st->st_size) {
            fprintf(stderr, "%s %lld != %lld before unlink\n", target,
                    (long long)target_st.st_size, (long long)st->st_size);
        }
    }

    /* Now unlink the tmp file; it *NEVER EXISTS* at this point. */
    if (unlink(tmp) == -1) {
        unlink_error = errno;
        if (errno != ENOENT) {
            fprintf(stderr, "unlink(%s): ", tmp);
            perror("");
            exit(errno);
        }
    }

    /* Check target size again; random files in our data set have become
       size 0 now, in many but not all runs. */
    if (stat(target, &target_st) == -1) {
        fprintf(stderr, "stat(%s): ", tmp);
        perror("");
    } else {
        if (target_st.st_size != st->st_size) {
            fprintf(stderr, "%s %lld != %lld, unlink_error=%d\n", target,
                    (long long)target_st.st_size, (long long)st->st_size,
                    unlink_error);
        }
    }

    free(target);
    free(tmp);
    free(buf);
}

/* Iterate over all files in the data directory, pass them to work. */
int
main(void)
{
    char *paths[] = { "data", NULL };
    FTS *ftp;
    FTSENT *fte;

    if (mkdir("work", 0755) == -1) {
        perror("mkdir(work)");
        exit(errno);
    }

    ftp = fts_open(paths, FTS_NOCHDIR | FTS_PHYSICAL | FTS_XDEV, NULL);
    if (ftp == NULL) {
        perror("fts_open(data)");
        exit(errno);
    }

    while (errno = 0, (fte = fts_read(ftp)) != NULL) {
        if (fte->fts_info == FTS_ERR) {
            fprintf(stderr, "%s: %s\n",
                    fte->fts_path, strerror(fte->fts_errno));
            continue;
        }
        if (fte->fts_info != FTS_F) {
            continue;
        }
        work(fte->fts_path, fte->fts_accpath, fte->fts_statp, "work");
    }
    if (errno != 0) {
        perror("fts_read(data)");
        exit(errno);
    }

    if (fts_close(ftp) == -1) {
        perror("fts_close(data)");
        exit(errno);
    }

    return 0;
}

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Tue Nov 13 22:13:13 2007

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.