code: drawterm

Download patch

ref: 693b886abbc71ece150ca9d462488672d2ebea98
parent: a090f5f5efa78bb496bd9f033903f842250dc7f3
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Fri Feb 19 12:43:04 EST 2016

devfs-win32: allow access to all windows drive letters exporting them under /mnt/term/C: ect... 64-bit file offsets

--- a/kern/devfs-win32.c
+++ b/kern/devfs-win32.c
@@ -1,16 +1,8 @@
-/*
- * Disable Unicode until the calls to FindFirstFile etc
- * are changed to use wide character strings.
- */
-#undef UNICODE
 #include	<windows.h>
 #include	<sys/types.h>
 #include	<sys/stat.h>
 #include	<fcntl.h>
 
-#ifndef NAME_MAX
-#	define NAME_MAX 256
-#endif
 #include	"u.h"
 #include	"lib.h"
 #include	"dat.h"
@@ -20,48 +12,149 @@
 typedef struct DIR	DIR;
 typedef	struct Ufsinfo	Ufsinfo;
 
+
 enum
 {
-	NUID	= 256,
-	NGID	= 256,
 	MAXPATH	= 1024,
-	MAXCOMP	= 128
+	TPATH_ROOT	= 0,	// ""
+	TPATH_VOLUME	= 1,	// "C:"
+	TPATH_FILE	= 2,	// "C:\bla"
 };
 
 struct DIR
 {
-	HANDLE	handle;
-	char*	path;
-	int	index;
+	// for FindFileFirst()
+	HANDLE		handle;
 	WIN32_FIND_DATA	wfd;
+
+	// for GetLogicalDriveStrings()
+	TCHAR		*drivebuf;
+	TCHAR		*drivep;
+
+	// dont move to the next item
+	int		keep;
 };
 
 struct Ufsinfo
 {
 	int	mode;
-	int	fd;
-	int	uid;
-	int	gid;
+	HANDLE	fh;
 	DIR*	dir;
-	ulong	offset;
+	vlong	offset;
 	QLock	oq;
-	char nextname[NAME_MAX];
 };
 
-DIR*	opendir(char*);
-int	readdir(char*, DIR*);
-void	closedir(DIR*);
-void	rewinddir(DIR*);
+static	void	fspath(Chan *, char *, TCHAR *, int);
+static	ulong	fsdirread(Chan*, uchar*, int, vlong);
+static	int	fsomode(int);
+static	ulong	fsaccess(int);
+static	ulong	pathtype(TCHAR *);
+static	int	checkvolume(TCHAR *);
 
-char	*base = "c:/.";
+char *base = "";
 
-static	Qid	fsqid(char*, struct stat *);
-static	void	fspath(Chan*, char*, char*);
-// static	void	fsperm(Chan*, int);
-static	ulong	fsdirread(Chan*, uchar*, int, ulong);
-static	int	fsomode(int);
-static  int	chown(char *path, int uid, int);
+static ulong
+unixtime(FILETIME *ft)
+{
+	vlong t;
+	t = ((vlong)ft->dwHighDateTime << 32)|((vlong)ft->dwLowDateTime);
+	t -= 116444736000000000LL;
+	return ((t<0)?(-1 - (-t - 1)) : t)/10000000;
+}
 
