Plan 9 from Bell Labs’s /usr/web/sources/contrib/steve/root/sys/src/cmd/exifsrv/main.c

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


#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <libsec.h>
#include <9p.h>
#include <auth.h>
#include <bio.h>
#include "exif.h"

typedef struct Aux Aux;
typedef struct Vfile Vfile;
typedef struct Ptype Ptype;

static struct Ptype {
	char *ext;
	int (*open)(char *file, int mode);
	long (*pread)(int fd, void *buf, long len, vlong off);
	int (*close)(int fd);
};

struct Vfile {
	char *name;	/* firtual file name */
	int inherit;	/* inherit extension from parent directory */
};

struct Aux {
	char *path;	/* full path fo file */
	int fd;		/* file descriptor of open file */
	Dir *dir;	/* cached directory contents */
	int ndir;	/* number of entries in above */
	int pt;		/* index into photo types or -1 if regular file/dir */
	int vf;		/* index into virtual file table or -1 */
};

extern int chatty9p;

static char *User;
static char *Magic = "exif is over-complex";
static int Debug = 0;
Ptype Ptypes[] = {
	{ ".jpg",	jpgopen,	jpgpread,	jpgclose },
	{ ".jpeg",	jpgopen,	jpgpread,	jpgclose },
	{ ".jfif",	jpgopen,	jpgpread,	jpgclose },
//	{ ".tiff",	tiffopen,	tiffpread,	tiffclose },
//	{ ".tif",	tiffopen,	tiffpread,	tiffclose },
//	{ ".gif",	gifopen,	gifpread,	gifclose },
//	{ ".png",	pngopen,	pngpread,	pngclose },
//	{ ".dca",	dcaopen,	dcapread,	dcaclose },
//	{ ".raw",	rawopen,	rawpread,	rawclose },
};

Vfile Vfiles[] = {
	{ "fullsize",		1 },
	{ "thumbnail",		1 },
	{ "metadata",		0 },
};

static void
dircpy(Dir *d, Dir *s)
{
	int i;
	char *p;
	DigestState *ds;
	uchar digest[SHA1dlen];

	d->name = estrdup9p(s->name);
	d->type = s->type;
	d->dev = s->dev;
	d->uid = estrdup9p(s->uid);
	d->gid = estrdup9p(s->gid);
	d->muid = estrdup9p(s->muid);
	d->atime = s->atime;
	d->mtime = s->mtime;
	d->mode = s->mode;
	d->length = s->length;
	d->qid = s->qid;

	if((p = strrchr(d->name, '.')) == nil)
		return;
	for(i = 0; i < nelem(Ptypes); i++)
		if(cistrcmp(Ptypes[i].ext, p) == 0)
			break;
	if(i >= nelem(Ptypes))
		return;		// not a photo

	ds = sha1((uchar *)Magic, strlen(Magic), nil, nil);
	sha1((uchar *)d->name, strlen(d->name), digest, ds);
	free(d->muid);
	d->muid = smprint("%.*[", 8, digest);
	d->mode = DMDIR|0755;
	d->length = 0;
	d->qid.type |= QTDIR;
}

static Qid 
qidgen(char *path, char *name)
{
	static Qid q;
	DigestState *ds;
	uchar digest[SHA1dlen];

	ds = sha1((uchar *)path, strlen(path), nil, nil);
	sha1((uchar *)name, strlen(name), digest, ds);
	q.vers = 1;
	q.type = 'P';
	q.path = *((uvlong *)digest);
	return q;
}

static char *
newpath(char *path, char *name)
{
	char *p;

	p = smprint("%s/%s", path, name);
	if(p == nil)
		sysfatal("smprint: %r");
	return cleanname(p);
}

