diff options
author | Tharre <tharre3@gmail.com> | 2015-01-25 17:23:32 +0100 |
---|---|---|
committer | Tharre <tharre3@gmail.com> | 2015-01-25 17:23:32 +0100 |
commit | c4a2f0dec7bd7d3f50115bb88467a0841c91d957 (patch) | |
tree | ee0a226f7d97dab4f833c01435aee08400fe7730 | |
parent | e081190f7ed25d3628d76bc726a3203c34100325 (diff) | |
download | redo-c4a2f0dec7bd7d3f50115bb88467a0841c91d957.tar.gz redo-c4a2f0dec7bd7d3f50115bb88467a0841c91d957.tar.xz redo-c4a2f0dec7bd7d3f50115bb88467a0841c91d957.zip |
Rewrite add_dep() to be atomic through O_APPEND
-rw-r--r-- | src/build.c | 44 |
1 files changed, 24 insertions, 20 deletions
diff --git a/src/build.c b/src/build.c index 4342033..c1dd602 100644 --- a/src/build.c +++ b/src/build.c @@ -339,38 +339,42 @@ static char *get_dep_path(const char *target) { return dep_path; } -/* Add the dependency target, with the identifier ident. */ +/* Declare that `parent` depends on `target`. */ void add_dep(const char *target, const char *parent, int ident) { char *dep_path = get_dep_path(parent); - FILE *fp = fopen(dep_path, "rb+"); - if (!fp) { - if (errno == ENOENT) { - fp = fopen(dep_path, "w"); - if (!fp) - fatal("redo: failed to open %s", dep_path); - /* skip the first n bytes that are reserved for the header */ - fseek(fp, HEADERSIZE, SEEK_SET); - } else { + int fd = open(dep_path, O_WRONLY | O_APPEND); + if (fd < 0) { + if (errno != ENOENT) + fatal("redo: failed to open %s", dep_path); + + /* no dependency record was found, so we create one */ + mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH; + fd = open(dep_path, O_WRONLY | O_APPEND | O_CREAT, mode); + if (fd < 0) fatal("redo: failed to open %s", dep_path); - } - } else { - fseek(fp, 0L, SEEK_END); } - char *reltarget = get_relpath(target); + char garbage[HEADERSIZE]; - putc(ident, fp); - fputs(reltarget, fp); - putc('\0', fp); + /* skip header */ + if (lseek(fd, 0, SEEK_END) < (off_t) HEADERSIZE) + pwrite(fd, garbage, HEADERSIZE, 0); - if (ferror(fp)) + char *reltarget = get_relpath(target); + int bufsize = strlen(reltarget) + 2; + char *buf = xmalloc(bufsize); + buf[0] = ident; + strcpy(buf+1, reltarget); + if (write(fd, buf, bufsize) < bufsize) fatal("redo: failed to write to %s", dep_path); - if (fclose(fp)) + if (close(fd)) fatal("redo: failed to close %s", dep_path); - free(dep_path); + + free(buf); free(reltarget); + free(dep_path); } /* Hash target, storing the result in hash. */ |