Plan 9 from Bell Labs’s /usr/web/sources/contrib/mjl/wip/deluge/file.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


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

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.