- - | 13 May 2010 23:46
Picon

Re: Re: Re: windows disks names

Hello. I'm trying to do the job with windose disks, such as

; bind -b '#U*' /n
; lc /n
c:    d:    e:   (etc.)

; cd /n/c:
; lc
windows        progra~1      (etc.)

Now ls enters an infinite loop when reading folder /n. how to know the system that all data has been read?

that there are:

// devfs.c

//----------------------------------
// void fsinit(void)
// ...
	if(tmp[0] == '*' && tmp[1] == '\0'){
	  rootdir[0] = '*';
	  rootdir[1] = '\0';
	  rootqid.path = fsqidpath(rootdir);
		rootqid.type |= QTDIR;
	  rootqid.vers = time(0);
	  return;
	}
// ...

//---------------------------------------------
(Continue reading)

Gabriel Diaz | 14 May 2010 08:48
Picon

Re: Re: Re: windows disks names

Hello

may be something like styxmon(8) could help? not sure if can be used with something like #U, but i guess at least you can do


% bind -b '#Uc:' /n/disk
% mount {styxmon {export /n/disk}} /n/local
Tmsg.Version(65535,8216,"9P2000")
Rmsg.Version(65535,8216,"9P2000")
Tmsg.Attach(1,102,4294967295,"gdiaz","")
Rmsg.Attach(1,Qid(16r75f1b1b3,1270707523,16r80))
% ls /n/local
Tmsg.Stat(1,102)
Rmsg.Stat(1,Dir(".","gdiaz","admin",Qid(16rb3d47c22,1273819353,16r80),8r20000000770,1273819353,1273819353,0,16r55,7))
Tmsg.Walk(1,102,105,nil)
Rmsg.Walk(1,array[] of {})
Tmsg.Open(1,105,0)
Rmsg.Open(1,Qid(16r763,0,16r80),8192)
Tmsg.Read(1,105,0,2048)
Rmsg.Read(1,array[2005] of byte)
Tmsg.Read(1,105,2005,2048)
Rmsg.Read(1,array[1339] of byte)
Tmsg.Read(1,105,3344,2048)
Rmsg.Read(1,array[0] of byte)
Tmsg.Clunk(1,105)
Rmsg.Clunk(1)
... ls here...

i guess that could give you at least where is hanging ls?

Rmsg.Read(1,array[0] of byte) this message marks the end no (read 0)?


i tried your devfs.c but here "just" crashes if i try 

% bind -b '#U*' /n/disk; ls /n/disk

slds.

gabi


On Thu, May 13, 2010 at 11:46 PM, - - <basmp-Q+l+L2ir60w@public.gmane.org> wrote:
Hello. I'm trying to do the job with windose disks, such as

; bind -b '#U*' /n
; lc /n
c:    d:    e:   (etc.)

; cd /n/c:
; lc
windows        progra~1      (etc.)

Now ls enters an infinite loop when reading folder /n. how to know the system that all data has been read?

that there are:

// devfs.c

//----------------------------------
// void fsinit(void)
// ...
       if(tmp[0] == '*' && tmp[1] == '\0'){
         rootdir[0] = '*';
         rootdir[1] = '\0';
         rootqid.path = fsqidpath(rootdir);
               rootqid.type |= QTDIR;
         rootqid.vers = time(0);
         return;
       }
// ...

//---------------------------------------------
// Chan* fsattach(char *spec)
// ...
       if (!emptystr(drive) &&
               (
                       (drive[0] != '*' || drive[1] != '\0') &&
                       (drive[1] != ':' || drive[2] != '\0')
               )
       )
               error(Ebadspec);
// ...

//-----------------------------------------------------
// long fsdirread(Chan *c, uchar *va, int count, vlong offset)
// ...
       if(FS(c)->spec[0] == '*' && FS(c)->spec[1] == '\0')
               return fsdisksread(c, va, count, offset);
// ...

//----------------------------------------------------------
typedef struct {
       TCHAR *disks_names;
       DWORD len;
       int offset;
} disks_names_t;

static long
fsdisksread(Chan *c, uchar *va, int count, vlong offset)
{
       int i, r;
       DWORD len;
       TCHAR *disks_names, *p;
       char path[MAX_PATH];
       disks_names_t *dsks;

       if(FS(c)->offset == 0){
               len = GetLogicalDriveStrings(0, 0);
               if(!len)
                       return 0;

               disks_names = smalloc(len + 4);
               dsks = smalloc(sizeof(disks_names_t));
               dsks->disks_names = disks_names;
               dsks->len = len;
               dsks->offset = 0;

               if(!GetLogicalDriveStrings(len, disks_names)){
                       free(disks_names);
                       free(dsks);
                       return 0;
               }
               FS(c)->fd = (int)dsks;
       }else{
               dsks = (disks_names_t*)FS(c)->fd;
               if(!dsks)
                       return nil;
               disks_names = dsks->disks_names;
               len = dsks->len;
       }

       i = 0;
       for(    (int)p = (int)disks_names + dsks->offset;
               1;
               (int)p = (int)disks_names + dsks->offset)
       {
               if((DWORD)p >= (DWORD)disks_names + len){
                       free(disks_names);
                       free(dsks);
                       FS(c)->fd = 0;
                       break;
               }

               runestoutf(path, p, (int)&path[MAX_PATH-1] - (int)path);
               path[MAX_PATH-1] = '\0';

               { // from fsdirset(char *edir, int n, WIN32_FIND_DATA *data, char *path, Chan *c, int isdir)
                       Dir dir;
                       char *edir = va + i;
                       int n = count - i;
                       static char neveryone[] = "Everyone";

                       dir.name = narrowen(p);
                       dir.muid = nil;
                       dir.qid.path = fsqidpath(path);
                       dir.qid.vers = 0;
                       dir.qid.type = QTDIR;
                       dir.atime = 0; //unixtime(0);
                       dir.mtime = 0; //unixtime(0);
                       dir.qid.vers = dir.mtime;
                       dir.length = 0;
                       dir.type = 'U';
                       dir.dev = c->dev;

                       /* no NT security so make something up */
                       dir.uid = neveryone;
                       dir.gid = neveryone;
                       dir.mode = 0777;

                       dir.mode |= DMDIR;

                       if(isdir && sizeD2M(&dir) > n)
                               n = -1;
                       else
                               n = convD2M(&dir, edir, n);
                       free(dir.name);
                       r = n;
               }
               if(r <= 0){
                       free(disks_names);
                       free(dsks);
                       FS(c)->fd = 0;
                       break;
               }
               i += r;
               FS(c)->offset += r;

               for(; *p; p++);
               p++;

               dsks->offset = (int)p - (int)disks_names;
       }

       return i;
}


full devfs.c in the attach.

thanks

03.04.10, 17:17, "Charles Forsyth" <forsyth-SXSQbKlNroUXhy9q4Lf3Ug@public.gmane.org>:

> yes, it probably should do something along the lines you suggest.
>  the existing scheme was a fairly arbitrary choice

#define UNICODE
#define Unknown win_Unknown
#include        <windows.h>
#include        <winbase.h>
#undef Unknown
#undef  Sleep
#include        "dat.h"
#include        "fns.h"
#include        "error.h"
#include        <lm.h>

/* TODO: try using / in place of \ in path names */

#ifndef SID_MAX_SUB_AUTHORITIES
#define SID_MAX_SUB_AUTHORITIES 15
#endif

enum
{
       MAX_SID         = sizeof(SID) + SID_MAX_SUB_AUTHORITIES*sizeof(DWORD),
       ACL_ROCK        = sizeof(ACL) + 20*(sizeof(ACCESS_ALLOWED_ACE)+MAX_SID),
       SD_ROCK         = SECURITY_DESCRIPTOR_MIN_LENGTH + MAX_SID + ACL_ROCK,
       MAXCOMP         = 128,
};

typedef struct  User    User;
typedef struct  Gmem    Gmem;
typedef struct  Stat    Stat;
typedef struct Fsinfo   Fsinfo;
typedef WIN32_FIND_DATA Fsdir;

#ifndef INVALID_SET_FILE_POINTER
#define INVALID_SET_FILE_POINTER        ((DWORD)-1)
#endif

struct Fsinfo
{
       int     uid;
       int     gid;
       int     mode;
       int     fd;
       vlong   offset;
       QLock   oq;
       char*   spec;
       Rune*   srv;
       Cname*  name;   /* Windows' idea of the file name */
       ushort  usesec;
       ushort  checksec;
       Fsdir*  de;     /* non-nil for saved entry from last dirread at offset */
};
#define FS(c)   ((Fsinfo*)(c)->aux)

/*
 * info about a user or group
 * there are two ways to specify a user:
 *      by sid, a unique identifier
 *      by user and domain names
 * this structure is used to convert between the two,
 * as well as figure out which groups a users belongs to.
 * the user information never gets thrown away,
 * but the group information gets refreshed with each setid.
 */
struct User
{
       QLock   lk;             /* locks the gotgroup and group fields */
       SID     *sid;
       Rune    *name;
       Rune    *dom;
       int     type;           /* the type of sid, ie SidTypeUser, SidTypeAlias, ... */
       int     gotgroup;       /* tried to add group */
       Gmem    *group;         /* global and local groups to which this user or group belongs. */
       User    *next;
};

struct Gmem
{
       User    *user;
       Gmem    *next;
};

/*
 * intermediate stat information
 */
struct Stat
{
       User    *owner;
       User    *group;
       ulong   mode;
};

/*
 * some "well-known" sids
 */
static  SID     *creatorowner;
static  SID     *creatorgroup;
static  SID     *everyone;
static  SID     *ntignore;
static  SID     *ntroot;        /* user who is supposed to run emu as a server */

/*
 * all users we ever see end up in this table
 * users are never deleted, but we should update
 * group information for users sometime
 */
static struct
{
       QLock   lk;
       User    *u;
}users;

/*
 * conversion from inferno permission modes to nt access masks
 * is this good enough?  this is what nt sets, except for NOMODE
 */
#define NOMODE  (READ_CONTROL|FILE_READ_EA|FILE_READ_ATTRIBUTES)
#define RMODE   (READ_CONTROL|SYNCHRONIZE\
               |FILE_READ_DATA|FILE_READ_EA|FILE_READ_ATTRIBUTES)
#define XMODE   (READ_CONTROL|SYNCHRONIZE\
               |FILE_EXECUTE|FILE_READ_ATTRIBUTES)
#define WMODE   (DELETE|READ_CONTROL|SYNCHRONIZE|WRITE_DAC|WRITE_OWNER\
               |FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_WRITE_EA\
               |FILE_DELETE_CHILD|FILE_WRITE_ATTRIBUTES)

