#include <9pm/windows.h>
#include <9pm/u.h>
#include <9pm/libc.h>
#include <9pm/ctype.h>
#include <9pm/fcall.h>
#include "dat.h"
#include "fns.h"
#include "syscall.h"
#pragma comment(lib, "mpr.lib")
#define DPRINT if(1)print
typedef struct Uchan Uchan;
struct Uchan
{
HANDLE h;
Cname* syspath;
int type;
int eof;
Dir dir;
Rune rtmp[128];
WIN32_FIND_DATA finddata;
uchar *buf;
int nbuf;
int havedir;
int rootoff;
char rootent[10];
QLock qlk;
};
enum
{
Trootdir,
Tmntdir,
Tserver,
Tdir,
Tvolume, /* root of drive (like c:\) */
Tfile,
Tpipe,
Tconsole,
Tstdin, /* do we need this? */
};
static int pathqid(int, char*);
static long unixtime(FILETIME ft);
static FILETIME wintime(ulong);
static long consolewrite(Uchan*, void*, long);
static long consoleread(Uchan*, void*, long);
static long serverread(Uchan*, void*, long, vlong);
static void serveropen(Uchan*, int);
static void serverclose(Uchan*);
static long directoryread(Uchan*, uchar*, long, vlong);
static void data2dir(WIN32_FIND_DATA*, Dir*, char*);
static long rootdirread(Uchan*, uchar*, long, vlong);
static void rootdiropen(Uchan*);
/*
* Convert between real Windows paths and the paths we present.
* We map x:\ to /x, \\server to /mnt/server, and \\. to /mnt/local
* (BUG not yet)
*/
static struct {
Rune *r;
char *s;
} pathtab[] = {
L"X:\\", "/X",
L"\\\\.\\", "/mnt/local",
L"\\\\", "/mnt",
};
static Rune*
towin(char *s)
{
char *ss;
Rune *r, *rr;
int drive, i, match, len;
drive = '?';
if(s[0]=='/' && islower(s[1]) && (s[2]=='/' || s[2]=='\0')){
drive = s[1];
match = 0;
}else{
match = -1;
for(i=0; i<nelem(pathtab); i++){
len = strlen(pathtab[i].s);
if(strncmp(s, pathtab[i].s, len)==0 && (s[len]=='\0' || s[len]=='/')){
match = i;
break;
}
}
if(match < 0)
return nil;
}
r = smalloc(sizeof(Rune)*(utflen(s)+runestrlen(pathtab[match].r)+10));
runestrcpy(r, pathtab[match].r);
rr = r+runestrlen(r);
ss = s+strlen(pathtab[match].s);
if(*ss == '/')
ss++;
while(*ss){
ss += chartorune(rr, ss);
if(*rr == '/')
*rr = L'\\';
rr++;
}
*rr = '\0';
if(match == 0)
r[0] = drive;
return r;
}
Rune*
_execpath(char *s)
{
return towin(s);
}
static char*
fromwin(Rune *r)
{
int drive, i, match;
char *s, *ss;
Rune *rr;
match = -1;
if(isalpha(r[0]) && r[1]==':' && r[2]=='\\'){
match = 0;
drive = tolower(r[0]);
}else{
match = -1;
for(i=0; i<nelem(pathtab); i++){
if(runestrncmp(r, pathtab[i].r, runestrlen(pathtab[i].r))==0){
match = i;
break;
}
}
if(match < 0)
return nil;
}
s = smalloc(strlen(pathtab[match].s)+runenlen(r, runestrlen(r))+10);
strcpy(s, pathtab[match].s);
rr = r+runestrlen(pathtab[match].r);
ss = s+strlen(s);
if(*rr != '\0')
*ss++ = '/';
while(*rr){
ss += runetochar(ss, rr);
if(*(ss-1) == '\\')
*(ss-1) = '/';
rr++;
}
*ss = '\0';
if(match == 0)
s[1] = drive;
return s;
}
static int
pathtype(char *s)
{
Rune *r;
int attr;
if(strcmp(s, "/mnt") == 0)
return Tmntdir;
if(strcmp(s, "/") == 0)
return Trootdir;
if(strncmp(s, "/mnt/", 5)==0 && strchr(s+5, '/')==nil)
return Tserver;
if(s[0]=='/' && s[2]=='\0')
return Tvolume;
r = towin(s);
if(r == nil){
werrstr("towin %s?", s);
return -1;
}
attr = GetFileAttributes(r);
free(r);
if(attr == -1){
oserror();
return -1;
}
if(attr & FILE_ATTRIBUTE_DIRECTORY)
return Tdir;
return Tfile;
}
static Rune*
fspath(char *dir, char *elem)
{
int len;
char *e;
Rune *s, *t;
s = towin(dir);
if(elem){
t = smalloc(sizeof(Rune)*(runestrlen(s)+1+strlen(elem)+1));
runestrcpy(t, s);
len = runestrlen(t);
if(t[len-1] != '\\'){
t[len] = '\\';
t[++len] = '\0';
}
free(s);
s = t;
t += len;
e = elem;
for(; *e; t++)
e += chartorune(t, e);
*t = '\0';
}
return s;
}
static Chan*
handle2chan(HANDLE h, int mode, Qid qid, char *buf)
{
Chan *c;
Uchan *uc;
c = newchan();
c->name = newcname(buf);
c->mode = mode;
c->flag |= COPEN;
c->type = devno('U', 1);
c->qid = qid;
uc = smalloc(sizeof *uc);
uc->syspath = newcname(buf);
uc->type = pathtype(buf);
uc->h = h;
c->aux = uc;
return c;
}
static Chan*
syspath2chan(Qid qid, char *buf)
{
Chan *c;
Uchan *uc;
c = newchan();
c->name = newcname(buf);
c->type = devno('U', 1);
c->qid = qid;
uc = smalloc(sizeof *uc);
uc->syspath = newcname(buf);
uc->type = pathtype(buf);
c->aux = uc;
return c;
}
int
issysfd(Chan *c)
{
Uchan *uc;
if(devtab[c->type]->dc != 'U')
return 0;
uc = c->aux;
return uc->h != nil && uc->h != INVALID_HANDLE_VALUE && uc->type >= Tfile;
}
static void
fsreset(void)
{
char *s;
for(s="\\:?*;"; *s; s++)
isfrog[*s] = 1;
}
static Chan*
fsattach(char *spec)
{
Chan *c;
Qid q;
Uchan *uc;
static Lock lk;
static int devno;
if(spec && spec[0])
error(Ebadspec);
mkqid(&q, pathqid(0, "/"), 0, QTDIR);
c = devattach('U', "");
c->qid = q;
uc = smalloc(sizeof *uc);
uc->syspath = newcname("/");
c->aux = uc;
lock(&lk);
c->dev = devno++;
unlock(&lk);
return c;
}
Walkqid*
fswalk(Chan *c, Chan *nc, char **name, int nname)
{
int i, type;
int volatile alloc;
Cname *p, *oname;
Uchan *uc;
Walkqid *wq;
if(nname > MAXWELEM)
error("devfs: too many name elements");
alloc = 0;
wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
if(waserror()){
if(alloc && wq->clone!=nil)
cclose(wq->clone);
free(wq);
return nil;
}
if(nc == nil){
nc = devclone(c);
uc = smalloc(sizeof *uc);
*uc = *(Uchan*)c->aux;
nc->aux = uc;
incref(&uc->syspath->ref);
alloc = 1;
}
wq->clone = nc;
uc = nc->aux;
oname = uc->syspath;
incref(&oname->ref);
type = uc->type;
for(i = 0; i < nname; i++){
p = addelem(uc->syspath, name[i]);
if(strcmp(name[i], "..") == 0)
cleancname(p);
uc->syspath = p;
if((type = pathtype(p->s)) < 0){
//print("cannot find %s\n", p->s);
cnameclose(uc->syspath);
uc->syspath = oname;
if(alloc)
cclose(nc);
wq->clone = nil;
if(i == 0){
free(wq);
poperror();
return nil;
}
break;
}
mkqid(&wq->qid[i], pathqid(0, uc->syspath->s), 0, 0);
if(type < Tfile)
wq->qid[i].type |= QTDIR;
}
poperror();
if(i == nname){
cnameclose(oname);
uc->type = type;
}
if(i > 0 && wq->clone != nil)
wq->clone->qid = wq->qid[i-1];
wq->nqid = i;
return wq;
}
static Chan*
fsopen(Chan *c, int mode)
{
int acc, aflag, cflag, share;
HANDLE h;
Rune *wpath;
Uchan *uc;
uc = c->aux;
switch(uc->type){
case Tserver:
serveropen(uc, mode);
break;
case Trootdir:
rootdiropen(uc);
case Tdir:
uc->h = INVALID_HANDLE_VALUE;
break;
case Tfile:
switch(mode&3){
default:
acc = 0;
break;
case OREAD:
case OEXEC:
acc = GENERIC_READ;
break;
case OWRITE:
acc = GENERIC_WRITE;
break;
case ORDWR:
acc = GENERIC_READ|GENERIC_WRITE;
break;
}
cflag = OPEN_EXISTING;
if(mode&OTRUNC)
cflag = TRUNCATE_EXISTING;
aflag = 0;
if(mode&ORCLOSE)
aflag |= FILE_FLAG_DELETE_ON_CLOSE;
share = FILE_SHARE_READ|FILE_SHARE_WRITE;
wpath = towin(uc->syspath->s);
h = CreateFile(wpath, acc, share, 0, cflag, aflag, 0);
free(wpath);
if(h == INVALID_HANDLE_VALUE){
oserror();
//print("open %S: %r\n", wpath);
nexterror();
}
uc->h = h;
break;
}
c->mode = openmode(mode);
c->offset = 0;
c->flag |= COPEN;
return c;
}
static void
fscreate(Chan *c, char *name, int mode, ulong perm)
{
int acc, cflag, aflag, share;
Rune *path;
Uchan *uc;
uc = c->aux;
switch(uc->type){
case Tserver:
case Tvolume:
case Tfile:
error(Eperm);
case Tdir:
path = fspath(uc->syspath->s, name);
if(perm&DMDIR){
if(mode != OREAD)
error(Ebadarg);
if(!CreateDirectory(path, 0)){
oserror();
free(path);
nexterror();
}
free(path);
uc->h = INVALID_HANDLE_VALUE;
}else{
switch(mode&3){
default:
acc = 0;
break;
case OREAD:
case OEXEC:
acc = GENERIC_READ;
break;
case OWRITE:
acc = GENERIC_WRITE;
break;
case ORDWR:
acc = GENERIC_READ|GENERIC_WRITE;
break;
}
/*BUG: OEXCL, OTRUNC? */
cflag = CREATE_ALWAYS;
aflag = 0;
if(mode&ORCLOSE)
aflag |= FILE_FLAG_DELETE_ON_CLOSE;
share = FILE_SHARE_READ|FILE_SHARE_WRITE;
uc->h = CreateFile(path, acc, share, 0, cflag, aflag, 0);
free(path);
if(uc->h == INVALID_HANDLE_VALUE){
oserror();
nexterror();
}
uc->type = Tfile;
c->qid.type = 0;
}
uc->syspath = addelem(uc->syspath, name);
c->qid.path = pathqid(0, uc->syspath->s);
break;
}
c->mode = openmode(mode);
c->offset = 0;
c->flag |= COPEN;
}
static void fsremove(Chan*);
static void
fsclose(Chan *c)
{
Uchan *uc;
uc = c->aux;
if(c->flag & COPEN){
switch(uc->type){
case Tconsole:
case Tpipe:
case Tfile:
case Tstdin:
if(uc->h != INVALID_HANDLE_VALUE)
CloseHandle(uc->h);
break;
case Tdir:
FindClose(uc->h);
if(uc->havedir){
free(uc->dir.name);
free(uc->dir.uid);
free(uc->dir.gid);
}
break;
case Tserver:
WNetCloseEnum(uc->h);
break;
}
}
cnameclose(uc->syspath);
free(uc);
}
static long
fsread(Chan *c, void *a, long n, vlong vo)
{
int r;
Uchan *uc;
OVERLAPPED o;
DWORD m;
uc = c->aux;
if(uc->eof)
return 0;
DPRINT("fsread\n");
memset(&o, 0, sizeof o);
o.Offset = vo;
o.OffsetHigh = vo>>32;
switch(uc->type){
case Tfile:
r = ReadFile(uc->h, a, n, &m, &o);
if(r == 0){
if(uc->type==Tpipe || GetLastError() == ERROR_HANDLE_EOF)
m = 0;
else{
oserror();
nexterror();
}
}
break;
case Tmntdir:
m = 0;
break;
case Trootdir:
m = rootdirread(uc, a, n, vo);
break;
case Tconsole:
m = consoleread(uc, a, n);
break;
case Tvolume:
case Tdir:
m = directoryread(uc, a, n, vo);
break;
case Tserver:
m = serverread(uc, a, n, vo);
break;
}
return m;
}
static long
fswrite(Chan *c, void *a, long n, vlong vo)
{
Uchan *uc;
OVERLAPPED o;
DWORD m;
memset(&o, 0, sizeof o);
o.Offset = vo;
o.OffsetHigh = vo>>32;
uc = c->aux;
switch(uc->type){
case Tpipe:
if(!WriteFile(uc->h, a, n, &m, nil)){
oserror();
print("pipewrite: %r\n");
nexterror();
}
break;
case Tfile:
if(!WriteFile(uc->h, a, n, &m, &o)){
oserror();
print("writefile: %r\n");
nexterror();
}
break;
case Tconsole:
m = consolewrite(uc, a, n);
break;
case Tdir:
error(Ebadusefd);
break;
}
return m;
}
static void
fsremove(Chan *c)
{
int err;
Rune *path;
Uchan *uc;
uc = c->aux;
path = towin(uc->syspath->s);
if(path == nil)
error(Eperm);
if(c->qid.type&QTDIR)
err = !RemoveDirectory(path);
else
err = !DeleteFile(path);
free(path);
if(err)
oserror();
if(c->flag & COPEN){
CloseHandle(uc->h);
uc->h = INVALID_HANDLE_VALUE;
}
cnameclose(uc->syspath);
free(uc);
if(err)
nexterror();
}
static int
fsstat(Chan *c, uchar *buf, int n)
{
Dir d;
HANDLE h;
char *dpath, *s;
Rune *path, *p;
Uchan *uc;
WIN32_FIND_DATA data;
uc = c->aux;
switch(uc->type){
case Tpipe:
case Tconsole:
case Tstdin:
error("cannot stat pipe, console, stdin");
}
dpath = strdup(uc->syspath->s);
if(dpath == nil)
error("out of memory");
s = strrchr(dpath, '/');
if(s == dpath)
*(s+1) = '\0';
else
*s = '\0';
path = towin(uc->syspath->s);
if(waserror()){
free(dpath);
free(path);
nexterror();
}
memset(&d, 0, sizeof d);
switch(uc->type){
default:
error("windows fsstat bug");
case Trootdir:
d.name = strdup("/");
goto Fakedir;
case Tmntdir:
d.name = strdup("mnt");
goto Fakedir;
case Tserver:
p = runestrrchr(path, '\\');
p++;
memset(&d, 0, sizeof d);
d.name = win_wstr2utf(p);
Fakedir:
d.uid = strdup("sys");
d.gid = strdup("sys");
d.mtime = d.atime = seconds();
d.type = 'U';
d.dev = 0;
d.qid = c->qid;
d.mode = DMDIR|0777;
break;
case Tvolume:
memset(&data, 0, sizeof data);
data.dwFileAttributes = GetFileAttributes(path);
if(data.dwFileAttributes == (DWORD)-1){
oserror();
nexterror();
}
data.ftCreationTime =
data.ftLastAccessTime =
data.ftLastWriteTime = wintime(seconds());
data.nFileSizeHigh = 0;
data.nFileSizeLow = 0;
p = runestrrchr(path, '\\');
p++;
runestrecpy(data.cFileName, data.cFileName+nelem(data.cFileName), p);
data2dir(&data, &d, uc->syspath->s);
break;
case Tdir:
case Tfile:
memset(&data, 0, sizeof data);
h = FindFirstFile(path, &data);
if(h == INVALID_HANDLE_VALUE){
oserror();
nexterror();
}
FindClose(h);
data2dir(&data, &d, dpath);
break;
}
n = convD2M(&d, buf, n);
free(dpath);
free(d.uid);
free(d.gid);
free(d.name);
free(path);
poperror();
return n;
}
static int
fswstat(Chan *c, uchar *edir, int nedir)
{
char *strs;
int attr, n;
Dir dir;
FILETIME *pat, at, *pmt, mt;
HANDLE h;
Rune *path, *newpath, *p;
Uchan *uc;
WIN32_FIND_DATA *data;
uc = c->aux;
path = towin(uc->syspath->s);
strs = smalloc(nedir);
data = smalloc(sizeof(*data));
if(waserror()){
free(path);
free(strs);
free(data);
nexterror();
}
if((nedir=convM2D(edir, nedir, &dir, strs)) == 0)
error("convM2D failed");
switch(uc->type){
case Trootdir:
case Tmntdir:
case Tserver:
error(Eperm);
case Tvolume:
if(!win_usesecurity)
error("cannot wstat volume: no security");
if(win_secwperm(path, &dir, 1) < 0)
nexterror();
break;
case Tdir:
case Tfile:
h = FindFirstFile(path, data);
if(h == INVALID_HANDLE_VALUE) {
oserror();
nexterror();
}
FindClose(h);
pat = pmt = NULL;
if(~dir.atime != 0){
at = wintime(dir.atime);
pat = &at;
}
if(~dir.mtime != 0){
mt = wintime(dir.mtime);
pmt = &mt;
}
if(pat || pmt){
h = CreateFile(path, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(h == INVALID_HANDLE_VALUE){
oserror();
nexterror();
}
if(!SetFileTime(h, 0, pat, pmt)){
oserror();
CloseHandle(h);
nexterror();
}
CloseHandle(h);
}
attr = data->dwFileAttributes;
if(~dir.mode != 0){
if(dir.mode & 0222)
attr &= ~FILE_ATTRIBUTE_READONLY;
else
attr |= FILE_ATTRIBUTE_READONLY;
if(attr&FILE_ATTRIBUTE_READONLY)
SetFileAttributes(path, attr);
}
//print("win_usesecurity %d\n", win_usesecurity);
if(win_usesecurity && win_secwperm(path, &dir, attr&FILE_ATTRIBUTE_DIRECTORY) < 0)
nexterror();
if(~dir.mode != 0 && !(attr & FILE_ATTRIBUTE_READONLY))
SetFileAttributes(path, attr);
/* do last so path is valid throughout */
if(dir.name != nil && dir.name[0]){
p = runestrrchr(path, '\\');
n = p-path+1;
newpath = smalloc(sizeof(Rune)*(n+utflen(dir.name)+1));
memmove(newpath, path, n*sizeof(Rune));
win_utf2wstrn(newpath+n, utflen(dir.name)+1, dir.name);
if(runestrcmp(path, newpath) != 0 && !MoveFile(path, newpath)){
free(newpath);
oserror();
nexterror();
}
free(newpath);
}
break;
case Tpipe:
error("cannot wstat pipe");
case Tstdin:
error("cannot wstat stdin");
case Tconsole:
error("cannot wstat console");
}
free(path);
free(strs);
free(data);
poperror();
return nedir;
}
static void
fschdir(Chan *c)
{
Rune *path;
Uchan *uc;
uc = c->aux;
switch(uc->type){
default:
error("unKnown path type (BUG)");
case Tserver:
error("can't chdir to server yet");
case Tvolume:
case Tfile:
path = towin(uc->syspath->s);
if(path == nil)
error("can't chdir there");
if(!SetCurrentDirectory(path)){
free(path);
oserror();
nexterror();
}
free(path);
break;
}
}
Dev devfs =
{
(Rune)'U',
"fs",
fsreset,
fsattach,
fswalk,
fsstat,
fsopen,
fscreate,
fsclose,
fsread,
devbread,
fswrite,
devbwrite,
fsremove,
fswstat,
};
static int
pathqid(int oh, char *path)
{
uint h;
char *p;
h = oh;
h = (h*1000003 + '\\');
for(p=path; *p; p++)
h = (h*1000003 + *p);
return h;
}
static long
unixtime(FILETIME ft)
{
vlong t;
t = (vlong)ft.dwLowDateTime + ((vlong)ft.dwHighDateTime<<32);
t -= (vlong)10000000*134774*24*60*60;
return (long)(t/10000000);
}
static FILETIME
wintime(ulong t)
{
FILETIME ft;
vlong vt;
vt = (vlong)t*10000000+(vlong)10000000*134774*24*60*60;
ft.dwLowDateTime = vt;
ft.dwHighDateTime = vt>>32;
return ft;
}
static long
consolewrite(Uchan *uc, void *buf, long n)
{
char *p;
Rune buf2[1000], *q;
int i, n2, on;
static Lock lk;
static int tmpconsole;
qlock(&uc->qlk);
if(uc->h == INVALID_HANDLE_VALUE) {
lock(&lk);
if(!tmpconsole)
tmpconsole = AllocConsole();
unlock(&lk);
uc->h = GetStdHandle(STD_OUTPUT_HANDLE);
}
p = buf;
on = n;
/* handle partial runes */
if(uc->nbuf) {
i = uc->nbuf;
assert(i < UTFmax);
while(i < UTFmax && n>0) {
uc->buf[i] = *p;
i++;
p++;
n--;
if(fullrune(uc->buf, i)) {
uc->nbuf = 0;
chartorune(buf2, uc->buf);
if(!WriteConsole(uc->h, buf2, 1, &n, 0)) {
qunlock(&uc->qlk);
oserror();
nexterror();
}
break;
}
}
}
while(n >= UTFmax || fullrune(p, n)) {
n2 = nelem(buf2);
q = buf2;
while(n2) {
if(n < UTFmax && !fullrune(p, n))
break;
i = chartorune(q, p);
p += i;
n -= i;
n2--;
q++;
}
if(!WriteConsole(uc->h, buf2, q-buf2, &n2, 0)) {
qunlock(&uc->qlk);
oserror();
nexterror();
}
}
if(n != 0) {
if(uc->buf == 0)
uc->buf = mallocz(UTFmax, 1);
assert(n+uc->nbuf < UTFmax);
memcpy(uc->buf+uc->nbuf, p, n);
uc->nbuf += n;
}
qunlock(&uc->qlk);
return on;
}
/*
* It appears that win NT 4.0 has a bug in ReadConsole
* If ReadConsole is called with a buffer that is smaller than
* the input that is buffered, it appears that readconsoles buffer
* can get corrupted by WriteConsole
* i.e
* char buf[3];
* int n2;
*
* for(;;) {
* if(!ReadConsole(h, buf, sizeof(buf), &n2, 0))
* exit(0);
* if(!WriteConsole(h2, buf, n2, &n2, 0))
* exit(0);
*
* Sleep(100);
* }
*
*/
static long
consoleread(Uchan *uc, void *a, long n)
{
int n2, i;
Rune r, rbuf[200];
char *p;
uchar *buf;
buf = a;
qlock(&uc->qlk);
if(n == 0 || uc->h == INVALID_HANDLE_VALUE){
qunlock(&uc->qlk);
return 0;
}
while(uc->nbuf==0 && uc->eof==0) {
if(!ReadConsole(uc->h, rbuf, nelem(rbuf), &n2, 0)){
qunlock(&uc->qlk);
oserror();
nexterror();
return -1;
}
if(n2 == 0)
continue;
if(uc->buf == 0)
uc->buf = mallocz(sizeof(rbuf)*UTFmax, 1);
for(i=0,p=uc->buf; i<n2; i++) {
r = rbuf[i];
if(r == '\r')
continue;
if(r == 0x4) {
uc->eof = 1;
break;
}
p += runetochar(p, &r);
}
uc->nbuf = p-uc->buf;
}
if(n > uc->nbuf)
n = uc->nbuf;
memmove(buf, uc->buf, n);
memmove(uc->buf, uc->buf+n, uc->nbuf-n);
uc->nbuf -= n;
qunlock(&uc->qlk);
return n;
}
static void
serveropen(Uchan *uc, int mode)
{
NETRESOURCE res;
HANDLE h;
res.dwScope = RESOURCE_GLOBALNET;
res.dwType = RESOURCETYPE_DISK;
res.dwDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
res.dwUsage = RESOURCEUSAGE_CONTAINER;
res.lpLocalName = 0;
res.lpRemoteName = towin(uc->syspath->s);
res.lpComment = 0;
res.lpProvider = 0;
if (WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0,
&res, &h) != NO_ERROR) {
free(res.lpRemoteName);
uc->h = INVALID_HANDLE_VALUE;
oserror();
nexterror();
}
free(res.lpRemoteName);
uc->h = h;
}
static int
serverentry(Uchan *uc, int t)
{
int count, n;
uchar buf[1000];
Dir *dir;
Rune *p;
NETRESOURCE *res;
n = sizeof(buf);
count = 1;
if(WNetEnumResource(uc->h, &count, buf, &n) != NO_ERROR)
return 0;
res = (NETRESOURCE*)buf;
dir = &uc->dir;
memset(dir, 0, sizeof(Dir));
p = runestrrchr(res->lpRemoteName, (Rune)'\\');
dir->name = win_wstr2utf(p+1);
dir->mode = DMDIR | 0777;
dir->qid.path = DMDIR; /* BUG */
dir->qid.vers = t;
dir->atime = t;
dir->mtime = t;
uc->havedir = 1;
return 1;
}
static long
serverread(Uchan *uc, void *a, long n, vlong off)
{
int i, m, t;
uchar *buf;
qlock(&uc->qlk);
if(waserror()){ /* serveropen can error */
qunlock(&uc->qlk);
nexterror();
}
if(off == 0){
if(uc->havedir){
uc->havedir = 0;
free(uc->dir.name);
}
if(uc->h != INVALID_HANDLE_VALUE){
WNetCloseEnum(uc->h);
uc->h = INVALID_HANDLE_VALUE;
}
serveropen(uc, OREAD);
}
buf = a;
t = seconds();
for(i=0; i<n; i+=m){
if(!uc->havedir && !serverentry(uc, t))
break;
if((m=convD2M(&uc->dir, buf+i, n-i)) <= BIT16SZ)
break;
uc->havedir = 0;
free(uc->dir.name);
}
qunlock(&uc->qlk);
poperror();
return i;
}
static void
serverclose(Uchan *uc)
{
if(uc->havedir){
uc->havedir = 0;
free(uc->dir.name);
}
if(uc->h != INVALID_HANDLE_VALUE){
WNetCloseEnum(uc->h);
uc->h = INVALID_HANDLE_VALUE;
}
}
static void
data2dir(WIN32_FIND_DATA *dat, Dir *dir, char *dirpath)
{
int ret;
Rune *p;
char *ext;
dir->name = win_wstr2utf(dat->cFileName);
dir->qid.type = 0;
dir->qid.path = pathqid(pathqid(0, dirpath), dir->name);
dir->atime = unixtime(dat->ftLastAccessTime);
dir->mtime = unixtime(dat->ftLastWriteTime);
dir->qid.vers = dir->mtime;
dir->length = dat->nFileSizeLow | (dat->nFileSizeHigh<<32);
dir->type = 'U';
dir->dev = 0;
if(!win_usesecurity){
//print("win_usesecurity = 0\n");
goto Nosec;
}
p = fspath(dirpath, dir->name);
ret = win_secperm(dir, p);
//print("win_secperm %S = %d\n", p, ret);
free(p);
if(ret < 0){
Nosec:
/* no NT security so make something up */
dir->uid = strdup(eve);
dir->gid = strdup(eve);
dir->mode = 0666;
ext = strrchr(dir->name, '.');
if(ext != 0 && (cistrcmp(ext, ".exe") == 0 || cistrcmp(ext, ".rsh") == 0))
dir->mode |= 0111;
if(dat->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
dir->mode |= 0111;
}
if(dat->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
dir->mode &= ~0222;
if(dat->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
dir->qid.type |= QTDIR;
dir->mode |= DMDIR;
}
}
static void
udata2dir(Uchan *uc)
{
Rune *s;
s = uc->finddata.cFileName;
if(s[0] == 0)
return;
if(s[0]=='.' && (s[1]==0 || (s[1]=='.' && s[2]==0)))
return;
data2dir(&uc->finddata, &uc->dir, uc->syspath->s);
uc->havedir = 1;
}
int
dirbadentry(WIN32_FIND_DATA *data)
{
Rune *s;
s = data->cFileName;
if(s[0] == 0)
return 1;
if(s[0] == '.' && (s[1] == 0 || s[1] == '.' && s[2] == 0))
return 1;
return 0;
}
static int
dirnext(Uchan *uc)
{
while(!uc->havedir){
if(!FindNextFile(uc->h, &uc->finddata))
return 0;
udata2dir(uc);
}
return 1;
}
static long
directoryread(Uchan *uc, uchar *buf, long n, vlong off)
{
int i, m;
Rune *path;
qlock(&uc->qlk);
if(off == 0){
DPRINT("\toffset zero\n");
if(uc->h != INVALID_HANDLE_VALUE && uc->h != nil){
DPRINT("FindClose %p\n", uc->h);
FindClose(uc->h);
}
if(uc->havedir){
DPRINT("Havedir\n");
uc->havedir = 0;
free(uc->dir.name);
free(uc->dir.uid);
free(uc->dir.gid);
}
path = fspath(uc->syspath->s, "*.*");
uc->h = FindFirstFile(path, &uc->finddata);
free(path);
if(uc->h == INVALID_HANDLE_VALUE){
qunlock(&uc->qlk);
DPRINT("\tfindfirst failed; ret 0\n");
return 0;
}
udata2dir(uc);
}
for(i=0; i<n; i+=m){
if(!uc->havedir && !dirnext(uc)){
break;
}
if((m=convD2M(&uc->dir, buf+i, n-i)) <= BIT16SZ){
DPRINT("\tconvD2M returns %d; ret %d\n", m, i);
break;
}
uc->havedir = 0;
free(uc->dir.name);
free(uc->dir.uid);
free(uc->dir.gid);
}
qunlock(&uc->qlk);
return i;
}
static void
rootdiropen(Uchan *uc)
{
uc->dir.name = uc->rootent;
uc->dir.uid = eve;
uc->dir.gid = eve;
uc->dir.mode = DMDIR|0777;
uc->dir.qid.type = QTDIR;
uc->dir.qid.vers = 0;
uc->dir.length = 0;
uc->rootoff = 0;
}
static long
rootdirread(Uchan *uc, uchar *buf, long n, vlong off)
{
int i, m;
qlock(&uc->qlk);
if(off==0){
uc->rootoff = 0;
uc->havedir = 0;
}
for(i=0; i<n; i+=m){
if(!uc->havedir){
if(uc->rootoff>26)
break;
else if(uc->rootoff==26)
strcpy(uc->rootent, "mnt");
else{
uc->rootent[0] = 'a'+uc->rootoff;
uc->rootent[1] = '\0';
}
uc->dir.qid.path = pathqid(pathqid(0, "/"), uc->dir.name);
uc->havedir = 1;
uc->rootoff++;
}
if((m=convD2M(&uc->dir, buf+i, n-i)) <= BIT16SZ){
DPRINT("\tconvD2M returns %d; ret %d\n", m, i);
break;
}
uc->havedir = 0;
}
qunlock(&uc->qlk);
return i;
}
long
syspassfd(ulong *arg)
{
int j, fd;
char buf[ERRMAX];
Chan *c;
HANDLE h, nh;
Qid qid;
Syscallmem *scm;
Uchan *uc;
if(waserror()){
print("up %p syspassfd: %s\n", up, up->errstr);
nexterror();
}
fd = arg[0];
if(fd < 0 || fd > 2)
error(Ebadarg);
h = (HANDLE)arg[1];
if(h==nil || h==INVALID_HANDLE_VALUE)
error(Ebadarg);
scm = up->sysaux;
if(!DuplicateHandle(scm->hs.hclientproc, h, GetCurrentProcess(), &nh, 0, 0, DUPLICATE_SAME_ACCESS)){
osrerrstr(buf, sizeof buf);
snprint(up->errstr, ERRMAX, "DuplicateHandle: %s", buf);
nexterror();
}
uc = smalloc(sizeof(Uchan));
uc->h = nh;
if(waserror()){
free(uc);
CloseHandle(nh);
nexterror();
}
/*
* This won't happen, since
* we can't pass console handles
* between processes. Idiots.
*/
if(GetConsoleMode(nh, &j))
uc->type = Tconsole;
else{
j = GetFileType(nh);
switch(j){
default:
error("unknown handle type");
case FILE_TYPE_UNKNOWN:
case FILE_TYPE_DISK:
case FILE_TYPE_CHAR:
uc->type = Tfile;
break;
case FILE_TYPE_PIPE:
uc->type = Tpipe;
break;
}
}
c = newchan();
if(waserror()){
cclose(c);
nexterror();
}
sprint(up->genbuf, "/fd/%d", fd);
c->name = newcname(up->genbuf);
c->type = devno('U', 1);
mkqid(&qid, fd, 0, 0);
c->qid = qid;
c->aux = uc;
c->flag |= COPEN;
c->mode = fd==0 ? OREAD: OWRITE;
print("up %p newfd chan %p fd %d\n", up, c, fd);
if(newfdx(c, fd) < 0){
print("failed %s\n", up->errstr);
nexterror();
}
poperror();
poperror();
poperror();
return 0;
}
long
syspassdir(ulong *arg)
{
USED(arg);
error(Ebadarg);
return -1;
}
syntax highlighted by Code2HTML, v. 0.9.1