#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;
}
|