static  int
modetomask[] =
{
       NOMODE,
       XMODE,
       WMODE,
       WMODE|XMODE,
       RMODE,
       RMODE|XMODE,
       RMODE|WMODE,
       RMODE|WMODE|XMODE,
};

extern  DWORD   PlatformId;
       char    rootdir[MAXROOT] = "\\inferno";
       Rune    rootname[] = L"inferno-server";
static  Qid     rootqid;
static  User    *fsnone;
static  User    *fsuser;
static  Rune    *ntsrv;
static  int     usesec;
static  int     checksec;
static  int     isserver;
static  int     file_share_delete;
static  uchar   isntfrog[256];

static  void            fsremove(Chan*);

       wchar_t *widen(char *s);
       char            *narrowen(wchar_t *ws);
       int             widebytes(wchar_t *ws);

static char Etoolong[] = "file name too long";

/*
 * these lan manager functions are not supplied
 * on windows95, so we have to load the dll by hand
 */
static struct {
       NET_API_STATUS (NET_API_FUNCTION *UserGetLocalGroups)(
               LPWSTR servername,
               LPWSTR username,
               DWORD level,
               DWORD flags,
               LPBYTE *bufptr,
               DWORD prefmaxlen,
               LPDWORD entriesread,
               LPDWORD totalentries);
       NET_API_STATUS (NET_API_FUNCTION *UserGetGroups)(
               LPWSTR servername,
               LPWSTR username,
               DWORD level,
               LPBYTE *bufptr,
               DWORD prefmaxlen,
               LPDWORD entriesread,
               LPDWORD totalentries);
       NET_API_STATUS (NET_API_FUNCTION *GetAnyDCName)(
               LPCWSTR ServerName,
               LPCWSTR DomainName,
               LPBYTE *Buffer);
       NET_API_STATUS (NET_API_FUNCTION *ApiBufferFree)(LPVOID Buffer);
} net;

extern  int             nth2fd(HANDLE);
extern  HANDLE          ntfd2h(int);
static  int             cnisroot(Cname*);
static  int             fsisroot(Chan*);
static  int             okelem(char*, int);
static  int             fsexist(char*, Qid*);
static  char*   fspath(Cname*, char*, char*, char*);
static  Cname*  fswalkpath(Cname*, char*, int);
static  char*   fslastelem(Cname*);
static  long            fsdirread(Chan*, uchar*, int, vlong);
static  ulong           fsqidpath(char*);
static  int             fsomode(int);
static  int             fsdirset(char*, int, WIN32_FIND_DATA*, char*, Chan*, int isdir);
static  int             fsdirsize(WIN32_FIND_DATA*, char*, Chan*);
static  void            fssettime(char*, long, long);
static  long            unixtime(FILETIME);
static  FILETIME        wintime(ulong);
static  void            secinit(void);
static  int             secstat(Dir*, char*, Rune*);
static  int             secsize(char*, Rune*);
static  void            seccheck(char*, ulong, Rune*);
static  int             sechasperm(char*, ulong, Rune*);
static  SECURITY_DESCRIPTOR* secsd(char*, char[SD_ROCK]);
static  int             secsdhasperm(SECURITY_DESCRIPTOR*, ulong, Rune*);
static  int             secsdstat(SECURITY_DESCRIPTOR*, Stat*, Rune*);
static  SECURITY_DESCRIPTOR* secmksd(char[SD_ROCK], Stat*, ACL*, int);
static  SID             *dupsid(SID*);
static  int             ismembersid(Rune*, User*, SID*);
static  int             ismember(User*, User*);
static  User            *sidtouser(Rune*, SID*);
static  User            *domnametouser(Rune*, Rune*, Rune*);
static  User            *nametouser(Rune*, Rune*);
static  User            *unametouser(Rune*, char*);
static  void            addgroups(User*, int);
static  User            *mkuser(SID*, int, Rune*, Rune*);
static  Rune            *domsrv(Rune *, Rune[MAX_PATH]);
static  Rune            *filesrv(char*);
static  int             fsacls(char*);
static  User            *secuser(void);

       int             runeslen(Rune*);
       Rune*           runesdup(Rune*);
       Rune*           utftorunes(Rune*, char*, int);
       char*           runestoutf(char*, Rune*, int);
       int             runescmp(Rune*, Rune*);


int
winfilematch(char *path, WIN32_FIND_DATA *data)
{
       char *p;
       wchar_t *wpath;
       int r;

       p = path+strlen(path);
       while(p > path && p[-1] != '\\')
               --p;
       wpath = widen(p);
       r = (data->cFileName[0] == '.' && runeslen(data->cFileName) == 1)
                       || runescmp(data->cFileName, wpath) == 0;
       free(wpath);
       return r;
}

int
winfileclash(char *path)
{
       HANDLE h;
       WIN32_FIND_DATA data;
       wchar_t *wpath;

       wpath = widen(path);
       h = FindFirstFile(wpath, &data);
       free(wpath);
       if (h != INVALID_HANDLE_VALUE) {
               FindClose(h);
               return !winfilematch(path, &data);
       }
       return 0;
}


/*
 * this gets called to set up the environment when we switch users
 */
void
setid(char *name, int owner)
{
       User *u;

       if(owner && !iseve())
               return;

       kstrdup(&up->env->user, name);

       if(!usesec)
               return;

       u = unametouser(ntsrv, up->env->user);
       if(u == nil)
               u = fsnone;
       else {
               qlock(&u->lk);
               addgroups(u, 1);
               qunlock(&u->lk);
       }
       if(u == nil)
               panic("setid: user nil\n");

       up->env->ui = u;
}

static void
fsfree(Chan *c)
{
       cnameclose(FS(c)->name);
       if(FS(c)->de != nil)
               free(FS(c)->de);
       free(FS(c));
}

void
fsinit(void)
{
       int n, isvol;
       ulong attr;
       char *p, tmp[MAXROOT];
       wchar_t *wp, *wpath, *last;
       wchar_t wrootdir[MAXROOT];

       isntfrog['/'] = 1;
       isntfrog['\\'] = 1;
       isntfrog[':'] = 1;
       isntfrog['*'] = 1;
       isntfrog['?'] = 1;
       isntfrog['"'] = 1;
       isntfrog['<'] = 1;
       isntfrog['>'] = 1;

       /*
        * vet the root
        */
       strcpy(tmp, rootdir);

//{*} b
       if(tmp[0] == '*' && tmp[1] == '\0'){
         rootdir[0] = '*';
         rootdir[1] = '\0';
         rootqid.path = fsqidpath(rootdir);
               rootqid.type |= QTDIR;
         rootqid.vers = time(0);
         return;
       }
 //{*} e

       for(p = tmp; *p; p++)
               if(*p == '/')
                       *p = '\\';
       if(tmp[0] != 0 && tmp[1] == ':') {
               if(tmp[2] == 0) {
                       tmp[2] = '\\';
                       tmp[3] = 0;
               }
               else if(tmp[2] != '\\') {
                       /* don't allow c:foo - only c:\foo */
                       panic("illegal root pathX");
               }
       }
       wrootdir[0] = '\0';
       wpath = widen(tmp);
       for(wp = wpath; *wp; wp++) {
               if(*wp < 32 || (*wp < 256 && isntfrog[*wp] && *wp != '\\' && *wp != ':'))
                       panic("illegal root path");
       }
       n = GetFullPathName(wpath, MAXROOT, wrootdir, &last);
       free(wpath);
       runestoutf(rootdir, wrootdir, MAXROOT);
       if(n >= MAXROOT || n == 0)
               panic("illegal root path");

       /* get rid of trailing \ */
       while(rootdir[n-1] == '\\') {
               if(n <= 2) {
                       panic("illegal root path");
               }
               rootdir[--n] = '\0';
       }

       isvol = 0;
       if(rootdir[1] == ':' && rootdir[2] == '\0')
               isvol = 1;
       else if(rootdir[0] == '\\' && rootdir[1] == '\\') {
               p = strchr(&rootdir[2], '\\');
               if(p == nil)
                       panic("inferno root can't be a server");
               isvol = strchr(p+1, '\\') == nil;
       }

       if(strchr(rootdir, '\\') == nil)
               strcat(rootdir, "\\.");
       attr = GetFileAttributes(wrootdir);
       if(attr == 0xFFFFFFFF)
               panic("root path '%s' does not exist", narrowen(wrootdir));
       rootqid.path = fsqidpath(rootdir);
       if(attr & FILE_ATTRIBUTE_DIRECTORY)
               rootqid.type |= QTDIR;
       rootdir[n] = '\0';

       rootqid.vers = time(0);

       /*
        * set up for nt file security checking
        */
       ntsrv = filesrv(rootdir);
       usesec = PlatformId == VER_PLATFORM_WIN32_NT;   /* true for NT and 2000 */
       if(usesec){
               file_share_delete = FILE_SHARE_DELETE;  /* sensible handling of shared files by delete and rename */
               secinit();
               if(!fsacls(rootdir))
                       usesec = 0;
       }
       checksec = usesec && isserver;
}

Chan*
fsattach(char *spec)
{
       Chan *c;
       static int devno;
       static Lock l;
       char *drive = (char *)spec;

       if (!emptystr(drive) &&
               (
//{*} b
                       (drive[0] != '*' || drive[1] != '\0') &&
//{*} e
                       (drive[1] != ':' || drive[2] != '\0')
               )
       )
               error(Ebadspec);

       c = devattach('U', spec);
       lock(&l);
       c->dev = devno++;
       unlock(&l);
       c->qid = rootqid;
       c->aux = smalloc(sizeof(Fsinfo));
       FS(c)->srv = ntsrv;
       if(!emptystr(spec)) {
               char *s = smalloc(strlen(spec)+1);
               strcpy(s, spec);
               FS(c)->spec = s;
               FS(c)->srv = filesrv(spec);
               if(usesec)
                       FS(c)->usesec = fsacls(spec);
               FS(c)->checksec = FS(c)->usesec && isserver;
               c->qid.path = fsqidpath(spec);
               c->qid.type = QTDIR;
               c->qid.vers = 0;
       }else{
               FS(c)->usesec = usesec;
               FS(c)->checksec = checksec;
       }
       FS(c)->name = newcname("/");
       return c;
}

