version 1.3, 2019/02/11 19:18:36 |
version 1.4, 2019/02/11 21:41:22 |
|
|
{ |
{ |
int32_t seed = htole32(sess->seed); |
int32_t seed = htole32(sess->seed); |
|
|
assert(DOWNLOAD_READ_NEXT == p->state); |
assert(p->state == DOWNLOAD_READ_NEXT); |
|
|
p->idx = idx; |
p->idx = idx; |
memset(&p->blk, 0, sizeof(struct blkset)); |
memset(&p->blk, 0, sizeof(struct blkset)); |
|
|
download_cleanup(struct download *p, int cleanup) |
download_cleanup(struct download *p, int cleanup) |
{ |
{ |
|
|
if (MAP_FAILED != p->map) { |
if (p->map != MAP_FAILED) { |
assert(p->mapsz); |
assert(p->mapsz); |
munmap(p->map, p->mapsz); |
munmap(p->map, p->mapsz); |
p->map = MAP_FAILED; |
p->map = MAP_FAILED; |
p->mapsz = 0; |
p->mapsz = 0; |
} |
} |
if (-1 != p->ofd) { |
if (p->ofd != -1) { |
close(p->ofd); |
close(p->ofd); |
p->ofd = -1; |
p->ofd = -1; |
} |
} |
if (-1 != p->fd) { |
if (p->fd != -1) { |
close(p->fd); |
close(p->fd); |
if (cleanup && NULL != p->fname) |
if (cleanup && p->fname != NULL) |
unlinkat(p->rootfd, p->fname, 0); |
unlinkat(p->rootfd, p->fname, 0); |
p->fd = -1; |
p->fd = -1; |
} |
} |
|
|
{ |
{ |
struct download *p; |
struct download *p; |
|
|
if (NULL == (p = malloc(sizeof(struct download)))) { |
if ((p = malloc(sizeof(struct download))) == NULL) { |
ERR(sess, "malloc"); |
ERR(sess, "malloc"); |
return NULL; |
return NULL; |
} |
} |
|
|
p->obufsz = 0; |
p->obufsz = 0; |
p->obuf = NULL; |
p->obuf = NULL; |
p->obufmax = OBUF_SIZE; |
p->obufmax = OBUF_SIZE; |
if (p->obufmax && |
if (p->obufmax && (p->obuf = malloc(p->obufmax)) == NULL) { |
NULL == (p->obuf = malloc(p->obufmax))) { |
|
ERR(sess, "malloc"); |
ERR(sess, "malloc"); |
free(p); |
free(p); |
return NULL; |
return NULL; |
|
|
download_free(struct download *p) |
download_free(struct download *p) |
{ |
{ |
|
|
if (NULL == p) |
if (p == NULL) |
return; |
return; |
download_cleanup(p, 1); |
download_cleanup(p, 1); |
free(p->obuf); |
free(p->obuf); |
|
|
*/ |
*/ |
|
|
if (sz && p->obufsz < p->obufmax) { |
if (sz && p->obufsz < p->obufmax) { |
assert(NULL != p->obuf); |
assert(p->obuf != NULL); |
rem = p->obufmax - p->obufsz; |
rem = p->obufmax - p->obufsz; |
assert(rem > 0); |
assert(rem > 0); |
tocopy = rem < sz ? rem : sz; |
tocopy = rem < sz ? rem : sz; |
|
|
buf += tocopy; |
buf += tocopy; |
p->obufsz += tocopy; |
p->obufsz += tocopy; |
assert(p->obufsz <= p->obufmax); |
assert(p->obufsz <= p->obufmax); |
if (0 == sz) |
if (sz == 0) |
return 1; |
return 1; |
} |
} |
|
|
|
|
if (p->obufsz) { |
if (p->obufsz) { |
assert(p->obufmax); |
assert(p->obufmax); |
assert(p->obufsz <= p->obufmax); |
assert(p->obufsz <= p->obufmax); |
assert(NULL != p->obuf); |
assert(p->obuf != NULL); |
if ((ssz = write(p->fd, p->obuf, p->obufsz)) < 0) { |
if ((ssz = write(p->fd, p->obuf, p->obufsz)) < 0) { |
ERR(sess, "%s: write", p->fname); |
ERR(sess, "%s: write", p->fname); |
return 0; |
return 0; |
|
|
* metadata, in which case we open our file and wait for data. |
* metadata, in which case we open our file and wait for data. |
*/ |
*/ |
|
|
if (DOWNLOAD_READ_NEXT == p->state) { |
if (p->state == DOWNLOAD_READ_NEXT) { |
if (!io_read_int(sess, p->fdin, &idx)) { |
if (!io_read_int(sess, p->fdin, &idx)) { |
ERRX1(sess, "io_read_int"); |
ERRX1(sess, "io_read_int"); |
return -1; |
return -1; |
|
|
|
|
p->state = DOWNLOAD_READ_LOCAL; |
p->state = DOWNLOAD_READ_LOCAL; |
f = &p->fl[idx]; |
f = &p->fl[idx]; |
p->ofd = openat(p->rootfd, f->path, |
p->ofd = openat(p->rootfd, f->path, O_RDONLY | O_NONBLOCK, 0); |
O_RDONLY | O_NONBLOCK, 0); |
|
|
|
if (-1 == p->ofd && ENOENT != errno) { |
if (p->ofd == -1 && errno != ENOENT) { |
ERR(sess, "%s: openat", f->path); |
ERR(sess, "%s: openat", f->path); |
goto out; |
goto out; |
} else if (-1 != p->ofd) { |
} else if (p->ofd != -1) { |
*ofd = p->ofd; |
*ofd = p->ofd; |
return 1; |
return 1; |
} |
} |
|
|
* original file in a nonblocking way, and we can map it. |
* original file in a nonblocking way, and we can map it. |
*/ |
*/ |
|
|
if (DOWNLOAD_READ_LOCAL == p->state) { |
if (p->state == DOWNLOAD_READ_LOCAL) { |
assert(NULL == p->fname); |
assert(p->fname == NULL); |
|
|
/* |
/* |
* Try to fstat() the file descriptor if valid and make |
* Try to fstat() the file descriptor if valid and make |
|
|
* Then, if it has non-zero size, mmap() it for hashing. |
* Then, if it has non-zero size, mmap() it for hashing. |
*/ |
*/ |
|
|
if (-1 != p->ofd && |
if (p->ofd != -1 && |
-1 == fstat(p->ofd, &st)) { |
fstat(p->ofd, &st) == -1) { |
ERR(sess, "%s: fstat", f->path); |
ERR(sess, "%s: fstat", f->path); |
goto out; |
goto out; |
} else if (-1 != p->ofd && !S_ISREG(st.st_mode)) { |
} else if (p->ofd != -1 && !S_ISREG(st.st_mode)) { |
WARNX(sess, "%s: not regular", f->path); |
WARNX(sess, "%s: not regular", f->path); |
goto out; |
goto out; |
} |
} |
|
|
if (-1 != p->ofd && st.st_size > 0) { |
if (p->ofd != -1 && st.st_size > 0) { |
p->mapsz = st.st_size; |
p->mapsz = st.st_size; |
p->map = mmap(NULL, p->mapsz, |
p->map = mmap(NULL, p->mapsz, |
PROT_READ, MAP_SHARED, p->ofd, 0); |
PROT_READ, MAP_SHARED, p->ofd, 0); |
if (MAP_FAILED == p->map) { |
if (p->map == MAP_FAILED) { |
ERR(sess, "%s: mmap", f->path); |
ERR(sess, "%s: mmap", f->path); |
goto out; |
goto out; |
} |
} |
|
|
f->path, hash) < 0) |
f->path, hash) < 0) |
p->fname = NULL; |
p->fname = NULL; |
} |
} |
if (NULL == p->fname) { |
if (p->fname == NULL) { |
ERR(sess, "asprintf"); |
ERR(sess, "asprintf"); |
goto out; |
goto out; |
} |
} |
|
|
p->fd = openat(p->rootfd, p->fname, |
p->fd = openat(p->rootfd, p->fname, |
O_APPEND|O_WRONLY|O_CREAT|O_EXCL, perm); |
O_APPEND|O_WRONLY|O_CREAT|O_EXCL, perm); |
|
|
if (-1 == p->fd) { |
if (p->fd == -1) { |
ERR(sess, "%s: openat", p->fname); |
ERR(sess, "%s: openat", p->fname); |
goto out; |
goto out; |
} |
} |
|
|
* a token indicator. |
* a token indicator. |
*/ |
*/ |
|
|
assert(DOWNLOAD_READ_REMOTE == p->state); |
assert(p->state == DOWNLOAD_READ_REMOTE); |
assert(NULL != p->fname); |
assert(p->fname != NULL); |
assert(-1 != p->fd); |
assert(p->fd != -1); |
assert(-1 != p->fdin); |
assert(p->fdin != -1); |
|
|
if (!io_read_int(sess, p->fdin, &rawtok)) { |
if (!io_read_int(sess, p->fdin, &rawtok)) { |
ERRX1(sess, "io_read_int"); |
ERRX1(sess, "io_read_int"); |
|
|
|
|
if (rawtok > 0) { |
if (rawtok > 0) { |
sz = rawtok; |
sz = rawtok; |
if (NULL == (buf = malloc(sz))) { |
if ((buf = malloc(sz)) == NULL) { |
ERR(sess, "realloc"); |
ERR(sess, "realloc"); |
goto out; |
goto out; |
} |
} |
|
|
} |
} |
sz = tok == p->blk.blksz - 1 ? p->blk.rem : p->blk.len; |
sz = tok == p->blk.blksz - 1 ? p->blk.rem : p->blk.len; |
assert(sz); |
assert(sz); |
assert(MAP_FAILED != p->map); |
assert(p->map != MAP_FAILED); |
buf = p->map + (tok * p->blk.len); |
buf = p->map + (tok * p->blk.len); |
|
|
/* |
/* |
|
|
* profile from it. |
* profile from it. |
*/ |
*/ |
|
|
assert(MAP_FAILED != p->map); |
assert(p->map != MAP_FAILED); |
if (!buf_copy(sess, buf, sz, p)) { |
if (!buf_copy(sess, buf, sz, p)) { |
ERRX1(sess, "buf_copy"); |
ERRX1(sess, "buf_copy"); |
goto out; |
goto out; |
|
|
goto out; |
goto out; |
} |
} |
|
|
assert(0 == rawtok); |
assert(rawtok == 0); |
assert(0 == p->obufsz); |
assert(p->obufsz == 0); |
|
|
/* |
/* |
* Make sure our resulting MD4 hashes match. |
* Make sure our resulting MD4 hashes match. |
|
|
tv[0].tv_nsec = 0; |
tv[0].tv_nsec = 0; |
tv[1].tv_sec = f->st.mtime; |
tv[1].tv_sec = f->st.mtime; |
tv[1].tv_nsec = 0; |
tv[1].tv_nsec = 0; |
if (-1 == futimens(p->fd, tv)) { |
if (futimens(p->fd, tv) == -1) { |
ERR(sess, "%s: futimens", p->fname); |
ERR(sess, "%s: futimens", p->fname); |
goto out; |
goto out; |
} |
} |
|
|
|
|
/* Finally, rename the temporary to the real file. */ |
/* Finally, rename the temporary to the real file. */ |
|
|
if (-1 == renameat(p->rootfd, p->fname, p->rootfd, f->path)) { |
if (renameat(p->rootfd, p->fname, p->rootfd, f->path) == -1) { |
ERR(sess, "%s: renameat: %s", p->fname, f->path); |
ERR(sess, "%s: renameat: %s", p->fname, f->path); |
goto out; |
goto out; |
} |
} |