#include "deluge.h" static void fileadd(Torrent *t, char **names, vlong length, int needopen, int maycreate) { File *lf; vlong offset; String *spath; char *path; int fd; File *f; Dir *d; spath = s_new(); s_append(spath, "./"); while(names[1] != nil){ s_append(spath, names[0]); if(strstr(s_to_c(spath), "/..") != 0) sysfatal("dangerous path component, contains dotdot"); if(maycreate){ fd = create(s_to_c(spath), 0, 0777|DMDIR); close(fd); DEBUG(2, "result to creating path: %d (%r)\n", fd); } s_append(spath, "/"); names++; } s_append(spath, names[0]); path = estrdup(s_to_c(spath)); s_free(spath); offset = 0; if(filelen(t->files) > 0){ lf = filelast(t->files); offset = lf->offset + lf->length; } fd = -1; if(maycreate) fd = create(path, ORDWR|OEXCL, 0666); if(fd >= 0){ t->filecreated = 1; d = dirfstat(fd); if(d == nil) sysfatal("dirfstat %s: %r", path); d->length = length; if(dirfwstat(fd, d) < 0) sysfatal("dirwfstat %s: %r", path); DEBUG(2, "fileadd created file=%s fd=%d\n", path, fd); }else{ fd = open(path, ORDWR); if(fd < 0){ if(needopen) sysfatal("open %s: %r", path); }else{ d = dirfstat(fd); if(d == nil) sysfatal("dirfstat %s: %r", path); if(d->length != length) sysfatal("existing file (%s) length is not torrent file length: %lld != %lld", path, d->length, length); DEBUG(2, "fileadd opened file fd=%d\n", fd); } } f = emalloc(sizeof f[0]); f->fd = fd; f->path = path; f->length = length; f->offset = offset; f->next = nil; lf = filelast(t->files); if(lf == nil) t->files = f; else lf->next = f; } void fileinit(Torrent *t, Bee *info, int needopen, int maycreate) { char *name; vlong length; Bee *b; Bee *fl, *f; char **names; int i, j; File *lf; t->files = nil; ebeedictget(info, "name", TString, &name, nil); b = beedictget(info, "length", TInteger); if(b){ names = emalloc(sizeof names[0] * 2); names[0] = name; names[1] = nil; length = b->i; fileadd(t, names, length, needopen, maycreate); free(names); }else{ ebeedictget(info, "files", TList, &fl, nil); if(fl->nl == 0) sysfatal("no files in torrent"); for(i = 0; i < fl->nl; i++){ ebeedictget(&fl->l[i], "length", TInteger, &length, nil); ebeedictget(&fl->l[i], "path", TList, &f, nil); names = emalloc(sizeof names[0] * (f->nl+2)); names[0] = name; for(j = 0; j < f->nl; j++) names[j+1] = f->l[j].s; names[j+1] = nil; fileadd(t, names, length, needopen, maycreate); free(names); } } lf = filelast(t->files); t->length = lf->offset + lf->length; } int filepiecehashokay(Torrent *t, Piece *p) { DigestState *ds = nil; vlong offset; File *f; char *data; int dlen; vlong need, have; uchar hash[Piecehashlen]; dlen = 256*1024; data = emalloc(dlen); need = p->length; offset = t->piecelen * p->n; f = t->files; while(need > 0){ while(offset >= f->offset + f->length){ f = f->next; if(f == nil) sysfatal("at offset=%lld, still need %lld bytes but no more files", offset, need); } have = pread(f->fd, data, MIN(need, dlen), offset - f->offset); if(have == 0) sysfatal("reading %s: unexpected eof", f->path); if(have < 0) sysfatal("reading %s: %r", f->path); need -= have; offset += have; ds = sha1((uchar*)data, have, nil, ds); } free(data); sha1((uchar*)"", 0, hash, ds); return memcmp(p->hash, hash, Piecehashlen) == 0; } vlong filebytesinpiece(Torrent *t, File *f, Piece *p) { vlong s = p->n * t->piecelen; vlong e = s + p->length; if(f->offset > s) s = f->offset; if(e > f->offset + f->length) e = f->offset + f->length; return e - s; } static void fileio(Torrent *t, vlong offset, vlong length, char *p, int isread) { File *f; long need, have; long (*fn)(int, void *, long, vlong); assert(offset+length <= t->length); fn = isread ? pread : pwrite; for(f = t->files; offset >= f->offset + f->length; f = f->next) ; while(length > 0){ need = MIN(length, f->length - (offset - f->offset)); have = 0; while(need > 0 && (have = fn(f->fd, p, need, offset - f->offset)) > 0){ need -= have; p += have; offset += have; length -= have; } /* XXX need more sane error handling */ if(have == 0){ DEBUG(2, "fileio: eof at reading/writing???\n"); return; } if(have < 0){ DEBUG(2, "fileio: error reading/writing file: %r\n"); return; } f = f->next; } } void filewrite(Torrent *t, vlong offset, vlong length, char *p) { fileio(t, offset, length, p, 0); } void fileread(Torrent *t, vlong offset, vlong length, char *p) { fileio(t, offset, length, p, 1); } int filelen(File *f) { int i = 0; while(f){ i++; f = f->next; } return i; } File * filelast(File *f) { while(f && f->next) f = f->next; return f; }