Walkqid*
fswalk(Chan *c, Chan *nc, char **name, int nname)
{
       int j, alloc;
       Walkqid *wq;
       char path[MAX_PATH], *p;
       Cname *ph;
       Cname *current, *next;

       if(nname > 0)
               isdir(c);

       alloc = 0;
       current = nil;
       wq = smalloc(sizeof(Walkqid)+(nname-1)*sizeof(Qid));
       if(waserror()){
               if(alloc && wq->clone != nil)
                       cclose(wq->clone);
               cnameclose(current);
               free(wq);
               return nil;
       }
       if(nc == nil){
               nc = devclone(c);
               nc->type = 0;
               alloc = 1;
       }
       wq->clone = nc;
       current = FS(c)->name;
       if(current != nil)
               incref(&current->r);
       for(j = 0; j < nname; j++){
               if(!(nc->qid.type&QTDIR)){
                       if(j==0)
                               error(Enotdir);
                       break;
               }
               if(!okelem(name[j], 0)){
                       if(j == 0)
                               error(Efilename);
                       break;
               }
               p = fspath(current, name[j], path, FS(c)->spec);
               if(FS(c)->checksec) {
                       *p = '\0';
                       if(!sechasperm(path, XMODE, FS(c)->srv)){
                               if(j == 0)
                                       error(Eperm);
                               break;
                       }
                       *p = '\\';
               }

               if(strcmp(name[j], "..") == 0) {
                       if(fsisroot(c))
                               nc->qid = rootqid;
                       else{
                               ph = fswalkpath(current, "..", 1);
                               if(cnisroot(ph)){
                                       nc->qid = rootqid;
                                       current = ph;
                                       if(current != nil)
                                               incref(&current->r);
                               }
                               else {
                                       fspath(ph, 0, path, FS(c)->spec);
                                       if(!fsexist(path, &nc->qid)){
                                               cnameclose(ph);
                                               if(j == 0)
                                                       error(Enonexist);
                                               break;
                                       }
                               }
                               next = fswalkpath(current, name[j], 1);
                               cnameclose(current);
                               current = next;
                               cnameclose(ph);
                       }
               }
               else{
                       if(!fsexist(path, &nc->qid)){
                               if(j == 0)
                                       error(Enonexist);
                               break;
                       }
                       next = fswalkpath(current, name[j], 1);
                       cnameclose(current);
                       current = next;
               }
               wq->qid[wq->nqid++] = nc->qid;
       }
       poperror();
       if(wq->nqid < nname){
               cnameclose(current);
               if(alloc)
                       cclose(wq->clone);
               wq->clone = nil;
       }else if(wq->clone){
               nc->aux = smalloc(sizeof(Fsinfo));
               nc->type = c->type;
               FS(nc)->spec = FS(c)->spec;
               FS(nc)->srv = FS(c)->srv;
               FS(nc)->name = current;
               FS(nc)->usesec = FS(c)->usesec;
               FS(nc)->checksec = FS(c)->checksec;
       }
       return wq;
}

Chan*
fsopen(Chan *c, int mode)
{
       HANDLE h;
       int m, isdir, aflag, cflag;
       char path[MAX_PATH];
       wchar_t *wpath;

       isdir = c->qid.type & QTDIR;
       if(isdir && mode != OREAD)
               error(Eperm);
       fspath(FS(c)->name, 0, path, FS(c)->spec);

       if(FS(c)->checksec) {
               switch(mode & (OTRUNC|3)) {
               case OREAD:
                       seccheck(path, RMODE, FS(c)->srv);
                       break;
               case OWRITE:
               case OWRITE|OTRUNC:
                       seccheck(path, WMODE, FS(c)->srv);
                       break;
               case ORDWR:
               case ORDWR|OTRUNC:
               case OREAD|OTRUNC:
                       seccheck(path, RMODE|WMODE, FS(c)->srv);
                       break;
               case OEXEC:
                       seccheck(path, XMODE, FS(c)->srv);
                       break;
               default:
                       error(Ebadarg);
               }
       }

       c->mode = openmode(mode);
       if(isdir)
               FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE);
       else {
               m = fsomode(mode & 3);
               cflag = OPEN_EXISTING;
               if(mode & OTRUNC)
                       cflag = TRUNCATE_EXISTING;
               aflag = FILE_FLAG_RANDOM_ACCESS;
               if(mode & ORCLOSE)
                       aflag |= FILE_FLAG_DELETE_ON_CLOSE;
               if (winfileclash(path))
                       error(Eexist);
               wpath = widen(path);
               h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, 0, cflag, aflag, 0);
               free(wpath);
               if(h == INVALID_HANDLE_VALUE)
                       oserror();
               FS(c)->fd = nth2fd(h);
       }

       c->offset = 0;
       FS(c)->offset = 0;
       c->flag |= COPEN;
       return c;
}

void
fscreate(Chan *c, char *name, int mode, ulong perm)
{
       Stat st;
       HANDLE h;
       int m, aflag;
       SECURITY_ATTRIBUTES sa;
       SECURITY_DESCRIPTOR *sd;
       BY_HANDLE_FILE_INFORMATION hi;
       char *p, path[MAX_PATH], sdrock[SD_ROCK];
       wchar_t *wpath;
       ACL *acl;

       if(!okelem(name, 1))
               error(Efilename);

       m = fsomode(mode & 3);
       p = fspath(FS(c)->name, name, path, FS(c)->spec);
       acl = (ACL*)smalloc(ACL_ROCK);
       sd = nil;
       if(FS(c)->usesec) {
               *p = '\0';
               sd = secsd(path, sdrock);
               *p = '\\';
               if(sd == nil){
                       free(acl);
                       oserror();
               }
               if(FS(c)->checksec && !secsdhasperm(sd, WMODE, FS(c)->srv)
               || !secsdstat(sd, &st, FS(c)->srv)){
                       if(sd != (void*)sdrock)
                               free(sd);
                       free(acl);
                       error(Eperm);
               }
               if(sd != (void*)sdrock)
                       free(sd);
               if(perm & DMDIR)
                       st.mode = (perm & ~0777) | (st.mode & perm & 0777);
               else
                       st.mode = (perm & ~0666) | (st.mode & perm & 0666);
               st.owner = up->env->ui;
               if(!isserver)
                       st.owner = fsuser;
               sd = secmksd(sdrock, &st, acl, perm & DMDIR);
               if(sd == nil){
                       free(acl);
                       oserror();
               }
       }
       sa.nLength = sizeof(sa);
       sa.lpSecurityDescriptor = sd;
       sa.bInheritHandle = 0;

       if(perm & DMDIR) {
               if(mode != OREAD) {
                       free(acl);
                       error(Eisdir);
               }
               wpath = widen(path);
               if(!CreateDirectory(wpath, &sa) || !fsexist(path, &c->qid)) {
                       free(wpath);
                       free(acl);
                       oserror();
               }
               free(wpath);
               FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE);
       }
       else {
               aflag = 0;
               if(mode & ORCLOSE)
                       aflag = FILE_FLAG_DELETE_ON_CLOSE;
               if (winfileclash(path))
                       error(Eexist);
               wpath = widen(path);
               h = CreateFile(wpath, m, FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, CREATE_ALWAYS, aflag, 0);
               free(wpath);
               if(h == INVALID_HANDLE_VALUE) {
                       free(acl);
                       oserror();
               }
               FS(c)->fd = nth2fd(h);
               c->qid.path = fsqidpath(path);
               c->qid.type = 0;
               c->qid.vers = 0;
               if(GetFileInformationByHandle(h, &hi))
                       c->qid.vers = unixtime(hi.ftLastWriteTime);
       }

       c->mode = openmode(mode);
       c->offset = 0;
       FS(c)->offset = 0;
       c->flag |= COPEN;
       FS(c)->name = fswalkpath(FS(c)->name, name, 0);
       free(acl);
}

void
fsclose(Chan *c)
{
       HANDLE h;

       if(c->flag & COPEN){
               h = ntfd2h(FS(c)->fd);
               if(h != INVALID_HANDLE_VALUE){
                       if(c->qid.type & QTDIR)
                               FindClose(h);
                       else
                               CloseHandle(h);
               }
       }
       if(c->flag & CRCLOSE){
               if(!waserror()){
                       fsremove(c);
                       poperror();
               }
               return;
       }
       fsfree(c);
}

/*
 * 64-bit seeks, using SetFilePointer because SetFilePointerEx
 * is not supported by NT
 */
