xref: /inferno-os/emu/Nt/devfs.c (revision 9dc22068e29604f4b484e746112a9a4efe6fd57f)
1 #define UNICODE
2 #define Unknown win_Unknown
3 #include	<windows.h>
4 #include	<winbase.h>
5 #undef Unknown
6 #undef	Sleep
7 #include	"dat.h"
8 #include	"fns.h"
9 #include	"error.h"
10 #include	<lm.h>
11 
12 /* TODO: try using / in place of \ in path names */
13 
14 enum
15 {
16 	MAX_SID		= sizeof(SID) + SID_MAX_SUB_AUTHORITIES*sizeof(DWORD),
17 	ACL_ROCK	= sizeof(ACL) + 20*(sizeof(ACCESS_ALLOWED_ACE)+MAX_SID),
18 	SD_ROCK		= SECURITY_DESCRIPTOR_MIN_LENGTH + MAX_SID + ACL_ROCK,
19 	MAXCOMP		= 128,
20 };
21 
22 typedef struct	User	User;
23 typedef struct  Gmem	Gmem;
24 typedef	struct	Stat	Stat;
25 typedef	struct Fsinfo	Fsinfo;
26 typedef	WIN32_FIND_DATA	Fsdir;
27 
28 #ifndef INVALID_SET_FILE_POINTER
29 #define	INVALID_SET_FILE_POINTER	((DWORD)-1)
30 #endif
31 
32 struct Fsinfo
33 {
34 	int	uid;
35 	int	gid;
36 	int	mode;
37 	int	fd;
38 	vlong	offset;
39 	QLock	oq;
40 	char*	spec;
41 	Rune*	srv;
42 	Cname*	name;	/* Windows' idea of the file name */
43 	ushort	usesec;
44 	ushort	checksec;
45 	Fsdir*	de;	/* non-nil for saved entry from last dirread at offset */
46 };
47 #define	FS(c)	((Fsinfo*)(c)->aux)
48 
49 /*
50  * info about a user or group
51  * there are two ways to specify a user:
52  *	by sid, a unique identifier
53  *	by user and domain names
54  * this structure is used to convert between the two,
55  * as well as figure out which groups a users belongs to.
56  * the user information never gets thrown away,
57  * but the group information gets refreshed with each setid.
58  */
59 struct User
60 {
61 	QLock	lk;		/* locks the gotgroup and group fields */
62 	SID	*sid;
63 	Rune	*name;
64 	Rune	*dom;
65 	int	type;		/* the type of sid, ie SidTypeUser, SidTypeAlias, ... */
66 	int	gotgroup;	/* tried to add group */
67 	Gmem	*group;		/* global and local groups to which this user or group belongs. */
68 	User	*next;
69 };
70 
71 struct Gmem
72 {
73 	User	*user;
74 	Gmem	*next;
75 };
76 
77 /*
78  * intermediate stat information
79  */
80 struct Stat
81 {
82 	User	*owner;
83 	User	*group;
84 	ulong	mode;
85 };
86 
87 /*
88  * some "well-known" sids
89  */
90 static	SID	*creatorowner;
91 static	SID	*creatorgroup;
92 static	SID	*everyone;
93 static	SID	*ntignore;
94 static	SID	*ntroot;	/* user who is supposed to run emu as a server */
95 
96 /*
97  * all users we ever see end up in this table
98  * users are never deleted, but we should update
99  * group information for users sometime
100  */
101 static struct
102 {
103 	QLock	lk;
104 	User	*u;
105 }users;
106 
107 /*
108  * conversion from inferno permission modes to nt access masks
109  * is this good enough?  this is what nt sets, except for NOMODE
110  */
111 #define	NOMODE	(READ_CONTROL|FILE_READ_EA|FILE_READ_ATTRIBUTES)
112 #define	RMODE	(READ_CONTROL|SYNCHRONIZE\
113 		|FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
114 #define	XMODE	(READ_CONTROL|SYNCHRONIZE\
115 		|FILE_EXECUTE|FILE_READ_ATTRIBUTES)
116 #define	WMODE	(DELETE|READ_CONTROL|SYNCHRONIZE|WRITE_DAC|WRITE_OWNER\
117 		|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA\
118 		|FILE_DELETE_CHILD|FILE_WRITE_ATTRIBUTES)
119 
120 static	int
121 modetomask[] =
122 {
123 	NOMODE,
124 	XMODE,
125 	WMODE,
126 	WMODE|XMODE,
127 	RMODE,
128 	RMODE|XMODE,
129 	RMODE|WMODE,
130 	RMODE|WMODE|XMODE,
131 };
132 
133 extern	DWORD	PlatformId;
134 	char    rootdir[MAXROOT] = "\\inferno";
135 	Rune	rootname[] = L"inferno-server";
136 static	Qid	rootqid;
137 static	User	*fsnone;
138 static	User	*fsuser;
139 static	Rune	*ntsrv;
140 static	int	usesec;
141 static	int	checksec;
142 static	int	isserver;
143 static	int	file_share_delete;
144 static	uchar	isntfrog[256];
145 
146 static	void		fsremove(Chan*);
147 
148 	wchar_t	*widen(char *s);
149 	char		*narrowen(wchar_t *ws);
150 	int		widebytes(wchar_t *ws);
151 
152 static char Etoolong[] = "file name too long";
153 
154 /*
155  * these lan manager functions are not supplied
156  * on windows95, so we have to load the dll by hand
157  */
158 static struct {
159 	NET_API_STATUS (NET_API_FUNCTION *UserGetLocalGroups)(
160 		LPWSTR servername,
161 		LPWSTR username,
162 		DWORD level,
163 		DWORD flags,
164 		LPBYTE *bufptr,
165 		DWORD prefmaxlen,
166 		LPDWORD entriesread,
167 		LPDWORD totalentries);
168 	NET_API_STATUS (NET_API_FUNCTION *UserGetGroups)(
169 		LPWSTR servername,
170 		LPWSTR username,
171 		DWORD level,
172 		LPBYTE *bufptr,
173 		DWORD prefmaxlen,
174 		LPDWORD entriesread,
175 		LPDWORD totalentries);
176 	NET_API_STATUS (NET_API_FUNCTION *GetAnyDCName)(
177 		LPCWSTR ServerName,
178 		LPCWSTR DomainName,
179 		LPBYTE *Buffer);
180 	NET_API_STATUS (NET_API_FUNCTION *ApiBufferFree)(LPVOID Buffer);
181 } net;
182 
183 extern	int		nth2fd(HANDLE);
184 extern	HANDLE		ntfd2h(int);
185 static	int		cnisroot(Cname*);
186 static	int		fsisroot(Chan*);
187 static	int		okelem(char*, int);
188 static	int		fsexist(char*, Qid*);
189 static	char*	fspath(Cname*, char*, char*, char*);
190 static	Cname*	fswalkpath(Cname*, char*, int);
191 static	char*	fslastelem(Cname*);
192 static	long		fsdirread(Chan*, uchar*, int, vlong);
193 static	ulong		fsqidpath(char*);
194 static	int		fsomode(int);
195 static	int		fsdirset(char*, int, WIN32_FIND_DATA*, char*, Chan*, int isdir);
196 static 	int		fsdirsize(WIN32_FIND_DATA*, char*, Chan*);
197 static	void		fssettime(char*, long, long);
198 static	long		unixtime(FILETIME);
199 static	FILETIME	wintime(ulong);
200 static	void		secinit(void);
201 static	int		secstat(Dir*, char*, Rune*);
202 static	int		secsize(char*, Rune*);
203 static	void		seccheck(char*, ulong, Rune*);
204 static	int		sechasperm(char*, ulong, Rune*);
205 static	SECURITY_DESCRIPTOR* secsd(char*, char[SD_ROCK]);
206 static	int		secsdhasperm(SECURITY_DESCRIPTOR*, ulong, Rune*);
207 static	int		secsdstat(SECURITY_DESCRIPTOR*, Stat*, Rune*);
208 static	SECURITY_DESCRIPTOR* secmksd(char[SD_ROCK], Stat*, ACL*, int);
209 static	SID		*dupsid(SID*);
210 static	int		ismembersid(Rune*, User*, SID*);
211 static	int		ismember(User*, User*);
212 static	User		*sidtouser(Rune*, SID*);
213 static	User		*domnametouser(Rune*, Rune*, Rune*);
214 static	User		*nametouser(Rune*, Rune*);
215 static	User		*unametouser(Rune*, char*);
216 static	void		addgroups(User*, int);
217 static	User		*mkuser(SID*, int, Rune*, Rune*);
218 static	Rune		*domsrv(Rune *, Rune[MAX_PATH]);
219 static	Rune		*filesrv(char*);
220 static	int		fsacls(char*);
221 static	User		*secuser(void);
222 
223 	int		runeslen(Rune*);
224 	Rune*		runesdup(Rune*);
225 	Rune*		utftorunes(Rune*, char*, int);
226 	char*		runestoutf(char*, Rune*, int);
227 	int		runescmp(Rune*, Rune*);
228 
229 
230 int
231 winfilematch(char *path, WIN32_FIND_DATA *data)
232 {
233 	char *p;
234 	wchar_t *wpath;
235 	int r;
236 
237 	p = path+strlen(path);
238 	while(p > path && p[-1] != '\\')
239 		--p;
240 	wpath = widen(p);
241 	r = (data->cFileName[0] == '.' && runeslen(data->cFileName) == 1)
242 			|| runescmp(data->cFileName, wpath) == 0;
243 	free(wpath);
244 	return r;
245 }
246 
247 int
248 winfileclash(char *path)
249 {
250 	HANDLE h;
251 	WIN32_FIND_DATA data;
252 	wchar_t *wpath;
253 
254 	wpath = widen(path);
255 	h = FindFirstFile(wpath, &data);
256 	free(wpath);
257 	if (h != INVALID_HANDLE_VALUE) {
258 		FindClose(h);
259 		return !winfilematch(path, &data);
260 	}
261 	return 0;
262 }
263 
264 
265 /*
266  * this gets called to set up the environment when we switch users
267  */
268 void
269 setid(char *name, int owner)
270 {
271 	User *u;
272 
273 	if(owner && !iseve())
274 		return;
275 
276 	kstrdup(&up->env->user, name);
277 
278 	if(!usesec)
279 		return;
280 
281 	u = unametouser(ntsrv, up->env->user);
282 	if(u == nil)
283 		u = fsnone;
284 	else {
285 		qlock(&u->lk);
286 		addgroups(u, 1);
287 		qunlock(&u->lk);
288 	}
289 	if(u == nil)
290 		panic("setid: user nil\n");
291 
292 	up->env->ui = u;
293 }
294 
295 static void
296 fsfree(Chan *c)
297 {
298 	cnameclose(FS(c)->name);
299 	if(FS(c)->de != nil)
300 		free(FS(c)->de);
301 	free(FS(c));
302 }
303 
304 void
305 fsinit(void)
306 {
307 	int n, isvol;
308 	ulong attr;
309 	char *p, tmp[MAXROOT];
310 	wchar_t *wp, *wpath, *last;
311 	wchar_t wrootdir[MAXROOT];
312 
313 	isntfrog['/'] = 1;
314 	isntfrog['\\'] = 1;
315 	isntfrog[':'] = 1;
316 	isntfrog['*'] = 1;
317 	isntfrog['?'] = 1;
318 	isntfrog['"'] = 1;
319 	isntfrog['<'] = 1;
320 	isntfrog['>'] = 1;
321 
322 	/*
323 	 * vet the root
324 	 */
325 	strcpy(tmp, rootdir);
326 	for(p = tmp; *p; p++)
327 		if(*p == '/')
328 			*p = '\\';
329 	if(tmp[0] != 0 && tmp[1] == ':') {
330 		if(tmp[2] == 0) {
331 			tmp[2] = '\\';
332 			tmp[3] = 0;
333 		}
334 		else if(tmp[2] != '\\') {
335 			/* don't allow c:foo - only c:\foo */
336 			panic("illegal root pathX");
337 		}
338 	}
339 	wrootdir[0] = '\0';
340 	wpath = widen(tmp);
341 	for(wp = wpath; *wp; wp++) {
342 		if(*wp < 32 || (*wp < 256 && isntfrog[*wp] && *wp != '\\' && *wp != ':'))
343 			panic("illegal root path");
344 	}
345 	n = GetFullPathName(wpath, MAXROOT, wrootdir, &last);
346 	free(wpath);
347 	runestoutf(rootdir, wrootdir, MAXROOT);
348 	if(n >= MAXROOT || n == 0)
349 		panic("illegal root path");
350 
351 	/* get rid of trailing \ */
352 	while(rootdir[n-1] == '\\') {
353 		if(n <= 2) {
354 			panic("illegal root path");
355 		}
356 		rootdir[--n] = '\0';
357 	}
358 
359 	isvol = 0;
360 	if(rootdir[1] == ':' && rootdir[2] == '\0')
361 		isvol = 1;
362 	else if(rootdir[0] == '\\' && rootdir[1] == '\\') {
363 		p = strchr(&rootdir[2], '\\');
364 		if(p == nil)
365 			panic("inferno root can't be a server");
366 		isvol = strchr(p+1, '\\') == nil;
367 	}
368 
369 	if(strchr(rootdir, '\\') == nil)
370 		strcat(rootdir, "\\.");
371 	attr = GetFileAttributes(wrootdir);
372 	if(attr == 0xFFFFFFFF)
373 		panic("root path '%s' does not exist", narrowen(wrootdir));
374 	rootqid.path = fsqidpath(rootdir);
375 	if(attr & FILE_ATTRIBUTE_DIRECTORY)
376 		rootqid.type |= QTDIR;
377 	rootdir[n] = '\0';
378 
379 	rootqid.vers = time(0);
380 
381 	/*
382 	 * set up for nt file security checking
383 	 */
384 	ntsrv = filesrv(rootdir);
385 	usesec = PlatformId == VER_PLATFORM_WIN32_NT; 	/* true for NT and 2000 */
386 	if(usesec){
387 		file_share_delete = FILE_SHARE_DELETE;	/* sensible handling of shared files by delete and rename */
388 		secinit();
389 		if(!fsacls(rootdir))
390 			usesec = 0;
391 	}
392 	checksec = usesec && isserver;
393 }
394 
395 Chan*
396 fsattach(char *spec)
397 {
398 	Chan *c;
399 	static int devno;
400 	static Lock l;
401 	char *drive = (char *)spec;
402 
403 	if (!emptystr(drive) && (drive[1] != ':' || drive[2] != '\0'))
404 		error(Ebadspec);
405 
406 	c = devattach('U', spec);
407 	lock(&l);
408 	c->dev = devno++;
409 	unlock(&l);
410 	c->qid = rootqid;
411 	c->aux = smalloc(sizeof(Fsinfo));
412 	FS(c)->srv = ntsrv;
413 	if(!emptystr(spec)) {
414 		char *s = smalloc(strlen(spec)+1);
415 		strcpy(s, spec);
416 		FS(c)->spec = s;
417 		FS(c)->srv = filesrv(spec);
418 		FS(c)->usesec = fsacls(spec);
419 		FS(c)->checksec = FS(c)->usesec && isserver;
420 		c->qid.path = fsqidpath(spec);
421 		c->qid.type = QTDIR;
422 		c->qid.vers = 0;
423 	}else{
424 		FS(c)->usesec = usesec;
425 		FS(c)->checksec = checksec;
426 	}
427 	FS(c)->name = newcname("/");
428 	return c;
429 }
430 
431 Walkqid*
432 fswalk(Chan *c, Chan *nc, char **name, int nname)
433 {
434 	int j, alloc;
435 	Walkqid *wq;
436 	char path[MAX_PATH], *p;
437 	Cname *ph;
438 	Cname *current, *next;
439 
440 	if(nname > 0)
441 		isdir(c);
442 
443 	alloc = 0;
444 	current = nil;
445 	wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
446 	if(waserror()){
447 		if(alloc && wq->clone != nil)
448 			cclose(wq->clone);
449 		cnameclose(current);
450 		free(wq);
451 		return nil;
452 	}
453 	if(nc == nil){
454 		nc = devclone(c);
455 		nc->type = 0;
456 		alloc = 1;
457 	}
458 	wq->clone = nc;
459 	current = FS(c)->name;
460 	if(current != nil)
461 		incref(&current->r);
462 	for(j = 0; j < nname; j++){
463 		if(!(nc->qid.type&QTDIR)){
464 			if(j==0)
465 				error(Enotdir);
466 			break;
467 		}
468 		if(!okelem(name[j], 0)){
469 			if(j == 0)
470 				error(Efilename);
471 			break;
472 		}
473 		p = fspath(current, name[j], path, FS(c)->spec);
474 		if(FS(c)->checksec) {
475 			*p = '\0';
476 			if(!sechasperm(path, XMODE, FS(c)->srv)){
477 				if(j == 0)
478 					error(Eperm);
479 				break;
480 			}
481 			*p = '\\';
482 		}
483 
484 		if(strcmp(name[j], "..") == 0) {
485 			if(fsisroot(c))
486 				nc->qid = rootqid;
487 			else{
488 				ph = fswalkpath(current, "..", 1);
489 				if(cnisroot(ph)){
490 					nc->qid = rootqid;
491 					current = ph;
492 					if(current != nil)
493 						incref(&current->r);
494 				}
495 				else {
496 					fspath(ph, 0, path, FS(c)->spec);
497 					if(!fsexist(path, &nc->qid)){
498 						cnameclose(ph);
499 						if(j == 0)
500 							error(Enonexist);
501 						break;
502 					}
503 				}
504 				next = fswalkpath(current, name[j], 1);
505 				cnameclose(current);
506 				current = next;
507 				cnameclose(ph);
508 			}
509 		}
510 		else{
511 			if(!fsexist(path, &nc->qid)){
512 				if(j == 0)
513 					error(Enonexist);
514 				break;
515 			}
516 			next = fswalkpath(current, name[j], 1);
517 			cnameclose(current);
518 			current = next;
519 		}
520 		wq->qid[wq->nqid++] = nc->qid;
521 	}
522 	poperror();
523 	if(wq->nqid < nname){
524 		cnameclose(current);
525 		if(alloc)
526 			cclose(wq->clone);
527 		wq->clone = nil;
528 	}else if(wq->clone){
529 		nc->aux = smalloc(sizeof(Fsinfo));
530 		nc->type = c->type;
531 		FS(nc)->spec = FS(c)->spec;
532 		FS(nc)->srv = FS(c)->srv;
533 		FS(nc)->name = current;
534 		FS(nc)->usesec = FS(c)->usesec;
535 		FS(nc)->checksec = FS(c)->checksec;
536 	}
537 	return wq;
538 }
539 
540 Chan*
541 fsopen(Chan *c, int mode)
542 {
543 	HANDLE h;
544 	int m, isdir, aflag, cflag;
545 	char path[MAX_PATH];
546 	wchar_t *wpath;
547 
548 	isdir = c->qid.type & QTDIR;
549 	if(isdir && mode != OREAD)
550 		error(Eperm);
551 	fspath(FS(c)->name, 0, path, FS(c)->spec);
552 
553 	if(FS(c)->checksec) {
554 		switch(mode & (OTRUNC|3)) {
555 		case OREAD:
556 			seccheck(path, RMODE, FS(c)->srv);
557 			break;
558 		case OWRITE:
559 		case OWRITE|OTRUNC:
560 			seccheck(path, WMODE, FS(c)->srv);
561 			break;
562 		case ORDWR:
563 		case ORDWR|OTRUNC:
564 		case OREAD|OTRUNC:
565 			seccheck(path, RMODE|WMODE, FS(c)->srv);
566 			break;
567 		case OEXEC:
568 			seccheck(path, XMODE, FS(c)->srv);
569 			break;
570 		default:
571 			error(Ebadarg);
572 		}
573 	}
574 
575 	c->mode = openmode(mode);
576 	if(isdir)
577 		FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE);
578 	else {
579 		m = fsomode(mode & 3);
580 		cflag = OPEN_EXISTING;
581 		if(mode & OTRUNC)
582 			cflag = TRUNCATE_EXISTING;
583 		aflag = FILE_FLAG_RANDOM_ACCESS;
584 		if(mode & ORCLOSE)
585 			aflag |= FILE_FLAG_DELETE_ON_CLOSE;
586 		if (winfileclash(path))
587 			error(Eexist);
588 		wpath = widen(path);
589 		h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, 0, cflag, aflag, 0);
590 		free(wpath);
591 		if(h == INVALID_HANDLE_VALUE)
592 			oserror();
593 		FS(c)->fd = nth2fd(h);
594 	}
595 
596 	c->offset = 0;
597 	FS(c)->offset = 0;
598 	c->flag |= COPEN;
599 	return c;
600 }
601 
602 void
603 fscreate(Chan *c, char *name, int mode, ulong perm)
604 {
605 	Stat st;
606 	HANDLE h;
607 	int m, aflag;
608 	SECURITY_ATTRIBUTES sa;
609 	SECURITY_DESCRIPTOR *sd;
610 	BY_HANDLE_FILE_INFORMATION hi;
611 	char *p, path[MAX_PATH], sdrock[SD_ROCK];
612 	wchar_t *wpath;
613 	ACL *acl;
614 
615 	if(!okelem(name, 1))
616 		error(Efilename);
617 
618 	m = fsomode(mode & 3);
619 	p = fspath(FS(c)->name, name, path, FS(c)->spec);
620 	acl = (ACL*)smalloc(ACL_ROCK);
621 	sd = nil;
622 	if(FS(c)->usesec) {
623 		*p = '\0';
624 		sd = secsd(path, sdrock);
625 		*p = '\\';
626 		if(sd == nil){
627 			free(acl);
628 			oserror();
629 		}
630 		if(FS(c)->checksec && !secsdhasperm(sd, WMODE, FS(c)->srv)
631 		|| !secsdstat(sd, &st, FS(c)->srv)){
632 			if(sd != (void*)sdrock)
633 				free(sd);
634 			free(acl);
635 			error(Eperm);
636 		}
637 		if(sd != (void*)sdrock)
638 			free(sd);
639 		if(perm & DMDIR)
640 			st.mode = (perm & ~0777) | (st.mode & perm & 0777);
641 		else
642 			st.mode = (perm & ~0666) | (st.mode & perm & 0666);
643 		st.owner = up->env->ui;
644 		if(!isserver)
645 			st.owner = fsuser;
646 		sd = secmksd(sdrock, &st, acl, perm & DMDIR);
647 		if(sd == nil){
648 			free(acl);
649 			oserror();
650 		}
651 	}
652 	sa.nLength = sizeof(sa);
653 	sa.lpSecurityDescriptor = sd;
654 	sa.bInheritHandle = 0;
655 
656 	if(perm & DMDIR) {
657 		if(mode != OREAD) {
658 			free(acl);
659 			error(Eisdir);
660 		}
661 		wpath = widen(path);
662 		if(!CreateDirectory(wpath, &sa) || !fsexist(path, &c->qid)) {
663 			free(wpath);
664 			free(acl);
665 			oserror();
666 		}
667 		free(wpath);
668 		FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE);
669 	}
670 	else {
671 		aflag = 0;
672 		if(mode & ORCLOSE)
673 			aflag = FILE_FLAG_DELETE_ON_CLOSE;
674 		if (winfileclash(path))
675 			error(Eexist);
676 		wpath = widen(path);
677 		h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, CREATE_ALWAYS, aflag, 0);
678 		free(wpath);
679 		if(h == INVALID_HANDLE_VALUE) {
680 			free(acl);
681 			oserror();
682 		}
683 		FS(c)->fd = nth2fd(h);
684 		c->qid.path = fsqidpath(path);
685 		c->qid.type = 0;
686 		c->qid.vers = 0;
687 		if(GetFileInformationByHandle(h, &hi))
688 			c->qid.vers = unixtime(hi.ftLastWriteTime);
689 	}
690 
691 	c->mode = openmode(mode);
692 	c->offset = 0;
693 	FS(c)->offset = 0;
694 	c->flag |= COPEN;
695 	FS(c)->name = fswalkpath(FS(c)->name, name, 0);
696 	free(acl);
697 }
698 
699 void
700 fsclose(Chan *c)
701 {
702 	HANDLE h;
703 
704 	if(c->flag & COPEN){
705 		h = ntfd2h(FS(c)->fd);
706 		if(h != INVALID_HANDLE_VALUE){
707 			if(c->qid.type & QTDIR)
708 				FindClose(h);
709 			else
710 				CloseHandle(h);
711 		}
712 	}
713 	if(c->flag & CRCLOSE){
714 		if(!waserror()){
715 			fsremove(c);
716 			poperror();
717 		}
718 		return;
719 	}
720 	fsfree(c);
721 }
722 
723 /*
724  * 64-bit seeks, using SetFilePointer because SetFilePointerEx
725  * is not supported by NT
726  */
727 static void
728 fslseek(HANDLE h, vlong offset)
729 {
730 	LONG hi;
731 
732 	if(offset <= 0x7fffffff){
733 		if(SetFilePointer(h, (LONG)offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
734 			oserror();
735 	}else{
736 		hi = offset>>32;
737 		if(SetFilePointer(h, (LONG)offset, &hi, FILE_BEGIN) == INVALID_SET_FILE_POINTER &&
738 		   GetLastError() != NO_ERROR)
739 			oserror();
740 	}
741 }
742 
743 long
744 fsread(Chan *c, void *va, long n, vlong offset)
745 {
746 	DWORD n2;
747 	HANDLE h;
748 
749 	qlock(&FS(c)->oq);
750 	if(waserror()){
751 		qunlock(&FS(c)->oq);
752 		nexterror();
753 	}
754 	if(c->qid.type & QTDIR) {
755 		n2 = fsdirread(c, va, n, offset);
756 	}
757 	else {
758 		h = ntfd2h(FS(c)->fd);
759 		if(FS(c)->offset != offset){
760 			fslseek(h, offset);
761 			FS(c)->offset = offset;
762 		}
763 		if(!ReadFile(h, va, n, &n2, NULL))
764 			oserror();
765 		FS(c)->offset += n2;
766 	}
767 	qunlock(&FS(c)->oq);
768 	poperror();
769 	return n2;
770 }
771 
772 long
773 fswrite(Chan *c, void *va, long n, vlong offset)
774 {
775 	DWORD n2;
776 	HANDLE h;
777 
778 	qlock(&FS(c)->oq);
779 	if(waserror()){
780 		qunlock(&FS(c)->oq);
781 		nexterror();
782 	}
783 	h = ntfd2h(FS(c)->fd);
784 	if(FS(c)->offset != offset){
785 		fslseek(h, offset);
786 		FS(c)->offset = offset;
787 	}
788 	if(!WriteFile(h, va, n, &n2, NULL))
789 		oserror();
790 	FS(c)->offset += n2;
791 	qunlock(&FS(c)->oq);
792 	poperror();
793 	return n2;
794 }
795 
796 int
797 fsstat(Chan *c, uchar *buf, int n)
798 {
799 	WIN32_FIND_DATA data;
800 	char path[MAX_PATH];
801 	wchar_t *wpath;
802 
803 	/*
804 	 * have to fake up a data for volumes like
805 	 * c: and \\server\share since you can't FindFirstFile them
806 	 */
807 	if(fsisroot(c)){
808 		strcpy(path, rootdir);
809 		if(strchr(path, '\\') == nil)
810 			strcat(path, "\\.");
811 		wpath = widen(path);
812 		data.dwFileAttributes = GetFileAttributes(wpath);
813 		free(wpath);
814 		if(data.dwFileAttributes == 0xffffffff)
815 			oserror();
816 		data.ftCreationTime =
817 		data.ftLastAccessTime =
818 		data.ftLastWriteTime = wintime(time(0));
819 		data.nFileSizeHigh = 0;
820 		data.nFileSizeLow = 0;
821 		utftorunes(data.cFileName, ".", MAX_PATH);
822 	} else {
823 		HANDLE h = INVALID_HANDLE_VALUE;
824 
825 		fspath(FS(c)->name, 0, path, FS(c)->spec);
826 		if (c->flag & COPEN)
827 			h = ntfd2h(FS(c)->fd);
828 
829 		if (h != INVALID_HANDLE_VALUE) {
830 			BY_HANDLE_FILE_INFORMATION fi;
831 			if (c->mode & OWRITE)
832 				FlushFileBuffers(h);
833 			if (!GetFileInformationByHandle(h, &fi))
834 				oserror();
835 			data.dwFileAttributes = fi.dwFileAttributes;
836 			data.ftCreationTime = fi.ftCreationTime;
837 			data.ftLastAccessTime = fi.ftLastAccessTime;
838 			data.ftLastWriteTime = fi.ftLastWriteTime;;
839 			data.nFileSizeHigh = fi.nFileSizeHigh;
840 			data.nFileSizeLow = fi.nFileSizeLow;
841 		} else {
842 			wpath = widen(path);
843 			h = FindFirstFile(wpath, &data);
844 			free(wpath);
845 			if(h == INVALID_HANDLE_VALUE)
846 				oserror();
847 			if (!winfilematch(path, &data)) {
848 				FindClose(h);
849 				error(Enonexist);
850 			}
851 			FindClose(h);
852 		}
853 		utftorunes(data.cFileName, fslastelem(FS(c)->name), MAX_PATH);
854 	}
855 
856 	return fsdirset(buf, n, &data, path, c, 0);
857 }
858 
859 int
860 fswstat(Chan *c, uchar *buf, int n)
861 {
862 	int wsd;
863 	Dir dir;
864 	Stat st;
865 	Cname * volatile ph;
866 	HANDLE h;
867 	ulong attr;
868 	User *ou, *gu;
869 	WIN32_FIND_DATA data;
870 	SECURITY_DESCRIPTOR *sd;
871 	char *last, sdrock[SD_ROCK], path[MAX_PATH], newpath[MAX_PATH], strs[4*256];
872 	wchar_t wspath[MAX_PATH], wsnewpath[MAX_PATH];
873 	wchar_t *wpath;
874 	int nmatch;
875 
876 	n = convM2D(buf, n, &dir, strs);
877 	if(n == 0)
878 		error(Eshortstat);
879 
880 	last = fspath(FS(c)->name, 0, path, FS(c)->spec);
881 	utftorunes(wspath, path, MAX_PATH);
882 
883 	if(fsisroot(c)){
884 		if(dir.atime != ~0)
885 			data.ftLastAccessTime = wintime(dir.atime);
886 		if(dir.mtime != ~0)
887 			data.ftLastWriteTime = wintime(dir.mtime);
888 		utftorunes(data.cFileName, ".", MAX_PATH);
889 	}else{
890 		h = FindFirstFile(wspath, &data);
891 		if(h == INVALID_HANDLE_VALUE)
892 			oserror();
893 		if (!winfilematch(path, &data)) {
894 			FindClose(h);
895 			error(Enonexist);
896 		}
897 		FindClose(h);
898 	}
899 
900 	wsd = 0;
901 	ou = nil;
902 	gu = nil;
903 	if(FS(c)->usesec) {
904 		if(FS(c)->checksec && up->env->ui == fsnone)
905 			error(Eperm);
906 
907 		/*
908 		 * find new owner and group
909 		 */
910 		if(!emptystr(dir.uid)){
911 			ou = unametouser(FS(c)->srv, dir.uid);
912 			if(ou == nil)
913 				oserror();
914 		}
915 		if(!emptystr(dir.gid)){
916 			gu = unametouser(FS(c)->srv, dir.gid);
917 			if(gu == nil){
918 				if(strcmp(dir.gid, "unknown") != 0
919 				&& strcmp(dir.gid, "deleted") != 0)
920 					oserror();
921 				gu = ou;
922 			}
923 		}
924 
925 		/*
926 		 * find old stat info
927 		 */
928 		sd = secsd(path, sdrock);
929 		if(sd == nil || !secsdstat(sd, &st, FS(c)->srv)){
930 			if(sd != nil && sd != (void*)sdrock)
931 				free(sd);
932 			oserror();
933 		}
934 		if(sd != (void*)sdrock)
935 			free(sd);
936 
937 		/*
938 		 * permission rules:
939 		 * if none, can't do anything
940 		 * chown => no way
941 		 * chgrp => current owner or group, and in new group
942 		 * mode/time => owner or in either group
943 		 * rename => write in parent
944 		 */
945 		if(ou == nil)
946 			ou = st.owner;
947 		if(FS(c)->checksec && st.owner != ou)
948 			error(Eperm);
949 
950 		if(gu == nil)
951 			gu = st.group;
952 		if(st.group != gu){
953 			if(FS(c)->checksec
954 			&&(!ismember(up->env->ui, ou) && !ismember(up->env->ui, gu)
955 			|| !ismember(up->env->ui, st.group)))
956 				error(Eperm);
957 			wsd = 1;
958 		}
959 
960 		if(dir.atime != ~0 && unixtime(data.ftLastAccessTime) != dir.atime
961 		|| dir.mtime != ~0 && unixtime(data.ftLastWriteTime) != dir.mtime
962 		|| dir.mode != ~0 && st.mode != dir.mode){
963 			if(FS(c)->checksec
964 			&& !ismember(up->env->ui, ou)
965 			&& !ismember(up->env->ui, gu)
966 			&& !ismember(up->env->ui, st.group))
967 				error(Eperm);
968 			if(dir.mode != ~0 && st.mode != dir.mode)
969 				wsd = 1;
970 		}
971 	}
972 	wpath = widen(dir.name);
973 	nmatch = runescmp(wpath, data.cFileName);
974 	free(wpath);
975 	if(!emptystr(dir.name) && nmatch != 0){
976 		if(!okelem(dir.name, 1))
977 			error(Efilename);
978 		ph = fswalkpath(FS(c)->name, "..", 1);
979 		if(waserror()){
980 			cnameclose(ph);
981 			nexterror();
982 		}
983 		ph = fswalkpath(ph, dir.name, 0);
984 		fspath(ph, 0, newpath, FS(c)->spec);
985 		utftorunes(wsnewpath, newpath, MAX_PATH);
986 		if(GetFileAttributes(wpath) != 0xffffffff && !winfileclash(newpath))
987 			error("file already exists");
988 		if(fsisroot(c))
989 			error(Eperm);
990 		if(FS(c)->checksec){
991 			*last = '\0';
992 			seccheck(path, WMODE, FS(c)->srv);
993 			*last = '\\';
994 		}
995 		poperror();
996 		cnameclose(ph);
997 	}
998 
999 	if(dir.atime != ~0 && unixtime(data.ftLastAccessTime) != dir.atime
1000 	|| dir.mtime != ~0 && unixtime(data.ftLastWriteTime) != dir.mtime)
1001 		fssettime(path, dir.atime, dir.mtime);
1002 
1003 	attr = data.dwFileAttributes;
1004 	if(dir.mode & 0222)
1005 		attr &= ~FILE_ATTRIBUTE_READONLY;
1006 	else
1007 		attr |= FILE_ATTRIBUTE_READONLY;
1008 	if(!fsisroot(c)
1009 	&& attr != data.dwFileAttributes
1010 	&& (attr & FILE_ATTRIBUTE_READONLY))
1011 		SetFileAttributes(wspath, attr);
1012 	if(FS(c)->usesec && wsd){
1013 		ACL *acl = (ACL *) smalloc(ACL_ROCK);
1014 		st.owner = ou;
1015 		st.group = gu;
1016 		if(dir.mode != ~0)
1017 			st.mode = dir.mode;
1018 		sd = secmksd(sdrock, &st, acl, data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
1019 		if(sd == nil || !SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd)){
1020 			free(acl);
1021 			oserror();
1022 		}
1023 		free(acl);
1024 	}
1025 
1026 	if(!fsisroot(c)
1027 	&& attr != data.dwFileAttributes
1028 	&& !(attr & FILE_ATTRIBUTE_READONLY))
1029 		SetFileAttributes(wspath, attr);
1030 
1031 	/* do last so path is valid throughout */
1032 	wpath = widen(dir.name);
1033 	nmatch = runescmp(wpath, data.cFileName);
1034 	free(wpath);
1035 	if(!emptystr(dir.name) && nmatch != 0) {
1036 		ph = fswalkpath(FS(c)->name, "..", 1);
1037 		if(waserror()){
1038 			cnameclose(ph);
1039 			nexterror();
1040 		}
1041 		ph = fswalkpath(ph, dir.name, 0);
1042 		fspath(ph, 0, newpath, FS(c)->spec);
1043 		utftorunes(wsnewpath, newpath, MAX_PATH);
1044 		/*
1045 		 * can't rename if it is open: if this process has it open, close it temporarily.
1046 		 */
1047 		if(!file_share_delete && c->flag & COPEN){
1048 			h = ntfd2h(FS(c)->fd);
1049 			if(h != INVALID_HANDLE_VALUE)
1050 				CloseHandle(h);	/* woe betide it if ORCLOSE */
1051 			FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE);
1052 		}
1053 		if(!MoveFile(wspath, wsnewpath)) {
1054 			oserror();
1055 		} else if(!file_share_delete && c->flag & COPEN) {
1056 			int	aflag;
1057 			SECURITY_ATTRIBUTES sa;
1058 
1059 			/* The move succeeded, so open new file to maintain handle */
1060 			sa.nLength = sizeof(sa);
1061 			sa.lpSecurityDescriptor = sd;
1062 			sa.bInheritHandle = 0;
1063 			if(c->flag & CRCLOSE)
1064 				aflag = FILE_FLAG_DELETE_ON_CLOSE;
1065 			h = CreateFile(wsnewpath, fsomode(c->mode & 0x3), FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, OPEN_EXISTING, aflag, 0);
1066 			if(h == INVALID_HANDLE_VALUE)
1067 				oserror();
1068 			FS(c)->fd = nth2fd(h);
1069 		}
1070 		cnameclose(FS(c)->name);
1071 		poperror();
1072 		FS(c)->name = ph;
1073 	}
1074 	return n;
1075 }
1076 
1077 static void
1078 fsremove(Chan *c)
1079 {
1080 	int n;
1081 	char *p, path[MAX_PATH];
1082 	wchar_t wspath[MAX_PATH];
1083 
1084 	if(waserror()){
1085 		fsfree(c);
1086 		nexterror();
1087 	}
1088 	if(fsisroot(c))
1089 		error(Eperm);
1090 	p = fspath(FS(c)->name, 0, path, FS(c)->spec);
1091 	utftorunes(wspath, path, MAX_PATH);
1092 	if(FS(c)->checksec){
1093 		*p = '\0';
1094 		seccheck(path, WMODE, FS(c)->srv);
1095 		*p = '\\';
1096 	}
1097 	if(c->qid.type & QTDIR)
1098 		n = RemoveDirectory(wspath);
1099 	else
1100 		n = DeleteFile(wspath);
1101 	if (!n) {
1102 		ulong attr, mode;
1103 		SECURITY_DESCRIPTOR *sd = nil;
1104 		char sdrock[SD_ROCK];
1105 		Stat st;
1106 		int secok;
1107 		attr = GetFileAttributes(wspath);
1108 		if(attr != 0xFFFFFFFF) {
1109 			if (FS(c)->usesec) {
1110 				sd = secsd(path, sdrock);
1111 				secok = (sd != nil) && secsdstat(sd, &st, FS(c)->srv);
1112 				if (secok) {
1113 					ACL *acl = (ACL *) smalloc(ACL_ROCK);
1114 					mode = st.mode;
1115 					st.mode |= 0660;
1116 					sd = secmksd(sdrock, &st, acl, attr & FILE_ATTRIBUTE_DIRECTORY);
1117 					if(sd != nil) {
1118 						SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd);
1119 					}
1120 					free(acl);
1121 					if(sd != nil && sd != (void*)sdrock)
1122 						free(sd);
1123 					sd = nil;
1124 				}
1125 			}
1126 			SetFileAttributes(wspath, FILE_ATTRIBUTE_NORMAL);
1127 			if(c->qid.type & QTDIR)
1128 				n = RemoveDirectory(wspath);
1129 			else
1130 				n = DeleteFile(wspath);
1131 			if (!n) {
1132 				if (FS(c)->usesec && secok) {
1133 					ACL *acl = (ACL *) smalloc(ACL_ROCK);
1134 					st.mode =  mode;
1135 					sd = secmksd(sdrock, &st, acl, attr & FILE_ATTRIBUTE_DIRECTORY);
1136 					if(sd != nil) {
1137 						SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd);
1138 					}
1139 					free(acl);
1140 				}
1141 				SetFileAttributes(wspath, attr);
1142 				if(sd != nil && sd != (void*)sdrock)
1143 					free(sd);
1144 			}
1145 		}
1146 	}
1147 	if(!n)
1148 		oserror();
1149 	poperror();
1150 	fsfree(c);
1151 }
1152 
1153 /*
1154  * check elem for illegal characters /\:*?"<>
1155  * ... and relatives are also disallowed,
1156  * since they specify grandparents, which we
1157  * are not prepared to handle
1158  */
1159 static int
1160 okelem(char *elem, int nodots)
1161 {
1162 	int c, dots;
1163 
1164 	dots = 0;
1165 	while((c = *(uchar*)elem) != 0){
1166 		if(isntfrog[c])
1167 			return 0;
1168 		if(c == '.' && dots >= 0)
1169 			dots++;
1170 		else
1171 			dots = -1;
1172 		elem++;
1173 	}
1174 	if(nodots)
1175 		return dots <= 0;
1176 	return dots <= 2;
1177 }
1178 
1179 static int
1180 cnisroot(Cname *c)
1181 {
1182 	return strcmp(c->s, "/") == 0;
1183 }
1184 
1185 static int
1186 fsisroot(Chan *c)
1187 {
1188 	return strcmp(FS(c)->name->s, "/") == 0;
1189 }
1190 
1191 static char*
1192 fspath(Cname *c, char *ext, char *path, char *spec)
1193 {
1194 	char *p, *last, *rootd;
1195 	int extlen = 0;
1196 
1197 	rootd = spec != nil ? spec : rootdir;
1198 	if(ext)
1199 		extlen = strlen(ext) + 1;
1200 	if(strlen(rootd) + extlen >= MAX_PATH)
1201 		error(Etoolong);
1202 	strcpy(path, rootd);
1203 	if(cnisroot(c)){
1204 		if(ext) {
1205 			strcat(path, "\\");
1206 			strcat(path, ext);
1207 		}
1208 	}else{
1209 		if(*c->s != '/') {
1210 			if(strlen(path) + 1 >= MAX_PATH)
1211 				error(Etoolong);
1212 			strcat(path, "\\");
1213 		}
1214 		if(strlen(path) + strlen(c->s) + extlen >= MAX_PATH)
1215 			error(Etoolong);
1216 		strcat(path, c->s);
1217 		if(ext){
1218 			strcat(path, "\\");
1219 			strcat(path, ext);
1220 		}
1221 	}
1222 	last = path;
1223 	for(p = path; *p != '\0'; p++){
1224 		if(*p == '/' || *p == '\\'){
1225 			*p = '\\';
1226 			last = p;
1227 		}
1228 	}
1229 	return last;
1230 }
1231 
1232 extern void cleancname(Cname*);
1233 
1234 static Cname *
1235 fswalkpath(Cname *c, char *name, int dup)
1236 {
1237 	if(dup)
1238 		c = newcname(c->s);
1239 	c = addelem(c, name);
1240 	if(isdotdot(name))
1241 		cleancname(c);
1242 	return c;
1243 }
1244 
1245 static char *
1246 fslastelem(Cname *c)
1247 {
1248 	char *p;
1249 
1250 	p = c->s + c->len;
1251 	while(p > c->s && p[-1] != '/')
1252 		p--;
1253 	return p;
1254 }
1255 
1256 static int
1257 fsdirbadentry(WIN32_FIND_DATA *data)
1258 {
1259 	wchar_t *s;
1260 
1261 	s = data->cFileName;
1262 	if(s[0] == 0)
1263 		return 1;
1264 	if(s[0] == '.' && (s[1] == 0 || s[1] == '.' && s[2] == 0))
1265 		return 1;
1266 
1267 	return 0;
1268 }
1269 
1270 static Fsdir*
1271 fsdirent(Chan *c, char *path, Fsdir *data)
1272 {
1273 	wchar_t *wpath;
1274 	HANDLE h;
1275 
1276 	h = ntfd2h(FS(c)->fd);
1277 	if(data == nil)
1278 		data = smalloc(sizeof(*data));
1279 	if(FS(c)->offset == 0){
1280 		if(h != INVALID_HANDLE_VALUE)
1281 			FindClose(h);
1282 		wpath = widen(path);
1283 		h = FindFirstFile(wpath, data);
1284 		free(wpath);
1285 		FS(c)->fd = nth2fd(h);
1286 		if(h == INVALID_HANDLE_VALUE){
1287 			free(data);
1288 			return nil;
1289 		}
1290 		if(!fsdirbadentry(data))
1291 			return data;
1292 	}
1293 	do{
1294 		if(!FindNextFile(h, data)){
1295 			free(data);
1296 			return nil;
1297 		}
1298 	}while(fsdirbadentry(data));
1299 	return data;
1300 }
1301 
1302 static long
1303 fsdirread(Chan *c, uchar *va, int count, vlong offset)
1304 {
1305 	int i, r;
1306 	char path[MAX_PATH], *p;
1307 	Fsdir *de;
1308 	vlong o;
1309 
1310 	if(count == 0 || offset < 0)
1311 		return 0;
1312 	p = fspath(FS(c)->name, "*.*", path, FS(c)->spec);
1313 	p++;
1314 	de = nil;
1315 	if(FS(c)->offset != offset){
1316 		de = FS(c)->de;
1317 		if(FS(c)->de != nil){
1318 			free(FS(c)->de);
1319 			FS(c)->de = nil;
1320 		}
1321 		FS(c)->offset = 0;
1322 		for(o = 0; o < offset;){
1323 			de = fsdirent(c, path, de);
1324 			if(de == nil){
1325 				FS(c)->offset = o;
1326 				return 0;
1327 			}
1328 			runestoutf(p, de->cFileName, &path[MAX_PATH]-p);
1329 			path[MAX_PATH-1] = '\0';
1330 			o += fsdirsize(de, path, c);
1331 		}
1332 		FS(c)->offset = offset;
1333 	}
1334 	for(i = 0; i < count;){
1335 		if(FS(c)->de != nil){	/* left over from previous read at offset */
1336 			de = FS(c)->de;
1337 			FS(c)->de = nil;
1338 		}else{
1339 			de = fsdirent(c, path, de);
1340 			if(de == nil)
1341 				break;
1342 		}
1343 		runestoutf(p, de->cFileName, &path[MAX_PATH]-p);
1344 		path[MAX_PATH-1] = '\0';
1345 		r = fsdirset(va+i, count-i, de, path, c, 1);
1346 		if(r <= 0){
1347 			/* won't fit; save for next read at this offset */
1348 			FS(c)->de = de;
1349 			break;
1350 		}
1351 		i += r;
1352 		FS(c)->offset += r;
1353 	}
1354 	return i;
1355 }
1356 
1357 static ulong
1358 fsqidpath(char *p)
1359 {
1360 	ulong h;
1361 	int c;
1362 
1363 	h = 0;
1364 	while(*p != '\0'){
1365 		/* force case insensitive file names */
1366 		c = *p++;
1367 		if(c >= 'A' && c <= 'Z')
1368 			c += 'a'-'A';
1369 		h = h * 19 ^ c;
1370 	}
1371 	return h;
1372 }
1373 
1374 /* TO DO: substitute fixed, made-up (unlikely) names for these */
1375 static char* devf[] = { "aux", "com1", "com2", "lpt1", "nul", "prn", nil };
1376 
1377 static int
1378 devfile(char *p)
1379 {
1380 	char *s, *t, *u, **ss;
1381 
1382 	if((u = strrchr(p, '\\')) != nil)
1383 		u++;
1384 	else if((u = strrchr(p, '/')) != nil)
1385 		u++;
1386 	else
1387 		u = p;
1388 	for(ss = devf; *ss != nil; ss++){
1389 		for(s = *ss, t = u; *s != '\0' && *t != '\0' && *t != '.'; s++, t++)
1390 			if(*s != *t && *s != *t+'a'-'A')
1391 				break;
1392 		if(*s == '\0' && (*t == '\0' || *t == '.'))
1393 			return 1;
1394 	}
1395 	return 0;
1396 }
1397 
1398 /*
1399  * there are other ways to figure out
1400  * the attributes and times for a file.
1401  * perhaps they are faster
1402  */
1403 static int
1404 fsexist(char *p, Qid *q)
1405 {
1406 	HANDLE h;
1407 	WIN32_FIND_DATA data;
1408 	wchar_t *wpath;
1409 
1410 	if(devfile(p))
1411 		return 0;
1412 	wpath = widen(p);
1413 	h = FindFirstFile(wpath, &data);
1414 	free(wpath);
1415 	if(h == INVALID_HANDLE_VALUE)
1416 		return 0;
1417 			if (!winfilematch(p, &data)) {
1418 				FindClose(h);
1419 				return 0;
1420 			}
1421 	FindClose(h);
1422 
1423 	q->path = fsqidpath(p);
1424 	q->type = 0;
1425 
1426 	if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1427 		q->type |= QTDIR;
1428 
1429 	q->vers = unixtime(data.ftLastWriteTime);
1430 
1431 	return 1;
1432 }
1433 
1434 static int
1435 fsdirset(char *edir, int n, WIN32_FIND_DATA *data, char *path, Chan *c, int isdir)
1436 {
1437 	Dir dir;
1438 	static char neveryone[] = "Everyone";
1439 
1440 	dir.name = narrowen(data->cFileName);
1441 	dir.muid = nil;
1442 	dir.qid.path = fsqidpath(path);
1443 	dir.qid.vers = 0;
1444 	dir.qid.type = 0;
1445 	dir.mode = 0;
1446 	dir.atime = unixtime(data->ftLastAccessTime);
1447 	dir.mtime = unixtime(data->ftLastWriteTime);
1448 	dir.qid.vers = dir.mtime;
1449 	dir.length = ((uvlong)data->nFileSizeHigh<<32) | ((uvlong)data->nFileSizeLow & ~((uvlong)0xFFFFFFFF<<32));
1450 	dir.type = 'U';
1451 	dir.dev = c->dev;
1452 
1453 	if(!FS(c)->usesec){
1454 		/* no NT security so make something up */
1455 		dir.uid = neveryone;
1456 		dir.gid = neveryone;
1457 		dir.mode = 0777;
1458 	}else if(!secstat(&dir, path, FS(c)->srv))
1459 		oserror();
1460 
1461 	if(data->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
1462 		dir.mode &= ~0222;
1463 	if(data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
1464 		dir.qid.type |= QTDIR;
1465 		dir.mode |= DMDIR;
1466 		dir.length = 0;
1467 	}
1468 
1469 	if(isdir && sizeD2M(&dir) > n)
1470 		n = -1;
1471 	else
1472 		n = convD2M(&dir, edir, n);
1473 	if(dir.uid != neveryone)
1474 		free(dir.uid);
1475 	if(dir.gid != neveryone)
1476 		free(dir.gid);
1477 	free(dir.name);
1478 	return n;
1479 }
1480 
1481 static int
1482 fsdirsize(WIN32_FIND_DATA *data, char *path, Chan *c)
1483 {
1484 	int i, n;
1485 
1486 	n = widebytes(data->cFileName);
1487 	if(!FS(c)->usesec)
1488 		n += 8+8;
1489 	else{
1490 		i = secsize(path, FS(c)->srv);
1491 		if(i < 0)
1492 			oserror();
1493 		n += i;
1494 	}
1495 	return STATFIXLEN+n;
1496 }
1497 
1498 static void
1499 fssettime(char *path, long at, long mt)
1500 {
1501 	HANDLE h;
1502 	FILETIME atime, mtime;
1503 	wchar_t *wpath;
1504 
1505 	wpath = widen(path);
1506 	h = CreateFile(wpath, GENERIC_WRITE,
1507 		0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
1508 	free(wpath);
1509 	if(h == INVALID_HANDLE_VALUE)
1510 		return;
1511 	mtime = wintime(mt);
1512 	atime = wintime(at);
1513 	if(!SetFileTime(h, 0, &atime, &mtime)){
1514 		CloseHandle(h);
1515 		oserror();
1516 	}
1517 	CloseHandle(h);
1518 }
1519 
1520 static int
1521 fsomode(int m)
1522 {
1523 	switch(m & 0x3) {
1524 	case OREAD:
1525 	case OEXEC:
1526 		return GENERIC_READ;
1527 	case OWRITE:
1528 		return GENERIC_WRITE;
1529 	case ORDWR:
1530 		return GENERIC_READ|GENERIC_WRITE;
1531 	}
1532 	error(Ebadarg);
1533 	return 0;
1534 }
1535 
1536 static long
1537 unixtime(FILETIME ft)
1538 {
1539 	vlong t;
1540 
1541 	t = (vlong)ft.dwLowDateTime + ((vlong)ft.dwHighDateTime<<32);
1542 	t -= (vlong)10000000*134774*24*60*60;
1543 
1544 	return (long)(t/10000000);
1545 }
1546 
1547 static FILETIME
1548 wintime(ulong t)
1549 {
1550 	FILETIME ft;
1551 	vlong vt;
1552 
1553 	vt = (vlong)t*10000000+(vlong)10000000*134774*24*60*60;
1554 
1555 	ft.dwLowDateTime = vt;
1556 	ft.dwHighDateTime = vt>>32;
1557 
1558 	return ft;
1559 }
1560 
1561 /*
1562  * the sec routines manage file permissions for nt.
1563  * nt files have an associated security descriptor,
1564  * which has in it an owner, a group,
1565  * and a discretionary acces control list, or acl,
1566  * which specifies the permissions for the file.
1567  *
1568  * the strategy for mapping between inferno owner,
1569  * group, other, and mode and nt file security is:
1570  *
1571  *	inferno owner == nt file owner
1572  *	inferno other == nt Everyone
1573  *	inferno group == first non-owner,
1574  *			non-Everyone user given in the acl,
1575  *			or the owner if there is no such user.
1576  * we examine the entire acl when check for permissions,
1577  * but only report a subset.
1578  *
1579  * when we write an acl, we also give all permissions to
1580  * the special user rootname, who is supposed to run emu in server mode.
1581  */
1582 static void
1583 secinit(void)
1584 {
1585 	HMODULE lib;
1586 	HANDLE token;
1587 	TOKEN_PRIVILEGES *priv;
1588 	char privrock[sizeof(TOKEN_PRIVILEGES) + 1*sizeof(LUID_AND_ATTRIBUTES)];
1589 	SID_IDENTIFIER_AUTHORITY id = SECURITY_CREATOR_SID_AUTHORITY;
1590 	SID_IDENTIFIER_AUTHORITY wid = SECURITY_WORLD_SID_AUTHORITY;
1591 	SID_IDENTIFIER_AUTHORITY ntid = SECURITY_NT_AUTHORITY;
1592 
1593 	lib = LoadLibraryA("netapi32");
1594 	if(lib == 0) {
1595 		usesec = 0;
1596 		return;
1597 	}
1598 
1599 	net.UserGetGroups = (void*)GetProcAddress(lib, "NetUserGetGroups");
1600 	if(net.UserGetGroups == 0)
1601 		panic("bad netapi32 library");
1602 	net.UserGetLocalGroups = (void*)GetProcAddress(lib, "NetUserGetLocalGroups");
1603 	if(net.UserGetLocalGroups == 0)
1604 		panic("bad netapi32 library");
1605 	net.GetAnyDCName = (void*)GetProcAddress(lib, "NetGetAnyDCName");
1606 	if(net.GetAnyDCName == 0)
1607 		panic("bad netapi32 library");
1608 	net.ApiBufferFree = (void*)GetProcAddress(lib, "NetApiBufferFree");
1609 	if(net.ApiBufferFree == 0)
1610 		panic("bad netapi32 library");
1611 
1612 	if(!AllocateAndInitializeSid(&id, 1,
1613 		SECURITY_CREATOR_OWNER_RID,
1614 		1, 2, 3, 4, 5, 6, 7, &creatorowner)
1615 	|| !AllocateAndInitializeSid(&id, 1,
1616 		SECURITY_CREATOR_GROUP_RID,
1617 		1, 2, 3, 4, 5, 6, 7, &creatorgroup)
1618 	|| !AllocateAndInitializeSid(&wid, 1,
1619 		SECURITY_WORLD_RID,
1620 		1, 2, 3, 4, 5, 6, 7, &everyone)
1621 	|| !AllocateAndInitializeSid(&ntid, 1,
1622 		0,
1623 		1, 2, 3, 4, 5, 6, 7, &ntignore))
1624 		panic("can't initialize well-known sids");
1625 
1626 	fsnone = sidtouser(ntsrv, everyone);
1627 	if(fsnone == nil)
1628 		panic("can't make none user");
1629 
1630 	/*
1631 	 * see if we are running as the emu server user
1632 	 * if so, set up SE_RESTORE_NAME privilege,
1633 	 * which allows setting the owner field in a security descriptor.
1634 	 * other interesting privileges are SE_TAKE_OWNERSHIP_NAME,
1635 	 * which enables changing the ownership of a file to yourself
1636 	 * regardless of the permissions on the file, SE_BACKUP_NAME,
1637 	 * which enables reading any files regardless of permission,
1638 	 * and SE_CHANGE_NOTIFY_NAME, which enables walking through
1639 	 * directories without X permission.
1640 	 * SE_RESTORE_NAME and SE_BACKUP_NAME together allow writing
1641 	 * and reading any file data, regardless of permission,
1642 	 * if the file is opened with FILE_BACKUP_SEMANTICS.
1643 	 */
1644 	isserver = 0;
1645 	fsuser = secuser();
1646 	if(fsuser == nil)
1647 		fsuser = fsnone;
1648 	else if(runescmp(fsuser->name, rootname) == 0
1649 	     && OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)){
1650 		priv = (TOKEN_PRIVILEGES*)privrock;
1651 		priv->PrivilegeCount = 1;
1652 		priv->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1653 		if(LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &priv->Privileges[0].Luid)
1654 		&& AdjustTokenPrivileges(token, 0, priv, 0, NULL, NULL))
1655 			isserver = 1;
1656 		CloseHandle(token);
1657 	}
1658 }
1659 
1660 /*
1661  * get the User for the executing process
1662  */
1663 static User*
1664 secuser(void)
1665 {
1666 	DWORD need;
1667 	HANDLE token;
1668 	TOKEN_USER *tu;
1669 	char turock[sizeof(TOKEN_USER) + MAX_SID];
1670 
1671 	if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
1672 		return nil;
1673 
1674 	tu = (TOKEN_USER*)turock;
1675 	if(!GetTokenInformation(token, TokenUser, tu, sizeof(turock), &need)){
1676 		CloseHandle(token);
1677 		return nil;
1678 	}
1679 	CloseHandle(token);
1680 	return sidtouser(nil, tu->User.Sid);
1681 }
1682 
1683 static int
1684 secstat(Dir *dir, char *file, Rune *srv)
1685 {
1686 	int ok, n;
1687 	Stat st;
1688 	char sdrock[SD_ROCK];
1689 	SECURITY_DESCRIPTOR *sd;
1690 
1691 	sd = secsd(file, sdrock);
1692 	if(sd == nil){
1693 		int e = GetLastError();
1694 		if(e == ERROR_ACCESS_DENIED || e == ERROR_SHARING_VIOLATION){
1695 			dir->uid = strdup("unknown");
1696 			dir->gid = strdup("unknown");
1697 			if(dir->uid == nil || dir->gid == nil){
1698 				free(dir->uid);
1699 				error(Enomem);	/* will change to use kstrdup */
1700 			}
1701 			dir->mode = 0;
1702 			return 1;
1703 		}
1704 		return 0;
1705 	}
1706 	ok = secsdstat(sd, &st, srv);
1707 	if(sd != (void*)sdrock)
1708 		free(sd);
1709 	if(ok){
1710 		dir->mode = st.mode;
1711 		n = runenlen(st.owner->name, runeslen(st.owner->name));
1712 		dir->uid = smalloc(n+1);
1713 		runestoutf(dir->uid, st.owner->name, n+1);
1714 		n = runenlen(st.group->name, runeslen(st.group->name));
1715 		dir->gid = smalloc(n+1);
1716 		runestoutf(dir->gid, st.group->name, n+1);
1717 	}
1718 	return ok;
1719 }
1720 
1721 static int
1722 secsize(char *file, Rune *srv)
1723 {
1724 	int ok;
1725 	Stat st;
1726 	char sdrock[SD_ROCK];
1727 	SECURITY_DESCRIPTOR *sd;
1728 
1729 	sd = secsd(file, sdrock);
1730 	if(sd == nil){
1731 		int e = GetLastError();
1732 		if(e == ERROR_ACCESS_DENIED || e == ERROR_SHARING_VIOLATION)
1733 			return 7+7;
1734 		return -1;
1735 	}
1736 	ok = secsdstat(sd, &st, srv);
1737 	if(sd != (void*)sdrock)
1738 		free(sd);
1739 	if(ok)
1740 		return runenlen(st.owner->name, runeslen(st.owner->name))+runenlen(st.group->name, runeslen(st.group->name));
1741 	return -1;
1742 }
1743 
1744 /*
1745  * verify that u had access to file
1746  */
1747 static void
1748 seccheck(char *file, ulong access, Rune *srv)
1749 {
1750 	if(!sechasperm(file, access, srv))
1751 		error(Eperm);
1752 }
1753 
1754 static int
1755 sechasperm(char *file, ulong access, Rune *srv)
1756 {
1757 	int ok;
1758 	char sdrock[SD_ROCK];
1759 	SECURITY_DESCRIPTOR *sd;
1760 
1761 	/*
1762 	 * only really needs dacl info
1763 	 */
1764 	sd = secsd(file, sdrock);
1765 	if(sd == nil)
1766 		return 0;
1767 	ok = secsdhasperm(sd, access, srv);
1768 	if(sd != (void*)sdrock)
1769 		free(sd);
1770 	return ok;
1771 }
1772 
1773 static SECURITY_DESCRIPTOR*
1774 secsd(char *file, char sdrock[SD_ROCK])
1775 {
1776 	DWORD need;
1777 	SECURITY_DESCRIPTOR *sd;
1778 	char *path, pathrock[6];
1779 	wchar_t *wpath;
1780 
1781 	path = file;
1782 	if(path[0] != '\0' && path[1] == ':' && path[2] == '\0'){
1783 		path = pathrock;
1784 		strcpy(path, "?:\\.");
1785 		path[0] = file[0];
1786 	}
1787 	sd = (SECURITY_DESCRIPTOR*)sdrock;
1788 	need = 0;
1789 	wpath = widen(path);
1790 	if(GetFileSecurity(wpath, OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, sd, SD_ROCK, &need)) {
1791 		free(wpath);
1792 		return sd;
1793 	}
1794 	 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
1795 		free(wpath);
1796 		return nil;
1797 	}
1798 	sd = malloc(need);
1799 	if(sd == nil) {
1800 		free(wpath);
1801 		error(Enomem);
1802 	}
1803 	if(GetFileSecurity(wpath, OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, sd, need, &need)) {
1804 		free(wpath);
1805 		return sd;
1806 	}
1807 	free(wpath);
1808 	free(sd);
1809 	return nil;
1810 }
1811 
1812 static int
1813 secsdstat(SECURITY_DESCRIPTOR *sd, Stat *st, Rune *srv)
1814 {
1815 	ACL *acl;
1816 	BOOL hasacl, b;
1817 	ACE_HEADER *aceh;
1818 	User *owner, *group;
1819 	SID *sid, *osid, *gsid;
1820 	ACCESS_ALLOWED_ACE *ace;
1821 	int i, allow, deny, *p, m;
1822 	ACL_SIZE_INFORMATION size;
1823 
1824 	st->mode = 0;
1825 
1826 	osid = nil;
1827 	gsid = nil;
1828 	if(!GetSecurityDescriptorOwner(sd, &osid, &b)
1829 	|| !GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b))
1830 		return 0;
1831 
1832 	if(acl == 0)
1833 		size.AceCount = 0;
1834 	else if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation))
1835 		return 0;
1836 
1837 	/*
1838 	 * first pass through acl finds group
1839 	 */
1840 	for(i = 0; i < size.AceCount; i++){
1841 		if(!GetAce(acl, i, &aceh))
1842 			continue;
1843 		if(aceh->AceFlags & INHERIT_ONLY_ACE)
1844 			continue;
1845 
1846 		if(aceh->AceType != ACCESS_ALLOWED_ACE_TYPE
1847 		&& aceh->AceType != ACCESS_DENIED_ACE_TYPE)
1848 			continue;
1849 
1850 		ace = (ACCESS_ALLOWED_ACE*)aceh;
1851 		sid = (SID*)&ace->SidStart;
1852 		if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
1853 			continue;
1854 
1855 		if(EqualSid(sid, everyone))
1856 			;
1857 		else if(EqualSid(sid, osid))
1858 			;
1859 		else if(EqualPrefixSid(sid, ntignore))
1860 			continue;		/* boring nt accounts */
1861 		else{
1862 			gsid = sid;
1863 			break;
1864 		}
1865 	}
1866 	if(gsid == nil)
1867 		gsid = osid;
1868 
1869 	owner = sidtouser(srv, osid);
1870 	group = sidtouser(srv, gsid);
1871 	if(owner == 0 || group == 0)
1872 		return 0;
1873 
1874 	/* no acl means full access */
1875 	allow = 0;
1876 	if(acl == 0)
1877 		allow = 0777;
1878 	deny = 0;
1879 	for(i = 0; i < size.AceCount; i++){
1880 		if(!GetAce(acl, i, &aceh))
1881 			continue;
1882 		if(aceh->AceFlags & INHERIT_ONLY_ACE)
1883 			continue;
1884 
1885 		if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE)
1886 			p = &allow;
1887 		else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE)
1888 			p = &deny;
1889 		else
1890 			continue;
1891 
1892 		ace = (ACCESS_ALLOWED_ACE*)aceh;
1893 		sid = (SID*)&ace->SidStart;
1894 		if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
1895 			continue;
1896 
1897 		m = 0;
1898 		if(ace->Mask & FILE_EXECUTE)
1899 			m |= 1;
1900 		if(ace->Mask & FILE_WRITE_DATA)
1901 			m |= 2;
1902 		if(ace->Mask & FILE_READ_DATA)
1903 			m |= 4;
1904 
1905 		if(ismembersid(srv, owner, sid))
1906 			*p |= (m << 6) & ~(allow|deny) & 0700;
1907 		if(ismembersid(srv, group, sid))
1908 			*p |= (m << 3) & ~(allow|deny) & 0070;
1909 		if(EqualSid(everyone, sid))
1910 			*p |= m & ~(allow|deny) & 0007;
1911 	}
1912 
1913 	st->mode = allow & ~deny;
1914 	st->owner = owner;
1915 	st->group = group;
1916 	return 1;
1917 }
1918 
1919 static int
1920 secsdhasperm(SECURITY_DESCRIPTOR *sd, ulong access, Rune *srv)
1921 {
1922 	User *u;
1923 	ACL *acl;
1924 	BOOL hasacl, b;
1925 	ACE_HEADER *aceh;
1926 	SID *sid, *osid, *gsid;
1927 	int i, allow, deny, *p, m;
1928 	ACCESS_ALLOWED_ACE *ace;
1929 	ACL_SIZE_INFORMATION size;
1930 
1931 	u = up->env->ui;
1932 	allow = 0;
1933 	deny = 0;
1934 	osid = nil;
1935 	gsid = nil;
1936 	if(!GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b))
1937 		return 0;
1938 
1939 	/* no acl means full access */
1940 	if(acl == 0)
1941 		return 1;
1942 	if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation))
1943 		return 0;
1944 	for(i = 0; i < size.AceCount; i++){
1945 		if(!GetAce(acl, i, &aceh))
1946 			continue;
1947 		if(aceh->AceFlags & INHERIT_ONLY_ACE)
1948 			continue;
1949 
1950 		if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE)
1951 			p = &allow;
1952 		else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE)
1953 			p = &deny;
1954 		else
1955 			continue;
1956 
1957 		ace = (ACCESS_ALLOWED_ACE*)aceh;
1958 		sid = (SID*)&ace->SidStart;
1959 		if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
1960 			continue;
1961 
1962 		m = ace->Mask;
1963 
1964 		if(ismembersid(srv, u, sid))
1965 			*p |= m & ~(allow|deny);
1966 	}
1967 
1968 	allow &= ~deny;
1969 
1970 	return (allow & access) == access;
1971 }
1972 
1973 static SECURITY_DESCRIPTOR*
1974 secmksd(char *sdrock, Stat *st, ACL *dacl, int isdir)
1975 {
1976 	int m;
1977 
1978 	ulong mode;
1979 	ACE_HEADER *aceh;
1980 	SECURITY_DESCRIPTOR *sd;
1981 
1982 	sd = (SECURITY_DESCRIPTOR*)sdrock;
1983 	if(!InitializeAcl(dacl, ACL_ROCK, ACL_REVISION))
1984 		return nil;
1985 
1986 	mode = st->mode;
1987 	if(st->owner == st->group){
1988 		mode |= (mode >> 3) & 0070;
1989 		mode |= (mode << 3) & 0700;
1990 	}
1991 
1992 
1993 	m = modetomask[(mode>>6) & 7];
1994 	if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, st->owner->sid))
1995 		return nil;
1996 
1997 	if(isdir && !AddAccessAllowedAce(dacl, ACL_REVISION, m, creatorowner))
1998 		return nil;
1999 
2000 	m = modetomask[(mode>>3) & 7];
2001 	if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, st->group->sid))
2002 		return nil;
2003 
2004 	m = modetomask[(mode>>0) & 7];
2005 	if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, everyone))
2006 		return nil;
2007 
2008 	if(isdir){
2009 		/* hack to add inherit flags */
2010 		if(!GetAce(dacl, 1, &aceh))
2011 			return nil;
2012 		aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
2013 		if(!GetAce(dacl, 2, &aceh))
2014 			return nil;
2015 		aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
2016 		if(!GetAce(dacl, 3, &aceh))
2017 			return nil;
2018 		aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
2019 	}
2020 
2021 	/*
2022 	 * allow server user to access any file
2023 	 */
2024 	if(isserver){
2025 		if(!AddAccessAllowedAce(dacl, ACL_REVISION, RMODE|WMODE|XMODE, fsuser->sid))
2026 			return nil;
2027 		if(isdir){
2028 			if(!GetAce(dacl, 4, &aceh))
2029 				return nil;
2030 			aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
2031 		}
2032 	}
2033 
2034 	if(!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION))
2035 		return nil;
2036 	if(!SetSecurityDescriptorDacl(sd, 1, dacl, 0))
2037 		return nil;
2038 //	if(isserver && !SetSecurityDescriptorOwner(sd, st->owner->sid, 0))
2039 //		return nil;
2040 	return sd;
2041 }
2042 
2043 /*
2044  * the user manipulation routines
2045  * just make it easier to deal with user identities
2046  */
2047 static User*
2048 sidtouser(Rune *srv, SID *s)
2049 {
2050 	SID_NAME_USE type;
2051 	Rune aname[100], dname[100];
2052 	DWORD naname, ndname;
2053 	User *u;
2054 
2055 	qlock(&users.lk);
2056 	for(u = users.u; u != 0; u = u->next)
2057 		if(EqualSid(s, u->sid))
2058 			break;
2059 	qunlock(&users.lk);
2060 
2061 	if(u != 0)
2062 		return u;
2063 
2064 	naname = sizeof(aname);
2065 	ndname = sizeof(dname);
2066 
2067 	if(!LookupAccountSidW(srv, s, aname, &naname, dname, &ndname, &type))
2068 		return nil;
2069 	return mkuser(s, type, aname, dname);
2070 }
2071 
2072 static User*
2073 domnametouser(Rune *srv, Rune *name, Rune *dom)
2074 {
2075 	User *u;
2076 
2077 	qlock(&users.lk);
2078 	for(u = users.u; u != 0; u = u->next)
2079 		if(runescmp(name, u->name) == 0 && runescmp(dom, u->dom) == 0)
2080 			break;
2081 	qunlock(&users.lk);
2082 	if(u == 0)
2083 		u = nametouser(srv, name);
2084 	return u;
2085 }
2086 
2087 static User*
2088 nametouser(Rune *srv, Rune *name)
2089 {
2090 	char sidrock[MAX_SID];
2091 	SID *sid;
2092 	SID_NAME_USE type;
2093 	Rune dom[MAX_PATH];
2094 	DWORD nsid, ndom;
2095 
2096 	sid = (SID*)sidrock;
2097 	nsid = sizeof(sidrock);
2098 	ndom = sizeof(dom);
2099 	if(!LookupAccountNameW(srv, name, sid, &nsid, dom, &ndom, &type))
2100 		return nil;
2101 
2102 	return mkuser(sid, type, name, dom);
2103 }
2104 
2105 /*
2106  * this mapping could be cached
2107  */
2108 static User*
2109 unametouser(Rune *srv, char *name)
2110 {
2111 	Rune rname[MAX_PATH];
2112 
2113 	utftorunes(rname, name, MAX_PATH);
2114 	return nametouser(srv, rname);
2115 }
2116 
2117 /*
2118  * make a user structure and add it to the global cache.
2119  */
2120 static User*
2121 mkuser(SID *sid, int type, Rune *name, Rune *dom)
2122 {
2123 	User *u;
2124 
2125 	qlock(&users.lk);
2126 	for(u = users.u; u != 0; u = u->next){
2127 		if(EqualSid(sid, u->sid)){
2128 			qunlock(&users.lk);
2129 			return u;
2130 		}
2131 	}
2132 
2133 	switch(type) {
2134 	default:
2135 		break;
2136 	case SidTypeDeletedAccount:
2137 		name = L"deleted";
2138 		break;
2139 	case SidTypeInvalid:
2140 		name = L"invalid";
2141 		break;
2142 	case SidTypeUnknown:
2143 		name = L"unknown";
2144 		break;
2145 	}
2146 
2147 	u = malloc(sizeof(User));
2148 	if(u == nil){
2149 		qunlock(&users.lk);
2150 		return 0;
2151 	}
2152 	u->next = nil;
2153 	u->group = nil;
2154 	u->sid = dupsid(sid);
2155 	u->type = type;
2156 	u->name = nil;
2157 	if(name != nil)
2158 		u->name = runesdup(name);
2159 	u->dom = nil;
2160 	if(dom != nil)
2161 		u->dom = runesdup(dom);
2162 
2163 	u->next = users.u;
2164 	users.u = u;
2165 
2166 	qunlock(&users.lk);
2167 	return u;
2168 }
2169 
2170 /*
2171  * check if u is a member of gsid,
2172  * which might be a group.
2173  */
2174 static int
2175 ismembersid(Rune *srv, User *u, SID *gsid)
2176 {
2177 	User *g;
2178 
2179 	if(EqualSid(u->sid, gsid))
2180 		return 1;
2181 
2182 	g = sidtouser(srv, gsid);
2183 	if(g == 0)
2184 		return 0;
2185 	return ismember(u, g);
2186 }
2187 
2188 static int
2189 ismember(User *u, User *g)
2190 {
2191 	Gmem *grps;
2192 
2193 	if(EqualSid(u->sid, g->sid))
2194 		return 1;
2195 
2196 	if(EqualSid(g->sid, everyone))
2197 		return 1;
2198 
2199 	qlock(&u->lk);
2200 	addgroups(u, 0);
2201 	for(grps = u->group; grps != 0; grps = grps->next){
2202 		if(EqualSid(grps->user->sid, g->sid)){
2203 			qunlock(&u->lk);
2204 			return 1;
2205 		}
2206 	}
2207 	qunlock(&u->lk);
2208 	return 0;
2209 }
2210 
2211 /*
2212  * find out what groups a user belongs to.
2213  * if force, throw out the old info and do it again.
2214  *
2215  * note that a global group is also know as a group,
2216  * and a local group is also know as an alias.
2217  * global groups can only contain users.
2218  * local groups can contain global groups or users.
2219  * this code finds all global groups to which a user belongs,
2220  * and all the local groups to which the user or a global group
2221  * containing the user belongs.
2222  */
2223 static void
2224 addgroups(User *u, int force)
2225 {
2226 	LOCALGROUP_USERS_INFO_0 *loc;
2227 	GROUP_USERS_INFO_0 *grp;
2228 	DWORD i, n, rem;
2229 	User *gu;
2230 	Gmem *g, *next;
2231 	Rune *srv, srvrock[MAX_PATH];
2232 
2233 	if(force){
2234 		u->gotgroup = 0;
2235 		for(g = u->group; g != nil; g = next){
2236 			next = g->next;
2237 			free(g);
2238 		}
2239 		u->group = nil;
2240 	}
2241 	if(u->gotgroup)
2242 		return;
2243 	u->gotgroup = 1;
2244 
2245 	rem = 1;
2246 	n = 0;
2247 	srv = domsrv(u->dom, srvrock);
2248 	while(rem != n){
2249 		i = net.UserGetGroups(srv, u->name, 0,
2250 			(BYTE**)&grp, 1024, &n, &rem);
2251 		if(i != NERR_Success && i != ERROR_MORE_DATA)
2252 			break;
2253 		for(i = 0; i < n; i++){
2254 			gu = domnametouser(srv, grp[i].grui0_name, u->dom);
2255 			if(gu == 0)
2256 				continue;
2257 			g = malloc(sizeof(Gmem));
2258 			if(g == nil)
2259 				error(Enomem);
2260 			g->user = gu;
2261 			g->next = u->group;
2262 			u->group = g;
2263 		}
2264 		net.ApiBufferFree(grp);
2265 	}
2266 
2267 	rem = 1;
2268 	n = 0;
2269 	while(rem != n){
2270 		i = net.UserGetLocalGroups(srv, u->name, 0, LG_INCLUDE_INDIRECT,
2271 			(BYTE**)&loc, 1024, &n, &rem);
2272 		if(i != NERR_Success && i != ERROR_MORE_DATA)
2273 			break;
2274 		for(i = 0; i < n; i++){
2275 			gu = domnametouser(srv, loc[i].lgrui0_name, u->dom);
2276 			if(gu == NULL)
2277 				continue;
2278 			g = malloc(sizeof(Gmem));
2279 			if(g == nil)
2280 				error(Enomem);
2281 			g->user = gu;
2282 			g->next = u->group;
2283 			u->group = g;
2284 		}
2285 		net.ApiBufferFree(loc);
2286 	}
2287 }
2288 
2289 static SID*
2290 dupsid(SID *sid)
2291 {
2292 	SID *nsid;
2293 	int n;
2294 
2295 	n = GetLengthSid(sid);
2296 	nsid = malloc(n);
2297 	if(nsid == nil || !CopySid(n, nsid, sid))
2298 		panic("can't copy sid");
2299 	return nsid;
2300 }
2301 
2302 /*
2303  * return the name of the server machine for file
2304  */
2305 static Rune*
2306 filesrv(char *file)
2307 {
2308 	int n;
2309 	Rune *srv;
2310 	char *p, uni[MAX_PATH], mfile[MAX_PATH];
2311 	wchar_t vol[3];
2312 
2313 	strcpy(mfile, file);
2314 	/* assume file is a fully qualified name - X: or \\server */
2315 	if(file[1] == ':') {
2316 		vol[0] = file[0];
2317 		vol[1] = file[1];
2318 		vol[2] = 0;
2319 		if(GetDriveType(vol) != DRIVE_REMOTE)
2320 			return 0;
2321 		n = sizeof(uni);
2322 		if(WNetGetUniversalName(vol, UNIVERSAL_NAME_INFO_LEVEL, uni, &n) != NO_ERROR)
2323 			return nil;
2324 		runestoutf(mfile, ((UNIVERSAL_NAME_INFO*)uni)->lpUniversalName, MAX_PATH);
2325 		file = mfile;
2326 	}
2327 	file += 2;
2328 	p = strchr(file, '\\');
2329 	if(p == 0)
2330 		n = strlen(file);
2331 	else
2332 		n = p - file;
2333 	if(n >= MAX_PATH)
2334 		n = MAX_PATH-1;
2335 
2336 	memmove(uni, file, n);
2337 	uni[n] = '\0';
2338 
2339 	srv = malloc((n + 1) * sizeof(Rune));
2340 	if(srv == nil)
2341 		panic("filesrv: no memory");
2342 	utftorunes(srv, uni, n+1);
2343 	return srv;
2344 }
2345 
2346 /*
2347  * does the file system support acls?
2348  */
2349 static int
2350 fsacls(char *file)
2351 {
2352 	char *p;
2353 	DWORD flags;
2354 	char path[MAX_PATH];
2355 	wchar_t wpath[MAX_PATH];
2356 
2357 	/* assume file is a fully qualified name - X: or \\server */
2358 	if(file[1] == ':') {
2359 		path[0] = file[0];
2360 		path[1] = file[1];
2361 		path[2] = '\\';
2362 		path[3] = 0;
2363 	} else {
2364 		strcpy(path, file);
2365 		p = strchr(path+2, '\\');
2366 		if(p == 0)
2367 			return 0;
2368 		p = strchr(p+1, '\\');
2369 		if(p == 0)
2370 			strcat(path, "\\");
2371 		else
2372 			p[1] = 0;
2373 	}
2374 	utftorunes(wpath, path, MAX_PATH);
2375 	if(!GetVolumeInformation(wpath, NULL, 0, NULL, NULL, &flags, NULL, 0))
2376 		return 0;
2377 
2378 	return flags & FS_PERSISTENT_ACLS;
2379 }
2380 
2381 /*
2382  * given a domain, find out the server to ask about its users.
2383  * we just ask the local machine to do the translation,
2384  * so it might fail sometimes.  in those cases, we don't
2385  * trust the domain anyway, and vice versa, so it's not
2386  * clear what benifit we would gain by getting the answer "right".
2387  */
2388 static Rune*
2389 domsrv(Rune *dom, Rune srv[MAX_PATH])
2390 {
2391 	Rune *psrv;
2392 	int n, r;
2393 
2394 	if(dom[0] == 0)
2395 		return nil;
2396 
2397 	r = net.GetAnyDCName(NULL, dom, (LPBYTE*)&psrv);
2398 	if(r == NERR_Success) {
2399 		n = runeslen(psrv);
2400 		if(n >= MAX_PATH)
2401 			n = MAX_PATH-1;
2402 		memmove(srv, psrv, n*sizeof(Rune));
2403 		srv[n] = 0;
2404 		net.ApiBufferFree(psrv);
2405 		return srv;
2406 	}
2407 
2408 	return nil;
2409 }
2410 
2411 Rune*
2412 runesdup(Rune *r)
2413 {
2414 	int n;
2415 	Rune *s;
2416 
2417 	n = runeslen(r) + 1;
2418 	s = malloc(n * sizeof(Rune));
2419 	if(s == nil)
2420 		error(Enomem);
2421 	memmove(s, r, n * sizeof(Rune));
2422 	return s;
2423 }
2424 
2425 int
2426 runeslen(Rune *r)
2427 {
2428 	int n;
2429 
2430 	n = 0;
2431 	while(*r++ != 0)
2432 		n++;
2433 	return n;
2434 }
2435 
2436 char*
2437 runestoutf(char *p, Rune *r, int nc)
2438 {
2439 	char *op, *ep;
2440 	int n, c;
2441 
2442 	op = p;
2443 	ep = p + nc;
2444 	while(c = *r++) {
2445 		n = 1;
2446 		if(c >= Runeself)
2447 			n = runelen(c);
2448 		if(p + n >= ep)
2449 			break;
2450 		if(c < Runeself)
2451 			*p++ = c;
2452 		else
2453 			p += runetochar(p, r-1);
2454 	}
2455 	*p = '\0';
2456 	return op;
2457 }
2458 
2459 Rune*
2460 utftorunes(Rune *r, char *p, int nc)
2461 {
2462 	Rune *or, *er;
2463 
2464 	or = r;
2465 	er = r + nc;
2466 	while(*p != '\0' && r + 1 < er)
2467 		p += chartorune(r++, p);
2468 	*r = '\0';
2469 	return or;
2470 }
2471 
2472 int
2473 runescmp(Rune *s1, Rune *s2)
2474 {
2475 	Rune r1, r2;
2476 
2477 	for(;;) {
2478 		r1 = *s1++;
2479 		r2 = *s2++;
2480 		if(r1 != r2) {
2481 			if(r1 > r2)
2482 				return 1;
2483 			return -1;
2484 		}
2485 		if(r1 == 0)
2486 			return 0;
2487 	}
2488 }
2489 
2490 Dev fsdevtab = {
2491 	'U',
2492 	"fs",
2493 
2494 	fsinit,
2495 	fsattach,
2496 	fswalk,
2497 	fsstat,
2498 	fsopen,
2499 	fscreate,
2500 	fsclose,
2501 	fsread,
2502 	devbread,
2503 	fswrite,
2504 	devbwrite,
2505 	fsremove,
2506 	fswstat
2507 };
2508