summaryrefslogtreecommitdiffstats
path: root/usr.bin/fetch
diff options
context:
space:
mode:
authordes <des@FreeBSD.org>2001-09-08 15:17:15 +0000
committerdes <des@FreeBSD.org>2001-09-08 15:17:15 +0000
commit80b788da323bf3caa76b050bf2801349b03942ea (patch)
treea6e3814decc00b485c147512c43aa4ec7f69a47a /usr.bin/fetch
parent6ac93bff0d0bb146e19307c26edffdbe235dd70f (diff)
downloadFreeBSD-src-80b788da323bf3caa76b050bf2801349b03942ea.zip
FreeBSD-src-80b788da323bf3caa76b050bf2801349b03942ea.tar.gz
If the local file does not exist, or is a regular file, and we're not trying
to resume a transfer, download the requested document into a temporary file which we later rename. This avoids leaving half-completed files around in case of a crash (it'll still leave a half-completed file, but with a hope- fully non-conflicting name), and should reduce the need for human inter- vention on ports-building machines. The temporary file name for "foo/bar" is constructed by invoking mkstemps() with the pattern "foo/.fetch.XXXXXX.bar" Requested by: obrien
Diffstat (limited to 'usr.bin/fetch')
-rw-r--r--usr.bin/fetch/fetch.c98
1 files changed, 71 insertions, 27 deletions
diff --git a/usr.bin/fetch/fetch.c b/usr.bin/fetch/fetch.c
index a8ef3a6..3d8c22e 100644
--- a/usr.bin/fetch/fetch.c
+++ b/usr.bin/fetch/fetch.c
@@ -245,17 +245,20 @@ fetch(char *URL, const char *path)
{
struct url *url;
struct url_stat us;
- struct stat sb;
+ struct stat sb, nsb;
struct xferstat xs;
FILE *f, *of;
size_t size, wr;
off_t count;
char flags[8];
+ const char *slash;
+ char *tmppath;
int r;
u_int timeout;
u_char *ptr;
f = of = NULL;
+ tmppath = NULL;
/* parse URL */
if ((url = fetchParseURL(URL)) == NULL) {
@@ -336,12 +339,13 @@ fetch(char *URL, const char *path)
* file was a truncated copy of the remote file; we can drop
* the connection later if we change our minds.
*/
- if ((r_flag || m_flag) && !o_stdout && stat(path, &sb) != -1) {
- if (r_flag)
- url->offset = sb.st_size;
- } else {
- sb.st_size = -1;
+ sb.st_size = -1;
+ if (!o_stdout && stat(path, &sb) == -1 && errno != ENOENT) {
+ warnx("%s: stat()", path);
+ goto failure;
}
+ if (!o_stdout && r_flag && S_ISREG(sb.st_mode))
+ url->offset = sb.st_size;
/* start the transfer */
if ((f = fetchXGet(url, &us, flags)) == NULL) {
@@ -387,7 +391,7 @@ fetch(char *URL, const char *path)
if (o_stdout) {
/* output to stdout */
of = stdout;
- } else if (sb.st_size != -1) {
+ } else if (r_flag && sb.st_size != -1) {
/* resume mode, local file exists */
if (!F_flag && us.mtime && sb.st_mtime != us.mtime) {
/* no match! have to refetch */
@@ -398,13 +402,6 @@ fetch(char *URL, const char *path)
"does not match remote", path);
goto failure_keep;
}
- url->offset = 0;
- if ((f = fetchXGet(url, &us, flags)) == NULL) {
- warnx("%s: %s", path, fetchLastErrString);
- goto failure;
- }
- if (sigint)
- goto signal;
} else {
if (us.size == sb.st_size)
/* nothing to do */
@@ -416,34 +413,73 @@ fetch(char *URL, const char *path)
(long long)sb.st_size, (long long)us.size);
goto failure;
}
- /* we got it, open local file and seek to offset */
- /*
- * XXX there's a race condition here - the
- * file we open is not necessarily the same as
- * the one we stat()'ed earlier...
- */
+ /* we got it, open local file */
if ((of = fopen(path, "a")) == NULL) {
warn("%s: fopen()", path);
goto failure;
}
- if (fseek(of, url->offset, SEEK_SET) == -1) {
- warn("%s: fseek()", path);
+ /* check that it didn't move under our feet */
+ if (fstat(fileno(of), &nsb) == -1) {
+ /* can't happen! */
+ warn("%s: fstat()", path);
goto failure;
}
+ if (nsb.st_dev != sb.st_dev ||
+ nsb.st_ino != nsb.st_ino ||
+ nsb.st_size != sb.st_size) {
+ warnx("%s: file has changed", path);
+ fclose(of);
+ of = NULL;
+ sb = nsb;
+ }
}
- }
- if (m_flag && sb.st_size != -1) {
+ } else if (m_flag && sb.st_size != -1) {
/* mirror mode, local file exists */
if (sb.st_size == us.size && sb.st_mtime == us.mtime)
goto success;
}
- if (!of) {
+
+ if (of == NULL) {
/*
* We don't yet have an output file; either this is a
* vanilla run with no special flags, or the local and
* remote files didn't match.
*/
- if ((of = fopen(path, "w")) == NULL) {
+
+ if (url->offset != 0) {
+ /*
+ * We tried to restart a transfer, but for
+ * some reason gave up - so we have to restart
+ * from scratch if we want the whole file
+ */
+ url->offset = 0;
+ if ((f = fetchXGet(url, &us, flags)) == NULL) {
+ warnx("%s: %s", path, fetchLastErrString);
+ goto failure;
+ }
+ if (sigint)
+ goto signal;
+ }
+
+ /* construct a temp file name */
+ if (sb.st_size != -1 && S_ISREG(sb.st_mode)) {
+ if ((slash = strrchr(path, '/')) == NULL)
+ slash = path;
+ else
+ ++slash;
+ asprintf(&tmppath, "%.*s.fetch.XXXXXX.%s",
+ slash - path, path, slash);
+ }
+
+ if (tmppath != NULL) {
+ mkstemps(tmppath, strlen(slash) + 1);
+ warnx("tmppath: %s", tmppath);
+ of = fopen(tmppath, "w");
+ } else {
+ of = fopen(path, "w");
+ }
+
+ if (of == NULL) {
warn("%s: open()", path);
goto failure;
}
@@ -545,11 +581,17 @@ fetch(char *URL, const char *path)
success:
r = 0;
+ if (tmppath != NULL && rename(tmppath, path) == -1) {
+ warn("%s: rename()", path);
+ goto failure_keep;
+ }
goto done;
failure:
if (of && of != stdout && !R_flag && !r_flag)
if (stat(path, &sb) != -1 && (sb.st_mode & S_IFREG))
- unlink(path);
+ unlink(tmppath ? tmppath : path);
+ if (R_flag && tmppath != NULL && sb.st_size == -1)
+ rename(tmppath, path); /* ignore errors here */
failure_keep:
r = -1;
goto done;
@@ -560,6 +602,8 @@ fetch(char *URL, const char *path)
fclose(of);
if (url)
fetchFreeURL(url);
+ if (tmppath != NULL)
+ free(tmppath);
return r;
}
OpenPOWER on IntegriCloud