static void
fslseek(HANDLE h, vlong offset)
{
       LONG hi;

       if(offset <= 0x7fffffff){
               if(SetFilePointer(h, (LONG)offset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
                       oserror();
       }else{
               hi = offset>>32;
               if(SetFilePointer(h, (LONG)offset, &hi, FILE_BEGIN) == INVALID_SET_FILE_POINTER &&
                  GetLastError() != NO_ERROR)
                       oserror();
       }
}

long
fsread(Chan *c, void *va, long n, vlong offset)
{
       DWORD n2;
       HANDLE h;

       qlock(&FS(c)->oq);
       if(waserror()){
               qunlock(&FS(c)->oq);
               nexterror();
       }
       if(c->qid.type & QTDIR) {
               n2 = fsdirread(c, va, n, offset);
       }
       else {
               h = ntfd2h(FS(c)->fd);
               if(FS(c)->offset != offset){
                       fslseek(h, offset);
                       FS(c)->offset = offset;
               }
               if(!ReadFile(h, va, n, &n2, NULL))
                       oserror();
               FS(c)->offset += n2;
       }
       qunlock(&FS(c)->oq);
       poperror();
       return n2;
}

long
fswrite(Chan *c, void *va, long n, vlong offset)
{
       DWORD n2;
       HANDLE h;

       qlock(&FS(c)->oq);
       if(waserror()){
               qunlock(&FS(c)->oq);
               nexterror();
       }
       h = ntfd2h(FS(c)->fd);
       if(FS(c)->offset != offset){
               fslseek(h, offset);
               FS(c)->offset = offset;
       }
       if(!WriteFile(h, va, n, &n2, NULL))
               oserror();
       FS(c)->offset += n2;
       qunlock(&FS(c)->oq);
       poperror();
       return n2;
}

int
fsstat(Chan *c, uchar *buf, int n)
{
       WIN32_FIND_DATA data;
       char path[MAX_PATH];
       wchar_t *wpath;

       /*
        * have to fake up a data for volumes like
        * c: and \\server\share since you can't FindFirstFile them
        */
       if(fsisroot(c)){
               strcpy(path, rootdir);
               if(strchr(path, '\\') == nil)
                       strcat(path, "\\.");
               wpath = widen(path);
               data.dwFileAttributes = GetFileAttributes(wpath);
               free(wpath);
               if(data.dwFileAttributes == 0xffffffff)
                       oserror();
               data.ftCreationTime =
               data.ftLastAccessTime =
               data.ftLastWriteTime = wintime(time(0));
               data.nFileSizeHigh = 0;
               data.nFileSizeLow = 0;
               utftorunes(data.cFileName, ".", MAX_PATH);
       } else {
               HANDLE h = INVALID_HANDLE_VALUE;

               fspath(FS(c)->name, 0, path, FS(c)->spec);
               if (c->flag & COPEN)
                       h = ntfd2h(FS(c)->fd);

               if (h != INVALID_HANDLE_VALUE) {
                       BY_HANDLE_FILE_INFORMATION fi;
                       if (c->mode & OWRITE)
                               FlushFileBuffers(h);
                       if (!GetFileInformationByHandle(h, &fi))
                               oserror();
                       data.dwFileAttributes = fi.dwFileAttributes;
                       data.ftCreationTime = fi.ftCreationTime;
                       data.ftLastAccessTime = fi.ftLastAccessTime;
                       data.ftLastWriteTime = fi.ftLastWriteTime;;
                       data.nFileSizeHigh = fi.nFileSizeHigh;
                       data.nFileSizeLow = fi.nFileSizeLow;
               } else {
                       wpath = widen(path);
                       h = FindFirstFile(wpath, &data);
                       free(wpath);
                       if(h == INVALID_HANDLE_VALUE)
                               oserror();
                       if (!winfilematch(path, &data)) {
                               FindClose(h);
                               error(Enonexist);
                       }
                       FindClose(h);
               }
               utftorunes(data.cFileName, fslastelem(FS(c)->name), MAX_PATH);
       }

       return fsdirset(buf, n, &data, path, c, 0);
}

int
fswstat(Chan *c, uchar *buf, int n)
{
       int wsd;
       Dir dir;
       Stat st;
       Cname * volatile ph;
       HANDLE h;
       ulong attr;
       User *ou, *gu;
       WIN32_FIND_DATA data;
       SECURITY_DESCRIPTOR *sd;
       char *last, sdrock[SD_ROCK], path[MAX_PATH], newpath[MAX_PATH], strs[4*256];
       wchar_t wspath[MAX_PATH], wsnewpath[MAX_PATH];
       wchar_t *wpath;
       int nmatch;

       n = convM2D(buf, n, &dir, strs);
       if(n == 0)
               error(Eshortstat);

       last = fspath(FS(c)->name, 0, path, FS(c)->spec);
       utftorunes(wspath, path, MAX_PATH);

       if(fsisroot(c)){
               if(dir.atime != ~0)
                       data.ftLastAccessTime = wintime(dir.atime);
               if(dir.mtime != ~0)
                       data.ftLastWriteTime = wintime(dir.mtime);
               utftorunes(data.cFileName, ".", MAX_PATH);
       }else{
               h = FindFirstFile(wspath, &data);
               if(h == INVALID_HANDLE_VALUE)
                       oserror();
               if (!winfilematch(path, &data)) {
                       FindClose(h);
                       error(Enonexist);
               }
               FindClose(h);
       }

       wsd = 0;
       ou = nil;
       gu = nil;
       if(FS(c)->usesec) {
               if(FS(c)->checksec && up->env->ui == fsnone)
                       error(Eperm);

               /*
                * find new owner and group
                */
               if(!emptystr(dir.uid)){
                       ou = unametouser(FS(c)->srv, dir.uid);
                       if(ou == nil)
                               oserror();
               }
               if(!emptystr(dir.gid)){
                       gu = unametouser(FS(c)->srv, dir.gid);
                       if(gu == nil){
                               if(strcmp(dir.gid, "unknown") != 0
                               && strcmp(dir.gid, "deleted") != 0)
                                       oserror();
                               gu = ou;
                       }
               }

               /*
                * find old stat info
                */
               sd = secsd(path, sdrock);
               if(sd == nil || !secsdstat(sd, &st, FS(c)->srv)){
                       if(sd != nil && sd != (void*)sdrock)
                               free(sd);
                       oserror();
               }
               if(sd != (void*)sdrock)
                       free(sd);

               /*
                * permission rules:
                * if none, can't do anything
                * chown => no way
                * chgrp => current owner or group, and in new group
                * mode/time => owner or in either group
                * rename => write in parent
                */
               if(ou == nil)
                       ou = st.owner;
               if(FS(c)->checksec && st.owner != ou)
                       error(Eperm);

               if(gu == nil)
                       gu = st.group;
               if(st.group != gu){
                       if(FS(c)->checksec
                       &&(!ismember(up->env->ui, ou) && !ismember(up->env->ui, gu)
                       || !ismember(up->env->ui, st.group)))
                               error(Eperm);
                       wsd = 1;
               }

               if(dir.atime != ~0 && unixtime(data.ftLastAccessTime) != dir.atime
               || dir.mtime != ~0 && unixtime(data.ftLastWriteTime) != dir.mtime
               || dir.mode != ~0 && st.mode != dir.mode){
                       if(FS(c)->checksec
                       && !ismember(up->env->ui, ou)
                       && !ismember(up->env->ui, gu)
                       && !ismember(up->env->ui, st.group))
                               error(Eperm);
                       if(dir.mode != ~0 && st.mode != dir.mode)
                               wsd = 1;
               }
       }
       wpath = widen(dir.name);
       nmatch = runescmp(wpath, data.cFileName);
       free(wpath);
       if(!emptystr(dir.name) && nmatch != 0){
               if(!okelem(dir.name, 1))
                       error(Efilename);
               ph = fswalkpath(FS(c)->name, "..", 1);
               if(waserror()){
                       cnameclose(ph);
                       nexterror();
               }
               ph = fswalkpath(ph, dir.name, 0);
               fspath(ph, 0, newpath, FS(c)->spec);
               utftorunes(wsnewpath, newpath, MAX_PATH);
               if(GetFileAttributes(wpath) != 0xffffffff && !winfileclash(newpath))
                       error("file already exists");
               if(fsisroot(c))
                       error(Eperm);
               if(FS(c)->checksec){
                       *last = '\0';
                       seccheck(path, WMODE, FS(c)->srv);
                       *last = '\\';
               }
               poperror();
               cnameclose(ph);
       }

       if(dir.atime != ~0 && unixtime(data.ftLastAccessTime) != dir.atime
       || dir.mtime != ~0 && unixtime(data.ftLastWriteTime) != dir.mtime)
               fssettime(path, dir.atime, dir.mtime);

       attr = data.dwFileAttributes;
       if(dir.mode & 0222)
               attr &= ~FILE_ATTRIBUTE_READONLY;
       else
               attr |= FILE_ATTRIBUTE_READONLY;
       if(!fsisroot(c)
       && attr != data.dwFileAttributes
       && (attr & FILE_ATTRIBUTE_READONLY))
               SetFileAttributes(wspath, attr);
       if(FS(c)->usesec && wsd){
               ACL *acl = (ACL *) smalloc(ACL_ROCK);
               st.owner = ou;
               st.group = gu;
               if(dir.mode != ~0)
                       st.mode = dir.mode;
               sd = secmksd(sdrock, &st, acl, data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
               if(sd == nil || !SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd)){
                       free(acl);
                       oserror();
               }
               free(acl);
       }

       if(!fsisroot(c)
       && attr != data.dwFileAttributes
       && !(attr & FILE_ATTRIBUTE_READONLY))
               SetFileAttributes(wspath, attr);

       /* do last so path is valid throughout */
       wpath = widen(dir.name);
       nmatch = runescmp(wpath, data.cFileName);
       free(wpath);
       if(!emptystr(dir.name) && nmatch != 0) {
               ph = fswalkpath(FS(c)->name, "..", 1);
               if(waserror()){
                       cnameclose(ph);
                       nexterror();
               }
               ph = fswalkpath(ph, dir.name, 0);
               fspath(ph, 0, newpath, FS(c)->spec);
               utftorunes(wsnewpath, newpath, MAX_PATH);
               /*
                * can't rename if it is open: if this process has it open, close it temporarily.
                */
               if(!file_share_delete && c->flag & COPEN){
                       h = ntfd2h(FS(c)->fd);
                       if(h != INVALID_HANDLE_VALUE)
                               CloseHandle(h); /* woe betide it if ORCLOSE */
                       FS(c)->fd = nth2fd(INVALID_HANDLE_VALUE);
               }
               if(!MoveFile(wspath, wsnewpath)) {
                       oserror();
               } else if(!file_share_delete && c->flag & COPEN) {
                       int     aflag;
                       SECURITY_ATTRIBUTES sa;

                       /* The move succeeded, so open new file to maintain handle */
                       sa.nLength = sizeof(sa);
                       sa.lpSecurityDescriptor = sd;
                       sa.bInheritHandle = 0;
                       if(c->flag & CRCLOSE)
                               aflag = FILE_FLAG_DELETE_ON_CLOSE;
                       h = CreateFile(wsnewpath, fsomode(c->mode & 0x3), FILE_SHARE_READ|FILE_SHARE_WRITE|file_share_delete, &sa, OPEN_EXISTING, aflag, 0);
                       if(h == INVALID_HANDLE_VALUE)
                               oserror();
                       FS(c)->fd = nth2fd(h);
               }
               cnameclose(FS(c)->name);
               poperror();
               FS(c)->name = ph;
       }
       return n;
}

static void
fsremove(Chan *c)
{
       int n;
       char *p, path[MAX_PATH];
       wchar_t wspath[MAX_PATH];

       if(waserror()){
               fsfree(c);
               nexterror();
       }
       if(fsisroot(c))
               error(Eperm);
       p = fspath(FS(c)->name, 0, path, FS(c)->spec);
       utftorunes(wspath, path, MAX_PATH);
       if(FS(c)->checksec){
               *p = '\0';
               seccheck(path, WMODE, FS(c)->srv);
               *p = '\\';
       }
       if(c->qid.type & QTDIR)
               n = RemoveDirectory(wspath);
       else
               n = DeleteFile(wspath);
       if (!n) {
               ulong attr, mode;
               SECURITY_DESCRIPTOR *sd = nil;
               char sdrock[SD_ROCK];
               Stat st;
               int secok;
               attr = GetFileAttributes(wspath);
               if(attr != 0xFFFFFFFF) {
                       if (FS(c)->usesec) {
                               sd = secsd(path, sdrock);
                               secok = (sd != nil) && secsdstat(sd, &st, FS(c)->srv);
                               if (secok) {
                                       ACL *acl = (ACL *) smalloc(ACL_ROCK);
                                       mode = st.mode;
                                       st.mode |= 0660;
                                       sd = secmksd(sdrock, &st, acl, attr & FILE_ATTRIBUTE_DIRECTORY);
                                       if(sd != nil) {
                                               SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd);
                                       }
                                       free(acl);
                                       if(sd != nil && sd != (void*)sdrock)
                                               free(sd);
                                       sd = nil;
                               }
                       }
                       SetFileAttributes(wspath, FILE_ATTRIBUTE_NORMAL);
                       if(c->qid.type & QTDIR)
                               n = RemoveDirectory(wspath);
                       else
                               n = DeleteFile(wspath);
                       if (!n) {
                               if (FS(c)->usesec && secok) {
                                       ACL *acl = (ACL *) smalloc(ACL_ROCK);
                                       st.mode =  mode;
                                       sd = secmksd(sdrock, &st, acl, attr & FILE_ATTRIBUTE_DIRECTORY);
                                       if(sd != nil) {
                                               SetFileSecurity(wspath, DACL_SECURITY_INFORMATION, sd);
                                       }
                                       free(acl);
                               }
                               SetFileAttributes(wspath, attr);
                               if(sd != nil && sd != (void*)sdrock)
                                       free(sd);
                       }
               }
       }
       if(!n)
               oserror();
       poperror();
       fsfree(c);
}

/*
 * check elem for illegal characters /\:*?"<>
 * ... and relatives are also disallowed,
 * since they specify grandparents, which we
 * are not prepared to handle
 */
static int
okelem(char *elem, int nodots)
{
       int c, dots;

       dots = 0;
       while((c = *(uchar*)elem) != 0){
               if(isntfrog[c])
                       return 0;
               if(c == '.' && dots >= 0)
                       dots++;
               else
                       dots = -1;
               elem++;
       }
       if(nodots)
               return dots <= 0;
       return dots <= 2;
}

static int
cnisroot(Cname *c)
{
       return strcmp(c->s, "/") == 0;
}

static int
fsisroot(Chan *c)
{
       return strcmp(FS(c)->name->s, "/") == 0;
}

static char*
fspath(Cname *c, char *ext, char *path, char *spec)
{
       char *p, *last, *rootd;
       int extlen = 0;

       rootd = spec != nil ? spec : rootdir;
       if(ext)
               extlen = strlen(ext) + 1;
       if(strlen(rootd) + extlen >= MAX_PATH)
               error(Etoolong);
       strcpy(path, rootd);
       if(cnisroot(c)){
               if(ext) {
                       strcat(path, "\\");
                       strcat(path, ext);
               }
       }else{
               if(*c->s != '/') {
                       if(strlen(path) + 1 >= MAX_PATH)
                               error(Etoolong);
                       strcat(path, "\\");
               }
               if(strlen(path) + strlen(c->s) + extlen >= MAX_PATH)
                       error(Etoolong);
               strcat(path, c->s);
               if(ext){
                       strcat(path, "\\");
                       strcat(path, ext);
               }
       }
       last = path;
       for(p = path; *p != '\0'; p++){
               if(*p == '/' || *p == '\\'){
                       *p = '\\';
                       last = p;
               }
       }
       return last;
}

extern void cleancname(Cname*);

static Cname *
fswalkpath(Cname *c, char *name, int dup)
{
       if(dup)
               c = newcname(c->s);
       c = addelem(c, name);
       if(isdotdot(name))
               cleancname(c);
       return c;
}

static char *
fslastelem(Cname *c)
{
       char *p;

       p = c->s + c->len;
       while(p > c->s && p[-1] != '/')
               p--;
       return p;
}

static int
fsdirbadentry(WIN32_FIND_DATA *data)
{
       wchar_t *s;

       s = data->cFileName;
       if(s[0] == 0)
               return 1;
       if(s[0] == '.' && (s[1] == 0 || s[1] == '.' && s[2] == 0))
               return 1;

       return 0;
}

static Fsdir*
fsdirent(Chan *c, char *path, Fsdir *data)
{
       wchar_t *wpath;
       HANDLE h;

       h = ntfd2h(FS(c)->fd);
       if(data == nil)
               data = smalloc(sizeof(*data));
       if(FS(c)->offset == 0){
               if(h != INVALID_HANDLE_VALUE)
                       FindClose(h);
               wpath = widen(path);
               h = FindFirstFile(wpath, data);
               free(wpath);
               FS(c)->fd = nth2fd(h);
               if(h == INVALID_HANDLE_VALUE){
                       free(data);
                       return nil;
               }
               if(!fsdirbadentry(data))
                       return data;
       }
       do{
               if(!FindNextFile(h, data)){
                       free(data);
                       return nil;
               }
       }while(fsdirbadentry(data));
       return data;
}

//{*} b
typedef struct {
       TCHAR *disks_names;
       DWORD len;
       int offset;
} disks_names_t;

static long
fsdisksread(Chan *c, uchar *va, int count, vlong offset)
{
       int i, r;
       DWORD len;
       TCHAR *disks_names, *p;
       char path[MAX_PATH];
       disks_names_t *dsks;

       if(FS(c)->offset == 0){
               len = GetLogicalDriveStrings(0, 0);
               if(!len)
                       return 0;

               disks_names = smalloc(len + 4);
               dsks = smalloc(sizeof(disks_names_t));
               dsks->disks_names = disks_names;
               dsks->len = len;
               dsks->offset = 0;

               if(!GetLogicalDriveStrings(len, disks_names)){
                       free(disks_names);
                       free(dsks);
                       return 0;
               }
               FS(c)->fd = (int)dsks;
       }else{
               dsks = (disks_names_t*)FS(c)->fd;
               if(!dsks)
                       return nil;
               disks_names = dsks->disks_names;
               len = dsks->len;
       }

       i = 0;
       for(    (int)p = (int)disks_names + dsks->offset;
               1;
               (int)p = (int)disks_names + dsks->offset)
       {
               if((DWORD)p >= (DWORD)disks_names + len){
                       free(disks_names);
                       free(dsks);
                       FS(c)->fd = 0;
                       break;
               }

               runestoutf(path, p, (int)&path[MAX_PATH-1] - (int)path);
               path[MAX_PATH-1] = '\0';

               { // from fsdirset(char *edir, int n, WIN32_FIND_DATA *data, char *path, Chan *c, int isdir)
                       Dir dir;
                       char *edir = va + i;
                       int n = count - i;
                       static char neveryone[] = "Everyone";

                       dir.name = narrowen(p);
                       dir.muid = nil;
                       dir.qid.path = fsqidpath(path);
                       dir.qid.vers = 0;
                       dir.qid.type = QTDIR;
                       dir.atime = 0; //unixtime(0);
                       dir.mtime = 0; //unixtime(0);
                       dir.qid.vers = dir.mtime;
                       dir.length = 0;
                       dir.type = 'U';
                       dir.dev = c->dev;

                       /* no NT security so make something up */
                       dir.uid = neveryone;
                       dir.gid = neveryone;
                       dir.mode = 0777;

                       dir.mode |= DMDIR;

                       if(isdir && sizeD2M(&dir) > n)
                               n = -1;
                       else
                               n = convD2M(&dir, edir, n);
                       free(dir.name);
                       r = n;
               }
               if(r <= 0){
                       free(disks_names);
                       free(dsks);
                       FS(c)->fd = 0;
                       break;
               }
               i += r;
               FS(c)->offset += r;

               for(; *p; p++);
               p++;

               dsks->offset = (int)p - (int)disks_names;
       }

       return i;
}
//{*} e

static long
fsdirread(Chan *c, uchar *va, int count, vlong offset)
{
       int i, r;
       char path[MAX_PATH], *p;
       Fsdir *de;
       vlong o;

       if(count == 0 || offset < 0)
               return 0;

//{*} b
       if(FS(c)->spec[0] == '*' && FS(c)->spec[1] == '\0')
               return fsdisksread(c, va, count, offset);
//{*} e

       p = fspath(FS(c)->name, "*.*", path, FS(c)->spec);
       p++;
       de = nil;
       if(FS(c)->offset != offset){
               de = FS(c)->de;
               if(FS(c)->de != nil){
                       free(FS(c)->de);
                       FS(c)->de = nil;
               }
               FS(c)->offset = 0;
               for(o = 0; o < offset;){
                       de = fsdirent(c, path, de);
                       if(de == nil){
                               FS(c)->offset = o;
                               return 0;
                       }
                       runestoutf(p, de->cFileName, &path[MAX_PATH]-p);
                       path[MAX_PATH-1] = '\0';
                       o += fsdirsize(de, path, c);
               }
               FS(c)->offset = offset;
       }
       for(i = 0; i < count;){
               if(FS(c)->de != nil){   /* left over from previous read at offset */
                       de = FS(c)->de;
                       FS(c)->de = nil;
               }else{
                       de = fsdirent(c, path, de);
                       if(de == nil)
                               break;
               }
               runestoutf(p, de->cFileName, &path[MAX_PATH]-p);
               path[MAX_PATH-1] = '\0';
               r = fsdirset(va+i, count-i, de, path, c, 1);
               if(r <= 0){
                       /* won't fit; save for next read at this offset */
                       FS(c)->de = de;
                       break;
               }
               i += r;
               FS(c)->offset += r;
       }
       return i;
}

static ulong
fsqidpath(char *p)
{
       ulong h;
       int c;

       h = 0;
       while(*p != '\0'){
               /* force case insensitive file names */
               c = *p++;
               if(c >= 'A' && c <= 'Z')
                       c += 'a'-'A';
               h = h * 19 ^ c;
       }
       return h;
}

/* TO DO: substitute fixed, made-up (unlikely) names for these */
static char* devf[] = { "aux", "com1", "com2", "lpt1", "nul", "prn", nil };

static int
devfile(char *p)
{
       char *s, *t, *u, **ss;

       if((u = strrchr(p, '\\')) != nil)
               u++;
       else if((u = strrchr(p, '/')) != nil)
               u++;
       else
               u = p;
       for(ss = devf; *ss != nil; ss++){
               for(s = *ss, t = u; *s != '\0' && *t != '\0' && *t != '.'; s++, t++)
                       if(*s != *t && *s != *t+'a'-'A')
                               break;
               if(*s == '\0' && (*t == '\0' || *t == '.'))
                       return 1;
       }
       return 0;
}

/*
 * there are other ways to figure out
 * the attributes and times for a file.
 * perhaps they are faster
 */
static int
fsexist(char *p, Qid *q)
{
       HANDLE h;
       WIN32_FIND_DATA data;
       wchar_t *wpath;

       if(devfile(p))
               return 0;
       wpath = widen(p);
       h = FindFirstFile(wpath, &data);
       free(wpath);
       if(h == INVALID_HANDLE_VALUE)
               return 0;
                       if (!winfilematch(p, &data)) {
                               FindClose(h);
                               return 0;
                       }
       FindClose(h);

       q->path = fsqidpath(p);
       q->type = 0;

       if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
               q->type |= QTDIR;

       q->vers = unixtime(data.ftLastWriteTime);

       return 1;
}

static int
fsdirset(char *edir, int n, WIN32_FIND_DATA *data, char *path, Chan *c, int isdir)
{
       Dir dir;
       static char neveryone[] = "Everyone";

       dir.name = narrowen(data->cFileName);
       dir.muid = nil;
       dir.qid.path = fsqidpath(path);
       dir.qid.vers = 0;
       dir.qid.type = 0;
       dir.mode = 0;
       dir.atime = unixtime(data->ftLastAccessTime);
       dir.mtime = unixtime(data->ftLastWriteTime);
       dir.qid.vers = dir.mtime;
       dir.length = ((uvlong)data->nFileSizeHigh<<32) | ((uvlong)data->nFileSizeLow & ~((uvlong)0xFFFFFFFF<<32));
       dir.type = 'U';
       dir.dev = c->dev;

       if(!FS(c)->usesec){
               /* no NT security so make something up */
               dir.uid = neveryone;
               dir.gid = neveryone;
               dir.mode = 0777;
       }else if(!secstat(&dir, path, FS(c)->srv))
               oserror();

       if(data->dwFileAttributes & FILE_ATTRIBUTE_READONLY)
               dir.mode &= ~0222;
       if(data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
               dir.qid.type |= QTDIR;
               dir.mode |= DMDIR;
               dir.length = 0;
       }

       if(isdir && sizeD2M(&dir) > n)
               n = -1;
       else
               n = convD2M(&dir, edir, n);
       if(dir.uid != neveryone)
               free(dir.uid);
       if(dir.gid != neveryone)
               free(dir.gid);
       free(dir.name);
       return n;
}

static int
fsdirsize(WIN32_FIND_DATA *data, char *path, Chan *c)
{
       int i, n;

       n = widebytes(data->cFileName);
       if(!FS(c)->usesec)
               n += 8+8;
       else{
               i = secsize(path, FS(c)->srv);
               if(i < 0)
                       oserror();
               n += i;
       }
       return STATFIXLEN+n;
}

static void
fssettime(char *path, long at, long mt)
{
       HANDLE h;
       FILETIME atime, mtime;
       wchar_t *wpath;

       wpath = widen(path);
       h = CreateFile(wpath, GENERIC_WRITE,
               0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
       free(wpath);
       if(h == INVALID_HANDLE_VALUE)
               return;
       mtime = wintime(mt);
       atime = wintime(at);
       if(!SetFileTime(h, 0, &atime, &mtime)){
               CloseHandle(h);
               oserror();
       }
       CloseHandle(h);
}

static int
fsomode(int m)
{
       switch(m & 0x3) {
       case OREAD:
       case OEXEC:
               return GENERIC_READ;
       case OWRITE:
               return GENERIC_WRITE;
       case ORDWR:
               return GENERIC_READ|GENERIC_WRITE;
       }
       error(Ebadarg);
       return 0;
}

static long
unixtime(FILETIME ft)
{
       vlong t;

       t = (vlong)ft.dwLowDateTime + ((vlong)ft.dwHighDateTime<<32);
       t -= (vlong)10000000*134774*24*60*60;

       return (long)(t/10000000);
}

static FILETIME
wintime(ulong t)
{
       FILETIME ft;
       vlong vt;

       vt = (vlong)t*10000000+(vlong)10000000*134774*24*60*60;

       ft.dwLowDateTime = vt;
       ft.dwHighDateTime = vt>>32;

       return ft;
}

/*
 * the sec routines manage file permissions for nt.
 * nt files have an associated security descriptor,
 * which has in it an owner, a group,
 * and a discretionary acces control list, or acl,
 * which specifies the permissions for the file.
 *
 * the strategy for mapping between inferno owner,
 * group, other, and mode and nt file security is:
 *
 *      inferno owner == nt file owner
 *      inferno other == nt Everyone
 *      inferno group == first non-owner,
 *                      non-Everyone user given in the acl,
 *                      or the owner if there is no such user.
 * we examine the entire acl when check for permissions,
 * but only report a subset.
 *
 * when we write an acl, we also give all permissions to
 * the special user rootname, who is supposed to run emu in server mode.
 */
static void
secinit(void)
{
       HMODULE lib;
       HANDLE token;
       TOKEN_PRIVILEGES *priv;
       char privrock[sizeof(TOKEN_PRIVILEGES) + 1*sizeof(LUID_AND_ATTRIBUTES)];
       SID_IDENTIFIER_AUTHORITY id = SECURITY_CREATOR_SID_AUTHORITY;
       SID_IDENTIFIER_AUTHORITY wid = SECURITY_WORLD_SID_AUTHORITY;
       SID_IDENTIFIER_AUTHORITY ntid = SECURITY_NT_AUTHORITY;

       lib = LoadLibraryA("netapi32");
       if(lib == 0) {
               usesec = 0;
               return;
       }

       net.UserGetGroups = (void*)GetProcAddress(lib, "NetUserGetGroups");
       if(net.UserGetGroups == 0)
               panic("bad netapi32 library");
       net.UserGetLocalGroups = (void*)GetProcAddress(lib, "NetUserGetLocalGroups");
       if(net.UserGetLocalGroups == 0)
               panic("bad netapi32 library");
       net.GetAnyDCName = (void*)GetProcAddress(lib, "NetGetAnyDCName");
       if(net.GetAnyDCName == 0)
               panic("bad netapi32 library");
       net.ApiBufferFree = (void*)GetProcAddress(lib, "NetApiBufferFree");
       if(net.ApiBufferFree == 0)
               panic("bad netapi32 library");

       if(!AllocateAndInitializeSid(&id, 1,
               SECURITY_CREATOR_OWNER_RID,
               1, 2, 3, 4, 5, 6, 7, &creatorowner)
       || !AllocateAndInitializeSid(&id, 1,
               SECURITY_CREATOR_GROUP_RID,
               1, 2, 3, 4, 5, 6, 7, &creatorgroup)
       || !AllocateAndInitializeSid(&wid, 1,
               SECURITY_WORLD_RID,
               1, 2, 3, 4, 5, 6, 7, &everyone)
       || !AllocateAndInitializeSid(&ntid, 1,
               0,
               1, 2, 3, 4, 5, 6, 7, &ntignore))
               panic("can't initialize well-known sids");

       fsnone = sidtouser(ntsrv, everyone);
       if(fsnone == nil)
               panic("can't make none user");

       /*
        * see if we are running as the emu server user
        * if so, set up SE_RESTORE_NAME privilege,
        * which allows setting the owner field in a security descriptor.
        * other interesting privileges are SE_TAKE_OWNERSHIP_NAME,
        * which enables changing the ownership of a file to yourself
        * regardless of the permissions on the file, SE_BACKUP_NAME,
        * which enables reading any files regardless of permission,
        * and SE_CHANGE_NOTIFY_NAME, which enables walking through
        * directories without X permission.
        * SE_RESTORE_NAME and SE_BACKUP_NAME together allow writing
        * and reading any file data, regardless of permission,
        * if the file is opened with FILE_BACKUP_SEMANTICS.
        */
       isserver = 0;
       fsuser = secuser();
       if(fsuser == nil)
               fsuser = fsnone;
       else if(runescmp(fsuser->name, rootname) == 0
            && OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token)){
               priv = (TOKEN_PRIVILEGES*)privrock;
               priv->PrivilegeCount = 1;
               priv->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
               if(LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &priv->Privileges[0].Luid)
               && AdjustTokenPrivileges(token, 0, priv, 0, NULL, NULL))
                       isserver = 1;
               CloseHandle(token);
       }
}

