#include #include #include #include #include #include #include #include "metainfo.h" #include "subr.h" #include "stream.h" struct bt_stream_ro * bts_open_ro(struct metainfo *meta, off_t off, F_fdcb fd_cb, void *fd_arg) { struct bt_stream_ro *bts = malloc(sizeof(*bts)); if (bts == NULL) return NULL; bts->meta = meta; bts->fd_cb = fd_cb; bts->fd_arg = fd_arg; bts->t_off = 0; bts->f_off = 0; bts->index = 0; bts->fd = -1; bts_seek_ro(bts, off); return bts; } void bts_seek_ro(struct bt_stream_ro *bts, off_t off) { struct fileinfo *files = bts->meta->files; assert(off >= 0 && off <= bts->meta->total_length); if (bts->fd != -1) { close(bts->fd); bts->fd = -1; } bts->t_off = off; bts->index = 0; while (off >= files[bts->index].length) { off -= files[bts->index].length; bts->index++; } bts->f_off = off; } int bts_read_ro(struct bt_stream_ro *bts, char *buf, size_t len) { struct fileinfo *files = bts->meta->files; size_t boff, wantread; ssize_t didread; assert(bts->t_off + len <= bts->meta->total_length); boff = 0; while (boff < len) { if (bts->fd == -1) { int err = bts->fd_cb(files[bts->index].path, &bts->fd, bts->fd_arg); if (err != 0) return err; if (bts->f_off != 0) lseek(bts->fd, bts->f_off, SEEK_SET); } wantread = min(len - boff, files[bts->index].length - bts->f_off); again: didread = read(bts->fd, buf + boff, wantread); if (didread == -1) { if (errno == EINTR) goto again; else return errno; } boff += didread; bts->f_off += didread; bts->t_off += didread; if (bts->f_off == files[bts->index].length) { close(bts->fd); bts->fd = -1; bts->f_off = 0; bts->index++; } if (didread != wantread) return ENOENT; } return 0; } void bts_close_ro(struct bt_stream_ro *bts) { if (bts->fd != -1) close(bts->fd); free(bts); } #define SHAFILEBUF (1 << 15) int bts_sha(struct bt_stream_ro *bts, off_t length, uint8_t *hash) { SHA_CTX ctx; char buf[SHAFILEBUF]; size_t wantread; int err = 0; SHA1_Init(&ctx); while (length > 0) { wantread = min(length, SHAFILEBUF); if ((err = bts_read_ro(bts, buf, wantread)) != 0) break; length -= wantread; SHA1_Update(&ctx, buf, wantread); } SHA1_Final(hash, &ctx); return err; } int bts_hashes(struct metainfo *meta, F_fdcb fd_cb, void (*cb)(uint32_t, uint8_t *, void *), void *arg) { int err = 0; uint8_t hash[SHA_DIGEST_LENGTH]; uint32_t piece; struct bt_stream_ro *bts; off_t plen = meta->piece_length; off_t llen = meta->total_length % plen; if ((bts = bts_open_ro(meta, 0, fd_cb, arg)) == NULL) return ENOMEM; for (piece = 0; piece < meta->npieces; piece++) { if (piece < meta->npieces - 1) err = bts_sha(bts, plen, hash); else err = bts_sha(bts, llen, hash); if (err == 0) cb(piece, hash, arg); else if (err == ENOENT) { cb(piece, NULL, arg); if (piece < meta->npieces - 1) bts_seek_ro(bts, (piece + 1) * plen); err = 0; } else break; } bts_close_ro(bts); return err; } struct bt_stream_wo * bts_open_wo(struct metainfo *meta, off_t off, F_fdcb fd_cb, void *fd_arg) { struct bt_stream_wo *bts = malloc(sizeof(*bts)); if (bts == NULL) return NULL; bts->meta = meta; bts->fd_cb = fd_cb; bts->fd_arg = fd_arg; bts->t_off = 0; bts->f_off = 0; bts->index = 0; bts->fd = -1; bts_seek_ro((struct bt_stream_ro *)bts, off); return bts; } int bts_write_wo(struct bt_stream_wo *bts, const char *buf, size_t len) { struct fileinfo *files = bts->meta->files; size_t boff, wantwrite; ssize_t didwrite; assert(bts->t_off + len <= bts->meta->total_length); boff = 0; while (boff < len) { if (bts->fd == -1) { int err = bts->fd_cb(files[bts->index].path, &bts->fd, bts->fd_arg); if (err != 0) return err; if (bts->f_off != 0) lseek(bts->fd, bts->f_off, SEEK_SET); } wantwrite = min(len - boff, files[bts->index].length - bts->f_off); didwrite = write(bts->fd, buf + boff, wantwrite); if (didwrite == -1) return errno; boff += didwrite; bts->f_off += didwrite; bts->t_off += didwrite; if (bts->f_off == files[bts->index].length) { if (fsync(bts->fd) == -1) { int err = errno; close(bts->fd); return err; } if (close(bts->fd) == -1) return errno; bts->fd = -1; bts->f_off = 0; bts->index++; } } return 0; } int bts_close_wo(struct bt_stream_wo *bts) { int err = 0; if (bts->fd != -1) { if (fsync(bts->fd) == -1) { err = errno; close(bts->fd); } else if (close(bts->fd) == -1) err = errno; } free(bts); return err; }