shithub: mapfs

ref: 5390510d0ae960fae9a0a9039bb9da3d1f4f969c
dir: /mapfs.c/

View raw version
#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "dat.h"
#include "fns.h"

void
usage(void)
{
	fprint(2, "usage: %s [-s srvname] [-m mtpt] [-c cachedir] [-z maxzoom]\n", argv0);
	exits("usage");
}

char *copyright = "© OpenStreetMap Contributors (ODbL)";
int copyrightlen;

char Ebadzoom[] = "bad zoom";
char Enofile[] = "file not found";
char Enotile[] = "invalid tile";

char *mtpt = "/mnt/map";
char *cache = "/tmp/mapcache";
char *uid;
int maxzoom = 19;

ulong *numtiles;

static void
initnumtiles(void)
{
	numtiles = mallocz(sizeof(ulong) * (maxzoom + 1), 1);
	if (!numtiles)
		sysfatal("%r");
	
	for (int i = 0; i <= maxzoom; i++) {
		numtiles[i] = 1;
		for (int j = 0; j < i; j++)
			numtiles[i] *= 2;
	}
}

static int
validtile(int z, int n)
{
	if (z < 0)
		return 0;
	if (z > maxzoom)
		return 0;
	
	if (n < 0)
		return 0;
	if (n > numtiles[z])
		return 0;
	return 1;
}

/* path:

	64 bits available
	
	3 - Q
	5 - z
	24 - x
	24 - y

*/

typedef struct Qpath Qpath;
struct Qpath {
	int q;
	int z;
	uvlong x;
	uvlong y;
};

static Qpath
unpackqpath(uvlong path)
{
	Qpath q;
	q.q = path & 0x7;
	q.z = (path>>3) & 0x1f;
	q.x = (path>>8) & 0xffffff;
	q.y = (path>>32) & 0xffffff;
	return q;
}

static uvlong
packqpath(Qpath q)
{
	uvlong r;
	r  = (q.y&0xffffff)<<32;
	r |= (q.x&0xffffff)<<8;
	r |= (q.z&0x1f)<<3;
	r |= q.q&0x7;
	return r;
}

#define QID(p) (p&0x7)

enum {
	Qroot,
		Qctl,
		Qcopy,
		Qz,
			Qx,
				Qy,
};

static Qid
mkqid(uvlong q, int type)
{
	Qid id;
	id.path = q;
	id.vers = 0;
	id.type = type;
	return id;
}

static Qid
mkzqid(int z)
{
	Qpath q;
	q.z = z;
	q.q = Qz;
	return mkqid(packqpath(q), QTDIR);
}

static Qid
mkxqid(int z, int x)
{
	Qpath q;
	q.z = z;
	q.x = x;
	q.q = Qx;
	return mkqid(packqpath(q), QTDIR);
}

static Qid
mkyqid(int z, int x, int y)
{
	Qpath q;
	q.z = z;
	q.x = x;
	q.y = y;
	q.q = Qy;
	return mkqid(packqpath(q), QTFILE);
}

static void
fsattach(Req *r)
{
	r->fid->qid = r->ofcall.qid = mkqid(Qroot, QTDIR);
	respond(r, nil);
}

static char*
fswalk(Fid *fid, char *name, Qid *qid)
{
	int i;
	Qpath path;
	
	if (QID(fid->qid.path) == Qroot) {
		if (strcmp(name, "..") == 0) {
			return nil;
		}
		if (strcmp(name, "ctl") == 0) {
			fid->qid = *qid = mkqid(Qctl, QTFILE);
			return nil;
		}
		if (strcmp(name, "copyright") == 0) {
			fid->qid = *qid = mkqid(Qcopy, QTFILE);
			return nil;
		}
		i = atoi(name);
		if (i < 0)
			return Ebadzoom;
		if (i > maxzoom)
			return Ebadzoom;
		fid->qid = *qid = mkzqid(i);
		return nil;
	}
	
	if (QID(fid->qid.path) == Qz) {
		if (strcmp(name, "..") == 0) {
			fid->qid = *qid = mkqid(Qroot, QTDIR);
			return nil;
		}
		i = atoi(name);
		path = unpackqpath(fid->qid.path);
		if (!validtile(path.z, i))
			return Enotile;
		fid->qid = *qid = mkxqid(path.z, i);
		return nil;
	}
	
	if (QID(fid->qid.path) == Qx) {
		path = unpackqpath(fid->qid.path);
		if (strcmp(name, "..") == 0) {
			fid->qid = *qid = mkxqid(path.z, path.x);
			return nil;
		}
		i = atoi(name);
		if (!validtile(path.z, i))
			return Enotile;
		fid->qid = *qid = mkyqid(path.z, path.x, i);
		return nil;
	}
	
	return Enofile;
}