/*
 * get the User for the executing process
 */
static User*
secuser(void)
{
       DWORD need;
       HANDLE token;
       TOKEN_USER *tu;
       char turock[sizeof(TOKEN_USER) + MAX_SID];

       if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
               return nil;

       tu = (TOKEN_USER*)turock;
       if(!GetTokenInformation(token, TokenUser, tu, sizeof(turock), &need)){
               CloseHandle(token);
               return nil;
       }
       CloseHandle(token);
       return sidtouser(nil, tu->User.Sid);
}

static int
secstat(Dir *dir, char *file, Rune *srv)
{
       int ok, n;
       Stat st;
       char sdrock[SD_ROCK];
       SECURITY_DESCRIPTOR *sd;

       sd = secsd(file, sdrock);
       if(sd == nil){
               int e = GetLastError();
               if(e == ERROR_ACCESS_DENIED || e == ERROR_SHARING_VIOLATION){
                       dir->uid = strdup("unknown");
                       dir->gid = strdup("unknown");
                       if(dir->uid == nil || dir->gid == nil){
                               free(dir->uid);
                               error(Enomem);  /* will change to use kstrdup */
                       }
                       dir->mode = 0;
                       return 1;
               }
               return 0;
       }
       ok = secsdstat(sd, &st, srv);
       if(sd != (void*)sdrock)
               free(sd);
       if(ok){
               dir->mode = st.mode;
               n = runenlen(st.owner->name, runeslen(st.owner->name));
               dir->uid = smalloc(n+1);
               runestoutf(dir->uid, st.owner->name, n+1);
               n = runenlen(st.group->name, runeslen(st.group->name));
               dir->gid = smalloc(n+1);
               runestoutf(dir->gid, st.group->name, n+1);
       }
       return ok;
}

