/* $Id$ */ /* * Copyright (c) 2005 Dimitri Sokolyuk * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include /* bitfield */ #include #include /* mkdir() */ #include #include #include #include #include #include "meta.h" #include "files.h" #include "tools.h" #ifndef lint static const char rcsid[] = "@(#)$Id$"; #endif /* not lint */ /* * 0 1 2 3 4 5 6 7 * pieces +---+---+---+---+---+---+---+-+ * files +--------+-------+-+----------+ * 0 1 2 3 */ /* TODO: rewrite btread() and btwrite() so, that * only required files are open (pending, DONE) * on the one hand it makes more overhead on opening files, on the other * hand we need only one file descriptor and we have no limitations on number of files * so more descriptors remains for the network communication by the same rlimit value */ #define FMODE "a+" int btwrite(struct btmeta *mp, char *piece, off_t len, int npiece) { FILE *fd; struct btfile *fp; off_t off, eof, left; fp = SIMPLEQ_FIRST(&mp->flist); /* TODO: check piece first */ off = mp->plength * npiece; eof = fp->length; /* find right file */ while (off > eof) { off -= fp->length; fp = SIMPLEQ_NEXT(fp, link); eof = fp->length; } if (fp == NULL) return -1; /* XXX */ left = len; fd = fopen(fp->path, FMODE); fseek(fd, off, SEEK_SET); if ((off + len) <= eof) { left -= fwrite(piece, sizeof(char), len, fd); } else { left -= fwrite(piece, sizeof(char), eof - off, fd); fclose(fd); fp = SIMPLEQ_NEXT(fp, link); for (; left > fp->length; fp = SIMPLEQ_NEXT(fp, link)) { fd = fopen(fp->path, FMODE); fseek(fd, 0L, SEEK_SET); left -= fwrite(piece + len - left, sizeof(char), fp->length, fd); fclose(fd); } fd = fopen(fp->path, FMODE); fseek(fd, 0L, SEEK_SET); left -= fwrite(piece + len - left, sizeof(char), left, fd); } fclose(fd); return ((len - left) == 0); } int btread(struct btmeta *mp, char *piece, off_t len, int npiece) { FILE *fd; struct btfile *fp; off_t off, eof, left, ret; fp = SIMPLEQ_FIRST(&mp->flist); off = mp->plength * npiece; eof = fp->length; /* find right file */ while (off > eof) { off -= fp->length; fp = SIMPLEQ_NEXT(fp, link); eof = fp->length; } left = len; fd = fopen(fp->path, FMODE); fseek(fd, off, SEEK_SET); if ((off + len) <= eof) { ret = fread(piece, sizeof(char), len, fd); left -= (ret > 0) ? ret : len; } else { ret = fread(piece, sizeof(char), eof - off, fd); left -= (ret > 0) ? ret : eof - off; fclose(fd); fp = SIMPLEQ_NEXT(fp, link); for (; left > fp->length; fp = SIMPLEQ_NEXT(fp, link)) { fd = fopen(fp->path, FMODE); fseek(fd, 0L, SEEK_SET); ret = fread(piece + len - left, sizeof(char), fp->length, fd); left -= (ret > 0) ? ret : fp->length; fclose(fd); } fd = fopen(fp->path, FMODE); fseek(fd, 0L, SEEK_SET); ret = fread(piece + len - left, sizeof(char), left, fd); left -= (ret > 0) ? ret : left; } fclose(fd); return (ret > 0); } off_t btsizeofpiece(struct btmeta *mp, int n) { return (n == mp->npieces - 1) ? mp->size % mp->plength : mp->plength; } int btmkhier(struct btmeta *mp) { struct btfile *fp; struct stat st; char path[MAXPATHLEN], *p; if (mp->nfiles > 1) { SIMPLEQ_FOREACH(fp, &mp->flist, link) { strlcpy(path, fp->path, sizeof(path)); p = path; while ((p = strchr(p, '/')) != NULL) { /* TODO: add proper checking */ *p = '\0'; if (stat(path, &st) == -1) mkdir(path, 0755); else if (!(st.st_mode & S_IFDIR)) { fprintf(stderr, "remove %s\n", path); return -1; } *p++ = '/'; } } } return 0; } int btcheck(u_char *key, char *src, off_t len) { u_char sha[SHA1LEN]; /* FIXME: it's so ugly */ SHA1((u_char *) src, len, sha); return (strncmp((char *) key, (char *) sha, SHA1LEN) == 0); } void #if 0 btchkfiles(struct btmeta *mp, void (*progress)(struct btmeta *, int)) #else btchkfiles(struct btmeta *mp, struct progresscb *cb) #endif { char *buf; off_t len; int n; if ((buf = calloc(mp->plength, sizeof(char))) == NULL) return; mp->left = mp->size; for (n = 0, mp->good = 0; n < mp->npieces; n++) { len = btsizeofpiece(mp, n); if (btread(mp, buf, len, n)) { if (btcheck(mp->pieces[n], buf, len)) { btsetbit(mp->bitfield, n); mp->good++; mp->left -= len; } } #if 0 if (progress != NULL) (*progress)(mp, n + 1); } if (progress != NULL) (*progress)(mp, n + 1); #else if (cb && cb->progress) (*cb->progress)(mp, n + 1); } if (cb && cb->done) (*cb->done)(mp); #endif free(buf); } /* XXX */ int btchkwrite(struct btmeta *mp, char *buf, int n) { off_t len; len = btsizeofpiece(mp, n); if (btcheck(mp->pieces[n], buf, len)) { if (btwrite(mp, buf, len, n)) { btsetbit(mp->bitfield, n); mp->left -= len; return 0; } } return -1; } /* fallback progress indicator */ void btchkprogress(struct btmeta *mp, int n) { printf("\r%s: checking %d of %d (%d ok)", mp->fname, n, mp->npieces, mp->good); fflush(stdout); } void btchkdone(struct btmeta *mp) { printf("\r%s: total good pieces %d/%d (%d%%)\n", mp->fname, mp->good, mp->npieces, mp->good * 100 / mp->npieces); }