static void
fillstat(int type, Dir *dir, Qpath *p)
{
	char buf[12];
	
	switch (type) {
	case Qroot:
		dir->qid = mkqid(Qroot, QTDIR);
		dir->name = estrdup9p(".");
		dir->mode = 0777|DMDIR;
		goto fulldefs;
	case Qctl:
		dir->qid = mkqid(Qctl, QTFILE);
		dir->mode = 0444;
		dir->name = estrdup9p("ctl");
		goto fulldefs;
	case Qcopy:
		dir->qid = mkqid(Qcopy, QTFILE);
		dir->mode = 0444;
		dir->name = estrdup9p("copyright");
		dir->length = copyrightlen;
		goto ugdefs;
	case Qz:
		dir->qid = mkzqid(p->z);
		snprint(buf, sizeof buf, "%d", p->z);
		break;
	case Qx:
		dir->qid = mkxqid(p->z, p->x);
		snprint(buf, sizeof buf, "%lld", p->x);
		break;
	case Qy:
		dir->qid = mkyqid(p->z, p->x, p->y);
		snprint(buf, sizeof buf, "%lld", p->y);
		break;
	}
	dir->name = estrdup9p(buf);
	dir->mode = 0777|DMDIR;
fulldefs:
	dir->length = 0;
ugdefs:
	dir->uid = estrdup9p(uid);
	dir->gid = estrdup9p(uid);
}

static void
fsstat(Req *r)
{
	Qpath path;
	
	path = unpackqpath(r->fid->qid.path);
	fillstat(QID(r->fid->qid.path), &r->d, &path);
	respond(r, nil);
	return;
}

static int
rootgen(int n, Dir *dir, void*)
{
	Qpath path;
	if (n > maxzoom + 2) {
		return -1;
	}
	if (n == maxzoom + 1) {
		fillstat(Qctl, dir, nil);
		return 0;
	}
	if (n == maxzoom + 2) {
		fillstat(Qcopy, dir, nil);
		return 0;
	}
	path.z = n;
	fillstat(Qz, dir, &path);
	return 0;
}

static void
fsread(Req *r)
{
	Bundle b;
	Qpath path;
	int fd;
	char buf[128];
	
	if (QID(r->fid->qid.path) == Qroot) {
		dirread9p(r, rootgen, nil);
		respond(r, nil);
		return;
	}
	
	if (QID(r->fid->qid.path) == Qctl) {
		snprint(buf, sizeof buf, "%d\n", maxzoom);
		readstr(r, buf);
		respond(r, nil);
		return;
	}
	
	if (QID(r->fid->qid.path) == Qcopy) {
		readstr(r, copyright);
		respond(r, nil);
		return;
	}
	
	if (QID(r->fid->qid.path) == Qz) {
		respond(r, nil);
		return;
	}
	
	if (QID(r->fid->qid.path) == Qx) {
		respond(r, nil);
		return;
	}
	
	if (QID(r->fid->qid.path) != Qy) {
		respond(r, Enofile);
		return;
	}
	
	path = unpackqpath(r->fid->qid.path);
	b.z = path.z;
	b.x = path.x;
	b.y = path.y;
	
	snprint(buf, sizeof buf, "%s/%d/%d/%d", cache, b.z, b.x, b.y);
	
	if (access(buf, AEXIST) < 0)
		requestfile(b, buf);
	
	fd = open(buf, OREAD);
	if (fd < 0) {
		responderror(r);
		return;
	}
	
	r->ofcall.count = pread(fd, r->ofcall.data, r->ifcall.count, r->ifcall.offset);
	close(fd);
	respond(r, nil);
}

static void
fsremove(Req *r)
{
	Qpath p;
	Bundle b;
	char buf[128];
	
	if (QID(r->fid->qid.path) != Qy) {
		respond(r, nil);
		return;
	}
	
	p = unpackqpath(r->fid->qid.path);
	b.z = p.z;
	b.x = p.x;
	b.y = p.y;
	
	snprint(buf, sizeof buf, "%s/%d/%d/%d", cache, b.z, b.x, b.y);
	
	if (access(buf, AEXIST) < 0) {
		respond(r, nil);
		return;
	}
	
	remove(buf);
	respond(r, nil);
	return;
}

Srv fs = {
	.attach = fsattach,
	.walk1 = fswalk,
	.stat = fsstat,
	.read = fsread,
	.remove = fsremove,
};

void
main(int argc, char **argv)
{
	char *srv = nil;
	int fd;
	
	ARGBEGIN{
	case 's':
		srv = EARGF(usage());
		break;
	case 'm':
		mtpt = EARGF(usage());
		break;
	case 'c':
		cache = EARGF(usage());
		break;
	case 'z':
		maxzoom = atoi(EARGF(usage()));
		break;
	case 'D':
		chatty9p++;
		break;
	}ARGEND;
	
	if (access(cache, AEXIST) < 0) {
		fd = create(cache, OREAD, 0777|DMDIR);
		if (fd < 0)
			sysfatal("unable to create cache directory: %r");
		close(fd);
	}
	
	initnumtiles();
	uid = getuser();
	copyrightlen = strlen(copyright);
	
	postmountsrv(&fs, srv, mtpt, MREPL|MCREATE);
}