static int
secsize(char *file, Rune *srv)
{
       int ok;
       Stat st;
       char sdrock[SD_ROCK];
       SECURITY_DESCRIPTOR *sd;

       sd = secsd(file, sdrock);
       if(sd == nil){
               int e = GetLastError();
               if(e == ERROR_ACCESS_DENIED || e == ERROR_SHARING_VIOLATION)
                       return 7+7;
               return -1;
       }
       ok = secsdstat(sd, &st, srv);
       if(sd != (void*)sdrock)
               free(sd);
       if(ok)
               return runenlen(st.owner->name, runeslen(st.owner->name))+runenlen(st.group->name, runeslen(st.group->name));
       return -1;
}

/*
 * verify that u had access to file
 */
static void
seccheck(char *file, ulong access, Rune *srv)
{
       if(!sechasperm(file, access, srv))
               error(Eperm);
}

static int
sechasperm(char *file, ulong access, Rune *srv)
{
       int ok;
       char sdrock[SD_ROCK];
       SECURITY_DESCRIPTOR *sd;

       /*
        * only really needs dacl info
        */
       sd = secsd(file, sdrock);
       if(sd == nil)
               return 0;
       ok = secsdhasperm(sd, access, srv);
       if(sd != (void*)sdrock)
               free(sd);
       return ok;
}

static SECURITY_DESCRIPTOR*
secsd(char *file, char sdrock[SD_ROCK])
{
       DWORD need;
       SECURITY_DESCRIPTOR *sd;
       char *path, pathrock[6];
       wchar_t *wpath;

       path = file;
       if(path[0] != '\0' && path[1] == ':' && path[2] == '\0'){
               path = pathrock;
               strcpy(path, "?:\\.");
               path[0] = file[0];
       }
       sd = (SECURITY_DESCRIPTOR*)sdrock;
       need = 0;
       wpath = widen(path);
       if(GetFileSecurity(wpath, OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, sd, SD_ROCK, &need)) {
               free(wpath);
               return sd;
       }
        if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
               free(wpath);
               return nil;
       }
       sd = malloc(need);
       if(sd == nil) {
               free(wpath);
               error(Enomem);
       }
       if(GetFileSecurity(wpath, OWNER_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION, sd, need, &need)) {
               free(wpath);
               return sd;
       }
       free(wpath);
       free(sd);
       return nil;
}

