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