static int
dirfake(Dir *d, char *path, int slot)
{
	Dir *s;
	char *ext;

 	if(slot < 0 || slot >= nelem(Vfiles))
		return -1;

	ext = "";
	if(Vfiles[slot].inherit && (ext = strrchr(path, '.')) == nil)
		return -1;
	if((s = dirstat(path)) == nil)
		return -1;
	d->name = smprint("%s%s", Vfiles[slot].name, ext);
	d->uid = estrdup9p(s->uid);
	d->gid = estrdup9p(s->gid);
	d->muid = estrdup9p(s->muid);
	d->atime = s->atime;
	d->mtime = s->mtime;
	d->length = s->length;
	d->mode = s->mode;
	d->qid = qidgen(path, d->name);
	free(s);
	return 0;
}

static int
dirgen(int slot, Dir *d, void *aux)
{
	Aux *a = aux;

	if(a->pt != -1)
		return dirfake(d, a->path, slot);

	if (a->ndir <= 0)
		if ((a->ndir = dirreadall(a->fd, &a->dir)) < 0){
			a->dir = nil;
			return -1;
		}
	if (slot >= a->ndir)
		return -1;
	dircpy(d, a->dir+slot);
	return 0;
}

static void
fsattach(Req *r)
{
	Dir *d;
	Aux *a;
	int fd;

	if(strcmp(r->ifcall.uname, User) != 0){
		fd = open("#c/user", OWRITE);
		if(fd < 0 || write(fd, "none", strlen("none")) < 0)
			sysfatal("can't become none");
		close(fd);
		if(newns("none", nil) < 0)
			sysfatal("can't build namespace");
	}

	if ((d = dirstat(r->ifcall.aname)) == nil){
		responderror(r);
		return;
	}
	a = emalloc9p(sizeof(Aux));
	a->path = estrdup9p(r->ifcall.aname);
	a->fd = -1;
	a->pt = -1;
	a->vf = -1;
	a->ndir = 0;
	a->dir = nil;
	r->fid->qid = d->qid;
	free(d);

	r->ofcall.qid = r->fid->qid;
	r->fid->aux = a;
	respond(r, nil);
}


static char*
fsclone(Fid *ofid, Fid *fid)
{
	Aux *a, *oa = ofid->aux;

	a = emalloc9p(sizeof(Aux));
	fid->aux = a;
	a->fd = -1;
	a->ndir = 0;
	a->dir = nil;
	a->vf = oa->vf;
	a->pt = oa->pt;
	a->path = estrdup9p(oa->path);
	return nil;
}

static char*
fswalk1(Fid *fid, char *name, Qid *qp)
{
	Dir *d;
	int i, n, fd;
	char *ext, *npath;
	static char e[ERRMAX], nbuf[128];
	Aux *a = fid->aux;

	if(strcmp(name, "..") == 0){
		ext = strrchr(a->path, '/');
		if(ext == nil){
			snprint(e, sizeof e, ".. from root");
			return e;
		}
		*ext = 0;
		ext = strrchr(a->path, '/');
		if(ext == nil)
			ext = a->path;
		else
			*ext++ = 0;
		snprint(nbuf, sizeof nbuf, "%s", ext);
		name = nbuf;
		a->pt = -1;
	}

	*e = 0;
	ext = strrchr(name, '.');
	npath = newpath(a->path, name);
	/*
	 * one of the virtual files:  fullsize.jpg, thumbnail.jpg, metadata
	 */
	if(a->pt != -1){
		if(ext == nil)
			ext = strchr(name, 0);
		for(i = 0; i < nelem(Vfiles); i++)
			if(strncmp(Vfiles[i].name, name, ext-name) == 0)
				break;
		if(i >= nelem(Vfiles))
			return "unknown file";
		*qp = qidgen(a->path, name);
		fid->qid = *qp;
		free(a->path);
		a->path = npath;
		a->vf = i;
		return nil;
	}

	if((fd = open(a->path, OREAD)) == -1){
		rerrstr(e, sizeof e);
		free(npath);
		return e;
	}

	if((n = dirreadall(fd, &d)) < 1){
		rerrstr(e, sizeof e);
		close(fd);
		free(npath);
		return e;
	}

	/*
	 * a real photo file which is going to become a directory
	 */
	if(ext && (d->qid.type & QTDIR) == 0)
		for(i = 0; i < nelem(Ptypes); i++)
			if(cistrcmp(ext, Ptypes[i].ext) == 0){
				*qp = d->qid;
				qp->type |= QTDIR;
				fid->qid = *qp;
				a->pt = i;
				free(a->path);
				a->path = npath;
				close(fd);
				free(d);
				return nil;
			}

	/*
	 * a vanilla file or directory
	 */
	for(i = 0; i < n; i++)
		if(strcmp(name, d[i].name) == 0){
			*qp = d[i].qid;
			fid->qid = *qp;
			close(fd);
			free(d);
			free(a->path);
			a->path = npath;
			return nil;
		}
	close(fd);
	free(d);
	free(npath);
	return e;
}