static int
secsdstat(SECURITY_DESCRIPTOR *sd, Stat *st, Rune *srv)
{
       ACL *acl;
       BOOL hasacl, b;
       ACE_HEADER *aceh;
       User *owner, *group;
       SID *sid, *osid, *gsid;
       ACCESS_ALLOWED_ACE *ace;
       int i, allow, deny, *p, m;
       ACL_SIZE_INFORMATION size;

       st->mode = 0;

       osid = nil;
       gsid = nil;
       if(!GetSecurityDescriptorOwner(sd, &osid, &b)
       || !GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b))
               return 0;

       if(acl == 0)
               size.AceCount = 0;
       else if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation))
               return 0;

       /*
        * first pass through acl finds group
        */
       for(i = 0; i < size.AceCount; i++){
               if(!GetAce(acl, i, &aceh))
                       continue;
               if(aceh->AceFlags & INHERIT_ONLY_ACE)
                       continue;

               if(aceh->AceType != ACCESS_ALLOWED_ACE_TYPE
               && aceh->AceType != ACCESS_DENIED_ACE_TYPE)
                       continue;

               ace = (ACCESS_ALLOWED_ACE*)aceh;
               sid = (SID*)&ace->SidStart;
               if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
                       continue;

               if(EqualSid(sid, everyone))
                       ;
               else if(EqualSid(sid, osid))
                       ;
               else if(EqualPrefixSid(sid, ntignore))
                       continue;               /* boring nt accounts */
               else{
                       gsid = sid;
                       break;
               }
       }
       if(gsid == nil)
               gsid = osid;

       owner = sidtouser(srv, osid);
       if(owner == nil)
               return 0;
       group = sidtouser(srv, gsid);
       if(group == nil)
               return 0;

       /* no acl means full access */
       allow = 0;
       if(acl == 0)
               allow = 0777;
       deny = 0;
       for(i = 0; i < size.AceCount; i++){
               if(!GetAce(acl, i, &aceh))
                       continue;
               if(aceh->AceFlags & INHERIT_ONLY_ACE)
                       continue;

               if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE)
                       p = &allow;
               else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE)
                       p = &deny;
               else
                       continue;

               ace = (ACCESS_ALLOWED_ACE*)aceh;
               sid = (SID*)&ace->SidStart;
               if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
                       continue;

               m = 0;
               if(ace->Mask & FILE_EXECUTE)
                       m |= 1;
               if(ace->Mask & FILE_WRITE_DATA)
                       m |= 2;
               if(ace->Mask & FILE_READ_DATA)
                       m |= 4;

               if(ismembersid(srv, owner, sid))
                       *p |= (m << 6) & ~(allow|deny) & 0700;
               if(ismembersid(srv, group, sid))
                       *p |= (m << 3) & ~(allow|deny) & 0070;
               if(EqualSid(everyone, sid))
                       *p |= m & ~(allow|deny) & 0007;
       }

       st->mode = allow & ~deny;
       st->owner = owner;
       st->group = group;
       return 1;
}

static int
secsdhasperm(SECURITY_DESCRIPTOR *sd, ulong access, Rune *srv)
{
       User *u;
       ACL *acl;
       BOOL hasacl, b;
       ACE_HEADER *aceh;
       SID *sid, *osid, *gsid;
       int i, allow, deny, *p, m;
       ACCESS_ALLOWED_ACE *ace;
       ACL_SIZE_INFORMATION size;

       u = up->env->ui;
       allow = 0;
       deny = 0;
       osid = nil;
       gsid = nil;
       if(!GetSecurityDescriptorDacl(sd, &hasacl, &acl, &b))
               return 0;

       /* no acl means full access */
       if(acl == 0)
               return 1;
       if(!GetAclInformation(acl, &size, sizeof(size), AclSizeInformation))
               return 0;
       for(i = 0; i < size.AceCount; i++){
               if(!GetAce(acl, i, &aceh))
                       continue;
               if(aceh->AceFlags & INHERIT_ONLY_ACE)
                       continue;

               if(aceh->AceType == ACCESS_ALLOWED_ACE_TYPE)
                       p = &allow;
               else if(aceh->AceType == ACCESS_DENIED_ACE_TYPE)
                       p = &deny;
               else
                       continue;

               ace = (ACCESS_ALLOWED_ACE*)aceh;
               sid = (SID*)&ace->SidStart;
               if(EqualSid(sid, creatorowner) || EqualSid(sid, creatorgroup))
                       continue;

               m = ace->Mask;

               if(ismembersid(srv, u, sid))
                       *p |= m & ~(allow|deny);
       }

       allow &= ~deny;

       return (allow & access) == access;
}

static SECURITY_DESCRIPTOR*
secmksd(char *sdrock, Stat *st, ACL *dacl, int isdir)
{
       int m;

       ulong mode;
       ACE_HEADER *aceh;
       SECURITY_DESCRIPTOR *sd;

       sd = (SECURITY_DESCRIPTOR*)sdrock;
       if(!InitializeAcl(dacl, ACL_ROCK, ACL_REVISION))
               return nil;

       mode = st->mode;
       if(st->owner == st->group){
               mode |= (mode >> 3) & 0070;
               mode |= (mode << 3) & 0700;
       }


       m = modetomask[(mode>>6) & 7];
       if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, st->owner->sid))
               return nil;

       if(isdir && !AddAccessAllowedAce(dacl, ACL_REVISION, m, creatorowner))
               return nil;

       m = modetomask[(mode>>3) & 7];
       if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, st->group->sid))
               return nil;

       m = modetomask[(mode>>0) & 7];
       if(!AddAccessAllowedAce(dacl, ACL_REVISION, m, everyone))
               return nil;

       if(isdir){
               /* hack to add inherit flags */
               if(!GetAce(dacl, 1, &aceh))
                       return nil;
               aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
               if(!GetAce(dacl, 2, &aceh))
                       return nil;
               aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
               if(!GetAce(dacl, 3, &aceh))
                       return nil;
               aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
       }

       /*
        * allow server user to access any file
        */
       if(isserver){
               if(!AddAccessAllowedAce(dacl, ACL_REVISION, RMODE|WMODE|XMODE, fsuser->sid))
                       return nil;
               if(isdir){
                       if(!GetAce(dacl, 4, &aceh))
                               return nil;
                       aceh->AceFlags |= OBJECT_INHERIT_ACE|CONTAINER_INHERIT_ACE;
               }
       }

       if(!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION))
               return nil;
       if(!SetSecurityDescriptorDacl(sd, 1, dacl, 0))
               return nil;
//      if(isserver && !SetSecurityDescriptorOwner(sd, st->owner->sid, 0))
//              return nil;
       return sd;
}

/*
 * the user manipulation routines
 * just make it easier to deal with user identities
 */
static User*
sidtouser(Rune *srv, SID *s)
{
       SID_NAME_USE type;
       Rune aname[100], dname[100];
       DWORD naname, ndname;
       User *u;

       qlock(&users.lk);
       for(u = users.u; u != 0; u = u->next)
               if(EqualSid(s, u->sid))
                       break;
       qunlock(&users.lk);

       if(u != 0)
               return u;

       naname = sizeof(aname);
       ndname = sizeof(dname);

       if(!LookupAccountSidW(srv, s, aname, &naname, dname, &ndname, &type))
               return mkuser(s, SidTypeUnknown, L"unknown", L"unknown");
       return mkuser(s, type, aname, dname);
}

static User*
domnametouser(Rune *srv, Rune *name, Rune *dom)
{
       User *u;

       qlock(&users.lk);
       for(u = users.u; u != 0; u = u->next)
               if(runescmp(name, u->name) == 0 && runescmp(dom, u->dom) == 0)
                       break;
       qunlock(&users.lk);
       if(u == 0)
               u = nametouser(srv, name);
       return u;
}

static User*
nametouser(Rune *srv, Rune *name)
{
       char sidrock[MAX_SID];
       SID *sid;
       SID_NAME_USE type;
       Rune dom[MAX_PATH];
       DWORD nsid, ndom;

       sid = (SID*)sidrock;
       nsid = sizeof(sidrock);
       ndom = sizeof(dom);
       if(!LookupAccountNameW(srv, name, sid, &nsid, dom, &ndom, &type))
               return nil;

       return mkuser(sid, type, name, dom);
}

/*
 * this mapping could be cached
 */
static User*
unametouser(Rune *srv, char *name)
{
       Rune rname[MAX_PATH];

       utftorunes(rname, name, MAX_PATH);
       return nametouser(srv, rname);
}

/*
 * make a user structure and add it to the global cache.
 */
static User*
mkuser(SID *sid, int type, Rune *name, Rune *dom)
{
       User *u;

       qlock(&users.lk);
       for(u = users.u; u != 0; u = u->next){
               if(EqualSid(sid, u->sid)){
                       qunlock(&users.lk);
                       return u;
               }
       }

       switch(type) {
       default:
               break;
       case SidTypeDeletedAccount:
               name = L"deleted";
               break;
       case SidTypeInvalid:
               name = L"invalid";
               break;
       case SidTypeUnknown:
               name = L"unknown";
               break;
       }

       u = malloc(sizeof(User));
       if(u == nil){
               qunlock(&users.lk);
               return 0;
       }
       u->next = nil;
       u->group = nil;
       u->sid = dupsid(sid);
       u->type = type;
       u->name = nil;
       if(name != nil)
               u->name = runesdup(name);
       u->dom = nil;
       if(dom != nil)
               u->dom = runesdup(dom);

       u->next = users.u;
       users.u = u;

       qunlock(&users.lk);
       return u;
}

/*
 * check if u is a member of gsid,
 * which might be a group.
 */
static int
ismembersid(Rune *srv, User *u, SID *gsid)
{
       User *g;

       if(EqualSid(u->sid, gsid))
               return 1;

       g = sidtouser(srv, gsid);
       if(g == 0)
               return 0;
       return ismember(u, g);
}

static int
ismember(User *u, User *g)
{
       Gmem *grps;

       if(EqualSid(u->sid, g->sid))
               return 1;

       if(EqualSid(g->sid, everyone))
               return 1;

       qlock(&u->lk);
       addgroups(u, 0);
       for(grps = u->group; grps != 0; grps = grps->next){
               if(EqualSid(grps->user->sid, g->sid)){
                       qunlock(&u->lk);
                       return 1;
               }
       }
       qunlock(&u->lk);
       return 0;
}

