ref: 03be0170dc8eb63834dd43db946a7012dd3fa286
dir: /mapfs.c/
#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] [-a maxage]\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;
int maxage = 7;
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;
Dir *fdir;
long age;
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);
fdir = dirstat(buf);
if (fdir) {
age = time(nil) - fdir->mtime;
if (age > maxage * 60 * 60 * 24)
requestfile(b, buf);
} else
requestfile(b, buf);
free(fdir);
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 'a':
maxage = 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);
}