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