1068 lines
18 KiB
C
1068 lines
18 KiB
C
/* Copyright (c) 20XX 9front
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
#include <u.h>
|
|
#include <lib9.h>
|
|
#include <thread.h>
|
|
#include <auth.h>
|
|
#include <9P2000.h>
|
|
#include <9p.h>
|
|
|
|
#include "usb.h"
|
|
|
|
enum
|
|
{
|
|
Qroot,
|
|
Qstore,
|
|
Qobj,
|
|
Qthumb,
|
|
};
|
|
|
|
enum {
|
|
/* flags */
|
|
DataSend = 0x00010000,
|
|
DataRecv = 0x00020000,
|
|
OutParam = 0x00040000,
|
|
|
|
/* rpc codes */
|
|
OpenSession = 0x1002,
|
|
CloseSession = 0x1003,
|
|
GetStorageIds = 0x1004,
|
|
GetStorageInfo = 0x1005,
|
|
GetObjectHandles = 0x1007,
|
|
GetObjectInfo = 0x1008,
|
|
GetObject = 0x1009,
|
|
GetThumb = 0x100A,
|
|
DeleteObject = 0x100B,
|
|
GetPartialObject = 0x101B,
|
|
|
|
Maxio = 0x2000,
|
|
};
|
|
|
|
typedef struct Ptprpc Ptprpc;
|
|
typedef struct Node Node;
|
|
|
|
struct Ptprpc
|
|
{
|
|
uint8_t length[4];
|
|
uint8_t type[2];
|
|
uint8_t code[2];
|
|
uint8_t transid[4];
|
|
uint8_t d[500];
|
|
};
|
|
|
|
struct Node
|
|
{
|
|
Dir d;
|
|
|
|
Node *parent;
|
|
Node *next;
|
|
Node *child;
|
|
|
|
int store;
|
|
int handle;
|
|
int format;
|
|
|
|
void *data;
|
|
int ndata;
|
|
};
|
|
|
|
enum {
|
|
In,
|
|
Out,
|
|
Int,
|
|
Setup,
|
|
};
|
|
|
|
static Dev *usbep[Setup+1];
|
|
|
|
static int debug = 0;
|
|
static uint32_t time0;
|
|
//static int maxpacket = 64; // NOT USED
|
|
static int sessionId = 1;
|
|
static int transId = 1;
|
|
|
|
static Node **nodes;
|
|
static int nnodes;
|
|
|
|
static Channel *iochan;
|
|
|
|
char Eperm[] = "permission denied";
|
|
char Einterrupt[] = "interrupted";
|
|
|
|
#define PATH(type, n) ((uint64_t)(type)|((uint64_t)(n)<<4))
|
|
#define TYPE(path) ((int)((path)&0xF))
|
|
#define NUM(path) ((int)((path)>>4))
|
|
|
|
static void
|
|
hexdump(char *prefix, uint8_t *p, int n)
|
|
{
|
|
char *s;
|
|
int i;
|
|
int m;
|
|
|
|
m = 12;
|
|
s = emalloc9p(1+((n+1)*((m*6)+7)));
|
|
s[0] = '\0';
|
|
for(i=0; i<n; i++){
|
|
int printable;
|
|
char x[8];
|
|
if((i % m)==0){
|
|
sprint(x, "\n%.4x: ", i);
|
|
strcat(s, x);
|
|
}
|
|
printable = (p[i] >= 32 && p[i]<=127);
|
|
sprint(x, "%.2x %c ", (int)p[i], printable ? p[i] : '.');
|
|
strcat(s, x);
|
|
}
|
|
fprint(2, "%20-s: %6d bytes %s\n", prefix, n, s);
|
|
free(s);
|
|
}
|
|
|
|
static int
|
|
wasinterrupt(void)
|
|
{
|
|
char err[ERRMAX];
|
|
|
|
rerrstr(err, sizeof(err));
|
|
if(strstr(err, Einterrupt) || strstr(err, "timed out")){
|
|
werrstr(Einterrupt);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static char *
|
|
ptperrstr(int code)
|
|
{
|
|
static char *a[] = {
|
|
"undefined",
|
|
nil ,
|
|
"general error" ,
|
|
"session not open" ,
|
|
"invalid transaction id" ,
|
|
"operation not supported" ,
|
|
"parameter not supported" ,
|
|
"incomplete transfer" ,
|
|
"invalid storage id" ,
|
|
"invalid object handle" ,
|
|
"device prop not supported" ,
|
|
"invalid object format code" ,
|
|
"storage full" ,
|
|
"object write protected" ,
|
|
"store read only" ,
|
|
"access denied" ,
|
|
"no thumbnail present" ,
|
|
"self test failed" ,
|
|
"partial deletion" ,
|
|
"store not available" ,
|
|
"specification by format unsupported" ,
|
|
"no valid object info" ,
|
|
"invalid code format" ,
|
|
"unknown vendor code",
|
|
"capture already terminated",
|
|
"device busy",
|
|
"invalid parent object",
|
|
"invalid device prop format",
|
|
"invalid device prop value",
|
|
"invalid parameter",
|
|
"session already opend",
|
|
"transaction canceld",
|
|
"specification of destination unsupported"
|
|
};
|
|
|
|
code -= 0x2000;
|
|
if(code < 0)
|
|
return nil;
|
|
if(code >= nelem(a))
|
|
return "invalid error number";
|
|
return a[code];
|
|
}
|
|
|
|
static int
|
|
ptpcheckerr(Ptprpc *rpc, int type, int transid, int length)
|
|
{
|
|
char *s;
|
|
|
|
if(length < 4+2+2+4){
|
|
werrstr("short response: %d < %d", length, 4+2+2+4);
|
|
return 1;
|
|
}
|
|
if(GET4(rpc->length) < length){
|
|
werrstr("unexpected response length 0x%x < 0x%x", GET4(rpc->length), length);
|
|
return 1;
|
|
}
|
|
if(GET4(rpc->transid) != transid){
|
|
werrstr("unexpected transaction id 0x%x != 0x%x", GET4(rpc->transid), transid);
|
|
return 1;
|
|
}
|
|
if(GET2(rpc->type) != type){
|
|
werrstr("unexpected response type 0x%x != 0x%x", GET2(rpc->type), type);
|
|
return 1;
|
|
}
|
|
if(s = ptperrstr(GET2(rpc->code))){
|
|
werrstr("%s", s);
|
|
return -GET2(rpc->code);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
vptprpc(Ioproc *io, int code, int flags, va_list a)
|
|
{
|
|
Ptprpc rpc;
|
|
int np, n, t, i, l;
|
|
uint8_t *b, *p, *e;
|
|
|
|
np = flags & 0xF;
|
|
n = 4+2+2+4+4*np;
|
|
t = transId++;
|
|
|
|
PUT4(rpc.length, n);
|
|
PUT2(rpc.type, 1);
|
|
PUT2(rpc.code, code);
|
|
PUT4(rpc.transid, t);
|
|
|
|
for(i=0; i<np; i++){
|
|
int x = va_arg(a, int);
|
|
PUT4(rpc.d + i*4, x);
|
|
}
|
|
if(debug)
|
|
hexdump("req>", (uint8_t*)&rpc, n);
|
|
werrstr("");
|
|
if(iowrite(io, usbep[Out]->dfd, &rpc, n) != n){
|
|
wasinterrupt();
|
|
return -1;
|
|
}
|
|
|
|
if(flags & DataSend){
|
|
void *sdata;
|
|
int sdatalen;
|
|
|
|
sdata = va_arg(a, void*);
|
|
sdatalen = va_arg(a, int);
|
|
|
|
b = (uint8_t*)sdata;
|
|
p = b;
|
|
e = b + sdatalen;
|
|
|
|
l = 4+2+2+4+sdatalen;
|
|
PUT4(rpc.length, l);
|
|
PUT2(rpc.type, 2);
|
|
|
|
if((n = sdatalen) > sizeof(rpc.d))
|
|
n = sizeof(rpc.d);
|
|
memmove(rpc.d, p, n);
|
|
p += n;
|
|
n += (4+2+2+4);
|
|
|
|
if(debug)
|
|
hexdump("data>", (uint8_t*)&rpc, n);
|
|
if(iowrite(io, usbep[Out]->dfd, &rpc, n) != n){
|
|
wasinterrupt();
|
|
return -1;
|
|
}
|
|
while(p < e){
|
|
n = e - p;
|
|
if(n > Maxio)
|
|
n = Maxio;
|
|
if((n = iowrite(io, usbep[Out]->dfd, p, n)) < 0){
|
|
wasinterrupt();
|
|
return -1;
|
|
}
|
|
p += n;
|
|
}
|
|
}
|
|
|
|
if(flags & DataRecv){
|
|
void **prdata;
|
|
int *prdatalen;
|
|
|
|
prdata = va_arg(a, void**);
|
|
prdatalen = va_arg(a, int*);
|
|
|
|
*prdata = nil;
|
|
*prdatalen = 0;
|
|
|
|
while((n = ioread(io, usbep[In]->dfd, &rpc, usbep[In]->maxpkt)) <= 0){
|
|
if(n < 0){
|
|
wasinterrupt();
|
|
return -1;
|
|
}
|
|
}
|
|
if((l = ptpcheckerr(&rpc, 2, t, n)) < 0)
|
|
return -1;
|
|
if(l && GET2(rpc.type) == 3)
|
|
goto Resp1;
|
|
|
|
l = GET4(rpc.length);
|
|
l -= (4+2+2+4);
|
|
n -= (4+2+2+4);
|
|
|
|
b = emalloc9p(l);
|
|
p = b;
|
|
e = b+l;
|
|
if(n <= l){
|
|
if(debug)
|
|
hexdump("data<", rpc.d, n);
|
|
memmove(p, rpc.d, n);
|
|
p += n;
|
|
while(p < e){
|
|
n = e-p;
|
|
if(n > Maxio)
|
|
n = Maxio;
|
|
while((n = ioread(io, usbep[In]->dfd, p, n)) <= 0){
|
|
if(n < 0){
|
|
wasinterrupt();
|
|
free(b);
|
|
return -1;
|
|
}
|
|
}
|
|
if(debug)
|
|
hexdump("data<", p, n);
|
|
p += n;
|
|
}
|
|
*prdata = b;
|
|
*prdatalen = e-b;
|
|
} else {
|
|
if(debug)
|
|
hexdump("data<", rpc.d, l);
|
|
memmove(p, rpc.d, l);
|
|
*prdata = b;
|
|
*prdatalen = e-b;
|
|
|
|
n -= l;
|
|
memmove(&rpc, rpc.d+l, n);
|
|
goto Resp1;
|
|
}
|
|
}
|
|
|
|
while((n = ioread(io, usbep[In]->dfd, &rpc, usbep[In]->maxpkt)) <= 0){
|
|
if(n < 0){
|
|
wasinterrupt();
|
|
return -1;
|
|
}
|
|
}
|
|
Resp1:
|
|
if(debug)
|
|
hexdump("resp<", (uint8_t*)&rpc, n);
|
|
if(ptpcheckerr(&rpc, 3, t, n))
|
|
return -1;
|
|
if(flags & OutParam){
|
|
int *pp;
|
|
|
|
for(i=0; i<5; i++){
|
|
if((pp = va_arg(a, int*)) == nil)
|
|
break;
|
|
*pp = GET4(rpc.d + i*4);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
ptprpc(Req *r, int code, int flags, ...)
|
|
{
|
|
va_list va;
|
|
Channel *c;
|
|
Ioproc *io;
|
|
Alt a[3];
|
|
char *m;
|
|
int i;
|
|
|
|
i = -1;
|
|
c = nil;
|
|
io = nil;
|
|
m = Einterrupt;
|
|
a[0].op = CHANRCV;
|
|
a[0].c = iochan;
|
|
a[0].v = &io;
|
|
if(r){
|
|
c = chancreate(sizeof(char*), 0);
|
|
a[1].op = CHANRCV;
|
|
a[1].c = c;
|
|
a[1].v = &m;
|
|
a[2].op = CHANEND;
|
|
r->aux = c;
|
|
srvrelease(r->srv);
|
|
} else
|
|
a[1].op = CHANEND;
|
|
if(alt(a) == 0){
|
|
va_start(va, flags);
|
|
i = vptprpc(io, code, flags, va);
|
|
va_end(va);
|
|
if(i < 0 && debug)
|
|
fprint(2, "rpc: %r\n");
|
|
} else
|
|
werrstr("%s", m);
|
|
if(r){
|
|
srvacquire(r->srv);
|
|
r->aux = nil;
|
|
}
|
|
if(io)
|
|
sendp(iochan, io);
|
|
if(c)
|
|
chanfree(c);
|
|
return i;
|
|
}
|
|
|
|
static int*
|
|
ptparray4(uint8_t *d, uint8_t *e)
|
|
{
|
|
int *a, i, n;
|
|
|
|
if(d + 4 > e)
|
|
return nil;
|
|
n = GET4(d);
|
|
d += 4;
|
|
if(d + n*4 > e)
|
|
return nil;
|
|
a = emalloc9p((1+n) * sizeof(int));
|
|
a[0] = n;
|
|
for(i=0; i<n; i++){
|
|
a[i+1] = GET4(d);
|
|
d += 4;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
static char*
|
|
ptpstring2(uint8_t *d, uint8_t *e)
|
|
{
|
|
int n, i;
|
|
char *s, *p;
|
|
|
|
if(d+1 > e)
|
|
return nil;
|
|
n = *d;
|
|
d++;
|
|
if(d + n*2 > e)
|
|
return nil;
|
|
p = s = emalloc9p((n+1)*UTFmax);
|
|
for(i=0; i<n; i++){
|
|
Rune r;
|
|
|
|
r = GET2(d);
|
|
d += 2;
|
|
if(r == 0)
|
|
break;
|
|
p += runetochar(p, &r);
|
|
}
|
|
*p = 0;
|
|
return s;
|
|
}
|
|
|
|
static void
|
|
cleardir(Dir *d)
|
|
{
|
|
free(d->name);
|
|
free(d->uid);
|
|
free(d->gid);
|
|
free(d->muid);
|
|
memset(d, 0, sizeof(*d));
|
|
}
|
|
|
|
static void
|
|
copydir(Dir *d, Dir *s)
|
|
{
|
|
memmove(d, s, sizeof(*d));
|
|
if(d->name)
|
|
d->name = estrdup9p(d->name);
|
|
if(d->uid)
|
|
d->uid = estrdup9p(d->uid);
|
|
if(d->gid)
|
|
d->gid = estrdup9p(d->gid);
|
|
if(d->muid)
|
|
d->muid = estrdup9p(d->muid);
|
|
}
|
|
|
|
static Node*
|
|
cachednode(uint64_t path, Node ***pf)
|
|
{
|
|
Node *x;
|
|
int i;
|
|
|
|
if(pf)
|
|
*pf = nil;
|
|
for(i=0; i<nnodes; i++){
|
|
if((x = nodes[i]) == nil){
|
|
if(pf)
|
|
*pf = &nodes[i];
|
|
continue;
|
|
}
|
|
if(x->d.qid.path == path)
|
|
return x;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
static Node*
|
|
getnode(Req *r, uint64_t path)
|
|
{
|
|
Node *x, *y, **f;
|
|
uint8_t *p;
|
|
int np;
|
|
char *s;
|
|
|
|
if(x = cachednode(path, &f))
|
|
return x;
|
|
|
|
y = nil;
|
|
x = emalloc9p(sizeof(*x));
|
|
memset(x, 0, sizeof(*x));
|
|
x->d.qid.path = path;
|
|
x->d.uid = estrdup9p("ptp");
|
|
x->d.gid = estrdup9p("usb");
|
|
x->d.atime = x->d.mtime = time0;
|
|
|
|
p = nil;
|
|
np = 0;
|
|
switch(TYPE(path)){
|
|
case Qroot:
|
|
x->d.qid.type = QTDIR;
|
|
x->d.mode = DMDIR|0777;
|
|
x->d.name = estrdup9p("/");
|
|
goto Addnode;
|
|
|
|
case Qstore:
|
|
x->store = NUM(path);
|
|
x->handle = 0xffffffff;
|
|
x->d.qid.type = QTDIR;
|
|
x->d.mode = DMDIR|0777;
|
|
x->d.name = emalloc9p(10);
|
|
sprint(x->d.name, "%x", x->store);
|
|
goto Addnode;
|
|
|
|
case Qobj:
|
|
case Qthumb:
|
|
if(ptprpc(r, GetObjectInfo, 1|DataRecv, NUM(path), &p, &np) < 0)
|
|
break;
|
|
if(debug)
|
|
hexdump("objectinfo", p, np);
|
|
if(np < 52){
|
|
werrstr("bad objectinfo");
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* another proc migh'v come in and done it for us,
|
|
* so check the cache again.
|
|
*/
|
|
if(y = cachednode(path, &f))
|
|
break;
|
|
|
|
if((x->d.name = ptpstring2(p+52, p+np)) == nil){
|
|
werrstr("bad objectinfo");
|
|
break;
|
|
}
|
|
x->handle = NUM(path);
|
|
x->store = GET4(p);
|
|
x->format = GET2(p+4);
|
|
if(x->format == 0x3001 && GET2(p+42) == 1){
|
|
x->d.qid.type = QTDIR;
|
|
x->d.mode = DMDIR|0777;
|
|
} else {
|
|
x->d.mode = 0666;
|
|
if(TYPE(path) == Qthumb){
|
|
char *t;
|
|
|
|
t = emalloc9p(8 + strlen(x->d.name));
|
|
sprint(t, "thumb_%s", x->d.name);
|
|
free(x->d.name);
|
|
x->d.name = t;
|
|
|
|
x->d.length = GET4(p+14);
|
|
} else {
|
|
x->d.length = GET4(p+8);
|
|
}
|
|
}
|
|
if(s = ptpstring2(p+(53+p[52]*2), p+np)){
|
|
if(strlen(s) >= 15){
|
|
Tm t;
|
|
|
|
// 0123 45 67 8 9A BC DF
|
|
// 2008 12 26 T 00 21 18
|
|
memset(&t, 0, sizeof(t));
|
|
|
|
s[0x10] = 0;
|
|
t.sec = atoi(s+0xD);
|
|
s[0xD] = 0;
|
|
t.min = atoi(s+0xB);
|
|
s[0xB] = 0;
|
|
t.hour = atoi(s+0x9);
|
|
s[0x8] = 0;
|
|
t.mday = atoi(s+0x6);
|
|
s[0x6] = 0;
|
|
t.mon = atoi(s+0x4) - 1;
|
|
s[0x4] = 0;
|
|
t.year = atoi(s) - 1900;
|
|
|
|
x->d.atime = x->d.mtime = tm2sec(&t);
|
|
}
|
|
free(s);
|
|
}
|
|
free(p);
|
|
Addnode:
|
|
if(f == nil){
|
|
if(nnodes % 64 == 0)
|
|
nodes = erealloc9p(nodes, sizeof(nodes[0]) * (nnodes + 64));
|
|
f = &nodes[nnodes++];
|
|
}
|
|
return *f = x;
|
|
}
|
|
|
|
cleardir(&x->d);
|
|
free(x);
|
|
free(p);
|
|
return y;
|
|
}
|
|
|
|
static void
|
|
freenode(Node *nod)
|
|
{
|
|
int i;
|
|
|
|
/* remove the node from the tree */
|
|
for(i=0; i<nnodes; i++){
|
|
if(nod == nodes[i]){
|
|
nodes[i] = nil;
|
|
break;
|
|
}
|
|
}
|
|
cleardir(&nod->d);
|
|
free(nod->data);
|
|
free(nod);
|
|
}
|
|
|
|
static int
|
|
readchilds(Req *r, Node *nod)
|
|
{
|
|
int e, i;
|
|
int *a;
|
|
uint8_t *p;
|
|
int np;
|
|
Node *x, **xx;
|
|
|
|
e = 0;
|
|
switch(TYPE(nod->d.qid.path)){
|
|
case Qroot:
|
|
if(ptprpc(r, GetStorageIds, 0|DataRecv, &p, &np) < 0)
|
|
return -1;
|
|
a = ptparray4(p, p+np);
|
|
free(p);
|
|
xx = &nod->child;
|
|
*xx = nil;
|
|
for(i=0; a && i<a[0]; i++){
|
|
if((x = getnode(r, PATH(Qstore, a[i+1]))) == nil){
|
|
e = -1;
|
|
break;
|
|
}
|
|
x->parent = nod;
|
|
*xx = x;
|
|
xx = &x->next;
|
|
*xx = nil;
|
|
}
|
|
free(a);
|
|
break;
|
|
|
|
case Qstore:
|
|
case Qobj:
|
|
if(ptprpc(r, GetObjectHandles, 3|DataRecv, nod->store, 0, nod->handle, &p, &np) < 0)
|
|
return -1;
|
|
a = ptparray4(p, p+np);
|
|
free(p);
|
|
xx = &nod->child;
|
|
*xx = nil;
|
|
for(i=0; a && i<a[0]; i++){
|
|
if((x = getnode(r, PATH(Qobj, a[i+1]))) == nil){
|
|
e = -1;
|
|
break;
|
|
}
|
|
x->parent = nod;
|
|
*xx = x;
|
|
xx = &x->next;
|
|
*xx = nil;
|
|
|
|
/* skip thumb when not image format */
|
|
if((x->format & 0xFF00) != 0x3800)
|
|
continue;
|
|
if((x = getnode(r, PATH(Qthumb, a[i+1]))) == nil){
|
|
e = -1;
|
|
break;
|
|
}
|
|
x->parent = nod;
|
|
*xx = x;
|
|
xx = &x->next;
|
|
*xx = nil;
|
|
}
|
|
free(a);
|
|
break;
|
|
}
|
|
|
|
return e;
|
|
}
|
|
|
|
static void
|
|
fsattach(Req *r)
|
|
{
|
|
if(r->ifcall.aname && r->ifcall.aname[0]){
|
|
respond(r, "invalid attach specifier");
|
|
return;
|
|
}
|
|
r->fid->qid.path = PATH(Qroot, 0);
|
|
r->fid->qid.type = QTDIR;
|
|
r->fid->qid.vers = 0;
|
|
r->ofcall.qid = r->fid->qid;
|
|
respond(r, nil);
|
|
}
|
|
|
|
static void
|
|
fsstat(Req *r)
|
|
{
|
|
Node *nod;
|
|
|
|
if((nod = getnode(r, r->fid->qid.path)) == nil){
|
|
responderror(r);
|
|
return;
|
|
}
|
|
copydir(&r->d, &nod->d);
|
|
respond(r, nil);
|
|
}
|
|
|
|
static int
|
|
nodegen(int i, Dir *d, void *aux)
|
|
{
|
|
Node *nod = aux;
|
|
|
|
for(nod=nod->child; nod && i; nod=nod->next, i--)
|
|
;
|
|
if(i==0 && nod){
|
|
copydir(d, &nod->d);
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static char*
|
|
fswalk1(Req *r, char *name, Qid *qid)
|
|
{
|
|
static char buf[ERRMAX];
|
|
uint64_t path;
|
|
Node *nod;
|
|
Fid *fid;
|
|
|
|
fid = r->newfid;
|
|
path = fid->qid.path;
|
|
if(!(fid->qid.type&QTDIR))
|
|
return "walk in non-directory";
|
|
if(nod = getnode(r, path)){
|
|
if(strcmp(name, "..") == 0){
|
|
if(nod = nod->parent)
|
|
*qid = nod->d.qid;
|
|
return nil;
|
|
}
|
|
if(readchilds(r, nod) == 0){
|
|
for(nod=nod->child; nod; nod=nod->next){
|
|
if(strcmp(nod->d.name, name) == 0){
|
|
*qid = nod->d.qid;
|
|
return nil;
|
|
}
|
|
}
|
|
return "directory entry not found";
|
|
}
|
|
}
|
|
rerrstr(buf, sizeof(buf));
|
|
return buf;
|
|
}
|
|
|
|
static char*
|
|
oldwalk1(Fid *fid, char *name, void *arg)
|
|
{
|
|
Qid qid;
|
|
char *e;
|
|
Req *r;
|
|
|
|
r = arg;
|
|
assert(fid == r->newfid);
|
|
if(e = fswalk1(r, name, &qid))
|
|
return e;
|
|
fid->qid = qid;
|
|
return nil;
|
|
}
|
|
|
|
static char*
|
|
oldclone(Fid * _, Fid * __, void * ___)
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
static void
|
|
fswalk(Req *r)
|
|
{
|
|
walkandclone(r, oldwalk1, oldclone, r);
|
|
}
|
|
|
|
static void
|
|
fsread(Req *r)
|
|
{
|
|
uint64_t path;
|
|
Node *nod;
|
|
uint8_t *p;
|
|
int np;
|
|
|
|
np = 0;
|
|
p = nil;
|
|
path = r->fid->qid.path;
|
|
if(nod = getnode(r, path)){
|
|
switch(TYPE(path)){
|
|
case Qroot:
|
|
case Qstore:
|
|
case Qobj:
|
|
if(nod->d.qid.type & QTDIR){
|
|
if(readchilds(r, nod) < 0)
|
|
break;
|
|
dirread9p(r, nodegen, nod);
|
|
respond(r, nil);
|
|
return;
|
|
}
|
|
if(nod->data == nil){
|
|
int offset, count, pos;
|
|
|
|
offset = r->ifcall.offset;
|
|
if(offset >= nod->d.length){
|
|
r->ofcall.count = 0;
|
|
respond(r, nil);
|
|
return;
|
|
}
|
|
/* are these people stupid? */
|
|
pos = (offset == 0) ? 1 : 2;
|
|
count = r->ifcall.count;
|
|
if((count + offset) > nod->d.length){
|
|
count = nod->d.length - offset;
|
|
pos = 3;
|
|
}
|
|
if(!ptprpc(r, GetPartialObject, 4|DataRecv,
|
|
nod->handle, offset, count, pos, &p, &np)){
|
|
if(np <= count){
|
|
memmove(r->ofcall.data, p, np);
|
|
r->ofcall.count = np;
|
|
respond(r, nil);
|
|
free(p);
|
|
return;
|
|
}
|
|
}
|
|
free(p);
|
|
}
|
|
/* no break */
|
|
case Qthumb:
|
|
if(nod->data == nil){
|
|
np = 0;
|
|
p = nil;
|
|
if(ptprpc(r, TYPE(path)==Qthumb ? GetThumb : GetObject,
|
|
1|DataRecv, nod->handle, &p, &np) < 0){
|
|
free(p);
|
|
break;
|
|
}
|
|
nod->data = p;
|
|
nod->ndata = np;
|
|
}
|
|
readbuf(r, nod->data, nod->ndata);
|
|
respond(r, nil);
|
|
return;
|
|
}
|
|
}
|
|
responderror(r);
|
|
}
|
|
|
|
static void
|
|
fsremove(Req *r)
|
|
{
|
|
Node *nod;
|
|
uint64_t path;
|
|
|
|
path = r->fid->qid.path;
|
|
if(nod = getnode(r, path)){
|
|
switch(TYPE(path)){
|
|
default:
|
|
werrstr(Eperm);
|
|
break;
|
|
case Qobj:
|
|
if(ptprpc(r, DeleteObject, 2, nod->handle, 0) < 0)
|
|
break;
|
|
/* no break */
|
|
case Qthumb:
|
|
if(nod = cachednode(path, nil))
|
|
freenode(nod);
|
|
respond(r, nil);
|
|
return;
|
|
}
|
|
}
|
|
responderror(r);
|
|
}
|
|
|
|
static void
|
|
fsopen(Req *r)
|
|
{
|
|
if(r->ifcall.mode != NP_OREAD){
|
|
respond(r, Eperm);
|
|
return;
|
|
}
|
|
respond(r, nil);
|
|
}
|
|
|
|
static void
|
|
fsflush(Req *r)
|
|
{
|
|
Channel *c;
|
|
|
|
if(c = r->oldreq->aux)
|
|
nbsendp(c, Einterrupt);
|
|
respond(r, nil);
|
|
}
|
|
|
|
static void
|
|
fsdestroyfid(Fid *fid)
|
|
{
|
|
Node *nod;
|
|
uint64_t path;
|
|
|
|
path = fid->qid.path;
|
|
switch(TYPE(path)){
|
|
case Qobj:
|
|
case Qthumb:
|
|
if(nod = cachednode(path, nil)){
|
|
free(nod->data);
|
|
nod->data = nil;
|
|
nod->ndata = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
fsend(Srv * _)
|
|
{
|
|
ptprpc(nil, CloseSession, 0);
|
|
closeioproc(recvp(iochan));
|
|
}
|
|
|
|
static int
|
|
findendpoints(Dev *d, int *epin, int *epout, int *epint)
|
|
{
|
|
int i;
|
|
Ep *ep;
|
|
Usbdev *ud;
|
|
|
|
ud = d->usb;
|
|
*epin = *epout = *epint = -1;
|
|
for(i=0; i<nelem(ud->ep); i++){
|
|
if((ep = ud->ep[i]) == nil)
|
|
continue;
|
|
if(ep->type == Eintr && *epint == -1)
|
|
*epint = ep->id;
|
|
if(ep->type != Ebulk)
|
|
continue;
|
|
if(ep->dir == Eboth || ep->dir == Ein)
|
|
if(*epin == -1)
|
|
*epin = ep->id;
|
|
if(ep->dir == Eboth || ep->dir == Eout)
|
|
if(*epout == -1)
|
|
*epout = ep->id;
|
|
}
|
|
if(*epin >= 0 && *epout >= 0)
|
|
return 0;
|
|
return -1;
|
|
}
|
|
|
|
Srv fs =
|
|
{
|
|
.attach = fsattach,
|
|
.destroyfid = fsdestroyfid,
|
|
.walk = fswalk,
|
|
.open = fsopen,
|
|
.read = fsread,
|
|
.remove = fsremove,
|
|
.stat = fsstat,
|
|
.flush = fsflush,
|
|
.end = fsend,
|
|
};
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
fprint(2, "usage: %s [-dD] devid\n", argv0);
|
|
threadexits("usage");
|
|
}
|
|
|
|
void
|
|
threadmain(int argc, char **argv)
|
|
{
|
|
char name[64], desc[64];
|
|
int epin, epout, epint;
|
|
Dev *d;
|
|
|
|
ARGBEGIN {
|
|
case 'd':
|
|
debug++;
|
|
break;
|
|
case 'D':
|
|
chatty9p++;
|
|
break;
|
|
default:
|
|
usage();
|
|
} ARGEND;
|
|
|
|
if(argc == 0)
|
|
usage();
|
|
if((d = getdev(*argv)) == nil)
|
|
sysfatal("opendev: %r");
|
|
if(findendpoints(d, &epin, &epout, &epint) < 0)
|
|
sysfatal("findendpoints: %r");
|
|
|
|
usbep[In] = openep(usbep[Setup] = d, epin);
|
|
if(epin == epout){
|
|
incref(&usbep[In]->ref);
|
|
usbep[Out] = usbep[In];
|
|
opendevdata(usbep[In], ORDWR);
|
|
} else {
|
|
usbep[Out] = openep(d, epout);
|
|
opendevdata(usbep[In], OREAD);
|
|
opendevdata(usbep[Out], OWRITE);
|
|
}
|
|
if(usbep[In]->dfd < 0 || usbep[Out]->dfd < 0)
|
|
sysfatal("open endpoints: %r");
|
|
if(usbep[In]->maxpkt < 12 || usbep[In]->maxpkt > sizeof(Ptprpc))
|
|
sysfatal("bad packet size: %d\n", usbep[In]->maxpkt);
|
|
iochan = chancreate(sizeof(Ioproc*), 1);
|
|
sendp(iochan, ioproc());
|
|
|
|
sessionId = getpid();
|
|
if(ptprpc(nil, OpenSession, 1, sessionId) < 0)
|
|
sysfatal("open session: %r");
|
|
|
|
time0 = time(0);
|
|
|
|
snprint(name, sizeof name, "sdU%s", d->hname);
|
|
snprint(desc, sizeof desc, "%d.ptp", d->id);
|
|
threadpostsharesrv(&fs, nil, name, desc);
|
|
|
|
threadexits(0);
|
|
}
|