/*
 * find out what groups a user belongs to.
 * if force, throw out the old info and do it again.
 *
 * note that a global group is also know as a group,
 * and a local group is also know as an alias.
 * global groups can only contain users.
 * local groups can contain global groups or users.
 * this code finds all global groups to which a user belongs,
 * and all the local groups to which the user or a global group
 * containing the user belongs.
 */
static void
addgroups(User *u, int force)
{
       LOCALGROUP_USERS_INFO_0 *loc;
       GROUP_USERS_INFO_0 *grp;
       DWORD i, n, rem;
       User *gu;
       Gmem *g, *next;
       Rune *srv, srvrock[MAX_PATH];

       if(force){
               u->gotgroup = 0;
               for(g = u->group; g != nil; g = next){
                       next = g->next;
                       free(g);
               }
               u->group = nil;
       }
       if(u->gotgroup)
               return;
       u->gotgroup = 1;

       n = 0;
       srv = domsrv(u->dom, srvrock);
       i = net.UserGetGroups(srv, u->name, 0,
               (BYTE**)&grp, MAX_PREFERRED_LENGTH, &n, &rem);
       if(i == NERR_Success || i == ERROR_MORE_DATA){
               for(i = 0; i < n; i++){
                       gu = domnametouser(srv, grp[i].grui0_name, u->dom);
                       if(gu == 0)
                               continue;
                       g = malloc(sizeof(Gmem));
                       if(g == nil)
                               error(Enomem);
                       g->user = gu;
                       g->next = u->group;
                       u->group = g;
               }
               net.ApiBufferFree(grp);
       }

       n = 0;
       i = net.UserGetLocalGroups(srv, u->name, 0, LG_INCLUDE_INDIRECT,
               (BYTE**)&loc, MAX_PREFERRED_LENGTH, &n, &rem);
       if(i == NERR_Success || i == ERROR_MORE_DATA){
               for(i = 0; i < n; i++){
                       gu = domnametouser(srv, loc[i].lgrui0_name, u->dom);
                       if(gu == NULL)
                               continue;
                       g = malloc(sizeof(Gmem));
                       if(g == nil)
                               error(Enomem);
                       g->user = gu;
                       g->next = u->group;
                       u->group = g;
               }
               net.ApiBufferFree(loc);
       }
}

static SID*
dupsid(SID *sid)
{
       SID *nsid;
       int n;

       n = GetLengthSid(sid);
       nsid = malloc(n);
       if(nsid == nil || !CopySid(n, nsid, sid))
               panic("can't copy sid");
       return nsid;
}

/*
 * return the name of the server machine for file
 */
static Rune*
filesrv(char *file)
{
       int n;
       Rune *srv;
       char *p, uni[MAX_PATH], mfile[MAX_PATH];
       wchar_t vol[3];

       strcpy(mfile, file);
       /* assume file is a fully qualified name - X: or \\server */
       if(file[1] == ':') {
               vol[0] = file[0];
               vol[1] = file[1];
               vol[2] = 0;
               if(GetDriveType(vol) != DRIVE_REMOTE)
                       return 0;
               n = sizeof(uni);
               if(WNetGetUniversalName(vol, UNIVERSAL_NAME_INFO_LEVEL, uni, &n) != NO_ERROR)
                       return nil;
               runestoutf(mfile, ((UNIVERSAL_NAME_INFO*)uni)->lpUniversalName, MAX_PATH);
               file = mfile;
       }
       file += 2;
       p = strchr(file, '\\');
       if(p == 0)
               n = strlen(file);
       else
               n = p - file;
       if(n >= MAX_PATH)
               n = MAX_PATH-1;

       memmove(uni, file, n);
       uni[n] = '\0';

       srv = malloc((n + 1) * sizeof(Rune));
       if(srv == nil)
               panic("filesrv: no memory");
       utftorunes(srv, uni, n+1);
       return srv;
}

/*
 * does the file system support acls?
 */
static int
fsacls(char *file)
{
       char *p;
       DWORD flags;
       char path[MAX_PATH];
       wchar_t wpath[MAX_PATH];

       /* assume file is a fully qualified name - X: or \\server */
       if(file[1] == ':') {
               path[0] = file[0];
               path[1] = file[1];
               path[2] = '\\';
               path[3] = 0;
       } else {
               strcpy(path, file);
               p = strchr(path+2, '\\');
               if(p == 0)
                       return 0;
               p = strchr(p+1, '\\');
               if(p == 0)
                       strcat(path, "\\");
               else
                       p[1] = 0;
       }
       utftorunes(wpath, path, MAX_PATH);
       if(!GetVolumeInformation(wpath, NULL, 0, NULL, NULL, &flags, NULL, 0))
               return 0;

       return flags & FS_PERSISTENT_ACLS;
}

/*
 * given a domain, find out the server to ask about its users.
 * we just ask the local machine to do the translation,
 * so it might fail sometimes.  in those cases, we don't
 * trust the domain anyway, and vice versa, so it's not
 * clear what benifit we would gain by getting the answer "right".
 */
static Rune*
domsrv(Rune *dom, Rune srv[MAX_PATH])
{
       Rune *psrv;
       int n, r;

       if(dom[0] == 0)
               return nil;

       r = net.GetAnyDCName(NULL, dom, (LPBYTE*)&psrv);
       if(r == NERR_Success) {
               n = runeslen(psrv);
               if(n >= MAX_PATH)
                       n = MAX_PATH-1;
               memmove(srv, psrv, n*sizeof(Rune));
               srv[n] = 0;
               net.ApiBufferFree(psrv);
               return srv;
       }

       return nil;
}

Rune*
runesdup(Rune *r)
{
       int n;
       Rune *s;

       n = runeslen(r) + 1;
       s = malloc(n * sizeof(Rune));
       if(s == nil)
               error(Enomem);
       memmove(s, r, n * sizeof(Rune));
       return s;
}

int
runeslen(Rune *r)
{
       int n;

       n = 0;
       while(*r++ != 0)
               n++;
       return n;
}

char*
runestoutf(char *p, Rune *r, int nc)
{
       char *op, *ep;
       int n, c;

       op = p;
       ep = p + nc;
       while(c = *r++) {
               n = 1;
               if(c >= Runeself)
                       n = runelen(c);
               if(p + n >= ep)
                       break;
               if(c < Runeself)
                       *p++ = c;
               else
                       p += runetochar(p, r-1);
       }
       *p = '\0';
       return op;
}

Rune*
utftorunes(Rune *r, char *p, int nc)
{
       Rune *or, *er;

       or = r;
       er = r + nc;
       while(*p != '\0' && r + 1 < er)
               p += chartorune(r++, p);
       *r = '\0';
       return or;
}

int
runescmp(Rune *s1, Rune *s2)
{
       Rune r1, r2;

       for(;;) {
               r1 = *s1++;
               r2 = *s2++;
               if(r1 != r2) {
                       if(r1 > r2)
                               return 1;
                       return -1;
               }
               if(r1 == 0)
                       return 0;
       }
}

Dev fsdevtab = {
       'U',
       "fs",

       fsinit,
       fsattach,
       fswalk,
       fsstat,
       fsopen,
       fscreate,
       fsclose,
       fsread,
       devbread,
       fswrite,
       devbwrite,
       fsremove,
       fswstat
};


Charles Forsyth | 14 May 2010 11:47

Re: Re: Re: windows disks names

this message is just to prevent further replies
from including a large chunk of source code.

- - | 15 May 2010 04:08
Picon

Re: Re: Re: Re: windows disks names

Hello. I corrected devfs. Now with a 

bind -b '#U*' /n
ls /n

a list of win disks names. but with 

cd /n/C:

on the disc is not moving yet.

devfs link.
http://basmp.narod.ru/9/devfs.zip

regards

14.05.10, 10:48, "Gabriel Diaz" <gdiaz@...>:
> Hello
> 
> may be something like styxmon(8) could help? not sure if can be used with something like #U, but i guess at
least you can do
> 
> 
> % bind -b '#Uc:' /n/disk
> % mount {styxmon {export /n/disk}} /n/local
> % ls /n/local
> 
> Rmsg.Read(1,array[0] of byte) this message marks the end no (read 0)?
yes

> i tried your devfs.c but here "just" crashes if i try
> 
> % bind -b '#U*' /n/disk; ls /n/disk
fixed

> 
> slds.
> 
> gabi

- - | 6 Jun 2010 19:02
Picon

Re: Re: Re: Re: Re: windows disks names

hello.
now everything works ok

bind [-b |-a] '# U *' <any path>

connects to <any path> list of windose drives. on them and their folders you can navigate and view files, you
can read/write files  (do not try exec)

devfs.c with this
http://basmp.narod.ru/9/devfs.zip

My fix for faster search and check I mark
//{*} b
.
.
//{*} e

due to the fact that the current situation with windose disks extremely inconvenient and not very
compatible with other ports of "emu". and other solutions to this problem have been proposed, or I do not
find these decisions, I would be grateful to Charles Forsyth if, after proper verification and,
possibly, correction, the ability to easily obtain the list of names of windose disks was incorporated
into the main source tree of inferno. 
In order to facilitate the assembly of further versions of inferno for me and for other users of windose port
of inferno wishing to have access to the list of names of windose disks directly from the inferno.

thanks

15.05.10, 06:08, "- -" <basmp@...>:

> Hello. I corrected devfs. Now with a 
>  
>  bind -b '#U*' /n
>  ls /n
>  
>  a list of win disks names. but with 
>  
>  cd /n/C:
>  
>  on the disc is not moving yet.
>  
>  devfs link.
>  http://basmp.narod.ru/9/devfs.zip
>  
>  regards
>  
>  14.05.10, 10:48, "Gabriel Diaz" :
>  > Hello
>  > 
>  > may be something like styxmon(8) could help? not sure if can be used with something like #U, but i guess at
least you can do
>  > 
>  > 
>  > % bind -b '#Uc:' /n/disk
>  > % mount {styxmon {export /n/disk}} /n/local
>  > % ls /n/local
>  > 
>  > Rmsg.Read(1,array[0] of byte) this message marks the end no (read 0)?
>  yes
>  
>  > i tried your devfs.c but here "just" crashes if i try
>  > 
>  > % bind -b '#U*' /n/disk; ls /n/disk
>  fixed
>  
>  > 
>  > slds.
>  > 
>  > gabi
>  
>  

--

-- 
Здесь спама нет http://mail.yandex.ru/nospam/sign

C H Forsyth | 9 Jun 2010 18:30
Favicon

Re: windows disks names

>possibly, correction, the ability to easily obtain the list of names
>of windose disks was incorporated into the main source tree of inferno. 

that seems reasonable


Gmane