xref: /inferno-os/emu/Nt/devfs.c (revision f60356712426da98d6097355b9a571cf4b13943b)
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