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