static void
fsstat(Req *r)
{
	Dir *d;
	char *s, *p;
	Aux *a = r->fid->aux;

	if(a->vf != -1){
		s = estrdup9p(a->path);
		if((p = strrchr(s, '/')) != nil)
			*p = 0;
		if(dirfake(&(r->d), s, a->vf) == -1){
			free(s);
			responderror(r);
			return;
		}
		free(s);
		respond(r, nil);
		return;
	}

	if ((d = dirstat(a->path)) == nil){
		responderror(r);
		return;
	}
		dircpy(&(r->d), d);
	free(d);
	respond(r, nil);
}

static void
fsopen(Req *r)
{
	Aux *a = r->fid->aux;
	int (*func)(char *, int);

	if(a->pt != -1 && a->vf == -1){	// virtual dir
		respond(r, nil);
		return;
	}

	if(a->vf != -1 && a->pt != -1)
		func = Ptypes[a->pt].open;
	else
		func = open;
	if ((a->fd = (*func)(a->path, r->ifcall.mode)) < 0){
		responderror(r);
		return;
	}
	respond(r, nil);
}

static void
fsread(Req *r)
{
	long n;
	Aux *a = r->fid->aux;
	long (*func)(int, void *, long, vlong);

	if (r->fid->qid.type & QTDIR){
		dirread9p(r, dirgen, a);
		respond(r, nil);
		return;
	}

	if(a->vf != -1 && a->pt != -1)
		func = Ptypes[a->pt].pread;
	else
		func = pread;
	if ((n = (*func)(a->fd, r->ofcall.data, r->ifcall.count, r->ifcall.offset)) < 0){
		responderror(r);
		return;
	}
	r->ofcall.count = n;
	respond(r, nil);
}


static void
fsdestroyfid(Fid *f)
{
	Aux *a = f->aux;
	int (*func)(int);

	if(a){
		if(a->vf != -1 && a->pt != -1)
			func = Ptypes[a->pt].close;
		else
			func = close;
		if(a->fd != -1)
			(*func)(a->fd);
		if(a->dir && a->ndir > 0)
			free(a->dir);
		free(a->path);
		free(a);
	}
}

Srv fs = {
	.destroyfid =	fsdestroyfid,
	.attach=	fsattach,
	.open=		fsopen,
	.read=		fsread,
	.stat=		fsstat,
	.clone= 	fsclone,
	.walk1= 	fswalk1,
};


void
usage(void)
{
	fprint(2, "usage: exifsrv [-dD] [-m mode] [-s srv]\n");
	exits("usage");
}

void
threadmain(int argc, char **argv)
{
	Dir d;
	int mode;
	char *srv, path[128];

	mode = -1;
	srv = "exif";
	User = getuser();
	fmtinstall('[', encodefmt);

	ARGBEGIN{
	case 'm':
		mode = strtoul(EARGF(usage()), nil, 8);
		break;
	case 'd':
		Debug++;
		break;
	case 'D':
		chatty9p++;
		break;
	case 's':
		srv = EARGF(usage());
		break;
	default:
		usage();
		break;
	}ARGEND
	if(argc != 0)
		usage();
	if(!Debug && !chatty9p){
		close(0);
		close(1);
		close(2);
	}

	threadpostmountsrv(&fs, srv, nil, 0);

	if(mode != -1){
		snprint(path, sizeof(path), "/srv/%s", srv);
		nulldir(&d);
		d.mode = mode;
		dirwstat(path, &d);
	}

	exits(nil);
}

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.