+static uvlong
+pathhash(TCHAR *p)
+{
+	uvlong h;
+	h = 0LL;
+	for(; *p; p++)
+		h += *p * 13;
+	return h;
+}
+
+static ulong
+wfdtodmode(WIN32_FIND_DATA *wfd)
+{
+	int m;
+	m = DMREAD|DMWRITE|DMEXEC;
+	if(wfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+		m |= DMDIR;
+	if(wfd->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+		m &= ~DMWRITE;
+	m |= (m & 07)<<3;
+	m |= (m & 07)<<6;
+	return m;
+}
+
+static Qid
+wfdtoqid(TCHAR *path, WIN32_FIND_DATA *wfd)
+{
+	ulong t;
+	WIN32_FIND_DATA f;
+	Qid q;
+
+	t = pathtype(path);
+
+	switch(t){
+	case TPATH_VOLUME:
+	case TPATH_ROOT:
+		q.type = QTDIR;
+		q.path = pathhash(path);
+		q.vers = 0;
+		break;
+
+	case TPATH_FILE:
+		if(!wfd){
+			HANDLE h;
+			if((h = FindFirstFile(path, &f))==INVALID_HANDLE_VALUE)
+				oserror();
+			FindClose(h);
+			wfd = &f;
+		}
+		q.type = (wfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? QTDIR : QTFILE;
+		q.path = pathhash(path);
+		q.vers = unixtime(&wfd->ftLastWriteTime);
+		break;
+	}
+	return q;
+}
+
+static void
+wfdtodir(TCHAR *path, Dir *d, WIN32_FIND_DATA *wfd)
+{
+	WIN32_FIND_DATA f;
+
+	d->uid	= "nul";
+	d->gid	= "nul";
+	d->muid	= "nul";
+
+	switch(pathtype(path)){
+	case TPATH_VOLUME:
+	case TPATH_ROOT:
+		wfd = nil;
+		d->mode = 0777 | DMDIR;
+		d->atime = 0;
+		d->mtime = 0;
+		d->length = 0;
+		break;
+
+	case TPATH_FILE:
+		if(!wfd){
+			HANDLE h;
+			if((h = FindFirstFile(path, &f))==INVALID_HANDLE_VALUE)
+				oserror();
+			FindClose(h);
+			wfd = &f;
+		}
+		d->mode		= wfdtodmode(wfd);
+		d->atime	= unixtime(&wfd->ftLastAccessTime);
+		d->mtime	= unixtime(&wfd->ftLastWriteTime);
+		d->length	= ((uvlong)wfd->nFileSizeHigh << 32)|((uvlong)wfd->nFileSizeLow);
+		break;
+	}
+	d->qid = wfdtoqid(path, wfd);
+}
+
 /* clumsy hack, but not worse than the Path stuff in the last one */
 static char*
 uc2name(Chan *c)
@@ -69,10 +162,12 @@
 	char *s;
 
 	if(c->name == nil)
-		return "/";
+		return "";
 	s = c2name(c);
 	if(s[0]=='#' && s[1]=='U')
-		return s+2;
+		s += 2;
+	if(*s=='/')
+		s++;
 	return s;
 }
 
@@ -88,29 +183,69 @@
 		return t;
 	return t+1;
 }
+
+static ulong
+pathtype(TCHAR *path)
+{
+	int n;
+	n = wcslen(path);
+	if(n < 2){
+		return TPATH_ROOT;
+	}
+	if(n==2){
+		return TPATH_VOLUME;
+	}
+	return TPATH_FILE;
+}
+
+static int
+checkvolume(TCHAR *path)
+{
+	TCHAR vol[MAX_PATH];
+	TCHAR volname[MAX_PATH];
+	TCHAR fsysname[MAX_PATH];
+	DWORD complen;
+	DWORD flags;
+
+	wcsncpy(vol, path, MAX_PATH);
+#ifdef UNICODE
+	wcsncat(vol, L"\\", MAXPATH);
+#else
+	wcsncat(vol, "\\", MAXPATH);
+#endif
+
+	if(!GetVolumeInformation(
+		vol,
+		volname,
+		MAX_PATH,
+		NULL,
+		&complen,
+		&flags,
+		fsysname,
+		MAX_PATH)){
+		return 0;
+	}
+	return 1;	
+}
+
+static int
+getfileowner(TCHAR *path, char *owner, int nowner)
+{
+	strncpy(owner, "Bill", nowner);
+	return 1;
+}
 	
 static Chan*
 fsattach(char *spec)
 {
 	Chan *c;
-	struct stat stbuf;
 	static int devno;
 	Ufsinfo *uif;
-
-	if(stat(base, &stbuf) < 0)
-		error(strerror(errno));
-
 	c = devattach('U', spec);
-
 	uif = mallocz(sizeof(Ufsinfo), 1);
-	uif->gid = stbuf.st_gid;
-	uif->uid = stbuf.st_uid;
-	uif->mode = stbuf.st_mode;
-
 	c->aux = uif;
 	c->dev = devno++;
 	c->qid.type = QTDIR;
-/*print("fsattach %s\n", c2name(c));*/
 
 	return c;
 }
@@ -130,25 +265,27 @@
 static int
 fswalk1(Chan *c, char *name)
 {
-	struct stat stbuf;
-	char path[MAXPATH];
-	Ufsinfo *uif;
+	HANDLE h;
+	WIN32_FIND_DATA wfd;
+	TCHAR path[MAXPATH];
 
-	fspath(c, name, path);
+	fspath(c, name, path, MAXPATH);
 
-	/*	print("** fs walk '%s' -> %s\n", path, name); */
+	switch(pathtype(path)){
+	case TPATH_VOLUME:
+		if(!checkvolume(path))
+			return 0;
+	case TPATH_ROOT:
+		c->qid = wfdtoqid(path, nil);
+		break;
 
-	if(stat(path, &stbuf) < 0)
-		return 0;
-
-	uif = c->aux;
-
-	uif->gid = stbuf.st_gid;
-	uif->uid = stbuf.st_uid;
-	uif->mode = stbuf.st_mode;
-
-	c->qid = fsqid(path, &stbuf);
-
+	case TPATH_FILE:
+		if((h = FindFirstFile(path, &wfd)) == INVALID_HANDLE_VALUE)
+			return 0;
+		FindClose(h);
+		c->qid = wfdtoqid(path, &wfd);
+		break;
+	}
 	return 1;
 }
 
@@ -190,25 +327,21 @@
 fsstat(Chan *c, uchar *buf, int n)
 {
 	Dir d;
-	struct stat stbuf;
-	char path[MAXPATH];
+	TCHAR path[MAXPATH];
+	char owner[MAXPATH];
 
 	if(n < BIT16SZ)
 		error(Eshortstat);
+	fspath(c, 0, path, MAXPATH);
+	d.name = lastelem(c);
+	wfdtodir(path, &d, nil);
 
-	fspath(c, 0, path);
-	if(stat(path, &stbuf) < 0)
-		error(strerror(errno));
+	if(getfileowner(path, owner, MAXPATH)){
+		d.uid = owner;
+		d.gid = owner;
+		d.muid = owner;
+	}
 
-	d.name = lastelem(c);
-	d.uid = "unknown";
-	d.gid = "unknown";
-	d.muid = "unknown";
-	d.qid = c->qid;
-	d.mode = (c->qid.type<<24)|(stbuf.st_mode&0777);
-	d.atime = stbuf.st_atime;
-	d.mtime = stbuf.st_mtime;
-	d.length = stbuf.st_size;
 	d.type = 'U';
 	d.dev = c->dev;
 	return convD2M(&d, buf, n);
@@ -217,11 +350,11 @@
 static Chan*
 fsopen(Chan *c, int mode)
 {
-	char path[MAXPATH];
+	TCHAR path[MAXPATH];
+	ulong t;
 	int m, isdir;
 	Ufsinfo *uif;
 
-/*print("fsopen %s\n", c2name(c));*/
 	m = mode & (OTRUNC|3);
 	switch(m) {
 	case 0:
@@ -240,31 +373,55 @@
 	}
 
 	isdir = c->qid.type & QTDIR;
-
 	if(isdir && mode != OREAD)
 		error(Eperm);
-
 	m = fsomode(m & 3);
 	c->mode = openmode(mode);
-
 	uif = c->aux;
-
-	fspath(c, 0, path);
-	if(isdir) {
-		uif->dir = opendir(path);
-		if(uif->dir == 0)
-			error(strerror(errno));
-	}	
-	else {
-		if(mode & OTRUNC)
-			m |= O_TRUNC;
-		uif->fd = open(path, m|_O_BINARY, 0666);
-
-		if(uif->fd < 0)
-			error(strerror(errno));
-	}
 	uif->offset = 0;
-
+	fspath(c, 0, path, MAXPATH);
+	t = pathtype(path);
+	if(isdir){
+		DIR *d;
+		d = malloc(sizeof(*d));
+		switch(t){
+		case TPATH_ROOT:
+			d->drivebuf = malloc(sizeof(TCHAR)*MAX_PATH);
+			if(GetLogicalDriveStrings(MAX_PATH-1, d->drivebuf) == 0){
+				free(d->drivebuf);
+				d->drivebuf = nil;
+				oserror();
+			}
+			d->drivep = d->drivebuf;
+			break;
+		case TPATH_VOLUME:
+		case TPATH_FILE:
+#ifdef UNICODE
+			wcsncat(path, L"\\*.*", MAXPATH);
+#else
+			wcsncat(path, "\\*.*", MAXPATH);
+#endif
+			if((d->handle = FindFirstFile(path, &d->wfd)) == INVALID_HANDLE_VALUE){
+				free(d);
+				oserror();
+			}
+			break;
+		}
+		d->keep = 1;
+		uif->dir = d;
+	} else {
+		uif->dir = nil;
+		if((uif->fh = CreateFile(
+			path,
+			fsaccess(mode),
+			FILE_SHARE_READ | FILE_SHARE_WRITE,
+			NULL,
+			(mode & OTRUNC) ? TRUNCATE_EXISTING : OPEN_EXISTING,
+			FILE_ATTRIBUTE_NORMAL,
+			0)) == INVALID_HANDLE_VALUE){
+			oserror();
+		}	
+	}
 	c->offset = 0;
 	c->flag |= COPEN;
 	return c;
@@ -273,58 +430,59 @@
 static void
 fscreate(Chan *c, char *name, int mode, ulong perm)
 {
-	int fd, m;
-	char path[MAXPATH];
-	struct stat stbuf;
+	int m;
+	TCHAR path[MAXPATH];
 	Ufsinfo *uif;
+	ulong t;
 
 	m = fsomode(mode&3);
 
-	fspath(c, name, path);
+	fspath(c, name, path, MAXPATH);
+	t = pathtype(path);
 
 	uif = c->aux;
 
 	if(perm & DMDIR) {
-		if(m)
+		TCHAR *p;
+		DIR *d;
+		if(m || t!=TPATH_FILE)
 			error(Eperm);
-
-		if(mkdir(path) < 0)
-			error(strerror(errno));
-
-		fd = open(path, 0);
-		if(fd >= 0) {
-			chmod(path, perm & 0777);
-			chown(path, uif->uid, uif->uid);
+		if(!CreateDirectory(path, NULL))
+			oserror();
+		d = malloc(sizeof(*d));
+		p = &path[wcslen(path)];
+#ifdef UNICODE
+		wcsncat(path, L"\\*.*", MAXPATH);
+#else
+		wcsncat(path, "\\*.*", MAXPATH);
+#endif
+		if((d->handle = FindFirstFile(path, &d->wfd)) == INVALID_HANDLE_VALUE){
+			free(d);
+			oserror();
 		}
-		close(fd);
-
-		uif->dir = opendir(path);
-		if(uif->dir == 0)
-			error(strerror(errno));
+		*p = 0;
+		d->keep = 1;
+		uif->dir = d;
+	} else {
+		uif->dir = nil;
+		if((uif->fh = CreateFile(
+			path,
+			fsaccess(mode),
+			FILE_SHARE_READ | FILE_SHARE_WRITE,
+			NULL,
+			CREATE_NEW,
+			FILE_ATTRIBUTE_NORMAL,
+			0)) == INVALID_HANDLE_VALUE){
+			oserror();
+		}	
 	}
-	else {
-		fd = open(path, _O_WRONLY|_O_BINARY|_O_CREAT|_O_TRUNC, 0666);
-		if(fd >= 0) {
-			if(m != 1) {
-				close(fd);
-				fd = open(path, m|_O_BINARY);
-			}
-			chmod(path, perm & 0777);
-			chown(path, uif->uid, uif->gid);
-		}
-		if(fd < 0)
-			error(strerror(errno));
-		uif->fd = fd;
-	}
-
-	if(stat(path, &stbuf) < 0)
-		error(strerror(errno));
-	c->qid = fsqid(path, &stbuf);
+	c->qid = wfdtoqid(path, nil);
 	c->offset = 0;
 	c->flag |= COPEN;
 	c->mode = openmode(mode);
 }
 
+
 static void
 fsclose(Chan *c)
 {
@@ -331,14 +489,19 @@
 	Ufsinfo *uif;
 
 	uif = c->aux;
-
 	if(c->flag & COPEN) {
-		if(c->qid.type & QTDIR)
-			closedir(uif->dir);
-		else
-			close(uif->fd);
+		if(uif->dir){
+			if(uif->dir->drivebuf){
+				free(uif->dir->drivebuf);
+				uif->dir->drivebuf = nil;
+			} else {
+				FindClose(uif->dir->handle);
+				free(uif->dir);
+			}
+		} else {
+			CloseHandle(uif->fh);
+		}
 	}
-
 	free(uif);
 }
 
@@ -345,10 +508,10 @@
 static long
 fsread(Chan *c, void *va, long n, vlong offset)
 {
-	int fd, r;
+	HANDLE fh;
+	DWORD r;
 	Ufsinfo *uif;
 
-/*print("fsread %s\n", c2name(c));*/
 	if(c->qid.type & QTDIR)
 		return fsdirread(c, va, n, offset);
 
@@ -358,22 +521,22 @@
 		qunlock(&uif->oq);
 		nexterror();
 	}
-	fd = uif->fd;
+	fh = uif->fh;
 	if(uif->offset != offset) {
-		r = lseek(fd, offset, 0);
-		if(r < 0)
-			error(strerror(errno));
+		LONG high;
+		high = offset>>32;
+		offset = SetFilePointer(fh, (LONG)(offset & 0xFFFFFFFF), &high, FILE_BEGIN);
+		offset |= (vlong)high<<32;
 		uif->offset = offset;
 	}
-
-	n = read(fd, va, n);
-	if(n < 0)
-		error(strerror(errno));
-
+	r = 0;
+	if(!ReadFile(fh, va, (DWORD)n, &r, NULL)){
+		oserror();
+	}
+	n = r;
 	uif->offset += n;
 	qunlock(&uif->oq);
 	poperror();
-
 	return n;
 }
 
@@ -380,32 +543,35 @@
 static long
 fswrite(Chan *c, void *va, long n, vlong offset)
 {
-	int fd, r;
+	HANDLE fh;
+	DWORD w;
 	Ufsinfo *uif;
 
-	uif = c->aux;
+	if(c->qid.type & QTDIR)
+		return fsdirread(c, va, n, offset);
 
+	uif = c->aux;
 	qlock(&uif->oq);
 	if(waserror()) {
 		qunlock(&uif->oq);
 		nexterror();
 	}
-	fd = uif->fd;
+	fh = uif->fh;
 	if(uif->offset != offset) {
-		r = lseek(fd, offset, 0);
-		if(r < 0)
-			error(strerror(errno));
+		LONG high;
+		high = offset>>32;
+		offset = SetFilePointer(fh, (LONG)(offset & 0xFFFFFFFF), &high, FILE_BEGIN);
+		offset |= (vlong)high<<32;
 		uif->offset = offset;
 	}
-
-	n = write(fd, va, n);
-	if(n < 0)
-		error(strerror(errno));
-
+	w = 0;
+	if(!WriteFile(fh, va, (DWORD)n, &w, NULL)){
+		oserror();
+	}
+	n = w;
 	uif->offset += n;
 	qunlock(&uif->oq);
 	poperror();
-
 	return n;
 }
 
@@ -412,114 +578,103 @@
 static void
 fsremove(Chan *c)
 {
-	int n;
-	char path[MAXPATH];
+	TCHAR path[MAXPATH];
 
-	fspath(c, 0, path);
-	if(c->qid.type & QTDIR)
-		n = rmdir(path);
-	else
-		n = remove(path);
-	if(n < 0)
-		error(strerror(errno));
+	fspath(c, 0, path, MAXPATH);
+	if(c->qid.type & QTDIR){
+		if(!RemoveDirectory(path))
+			error("RemoveDirectory...");
+	} else {
+		if(!DeleteFile(path))
+			error("DeleteFile...");
+	}
 }
 
 static int
 fswstat(Chan *c, uchar *buf, int n)
 {
+	TCHAR path[MAXPATH];
+	char strs[MAXPATH*3];
 	Dir d;
-	struct stat stbuf;
-	char old[MAXPATH], new[MAXPATH];
-	char strs[MAXPATH*3], *p;
-	Ufsinfo *uif;
+	ulong t;
 
 	if (convM2D(buf, n, &d, strs) != n)
 		error(Ebadstat);
-	
-	fspath(c, 0, old);
-	if(stat(old, &stbuf) < 0)
-		error(strerror(errno));
+	fspath(c, 0, path, MAXPATH);
+	t = pathtype(path);
+	if(t != TPATH_FILE)
+		error(Ebadstat);
+	/* change name */
+	if(d.name[0]){
+		int l;
+		TCHAR newpath[MAXPATH];
+		wcsncpy(newpath, path, MAXPATH);
 
-	uif = c->aux;
-
-//	if(uif->uid != stbuf.st_uid)
-//		error(Eowner);
-
-	if(d.name[0] && strcmp(d.name, lastelem(c)) != 0) {
-		fspath(c, 0, old);
-		strcpy(new, old);
-		p = strrchr(new, '/');
-		strcpy(p+1, d.name);
-		if(rename(old, new) < 0)
-			error(strerror(errno));
+		/* replace last path-element with d.name */
+		l = wcslen(newpath)-1;
+		if(l <= 0)
+			error(Ebadstat);
+		for(;l>0; l--){
+			if(newpath[l-1]=='\\')
+				break;
+		}
+		if(l <= 0)
+			error(Ebadstat);
+		newpath[l] = 0;
+#ifdef UNICODE
+		if(!MultiByteToWideChar(CP_UTF8,0,d.name,-1,&newpath[l],MAXPATH-l-1))
+			oserror();
+#else
+		wcsncpy(&newpath[l], d.name, MAXPATH-l-1);
+#endif
+		if(wcscmp(path, newpath)!=0){
+			if(!MoveFile(path, newpath))
+				oserror();
+			wcsncpy(path, newpath, MAXPATH);
+		}
 	}
 
-	fspath(c, 0, old);
-	if(~d.mode != 0 && (int)(d.mode&0777) != (int)(stbuf.st_mode&0777)) {
-		if(chmod(old, d.mode&0777) < 0)
-			error(strerror(errno));
-		uif->mode &= ~0777;
-		uif->mode |= d.mode&0777;
-	}
-/*
-	p = name2pass(gid, d.gid);
-	if(p == 0)
-		error(Eunknown);
-
-	if(p->id != stbuf.st_gid) {
-		if(chown(old, stbuf.st_uid, p->id) < 0)
-			error(sys_errlist[errno]);
-
-		uif->gid = p->id;
-	}
-*/
-	return n;
+	/* fixme: change attributes */
+	c->qid = wfdtoqid(path, nil);
+	return n;	
 }
 
-static Qid
-fsqid(char *p, struct stat *st)
-{
-	Qid q;
-	int dev;
-	ulong h;
-	static int nqdev;
-	static uchar *qdev;
 
-	if(qdev == 0)
-		qdev = mallocz(65536U, 1);
-
-	q.type = 0;
-	if((st->st_mode&S_IFMT) ==  S_IFDIR)
-		q.type = QTDIR;
-
-	dev = st->st_dev & 0xFFFFUL;
-	if(qdev[dev] == 0)
-		qdev[dev] = ++nqdev;
-
-	h = 0;
-	while(*p != '\0')
-		h += *p++ * 13;
-	
-	q.path = (vlong)qdev[dev]<<32;
-	q.path |= h;
-	q.vers = st->st_mtime;
-
-	return q;
-}
-
 static void
-fspath(Chan *c, char *ext, char *path)
+_fspath(Chan *c, char *ext, char *path, int npath)
 {
-	strcpy(path, base);
-	strcat(path, "/");
-	strcat(path, uc2name(c));
+	*path = 0;
+	strncat(path, uc2name(c), npath);
 	if(ext) {
-		strcat(path, "/");
-		strcat(path, ext);
+		if(*path)
+			strncat(path, "/", npath);
+		strncat(path, ext, npath);
 	}
 	cleanname(path);
+	if(*path == '.')
+		*path = 0;
 }
 
+static void
+fspath(Chan *c, char *ext, TCHAR *path, int npath)
+{
+	TCHAR *p;
+#ifdef UNICODE
+	char buf[MAXPATH];
+	_fspath(c, ext, buf, sizeof(buf));
+	if(!MultiByteToWideChar(CP_UTF8,0,buf,-1,path,npath)){
+		oserror();
+	}
+#else
+	_fspath(c, ext, path, npath);
+#endif
+	/* make a DOS path */
+	for(p=path; *p; p++){
+		if(*p == '/')
+			*p = '\\';
+	}
+}
+
 static int
 isdots(char *name)
 {
@@ -534,78 +689,116 @@
 	return 0;
 }
 
-static int
-p9readdir(char *name, Ufsinfo *uif)
-{
-	if(uif->nextname[0]){
-		strcpy(name, uif->nextname);
-		uif->nextname[0] = 0;
-		return 1;
-	}
-
-	return readdir(name, uif->dir);
-}
-
 static ulong
-fsdirread(Chan *c, uchar *va, int count, ulong offset)
+fsdirread(Chan *c, uchar *va, int count, vlong offset)
 {
 	int i;
+	ulong t;
 	Dir d;
 	long n;
-	char de[NAME_MAX];
-	struct stat stbuf;
-	char path[MAXPATH], dirpath[MAXPATH];
 	Ufsinfo *uif;
+	char de[MAX_PATH*3];
+	int ndirpath;
+	TCHAR dirpath[MAXPATH];
 
-/*print("fsdirread %s\n", c2name(c));*/
 	i = 0;
 	uif = c->aux;
-
 	errno = 0;
+
+	fspath(c, 0, dirpath, MAXPATH);
+	ndirpath = wcslen(dirpath);
+	t = pathtype(dirpath);
+
 	if(uif->offset != offset) {
 		if(offset != 0)
 			error("bad offset in fsdirread");
 		uif->offset = offset;  /* sync offset */
-		uif->nextname[0] = 0;
-		rewinddir(uif->dir);
+		switch(t){
+		case TPATH_ROOT:
+			uif->dir->drivep = uif->dir->drivebuf;
+			break;
+		case TPATH_VOLUME:
+		case TPATH_FILE:
+			FindClose(uif->dir->handle);
+#ifdef UNICODE
+			wcsncat(dirpath, L"\\*.*", MAXPATH);
+#else
+			wcsncat(dirpath, "\\*.*", MAXPATH);
+#endif
+			if((uif->dir->handle = FindFirstFile(dirpath, &uif->dir->wfd))==INVALID_HANDLE_VALUE){
+				oserror();
+			}
+			break;
+		}
+		uif->dir->keep = 1;
 	}
 
-	fspath(c, 0, dirpath);
-
 	while(i+BIT16SZ < count) {
-		if(!p9readdir(de, uif))
-			break;
+		char owner[MAXPATH];
 
+		if(!uif->dir->keep) {
+			switch(t){
+			case TPATH_ROOT:
+				uif->dir->drivep += 4;
+				if(*uif->dir->drivep == 0)
+					goto out;
+				break;
+			case TPATH_VOLUME:
+			case TPATH_FILE:
+				if(!FindNextFile(uif->dir->handle, &uif->dir->wfd))
+					goto out;
+				break;
+			}
+		} else {
+			uif->dir->keep = 0;
+		}
+		if(t == TPATH_ROOT){
+			uif->dir->drivep[2] = 0;
+#ifdef UNICODE
+			WideCharToMultiByte(CP_UTF8,0,uif->dir->drivep,-1,de,sizeof(de),0,0);
+#else
+			strncpy(de, uif->dir->drivep, sizeof(de));
+#endif
+		} else {
+#ifdef UNICODE
+			WideCharToMultiByte(CP_UTF8,0,uif->dir->wfd.cFileName,-1,de,sizeof(de),0,0);
+#else
+			strncpy(de, uif->dir->wfd.cFileName, sizeof(de));
+#endif
+		}
 		if(de[0]==0 || isdots(de))
 			continue;
-
 		d.name = de;
-		sprint(path, "%s/%s", dirpath, de);
-		memset(&stbuf, 0, sizeof stbuf);
+		dirpath[ndirpath] = 0;		
+		if(t == TPATH_ROOT){
+			wcsncat(dirpath, uif->dir->drivep, MAXPATH);
+			wfdtodir(dirpath, &d, nil);
+		} else {
+#ifdef UNICODE
+			wcsncat(dirpath, L"\\", MAXPATH);
+#else
+			wcsncat(dirpath, "\\", MAXPATH);
+#endif
+			wcsncat(dirpath, uif->dir->wfd.cFileName, MAXPATH);
+			wfdtodir(dirpath, &d, &uif->dir->wfd);
+		}
 
-		if(stat(path, &stbuf) < 0) {
-			print("dir: bad path %s\n", path);
-			/* but continue... probably a bad symlink */
+		if(getfileowner(dirpath, owner, MAXPATH)){
+			d.uid = owner;
+			d.gid = owner;
+			d.muid = owner;
 		}
 
-		d.uid = "unknown";
-		d.gid = "unknown";
-		d.muid = "unknown";
-		d.qid = fsqid(path, &stbuf);
-		d.mode = (d.qid.type<<24)|(stbuf.st_mode&0777);
-		d.atime = stbuf.st_atime;
-		d.mtime = stbuf.st_mtime;
-		d.length = stbuf.st_size;
 		d.type = 'U';
 		d.dev = c->dev;
 		n = convD2M(&d, (uchar*)va+i, count-i);
 		if(n == BIT16SZ){
-			strcpy(uif->nextname, de);
+			uif->dir->keep = 1;
 			break;
 		}
 		i += n;
 	}
-/*print("got %d\n", i);*/
+out:
 	uif->offset += i;
 	return i;
 }
@@ -625,65 +818,29 @@
 	error(Ebadarg);
 	return 0;
 }
-void
-closedir(DIR *d)
-{
-	FindClose(d->handle);
-	free(d->path);
-}
 
-int
-readdir(char *name, DIR *d)
+static ulong
+fsaccess(int m)
 {
-	if(d->index != 0) {
-		if(FindNextFile(d->handle, &d->wfd) == FALSE)
-			return 0;
+	ulong a;
+	a = 0;
+	switch(m & 3){
+	default:
+		error(Eperm);
+		break;
+	case OREAD:
+		a = GENERIC_READ;
+		break;
+	case OWRITE:
+		a = GENERIC_WRITE;
+		break;
+	case ORDWR:
+		a = GENERIC_READ | GENERIC_WRITE;
+		break;
 	}
-	strcpy(name, (char*)d->wfd.cFileName);
-	d->index++;
-
-	return 1;
+	return a;
 }
 
-void
-rewinddir(DIR *d)
-{
-	FindClose(d->handle);
-	d->handle = FindFirstFile(d->path, &d->wfd);
-	d->index = 0;
-}
-
-static int
-chown(char *path, int uid, int perm)
-{
-/*	panic("chown"); */
-	return 0;
-}
-
-DIR*
-opendir(char *p)
-{
-	DIR *d;
-	char path[MAX_PATH];
-
-	
-	snprint(path, sizeof(path), "%s/*.*", p);
-
-	d = mallocz(sizeof(DIR), 1);
-	if(d == 0)
-		return 0;
-
-	d->index = 0;
-
-	d->handle = FindFirstFile(path, &d->wfd);
-	if(d->handle == INVALID_HANDLE_VALUE) {
-		free(d);
-		return 0;
-	}
-
-	d->path = strdup(path);
-	return d;
-}
 
 Dev fsdevtab = {
 	'U',