From 4b550911d969d0c5930d1eac5a9c8dc15c0595a6 Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Mon, 9 May 2011 10:42:20 +0000 Subject: [PATCH] add cifsd --- sys/man/8/cifsd | 105 +++ sys/src/cmd/ip/cifsd/README | 20 + sys/src/cmd/ip/cifsd/dat.h | 232 +++++ sys/src/cmd/ip/cifsd/dir.c | 218 +++++ sys/src/cmd/ip/cifsd/error.c | 129 +++ sys/src/cmd/ip/cifsd/file.c | 319 +++++++ sys/src/cmd/ip/cifsd/find.c | 191 ++++ sys/src/cmd/ip/cifsd/fns.h | 85 ++ sys/src/cmd/ip/cifsd/main.c | 214 +++++ sys/src/cmd/ip/cifsd/mkfile | 21 + sys/src/cmd/ip/cifsd/pack.c | 305 ++++++ sys/src/cmd/ip/cifsd/rap.c | 143 +++ sys/src/cmd/ip/cifsd/share.c | 124 +++ sys/src/cmd/ip/cifsd/smb.c | 1691 ++++++++++++++++++++++++++++++++++ sys/src/cmd/ip/cifsd/tree.c | 233 +++++ sys/src/cmd/ip/cifsd/util.c | 409 ++++++++ 16 files changed, 4439 insertions(+) create mode 100644 sys/man/8/cifsd create mode 100644 sys/src/cmd/ip/cifsd/README create mode 100644 sys/src/cmd/ip/cifsd/dat.h create mode 100644 sys/src/cmd/ip/cifsd/dir.c create mode 100644 sys/src/cmd/ip/cifsd/error.c create mode 100644 sys/src/cmd/ip/cifsd/file.c create mode 100644 sys/src/cmd/ip/cifsd/find.c create mode 100644 sys/src/cmd/ip/cifsd/fns.h create mode 100644 sys/src/cmd/ip/cifsd/main.c create mode 100644 sys/src/cmd/ip/cifsd/mkfile create mode 100644 sys/src/cmd/ip/cifsd/pack.c create mode 100644 sys/src/cmd/ip/cifsd/rap.c create mode 100644 sys/src/cmd/ip/cifsd/share.c create mode 100644 sys/src/cmd/ip/cifsd/smb.c create mode 100644 sys/src/cmd/ip/cifsd/tree.c create mode 100644 sys/src/cmd/ip/cifsd/util.c diff --git a/sys/man/8/cifsd b/sys/man/8/cifsd new file mode 100644 index 000000000..276421f48 --- /dev/null +++ b/sys/man/8/cifsd @@ -0,0 +1,105 @@ +.TH cifsd 8 +.SH NAME +cifsd \- CIFS/SMB network daemon +.SH SYNOPSIS +.B ip/cifsd +[ +.B -t +] [ +.B -d +] [ +.B -f +.I debuglog +] [ +.B -w +.I name +] [ +.B -o +.I option +] ... [ +.I conndir +] +.SH DESCRIPTION +.I Cifsd +exports filesystems to +.SM CIFS or +.SM SMB +clients like Microsoft \(tm Windows. +.PP +It is normally started by the network listen process via the +.B /rc/bin/service/tcp445 +service script (see +.IR listen (8)), +wich passes +.I conndir +and filedescriptors 0 and 1 to the incoming connection. +.PP +Users are authenticated by ther Inferno/pop secret held by the auth server. +When successfull, +.I cifsd +changes its user id and namespace to the authenticated user. +Informative log messages are appended to +.B /sys/log/cifsd +if it exists. +.PP +By default the share +.B local +is offered, which represents the root of the namespace +described by +.B /lib/namespace. +If a different name is explicitly requested by the client then +.B /bin/9fs +(see +.IR srv (4)) +is invoked to attach that filesystem, which is then exported instead. +.PP +The flags are: +.TP +.B t +Run the server in trusted mode, so it will not require +authentication from the client and keep running in the callers +namespace. +.TP +.B d +Enable or increases debug verbosity for the +.I debuglog +file. Debug messages are never written to the system logfile. +.TP +.B f +Specify the filename for the +.I debuglog +file. If not specified no debug messages are generated. +.TP +.B w +Set the workgroup (or primary domain) to +.I name. The default is +.SM WORKGROUP +.TP +.B o +Enables the following +.I option +string. +This flag can appear multiple times when more than one option has to +be enabled. Valid options are: +.RS +.TP +.B trspaces +transforms whitespaces in filenames to non breaking whitespaces. This is usefull +when exporting filesystems other than fossil. +.TP +.B casesensitive +By default, filename lookups are done case insensitive to match +windows filesystem sematics. This option disables case insensitive +lookups wich can result in a performance improvement, but might break +some applications. +.SH FILES +.B /rc/bin/service/tcp445 +.br +.B /sys/log/cifsd +.br +.B /lib/namespace +.SH SOURCE +.B /sys/src/cmd/ip/cifsd +.SH "SEE ALSO" +.IR listen (8), +.IR srv (4) diff --git a/sys/src/cmd/ip/cifsd/README b/sys/src/cmd/ip/cifsd/README new file mode 100644 index 000000000..77d63d415 --- /dev/null +++ b/sys/src/cmd/ip/cifsd/README @@ -0,0 +1,20 @@ +look at the cifsd.man manpage for instrctions. + +check for new releases on the web: +http://9hal.ath.cx/usr/cinap_lenrek/cifsd.tgz +http://9hal.ath.cx/usr/cinap_lenrek/ + +or on sources: +/n/sources/contrib/cinap_lenrek/cifsd.tgz +/n/sources/contrib/cinap_lenrek + +you can email me for requests/bugreports/contrib at: +cinap_lenrek AT gmx DOT de + +special thanks goes to: + +capso (http://nanosouffle.net/) +for testing and bug reporting + +steve simon (http://quintile.net/) +factotum/authsrv support for NTLM auth and cifsd testing and bug reporting diff --git a/sys/src/cmd/ip/cifsd/dat.h b/sys/src/cmd/ip/cifsd/dat.h new file mode 100644 index 000000000..5e8198cc9 --- /dev/null +++ b/sys/src/cmd/ip/cifsd/dat.h @@ -0,0 +1,232 @@ +typedef struct Rop Rop; +typedef struct Req Req; +typedef struct Trans Trans; + +typedef struct Share Share; +typedef struct File File; +typedef struct Find Find; +typedef struct Tree Tree; + +struct Rop +{ + int (*strpack)(uchar *, uchar *, uchar *, void *); + int (*strunpack)(uchar *, uchar *, uchar *, void *); + int (*namepack)(uchar *, uchar *, uchar *, void *); + int (*nameunpack)(uchar *, uchar *, uchar *, void *); + int (*untermstrpack)(uchar *, uchar *, uchar *, void *); + int (*untermnamepack)(uchar *, uchar *, uchar *, void *); +}; + +struct Req +{ + int cmd; + int tid; + int pid; + int uid; + int mid; + int flags; + int flags2; + + uchar sig[8]; + + uchar *lh, *rh, *rp, *re; + + Rop *o; + char *name; + void (*respond)(Req *r, int err); + int (*namecmp)(char *, char *); +}; + +struct Trans +{ + int cmd; + int flags; + + struct { + struct { + uchar *b, *p, *e; + } param, data, setup; + } in, out; + + Req *r; + Rop *o; + char *name; + void (*respond)(Trans *t, int err); + int (*namecmp)(char *, char *); +}; + +struct File +{ + int ref; + int fd; + int rtype; + int dacc; + char *path; + void *aux; +}; + +struct Find +{ + int ref; + int attr; + char *base; + char *pattern; + int casesensitive; + int index; + Dir *dotdot; + Dir *dot; + Dir *dir; + int ndir; +}; + +struct Share +{ + Share *next; + + char *service; + int stype; + + char *name; + char *root; + char *remark; + + char *fsname; + int namelen; + vlong allocsize; + vlong freesize; + int sectorsize; + int blocksize; +}; + +struct Tree +{ + int tid; + + void **file; + int nfile; + + void **find; + int nfind; + + Share *share; +}; + +int debug; +int trspaces; +int needauth; +char *domain; +char *progname; +char *osname; + +char *remotesys; +char *remoteuser; +int remotebuffersize; + +long starttime; +int tzoff; + +enum +{ + BUFFERSIZE = 0x8000, + + STATUS_INVALID_SMB = 0x00010002, + STATUS_SMB_BAD_TID = 0x00050002, + STATUS_SMB_BAD_FID = 0x00060001, + STATUS_OS2_INVALID_ACCESS = 0x000C0001, + STATUS_SMB_BAD_UID = 0x005B0002, + STATUS_OS2_INVALID_LEVEL = 0x007C0001, + STATUS_NO_MORE_FILES = 0x80000006, + STATUS_INVALID_HANDLE = 0xC0000008, + STATUS_NO_SUCH_FILE = 0xC000000F, + STATUS_ACCESS_DENIED = 0xC0000022, + STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034, + STATUS_OBJECT_NAME_COLLISION = 0xC0000035, + STATUS_OBJECT_PATH_INVALID = 0xC0000039, + STATUS_OBJECT_PATH_NOT_FOUND = 0xC000003A, + STATUS_OBJECT_PATH_SYNTAX_BAD = 0xC000003B, + STATUS_SHARING_VIOLATION = 0xC0000043, + STATUS_LOGON_FAILURE = 0xC000006D, + STATUS_FILE_IS_A_DIRECTORY = 0xC00000BA, + STATUS_NOT_SUPPORTED = 0xC00000BB, + STATUS_BAD_DEVICE_TYPE = 0xC00000CB, + STATUS_BAD_NETWORK_NAME = 0xC00000CC, + STATUS_NOT_SAME_DEVICE = 0xC00000D4, + STATUS_DIRECTORY_NOT_EMPTY = 0xC0000101, + + /* resource type */ + FileTypeDisk = 0, + + /* stype */ + STYPE_DISKTREE = 0, + STYPE_PRINTQ = 1, + STYPE_DEVICE = 2, + STYPE_IPC = 3, + + /* capabilities */ + CAP_UNICODE = 0x4, + CAP_LARGEFILES = 0x8, + CAP_NT_SMBS = 0x10, + CAP_NT_STATUS = 0x40, + CAP_NT_FIND = 0x200, + + /* extended file attributes */ + ATTR_READONLY = 0x1, + ATTR_HIDDEN = 0x2, + ATTR_SYSTEM = 0x4, + ATTR_DIRECTORY = 0x10, + ATTR_ARCHIVE = 0x20, + ATTR_NORMAL = 0x80, + + DOSMASK = 0x37, + + /* access */ + FILE_READ_DATA = 0x1, + FILE_WRITE_DATA = 0x2, + FILE_APPEND_DATA = 0x4, + FILE_EXECUTE = 0x20, + FILE_DELETE = 0x10000, + GENERIC_ALL = 0x10000000, + GENERIC_EXECUTE = 0x20000000, + GENERIC_WRITE = 0x40000000, + GENERIC_READ = 0x80000000, + + READMASK = + FILE_READ_DATA | + FILE_EXECUTE | + GENERIC_ALL | + GENERIC_EXECUTE | + GENERIC_READ, + + WRITEMASK = + FILE_WRITE_DATA | + FILE_APPEND_DATA | + GENERIC_ALL | + GENERIC_WRITE, + + /* share access */ + FILE_SHARE_NONE = 0, + FILE_SHARE_READ = 1, + FILE_SHARE_WRITE = 2, + FILE_SHARE_DELETE = 4, + FILE_SHARE_COMPAT = -1, + + /* createdisposition */ + FILE_SUPERSEDE = 0, + FILE_OPEN, + FILE_CREATE, + FILE_OPEN_IF, + FILE_OVERWRITE, + FILE_OVERWRITE_IF, + + /* createaction */ + FILE_SUPERSEDED = 0, + FILE_OPEND, + FILE_CREATED, + FILE_OVERWRITTEN, + + /* createoptions */ + FILE_DIRECTORY_FILE = 0x1, + FILE_NON_DIRECTORY_FILE = 0x40, + FILE_DELETE_ON_CLOSE = 0x1000, + FILE_OPEN_BY_FILE_ID = 0x2000, +}; diff --git a/sys/src/cmd/ip/cifsd/dir.c b/sys/src/cmd/ip/cifsd/dir.c new file mode 100644 index 000000000..91db8506a --- /dev/null +++ b/sys/src/cmd/ip/cifsd/dir.c @@ -0,0 +1,218 @@ +#include +#include +#include "dat.h" +#include "fns.h" + +static char* +append(char **p, char *s) +{ + int n; + char *o; + + if(s == nil) + return nil; + n = strlen(s)+1; + memmove(o = *p, s, n); + *p += n; + return o; +} + +static Dir* +xdirdup(Dir *d, int n) +{ + char *p; + Dir *o; + int i; + + p = nil; + for(i=0; i 0) + *d = xdirdup(t, n); + else + *d = nil; + return n; +} + +static Dir* +xdirstat0(char **path, int (*namecmp)(char *, char *), char *err) +{ + char *base, *name; + Dir *d, *t; + int n, i; + + if(d = dirstat(*path)) + return d; + if(!splitpath(*path, &base, &name)) + return nil; + if((n = xdirread0(&base, namecmp, &t)) < 0) + goto out; + for(i=0; ipath); + free(x->dir); + free(x); +} + +static int +qidcmp(Qid *q1, Qid *q2) +{ + return (q1->type != q2->type) || (q1->path != q2->path) || (q1->vers != q2->vers); +} + +static XDir *xdirlist; +static int xdircount; + +static int +xdirread0(char **path, int (*namecmp)(char *, char *), Dir **d) +{ + XDir *x, *p; + int fd, n; + Dir *t; + + t = nil; + for(p = nil, x = xdirlist; x; p=x, x=x->next){ + if(namecmp(x->path, *path)) + continue; + if(x == xdirlist) + xdirlist = x->next; + else + p->next = x->next; + while(t = dirstat(x->path)){ + if(qidcmp(&t->qid, &x->qid)) + break; + free(t); + x->next = xdirlist; + xdirlist = x; + if(strcmp(x->path, *path)){ + free(*path); + *path = strdup(x->path); + } + if(d) *d = x->dir; + return x->ndir; + } + xdircount--; + freexdir(x); + break; + } + if((fd = open(*path, OREAD)) < 0){ + free(t); + if(t = xdirstat0(path, namecmp, "directory entry not found")) + fd = open(*path, OREAD); + } else if(t == nil) + t = dirfstat(fd); + + n = -1; + if(fd < 0 || t == nil) + goto out; + if(t->qid.type != QTDIR){ + werrstr("not a directory"); + goto out; + } + if((n = dirreadall(fd, d)) < 0) + goto out; + + if(xdircount >= 8){ + xdircount--; + for(p = xdirlist, x = xdirlist->next; x->next; p = x, x = x->next) + ; + p->next = nil; + freexdir(x); + } + + x = mallocz(sizeof(*x), 1); + x->qid = t->qid; + x->path = strdup(*path); + x->ndir = n; + x->dir = *d; + + x->next = xdirlist; + xdirlist = x; + xdircount++; + +out: + if(fd >= 0) + close(fd); + free(t); + return n; +} + +void +xdirflush(char *path, int (*namecmp)(char *, char *)) +{ + XDir **pp, **xx, *x; + char *d, *s; + int n; + + n = strlen(path); + if(s = strrchr(path, '/')) + n = s - path; + d = smprint("%.*s", n, path); + s = malloc(++n); + for(pp = &xdirlist; x = *pp; pp = xx){ + xx = &x->next; + snprint(s, n, "%s", x->path); + if(namecmp(d, s) == 0){ + *pp = *xx; xx = pp; + xdircount--; + freexdir(x); + } + } + free(s); + free(d); +} diff --git a/sys/src/cmd/ip/cifsd/error.c b/sys/src/cmd/ip/cifsd/error.c new file mode 100644 index 000000000..0b999dc94 --- /dev/null +++ b/sys/src/cmd/ip/cifsd/error.c @@ -0,0 +1,129 @@ +#include +#include +#include "dat.h" +#include "fns.h" + +enum { + /* error class */ + ERRDOS = 1, + ERRSRV = 2, + ERRHRD = 3, + ERRCMD = 0xFF, + + /* error codes */ + ERRbadfunc = 0x1, + ERRbadfile = 0x2, + ERRbadpath = 0x3, + ERRnofids = 0x4, + ERRnoaccess = 0x5, + ERRbadfid = 0x6, + ERRbadmcp = 0x7, + ERRnomem = 0x8, + ERRbadmem = 0x9, + ERRbadenv = 0xA, + ERRbadformat = 0xB, + ERRbadaccess = 0xC, + ERRbaddata = 0xD, + ERRbaddrive = 0xF, + ERRremcd = 0x10, + ERRdiffdevice = 0x11, + ERRnofiles = 0x12, + ERRgeneral = 0x1F, + ERRbadshare = 0x20, + ERRlock = 0x21, + ERReof = 0x26, + ERRunsup = 0x32, + ERRfilexists = 0x50, + ERRinvalidparam = 0x57, + ERRunknownlevel = 0x7C, + ERRbadpipe = 0xE6, + ERRinvnetname = 0x06, + ERRreqnotaccep = 0x47, + ERRnosuchshare = 0x43, + ERRerror = 0x1, + ERRbadpw = 0x2, + ERRaccess = 0x4, + ERRinvtid = 0x5, + ERRinvdevice = 0x7, + ERRbaduid = 0x5b, +}; + +int +doserror(int err) +{ +#define SE(c,e) e<<16 | c + static struct Ent { + int error; + int status; + } tab[] = { + SE(ERRSRV, ERRerror), STATUS_INVALID_SMB, + SE(ERRSRV, ERRinvtid), STATUS_SMB_BAD_TID, + SE(ERRDOS, ERRbadfid), STATUS_SMB_BAD_FID, + SE(ERRDOS, ERRbadaccess), STATUS_OS2_INVALID_ACCESS, + SE(ERRSRV, ERRbaduid), STATUS_SMB_BAD_UID, + SE(ERRDOS, ERRunknownlevel), STATUS_OS2_INVALID_LEVEL, + SE(ERRDOS, ERRnofiles), STATUS_NO_MORE_FILES, + SE(ERRDOS, ERRbadfid), STATUS_INVALID_HANDLE, + SE(ERRDOS, ERRnoaccess), STATUS_ACCESS_DENIED, + SE(ERRDOS, ERRbadfile), STATUS_OBJECT_NAME_NOT_FOUND, + SE(ERRDOS, ERRfilexists), STATUS_OBJECT_NAME_COLLISION, + SE(ERRDOS, ERRbadpath), STATUS_OBJECT_PATH_INVALID, + SE(ERRDOS, ERRbadpath), STATUS_OBJECT_PATH_NOT_FOUND, + SE(ERRDOS, ERRbadpath), STATUS_OBJECT_PATH_SYNTAX_BAD, + SE(ERRDOS, ERRbadshare), STATUS_SHARING_VIOLATION, + SE(ERRSRV, ERRbadpw), STATUS_LOGON_FAILURE, + SE(ERRDOS, ERRnoaccess), STATUS_FILE_IS_A_DIRECTORY, + SE(ERRDOS, ERRunsup), STATUS_NOT_SUPPORTED, + SE(ERRSRV, ERRinvdevice), STATUS_BAD_DEVICE_TYPE, + SE(ERRSRV, ERRinvnetname), STATUS_BAD_NETWORK_NAME, + SE(ERRDOS, ERRdiffdevice), STATUS_NOT_SAME_DEVICE, + SE(ERRDOS, ERRremcd), STATUS_DIRECTORY_NOT_EMPTY, + SE(ERRSRV, ERRerror), 0, + }; + struct Ent *p; + + for(p=tab; p->status; p++) + if(p->status == err) + break; + return p->error; +} + +int +smbmkerror(void) +{ + static struct Ent { + int status; + char *str; + } tab[] = { + STATUS_ACCESS_DENIED, "permission denied", + STATUS_ACCESS_DENIED, "access permission denied", + STATUS_ACCESS_DENIED, "create prohibited", + STATUS_ACCESS_DENIED, "mounted directory forbids creation", + STATUS_DIRECTORY_NOT_EMPTY, "directory not empty", + STATUS_NO_SUCH_FILE, "no such file", + STATUS_OBJECT_NAME_NOT_FOUND, "name not found", + STATUS_OBJECT_PATH_NOT_FOUND, "directory entry not found", + STATUS_OBJECT_PATH_NOT_FOUND, "not a directory", + STATUS_OBJECT_PATH_NOT_FOUND, "does not exist", + STATUS_OBJECT_PATH_SYNTAX_BAD, "bad character", + STATUS_OBJECT_PATH_SYNTAX_BAD, "file name syntax", + STATUS_OBJECT_NAME_COLLISION, "file already exists", + STATUS_FILE_IS_A_DIRECTORY, "is a directory", + /* kenfs */ + STATUS_OBJECT_NAME_COLLISION, "create/wstat -- file exists", + STATUS_ACCESS_DENIED, "wstat -- not owner", + STATUS_ACCESS_DENIED, "wstat -- not in group", + /* unknown error */ + STATUS_INVALID_SMB, nil, + }; + char buf[ERRMAX]; + struct Ent *p; + + rerrstr(buf, sizeof(buf)); + for(p = tab; p->str; p++) + if(strstr(buf, p->str)) + break; + if(debug) + fprint(2, "smbmkerror: %s -> %lux\n", buf, (ulong)p->status); + return p->status; +} diff --git a/sys/src/cmd/ip/cifsd/file.c b/sys/src/cmd/ip/cifsd/file.c new file mode 100644 index 000000000..9fc8c6c7d --- /dev/null +++ b/sys/src/cmd/ip/cifsd/file.c @@ -0,0 +1,319 @@ +#include +#include +#include "dat.h" +#include "fns.h" + +typedef struct Opl Opl; +struct Opl +{ + int ref; + ulong hash; + char *path; + Opl *next; + File *locked; + int dacc; + int sacc; + int delete; +}; + +static Opl *locktab[64]; + +static Opl* +getopl(char **path, int (*namecmp)(char *, char *), int dacc, int sacc) +{ + Opl *opl, **pp; + ulong h; + + h = namehash(*path); + for(pp = &locktab[h % nelem(locktab)]; *pp; pp=&((*pp)->next)){ + opl = *pp; + if(namecmp(opl->path, *path)) + continue; + if(sacc == FILE_SHARE_COMPAT){ + if(sacc != opl->sacc) + return nil; + if((opl->dacc | dacc) & WRITEMASK) + return nil; + } else { + if(opl->sacc == FILE_SHARE_COMPAT) + return nil; + if((dacc & READMASK) && (opl->sacc & FILE_SHARE_READ)==0) + return nil; + if((dacc & WRITEMASK) && (opl->sacc & FILE_SHARE_WRITE)==0) + return nil; + if((dacc & FILE_DELETE) && (opl->sacc & FILE_SHARE_DELETE)==0) + return nil; + } + opl->ref++; + if(strcmp(opl->path, *path)){ + free(*path); + *path = strdup(opl->path); + } + return opl; + } + + opl = mallocz(sizeof(*opl), 1); + opl->ref = 1; + opl->hash = h; + opl->dacc = dacc; + opl->sacc = sacc; + *pp = opl; + return opl; +} + +static void +putopl(Opl *opl) +{ + Opl **pp; + + if(opl==nil || --opl->ref) + return; + for(pp = &locktab[opl->hash % nelem(locktab)]; *pp; pp=&((*pp)->next)){ + if(*pp == opl){ + *pp = opl->next; + opl->next = nil; + break; + } + } + if(opl->path && opl->delete){ + if(debug) + fprint(2, "remove on close: %s\n", opl->path); + if(remove(opl->path) < 0) + logit("remove %s: %r", opl->path); + } + free(opl->path); + free(opl); +} + +File* +createfile(char *path, int (*namecmp)(char *, char *), + int dacc, int sacc, int cdisp, int copt, vlong csize, int fattr, int *pact, Dir **pdir, int *perr) +{ + int err, act, fd, mode, perm, isdir, delete; + Opl *o; + File *f; + Dir *d; + + o = nil; + f = nil; + d = nil; + fd = -1; + path = strdup(path); + + if(copt & FILE_OPEN_BY_FILE_ID){ +unsup: + err = STATUS_NOT_SUPPORTED; + goto out; + } + if((o = getopl(&path, namecmp, dacc, sacc)) == nil){ + err = STATUS_SHARING_VIOLATION; + goto out; + } + mode = -1; + if(dacc & READMASK) + mode += 1; + if(dacc & WRITEMASK) + mode += 2; + delete = isdir = 0; + if(d = xdirstat(&path, namecmp)){ + if(mode >= 0 && d->type != '/' && d->type != 'M'){ +noaccess: + err = STATUS_ACCESS_DENIED; + goto out; + } + + isdir = d->qid.type == QTDIR; + switch(cdisp){ + case FILE_SUPERSEDE: + act = FILE_SUPERSEDED; + if(remove(path) < 0){ + logit("remove: %r"); +oserror: + err = smbmkerror(); + goto out; + } + goto docreate; + case FILE_OVERWRITE: + case FILE_OVERWRITE_IF: + act = FILE_OVERWRITTEN; + if(isdir || (mode != OWRITE && mode != ORDWR)) + goto noaccess; + d->length = 0; + mode |= OTRUNC; + break; + case FILE_OPEN: + case FILE_OPEN_IF: + act = FILE_OPEND; + break; + case FILE_CREATE: + err = STATUS_OBJECT_NAME_COLLISION; + goto out; + default: + goto unsup; + } + if((copt & FILE_DIRECTORY_FILE) && !isdir) + goto noaccess; + if((copt & FILE_NON_DIRECTORY_FILE) && isdir){ + err = STATUS_FILE_IS_A_DIRECTORY; + goto out; + } + if(copt & FILE_DELETE_ON_CLOSE){ + if(isdir || (dacc & FILE_DELETE)==0) + goto noaccess; + delete = 1; + } + if(mode >= 0 && !isdir) + if((fd = open(path, mode)) < 0) + goto oserror; + } else { + switch(cdisp){ + case FILE_SUPERSEDE: + case FILE_CREATE: + case FILE_OPEN_IF: + case FILE_OVERWRITE_IF: + act = FILE_CREATED; + break; + case FILE_OVERWRITE: + case FILE_OPEN: + err = smbmkerror(); + goto out; + default: + goto unsup; + } + +docreate: + perm = 0666; + if(fattr & ATTR_READONLY) + perm &= ~0222; + if(copt & FILE_DIRECTORY_FILE){ + perm |= DMDIR | 0111; + mode = OREAD; + isdir = 1; + } + if(mode < 0) + mode = OREAD; + if(copt & FILE_DELETE_ON_CLOSE){ + if(isdir || (dacc & FILE_DELETE)==0) + goto noaccess; + delete = 1; + } + if((fd = create(path, mode, perm)) < 0){ + char *name, *base; + Dir *t; + + err = smbmkerror(); + if(!splitpath(path, &base, &name)) + goto out; + if((t = xdirstat(&base, namecmp)) == nil){ + free(base); free(name); + goto out; + } + free(t); + free(path); path = conspath(base, name); + free(base); free(name); + if((fd = create(path, mode, perm)) < 0) + goto oserror; + } + if(csize > 0 && !isdir){ + Dir nd; + + nulldir(&nd); + nd.length = csize; + if(dirfwstat(fd, &nd) < 0) + goto oserror; + } + if(pdir) + if((d = dirfstat(fd)) == nil) + goto oserror; + if(isdir){ + close(fd); + fd = -1; + } + } + + f = mallocz(sizeof(*f), 1); + f->ref = 1; + f->fd = fd; fd = -1; + f->rtype = FileTypeDisk; + f->dacc = dacc; + o->delete |= delete; + if(o->path == nil){ + o->path = path; + path = nil; + } + f->path = o->path; + f->aux = o; o = nil; + if(pact) + *pact = act; + if(pdir){ + *pdir = d; + d = nil; + } + err = 0; + +out: + if(perr) + *perr = err; + if(fd >= 0) + close(fd); + free(path); + putopl(o); + free(d); + + return f; +} + +Dir* +statfile(File *f) +{ + if(f == nil) + return nil; + if(f->fd >= 0) + return dirfstat(f->fd); + else + return dirstat(f->path); +} + +int +lockfile(File *f) +{ + Opl *opl = f->aux; + if(opl->locked && opl->locked != f) + return 0; + opl->locked = f; + return 1; +} + +void +deletefile(File *f, int delete) +{ + Opl *opl = f->aux; + if(opl->delete == delete) + return; + opl->delete = delete; +} + +int +deletedfile(File *f) +{ + Opl *opl = f->aux; + return opl->delete; +} + + +void +putfile(File *f) +{ + Opl *opl; + + if(f == nil || --f->ref) + return; + if(f->fd >= 0) + close(f->fd); + opl = f->aux; + if(opl->locked == f) + opl->locked = nil; + putopl(opl); + free(f); +} + diff --git a/sys/src/cmd/ip/cifsd/find.c b/sys/src/cmd/ip/cifsd/find.c new file mode 100644 index 000000000..fbf93b84f --- /dev/null +++ b/sys/src/cmd/ip/cifsd/find.c @@ -0,0 +1,191 @@ +#include +#include +#include "dat.h" +#include "fns.h" + +static int +iswild(char *pattern) +{ + return strchrs(pattern, "*?<>\"") != nil; +} + +static int +matchpattern(char *name, char *pattern, int casesensitive) +{ + Rune p, r; + int n; + + while(*pattern){ + pattern += chartorune(&p, pattern); + n = chartorune(&r, name); + switch(p){ + case '?': + if(r == 0) + return 0; + name += n; + break; + case '>': + switch(r){ + case '.': + if(!name[1] && matchpattern(name+1, pattern, casesensitive)) + return 1; + case 0: + return matchpattern(name, pattern, casesensitive); + } + name += n; + break; + case '*': + case '<': + while(r){ + if(matchpattern(name, pattern, casesensitive)) + return 1; + if(p == '<' && r == '.' && !strchrs(name+1, ".")){ + name++; + break; + } + n = chartorune(&r, name += n); + } + break; + case '"': + if(r == 0 && matchpattern(name, pattern, casesensitive)) + return 1; + if(r != '.') + return 0; + name += n; + break; + default: + if(p != r && casesensitive || toupperrune(p) != toupperrune(r)) + return 0; + name += n; + } + } + return *name == 0; +} + +int +matchattr(Dir *d, int s) +{ + int a, m; + + m = ATTR_HIDDEN | ATTR_SYSTEM | ATTR_DIRECTORY; + a = dosfileattr(d); + if((a & ~s) & m) + return 0; + m = (s >> 8) & m; + if(m && ((m & a) != m)) + return 0; + return 1; +} + + +Find* +openfind(char *path, int (*namecmp)(char *, char *), int attr, int withdot, int *perr) +{ + char *base, *pattern, *parent; + Dir *dir; + int ndir, err; + Find *f; + + f = nil; + path = strdup(path); + base = pattern = parent = nil; + if(!splitpath(path, &base, &pattern)){ + err = STATUS_OBJECT_PATH_SYNTAX_BAD; + goto out; + } + if(debug) + fprint(2, "base %s\npattern %s\nattr %x\nwithdot %d\n", base, pattern, attr, withdot); + + if(iswild(pattern)){ + if((ndir = xdirread(&base, namecmp, &dir)) < 0){ + err = smbmkerror(); + goto out; + } + } else { + ndir = 0; + withdot = 0; + if(dir = xdirstat(&path, namecmp)){ + free(base); + free(pattern); + splitpath(path, &base, &pattern); + ndir++; + } + } + + f = mallocz(sizeof(*f), 1); + f->ref = 1; + f->base = base; + f->pattern = pattern; + f->attr = attr; + f->dir = dir; + f->ndir = ndir; + f->index = 0; + f->casesensitive = (namecmp == strcmp); + + if(withdot){ + if(f->dot = dirstat(base)) + f->dot->name = "."; + if(splitpath(base, &parent, nil)) + if(f->dotdot = dirstat(parent)) + f->dotdot->name = ".."; + } + + base = nil; + pattern = nil; + err = 0; + +out: + if(perr) + *perr = err; + + free(base); + free(pattern); + free(parent); + free(path); + + return f; +} + +int +readfind(Find *f, int i, Dir **dp) +{ + Dir *d; + int x; + + x = i; + if(f->dot && f->dotdot) + x -= 2; + for(;;){ + if(x == -2){ + d = f->dot; + } else if(x == -1){ + d = f->dotdot; + } else if(x < f->ndir){ + d = f->dir + x; + } else { + d = nil; + i = -1; + break; + } + if(matchattr(d, f->attr) && matchpattern(d->name, f->pattern, f->casesensitive)) + break; + i++; x++; + } + if(debug && d) + fprint(2, "readfile [%d] attr=%x name=%s\n", i, extfileattr(d), d->name); + *dp = d; + return i; +} + +void +putfind(Find *f) +{ + if(f == nil || --f->ref) + return; + free(f->pattern); + free(f->base); + free(f->dot); + free(f->dotdot); + free(f->dir); + free(f); +} diff --git a/sys/src/cmd/ip/cifsd/fns.h b/sys/src/cmd/ip/cifsd/fns.h new file mode 100644 index 000000000..2bf1ae4b9 --- /dev/null +++ b/sys/src/cmd/ip/cifsd/fns.h @@ -0,0 +1,85 @@ +/* pack */ +int unpack(uchar *b, uchar *p, uchar *e, char *f, ...); +int vunpack(uchar *b, uchar *p, uchar *e, char *f, va_list a); +int pack(uchar *b, uchar *p, uchar *e, char *f, ...); +int vpack(uchar *b, uchar *p, uchar *e, char *f, va_list a); + +/* error */ +int smbmkerror(void); +int doserror(int err); + +/* util */ +void logit(char *fmt, ...); +#pragma varargck argpos logit 1 +char *getremote(char *dir); +char *conspath(char *base, char *name); +int splitpath(char *path, char **base, char **name); +void dumphex(char *s, uchar *h, uchar *e); +void todatetime(long time, int *pdate, int *ptime); +long fromdatetime(int date, int time); +vlong tofiletime(long time); +long fromfiletime(vlong filetime); +int filesize32(vlong); +vlong allocsize(vlong size, int blocksize); +int extfileattr(Dir *d); +int dosfileattr(Dir *d); +ulong namehash(char *s); +char *strtr(char *s, Rune (*tr)(Rune)); +char *strchrs(char *s, char *c); +int smbstrpack8(uchar *, uchar *p, uchar *e, void *arg); +int smbstrpack16(uchar *b, uchar *p, uchar *e, void *arg); +int smbstrunpack8(uchar *, uchar *p, uchar *e, void *arg); +int smbstrunpack16(uchar *b, uchar *p, uchar *e, void *arg); +int smbnamepack8(uchar *b, uchar *p, uchar *e, void *arg); +int smbnamepack16(uchar *b, uchar *p, uchar *e, void *arg); +int smbnameunpack8(uchar *b, uchar *p, uchar *e, void *arg); +int smbnameunpack16(uchar *b, uchar *p, uchar *e, void *arg); +int smbuntermstrpack8(uchar *b, uchar *p, uchar *e, void *arg); +int smbuntermstrpack16(uchar *b, uchar *p, uchar *e, void *arg); +int smbuntermnamepack8(uchar *b, uchar *p, uchar *e, void *arg); +int smbuntermnamepack16(uchar *b, uchar *p, uchar *e, void *arg); + +/* smb */ +void smbcmd(Req *r, int cmd, uchar *h, uchar *p, uchar *e); + +/* share */ +Share *mapshare(char *path); + +/* rap */ +void transrap(Trans *t); + +/* tree */ +Tree *connecttree(char *service, char *path, int *perr); +int disconnecttree(int tid); +void logoff(void); + +Tree *gettree(int tid); +int newfid(Tree *t, File *f); +void delfid(Tree *t, int fid); +File *getfile(int tid, int fid, Tree **ptree, int *perr); +char *getpath(int tid, char *name, Tree **ptree, int *perr); + +int newsid(Tree *t, Find *f); +void delsid(Tree *t, int sid); +Find *getfind(int tid, int sid, Tree **ptree, int *perr); + +/* file */ +File* createfile(char *path, int (*namecmp)(char *, char *), + int dacc, int sacc, int cdisp, int copt, vlong csize, int fattr, int *pact, Dir **pdir, int *perr); +Dir* statfile(File *f); +void putfile(File *f); +int lockfile(File *f); +void deletefile(File *f, int delete); +int deletedfile(File *f); + +/* find */ +Find *openfind(char *path, int (*namecmp)(char *, char *), + int attr, int withdot, int *perr); +int matchattr(Dir *d, int s); +int readfind(Find *f, int i, Dir **dp); +void putfind(Find *f); + +/* dir */ +int xdirread(char **path, int (*namecmp)(char *, char *), Dir **d); +Dir *xdirstat(char **path, int (*namecmp)(char *, char *)); +void xdirflush(char *path, int (*namecmp)(char *, char *)); diff --git a/sys/src/cmd/ip/cifsd/main.c b/sys/src/cmd/ip/cifsd/main.c new file mode 100644 index 000000000..b1838c9bd --- /dev/null +++ b/sys/src/cmd/ip/cifsd/main.c @@ -0,0 +1,214 @@ +#include +#include +#include "dat.h" +#include "fns.h" + +enum { + LENHDR = 4, + + MAGIC = 0xFF | ('S'<<8) | ('M'<<16) | ('B'<<24), + + SMB_FLAGS_CASE_INSENSITIVE = 0x08, + SMB_FLAGS_CANONICALIZED_PATHS = 0x10, + SMB_FLAGS_REPLY = 0x80, + + NOCASEMASK = SMB_FLAGS_CASE_INSENSITIVE | SMB_FLAGS_CANONICALIZED_PATHS, + + SMB_FLAGS2_LONG_NAMES = 0x0001, + SMB_FLAGS2_EAS = 0x0002, + SMB_FLAGS2_IS_LONG_NAME = 0x0040, + SMB_FLAGS2_NT_STATUS = 0x4000, + SMB_FLAGS2_UNICODE = 0x8000, +}; + +static int casesensitive = 0; + +static void +respond(Req *r, int err) +{ + int n, flags, flags2; + + if(err && !(r->flags2 & SMB_FLAGS2_NT_STATUS)) + err = doserror(err); + flags = (r->flags & (r->namecmp != strcmp ? NOCASEMASK : 0)) | + SMB_FLAGS_REPLY; + flags2 = (r->flags2 & (SMB_FLAGS2_NT_STATUS | + SMB_FLAGS2_LONG_NAMES | SMB_FLAGS2_UNICODE)) | + SMB_FLAGS2_IS_LONG_NAME; + if(r->cmd != 0x73) /* SMB_COM_SESSION_SETUP_ANDX */ + memset(r->sig, 0, sizeof(r->sig)); + n = pack(r->rh, r->rh, r->rh+32, "lblbww[]__wwww", + MAGIC, r->cmd, err, flags, flags2, r->pid>>16, r->sig, r->sig+sizeof(r->sig), + r->tid, r->pid & 0xFFFF, r->uid, r->mid); + if(err){ + r->rp = r->rh+n; + r->rp += pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}"); + } + if(debug > 1) + dumphex("respond", r->rh, r->rp); + if(debug) + fprint(2, "respond: err=%x\n\n", err); + n = r->rp - r->rh; + r->lh[0] = 0; + r->lh[1] = 0; + r->lh[2] = n>>8 & 0xFF; + r->lh[3] = n & 0xFF; + write(1, r->lh, LENHDR+n); +} + +static void +receive(uchar *h, uchar *e) +{ + static uchar buffer[LENHDR + BUFFERSIZE]; + static Rop rop8 = { + .strpack = smbstrpack8, + .strunpack = smbstrunpack8, + .namepack = smbnamepack8, + .nameunpack = smbnameunpack8, + .untermstrpack = smbuntermstrpack8, + .untermnamepack = smbuntermnamepack8, + }, rop16 = { + .strpack = smbstrpack16, + .strunpack = smbstrunpack16, + .namepack = smbnamepack16, + .nameunpack = smbnameunpack16, + .untermstrpack = smbuntermstrpack16, + .untermnamepack = smbuntermnamepack16, + }; + + uchar *sig; + int n, hpid, magic; + Req r; + + if(debug > 1) + dumphex("receive", h, e); + if((n = unpack(h, h, e, "lb____bww{.________}__wwww", &magic, + &r.cmd, &r.flags, &r.flags2, &hpid, &sig, &r.tid, &r.pid, &r.uid, &r.mid)) == 0){ + logit("bad smb header"); + return; + } + if(magic != MAGIC){ + logit("bad smb magic"); + return; + } + r.pid |= hpid<<16; + r.lh = buffer; + r.rh = r.lh + LENHDR; + r.rp = r.rh + n; + r.re = r.rh + remotebuffersize; + r.o = (r.flags2 & SMB_FLAGS2_UNICODE) ? &rop16 : &rop8; + memmove(r.sig, sig, sizeof(r.sig)); + r.name = nil; + r.respond = respond; + r.namecmp = ((r.flags & NOCASEMASK) && !casesensitive) ? cistrcmp : strcmp; + smbcmd(&r, r.cmd, h, h+n, e); +} + +static void +serve(void) +{ + static uchar buffer[LENHDR + BUFFERSIZE]; + uchar *m, *me; + uchar *p, *pe; + uchar *hl; + int n; + + p = hl = buffer; + pe = p + LENHDR+BUFFERSIZE; + + for(;;){ + n = read(0, p, pe - p); + if(n <= 0) + break; + p += n; +Next: + if(p - hl < LENHDR) + continue; + n = hl[2]<<8 | hl[3]; + m = hl + LENHDR; + me = m + n; + if(me > pe){ + logit("message too big"); + break; + } + if(me > p) + continue; + receive(m, me); + n = p - me; + p = hl + n; + if(n > 0){ + memmove(hl, me, n); + goto Next; + } + } +} + +void +main(int argc, char *argv[]) +{ + static struct { + char *s; + int *v; + } opts[] = { + { "trspaces", &trspaces }, + { "casesensitive", &casesensitive }, + { nil, nil } + }, *o; + + char *log, *opt; + Tm *tm; + int pid; + + debug = 0; + trspaces = 0; + needauth = 1; + domain = "WORKGROUP"; + progname = "cifsd"; + osname = "Plan 9"; + log = nil; + + ARGBEGIN { + case 't': + needauth = 0; + break; + case 'd': + debug++; + break; + case 'f': + log = EARGF(exits("bad arg")); + break; + case 'w': + domain = EARGF(exits("bad arg")); + break; + case 'o': + opt = EARGF(exits("bad arg")); + for(o=opts; o->s; o++) + if(strcmp(opt, o->s) == 0){ + *o->v = 1; + break; + } + if(o->s == nil) + exits("bad arg"); + break; + } ARGEND + + close(2); + if(!log || open(log, OWRITE) < 0){ + open("/dev/null", OWRITE); + debug = 0; + } + + remotesys = argc ? getremote(argv[argc-1]) : nil; + remoteuser = nil; + remotebuffersize = BUFFERSIZE; + starttime = time(nil); + pid = getpid(); + srand(starttime ^ pid); + tm = localtime(starttime); + tzoff = tm->tzoff; + + logit("started [%d]", pid); + serve(); + logoff(); + logit("exited [%d]", pid); +} diff --git a/sys/src/cmd/ip/cifsd/mkfile b/sys/src/cmd/ip/cifsd/mkfile new file mode 100644 index 000000000..da0ec1e50 --- /dev/null +++ b/sys/src/cmd/ip/cifsd/mkfile @@ -0,0 +1,21 @@ + +#include +#include "dat.h" +#include "fns.h" + +int +unpack(uchar *b, uchar *p, uchar *e, char *f, ...) +{ + va_list a; + int r; + + va_start(a, f); + r = vunpack(b, p, e, f, a); + va_end(a); + return r; +} + +int +pack(uchar *b, uchar *p, uchar *e, char *f, ...) +{ + va_list a; + int r; + + va_start(a, f); + r = vpack(b, p, e, f, a); + va_end(a); + return r; +} + +int +vunpack(uchar *b, uchar *p, uchar *e, char *f, va_list a) +{ + struct { + void *prev; + int o, c, i; + uchar *e; + uchar **ap, **ae; + } sub[8], *sp, *sa; + int (*funpack)(uchar *, uchar *, uchar *, void *); + char c, ff[2]; + int i, x, n; + uchar *t; + + memset(sub, 0, sizeof(sub)); + for(sp = sub; sp < sub+nelem(sub); sp++){ + sp->o = -1; + sp->c = -1; + sp->i = 1; + } + + t = p; + sp = nil; + sa = sub; + while(c = *f++){ + switch(c){ + case '_': + case 'b': + if(p >= e) + return 0; + if(c == 'b') + *va_arg(a, int*) = *p; + p++; + break; + case 'w': + if(p+1 >= e) + return 0; + *va_arg(a, int*) = (int)p[1]<<8 | (int)p[0]; + p+=2; + break; + case 'l': + if(p+3 >= e) + return 0; + *va_arg(a, int*) = (int)p[3]<<24 | (int)p[2]<<16 | (int)p[1]<<8 | (int)p[0]; + p+=4; + break; + case 'v': + if(p+7 >= e) + return 0; + *va_arg(a, vlong*) = + (vlong)p[7]<<56 | + (vlong)p[6]<<48 | + (vlong)p[5]<<40 | + (vlong)p[4]<<32 | + (vlong)p[3]<<24 | + (vlong)p[2]<<16 | + (vlong)p[1]<<8 | + (vlong)p[0]; + p += 8; + break; + case '%': + x = *f++ - '0'; + while((p - b) % x) + p++; + break; + case 'f': + funpack = va_arg(a, void*); + if((n = funpack(b, p, e, va_arg(a, void*))) == 0) + return 0; + p += n; + break; + case '#': + case '@': + x = *f++ - '0'; + ff[0] = *f++; + ff[1] = 0; + if((n = unpack(b, p, e, ff, &i)) == 0) + return 0; + p += n; + if(c == '#'){ + sub[x].c = i; + } else { + sub[x].o = i; + } + break; + case '{': + case '[': + sa->prev = sp; + sp = sa++; + if(*f == '*'){ + sp->i = f[1]-'0'; + f += 2; + } + if(sp->o >= 0 && b + sp->o > p) + if(b + sp->o <= e || *f != '?') + p = b + sp->o; + if(*f == '?') + f++; + sp->o = p - b; + sp->e = e; + if(sp->c >= 0){ + e = p + sp->c * sp->i; + if(e > sp->e) + return 0; + } + if(c == '['){ + sp->ap = va_arg(a, uchar**); + sp->ae = va_arg(a, uchar**); + } + break; + case '}': + case ']': + e = sp->e; + if(sp->c < 0) + sp->c = ((p - (b + sp->o))+sp->i-1)/sp->i; + p = b + sp->o + sp->c * sp->i; + if(p > e) + return 0; + if(sp->ap) + *sp->ap = b + sp->o; + if(sp->ae) + *sp->ae = p; + sp = sp->prev; + break; + case '.': + *va_arg(a, uchar**) = p; + break; + } + if(p > e) + return 0; + } + return p - t; +} + +vpack(uchar *b, uchar *p, uchar *e, char *f, va_list a) +{ + struct { + void *prev; + int o, i; + uchar *wc, *wo, wcf, wof; + } sub[8], *sp, *sa; + int (*fpack)(uchar *, uchar *, uchar *, void *); + char c, ff[2]; + int i, x, n; + vlong v; + uchar *t; + + memset(sub, 0, sizeof(sub)); + for(sp = sub; sp < sub+nelem(sub); sp++){ + sp->o = -1; + sp->i = 1; + } + + t = p; + sp = nil; + sa = sub; + while(c = *f++){ + switch(c){ + case '_': + case 'b': + if(p >= e) + return 0; + if(c == 'b') + *p++ = va_arg(a, int); + else + *p++ = 0; + break; + case 'w': + if(p+1 >= e) + return 0; + i = va_arg(a, int); + *p++ = i & 0xFF; + *p++ = i>>8 & 0xFF; + break; + case 'l': + if(p+3 >= e) + return 0; + i = va_arg(a, int); + *p++ = i & 0xFF; + *p++ = i>>8 & 0xFF; + *p++ = i>>16 & 0xFF; + *p++ = i>>24 & 0xFF; + break; + case 'v': + if(p+7 >= e) + return 0; + v = va_arg(a, vlong); + *p++ = v & 0xFF; + *p++ = v>>8 & 0xFF; + *p++ = v>>16 & 0xFF; + *p++ = v>>24 & 0xFF; + *p++ = v>>32 & 0xFF; + *p++ = v>>40 & 0xFF; + *p++ = v>>48 & 0xFF; + *p++ = v>>56 & 0xFF; + break; + case '%': + x = *f++ - '0'; + while((p - b) % x){ + if(p >= e) + return 0; + *p++ = 0; + } + break; + case 'f': + fpack = va_arg(a, void*); + if((n = fpack(b, p, e, va_arg(a, void*))) == 0) + return 0; + p += n; + break; + case '#': + case '@': + x = *f++ - '0'; + ff[0] = *f++; + ff[1] = 0; + if((n = pack(b, p, e, ff, 0)) == 0) + return 0; + if(c == '#'){ + sub[x].wc = p; + sub[x].wcf = ff[0]; + } else { + sub[x].wo = p; + sub[x].wof = ff[0]; + } + p += n; + break; + case '{': + case '[': + sa->prev = sp; + sp = sa++; + if(*f == '*'){ + sp->i = f[1]-'0'; + f += 2; + } + if(*f == '?') + f++; + sp->o = p - b; + if(c == '['){ + uchar *s, *se; + + s = va_arg(a, uchar*); + se = va_arg(a, uchar*); + n = se - s; + if(n < 0 || p + n > e) + return 0; + if(p != s) + memmove(p, s, n); + p += n; + } + break; + case '}': + case ']': + n = ((p - (b + sp->o))+sp->i-1)/sp->i; + p = b + sp->o + n * sp->i; + if(sp->wc){ + ff[0] = sp->wcf; + ff[1] = 0; + pack(b, sp->wc, e, ff, n); + } + if(sp->wo){ + ff[0] = sp->wof; + ff[1] = 0; + pack(b, sp->wo, e, ff, sp->o); + } + sp = sp->prev; + break; + case '.': + *va_arg(a, uchar**) = p; + break; + } + if(p > e) + return 0; + } + return p - t; +} + diff --git a/sys/src/cmd/ip/cifsd/rap.c b/sys/src/cmd/ip/cifsd/rap.c new file mode 100644 index 000000000..3b1b8522d --- /dev/null +++ b/sys/src/cmd/ip/cifsd/rap.c @@ -0,0 +1,143 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" + +static void +padname(uchar *buf, int len, char *name) +{ + int n; + n = strlen(name); + if(n >= len) + n = len-1; + memset(buf, 0, len); + memmove(buf, name, n); +} + +static int +packshareinfo(Trans *t, int level, char *name, int *pstatus) +{ + Share *share; + uchar buf[13]; + + if((share = mapshare(name)) == nil){ + if(pstatus) + *pstatus = 0x906; /* NERR_NetNameNotFound */ + return 0; + } + padname(buf, sizeof(buf), share->name); + switch(level){ + case 0: + return pack(t->out.data.b, t->out.data.p, t->out.data.e, "[]", + buf, buf+sizeof(buf)); + case 1: + return pack(t->out.data.b, t->out.data.p, t->out.data.e, "[]_w@1l{f}", + buf, buf+sizeof(buf), share->stype, smbstrpack8, share->remark); + case 2: + return pack(t->out.data.b, t->out.data.p, t->out.data.e, "[]_w@1l__ww@2l__________{f}{f}", + buf, buf+sizeof(buf), share->stype, 100, 1, smbstrpack8, share->remark, + smbnamepack8, share->root); + default: + return -1; + } +} + +void +transrap(Trans *t) +{ + char *pd, *dd, *name; + int n, code, status, level, rbs; + uchar *ip, *ipe, *op, *opb, *ope; + uchar buf[16]; + + code = 0; + name = nil; + pd = dd = nil; + ip = ipe = t->in.param.e; + if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "wff[]", &code, + smbstrunpack8, &pd, smbstrunpack8, &dd, &ip, nil)){ + t->respond(t, STATUS_NOT_SUPPORTED); + goto out; + } + + ope = t->out.param.e; + opb = op = t->out.param.b+2+2; + + n = status = level = 0; + switch(code){ + case 0x0000: /* NetShareEnum */ + op += pack(opb, op, ope, "ww", 0, 0); + if(!unpack(ip, ip, ipe, "ww", &level, &rbs)) + break; + if((n = packshareinfo(t, level, "local", nil)) > 0){ + t->out.data.p += n; + pack(opb, opb, ope, "ww", 1, 1); + } + break; + + case 0x0001: /* NetShareGetInfo */ + op += pack(opb, op, ope, "w", 0); + if(!unpack(ip, ip, ipe, "fww", smbstrunpack8, &name, &level, &rbs)) + break; + if((n = packshareinfo(t, level, name, &status)) > 0){ +outlen: + t->out.data.p += n; + pack(opb, opb, ope, "w", n); + } + break; + + case 0x000d: /* NetServerGetInfo */ + op += pack(opb, op, ope, "w", 0); + if(!unpack(ip, ip, ipe, "ww", &level, &rbs)) + break; + padname(buf, sizeof(buf), ""); + switch(level){ + case 0: + if((n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "[]", + buf, buf+sizeof(buf))) > 0) + goto outlen; + break; + case 1: + if((n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "[]bbl@1l{f}", + buf, buf+sizeof(buf), 0x05, 0x00, 2, smbstrpack8, osname)) > 0) + goto outlen; + default: + n = -1; + } + break; + + case 0x003f: /* NetWrkstaGetInfo */ + op += pack(opb, op, ope, "w", 0); + if(!unpack(ip, ip, ipe, "ww", &level, &rbs)) + break; + if(level != 10){ + n = -1; + break; + } + if((n = pack(t->out.data.b, t->out.data.p, t->out.data.e, + "@0l____@1lbb________{f}{f}", 0x05, 0x00, + smbstrpack8, sysname(), smbstrpack8, domain)) > 0) + goto outlen; + break; + + default: + logit("[%.4x] unknown rap command pd=%s dd=%s", code, pd, dd); + } + if(n < 0){ + logit("[%.4x] unknown rap level [%.4x]", code, level); + status = 0x7C; + } + if((n = pack(t->out.param.b, t->out.param.p, t->out.param.e, "w__[]", status, opb, op)) == 0){ + t->respond(t, STATUS_INVALID_SMB); + goto out; + } + t->out.param.p += n; + t->respond(t, 0); + +out: + free(name); + free(pd); + free(dd); + return; +} diff --git a/sys/src/cmd/ip/cifsd/share.c b/sys/src/cmd/ip/cifsd/share.c new file mode 100644 index 000000000..e14014085 --- /dev/null +++ b/sys/src/cmd/ip/cifsd/share.c @@ -0,0 +1,124 @@ +#include +#include +#include "dat.h" +#include "fns.h" + +static int +run9fs(char *arg) +{ + char buf[1024], *argv[3], *s; + Waitmsg *w; + int fd, pid; + + switch(pid = rfork(RFCFDG|RFREND|RFPROC)){ + case -1: + return -1; + case 0: + open("/dev/null", ORDWR); + snprint(buf, sizeof(buf), "/sys/log/%s", progname); + if((fd = open(buf, OWRITE)) >= 0) + seek(fd, 0, 2); + else + fd = 0; + dup(fd, 1); + dup(fd, 2); + argv[0] = "/bin/9fs"; + argv[1] = arg; + argv[2] = 0; + exec(argv[0], argv); + exits("failed to exec 9fs"); + } + for (;;) { + if((w = wait()) == nil) + return -1; + if (w->pid == pid) + break; + free(w); + } + if(w->msg[0]){ + if(s = strchr(w->msg, ':')) + s = s+1; + else + s = w->msg; + werrstr("%s", s); + free(w); + return -1; + } else { + free(w); + return 0; + } +} + +static Share *shares; + +Share* +mapshare(char *path) +{ + char *tmp, *tmp2, *name, *root, *service, *fsname, *remark; + int stype; + Share *s; + + if(name = strrchr(path, '/')) + name++; + else if(name = strrchr(path, '\\')) + name++; + else + name = path; + if(name==nil || *name==0 || *name=='.' || strchrs(name, "\\* ") || strstr(name, "..")) + return nil; + root = tmp = smprint("/n/%s", name); + name = strtr(strrchr(root, '/')+1, tolowerrune); + service = "A:"; + stype = STYPE_DISKTREE; + fsname = "9fs"; + remark = tmp2 = smprint("9fs %s; cd %s", name, root); + if(!strcmp(name, "local")){ + root = "/"; + fsname = "local"; + remark = "The standard namespace"; + } + if(!strcmp(name, "ipc$")){ + root = "/dev/null"; + name = "IPC$"; + fsname = ""; + service = "IPC"; + stype = STYPE_IPC; + remark = "The IPC service"; + } + + for(s = shares; s; s=s->next) + if(!strcmp(s->name, name)) + goto out; + + logit("mapshare %s -> %s %s %s", path, service, name, root); + + if(!strcmp(service, "A:") && (stype == STYPE_DISKTREE)){ + if(!strcmp(fsname, "9fs") && (run9fs(name) < 0)){ + logit("9fs %s: %r", name); + goto out; + } + } + + s = malloc(sizeof(*s)); + s->service = strdup(service); + s->stype = stype; + + s->name = strdup(name); + s->root = strdup(root); + + s->remark = strdup(remark); + s->fsname = strdup(fsname); + s->namelen = 255; + s->sectorsize = 0x200; + s->blocksize = 0x2000; + s->allocsize = 0; + s->freesize = s->blocksize; + + s->next = shares; + shares = s; + +out: + free(tmp); + free(tmp2); + return s; +} diff --git a/sys/src/cmd/ip/cifsd/smb.c b/sys/src/cmd/ip/cifsd/smb.c new file mode 100644 index 000000000..3b0eadb1f --- /dev/null +++ b/sys/src/cmd/ip/cifsd/smb.c @@ -0,0 +1,1691 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" + +enum { + NEGOTIATE_USER_SECURITY = 1, + NEGOTIATE_ENCRYPT_PASSWORDS = 2, +}; + +static Chalstate *smbcs; +static int sessionkey; +static int sessionuid; +static int negotiated; + +void +smbnegotiate(Req *r, uchar *h, uchar *p, uchar *e) +{ + uchar *d, *de, *c, *ce; + int i, x, mode; + char *s; + + if(!unpack(h, p, e, "#0b{*2}#1w[]", &d, &de)){ +err: + r->respond(r, STATUS_INVALID_SMB); + return; + } + i = 0; + x = -1; + while(unpack(h, d, de, "_f.", smbstrunpack8, &s, &d)){ + if(debug) + fprint(2, "[%d] %s\n", i, s); + if(x < 0 && !cistrcmp(s, "NT LM 0.12")) + x = i; + free(s); + i++; + } + if(x < 0) + x = i-1; + if(x < 0) + x = 0; + sessionkey = rand(); + c = ce = nil; + mode = 0; + if(needauth){ + if(smbcs) + auth_freechal(smbcs); + if(smbcs = auth_challenge("proto=mschap role=server")){ + c = (uchar*)smbcs->chal; + ce = c + smbcs->nchal; + mode = NEGOTIATE_USER_SECURITY | NEGOTIATE_ENCRYPT_PASSWORDS; + } else + logit("auth_challenge: %r"); + } + if(!pack(r->rh, r->rp, r->re, "#0b{*2wbwwllllvw#2b}#1w{[]f}.", + x, mode, 50, 1, BUFFERSIZE, 0x10000, sessionkey, + CAP_UNICODE | CAP_LARGEFILES | + CAP_NT_FIND | CAP_NT_SMBS | CAP_NT_STATUS, + tofiletime(time(0)), -tzoff/60, c, ce, r->o->strpack, domain, &r->rp)) + goto err; + negotiated = 1; + r->respond(r, 0); +} + +enum { + SMB_SETUP_GUEST = 1, + SMB_SETUP_USE_LANMAN_KEY = 2, +}; + +void +smbsessionsetupandx(Req *r, uchar *h, uchar *p, uchar *e) +{ + uchar *lm, *lme, *nt, *nte, *xp; + char *user, *dom, *os, *lanman; + int xcmd, cap, bs, sk; + AuthInfo *ai; + + user = dom = os = lanman = nil; + if(!unpack(h, p, e, "#0b{*2b_@4ww____l#2w#3w____l}#1w{[][]ffff}{?.}", + &xcmd, &bs, &sk, &cap, &lm, &lme, &nt, &nte, + r->o->strunpack, &user, r->o->strunpack, &dom, + r->o->strunpack, &os, r->o->strunpack, &lanman, &xp)){ + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + if(debug) + fprint(2, "bs=%x cap=%x user=%s dom=%s os=%s lanman=%s\n", + bs, cap, user, dom, os, lanman); + if(sk != sessionkey) + logit("ignoring bad session key"); + while(!remoteuser){ + if(needauth){ + MSchapreply mcr; + + if(smbcs == nil || strlen(user) == 0) + break; + memset(&mcr, 0, sizeof(mcr)); + if((lme - lm) == sizeof(mcr.LMresp)) + memmove(mcr.LMresp, lm, lme - lm); + if((nte - nt) == sizeof(mcr.NTresp)) + memmove(mcr.NTresp, nt, nte - nt); + smbcs->user = user; + smbcs->resp = &mcr; + smbcs->nresp = sizeof(mcr); + if((ai = auth_response(smbcs)) == nil) + logit("auth_response: %r"); + auth_freechal(smbcs); + smbcs = nil; + if(ai == nil) + break; + if(auth_chuid(ai, nil) < 0) + logit("auth_chuid: %r"); + auth_freeAI(ai); + } + remoteuser = getuser(); + logit("auth successfull"); + break; + } + sessionuid = (namehash(getuser()) & 0x7FFF) | 1; + r->uid = sessionuid; + if(bs >= 1024 || bs <= BUFFERSIZE) + remotebuffersize = bs; + if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2ww}#1w{fff}{.}", + xcmd, remoteuser ? 0 : SMB_SETUP_GUEST, + r->o->strpack, osname, r->o->strpack, progname, + r->o->strpack, domain, &r->rp)) + r->respond(r, STATUS_INVALID_SMB); + else + smbcmd(r, xcmd, h, xp, e); +out: + free(user); + free(dom); + free(lanman); + free(os); +} + +void +smblogoffandx(Req *r, uchar *h, uchar *p, uchar *e) +{ + int xcmd; + uchar *xp; + + if(!unpack(h, p, e, "#0b{*2b_}#1w{}{?.}", &xcmd, &xp)){ +unsup: + r->respond(r, STATUS_NOT_SUPPORTED); + return; + } + logit("logoff"); + if(remoteuser && needauth) + goto unsup; + logoff(); + remoteuser = nil; + r->tid = 0xFFFF; + r->uid = 0; + if(!pack(r->rh, r->rp, r->re, "#0b{*2b_}#1w{}{.}", xcmd, &r->rp)) + r->respond(r, STATUS_INVALID_SMB); + else + smbcmd(r, xcmd, h, xp, e); +} + +enum { + SMB_SUPPORT_SEARCH_BITS = 1, +}; + +void +smbtreeconnectandx(Req *r, uchar *h, uchar *p, uchar *e) +{ + int err, xcmd, flags; + char *path, *service; + uchar *pw, *pwe, *xp; + Tree *t; + + path = service = nil; + if(!unpack(h, p, e, "#0b{*2b_@3ww#2w}#1w{[]ff}{?.}", + &xcmd, &flags, &pw, &pwe, r->o->strunpack, &path, + smbstrunpack8, &service, &xp)){ + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + if(r->flags & 1){ + disconnecttree(r->tid); + r->tid = 0xFFFF; + } + if((t = connecttree(service, path, &err)) == nil){ + r->respond(r, err); + goto out; + } + if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2ww}#1w{ff}{.}", + xcmd, SMB_SUPPORT_SEARCH_BITS, + smbstrpack8, t->share->service, + r->o->strpack, t->share->fsname, &r->rp)){ + disconnecttree(t->tid); + r->respond(r, STATUS_INVALID_SMB); + } else { + r->tid = t->tid; + smbcmd(r, xcmd, h, xp, e); + } +out: + free(service); + free(path); +} + +enum { + READ_WRITE_LOCK = 0x00, + SHARED_LOCK = 0x01, + OPLOCK_RELEASE = 0x02, + CHANGE_LOCKTYPE = 0x04, + CANCEL_LOCK = 0x08, + LARGE_FILES = 0x10, +}; + +void +smblockingandx(Req *r, uchar *h, uchar *p, uchar *e) +{ + int i, err, xcmd, fid, tol, timeout, nunlock, nlock, pid, loff, hoff, llen, hlen; + uchar *d, *de, *xp; + vlong off, len; + File *f; + + f = nil; + if(!unpack(h, p, e, "#0b{*2b_@2wwb_lww}#1w[]{?.}", + &xcmd, &fid, &tol, &timeout, &nunlock, &nlock, &d, &de, &xp)){ +unsup: + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + if((f = getfile(r->tid, fid, nil, &err)) == nil){ + r->respond(r, err); + goto out; + } + if(debug) + fprint(2, "tol %x\ntimeout %d\nunnlock %d\nnlock %d\n", tol, timeout, nunlock, nlock); + if(tol & (SHARED_LOCK | CHANGE_LOCKTYPE)) + goto unsup; + for(i=0; irh, r->rp, r->re, "#0b{*2b_@2w}#1w{}{.}", xcmd, &r->rp)) + r->respond(r, STATUS_INVALID_SMB); + else + smbcmd(r, xcmd, h, xp, e); +out: + putfile(f); +} + +enum { + REQ_ATTRIB = 0x01, + REQ_OPLOCK = 0x02, + REQ_OPLOCK_BATCH = 0x04, +}; + +void +smbopenandx(Req *r, uchar *h, uchar *p, uchar *e) +{ + int err, nfid, xcmd, flags, amode, omode, fattr, act, csize, ctime; + char *name, *path; + uchar *xp; + Tree *t; + File *f; + Dir *d; + + static int amode2dacc[] = { + [0x00] GENERIC_READ, + [0x01] GENERIC_WRITE, + [0x02] GENERIC_READ | GENERIC_WRITE, + [0x03] GENERIC_EXECUTE, + }, amode2sacc[] = { + [0x00] FILE_SHARE_COMPAT, /* compat */ + [0x01] FILE_SHARE_NONE, /* exclusive use */ + [0x02] FILE_SHARE_READ, /* deny write */ + [0x03] FILE_SHARE_WRITE, /* deny read */ + [0x04] FILE_SHARE_READ | FILE_SHARE_WRITE, /* shared read/write */ + [0x05] -1, + [0x06] -1, + [0x07] -1, + }, omode2cdisp[] = { + [0x00] -1, + [0x01] FILE_OPEN, + [0x02] FILE_OVERWRITE, + [0x03] -1, + [0x10] FILE_CREATE, + [0x11] FILE_OPEN_IF, + [0x12] FILE_OVERWRITE_IF, + [0x13] -1, + }; + + f = nil; + d = nil; + name = path = nil; + if(!unpack(h, p, e, "#0b{*2b_@2www__wlwl________}#1w{f}{?.}", + &xcmd, &flags, &amode, &fattr, &ctime, &omode, + &csize, r->o->nameunpack, &name, &xp)){ + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + if((path = getpath(r->tid, name, &t, &err)) == nil) + goto errout; + if((f = createfile(path, r->namecmp, + amode2dacc[amode & 3], amode2sacc[(amode>>4) & 7], omode2cdisp[omode & 0x13], + FILE_NON_DIRECTORY_FILE, (ulong)csize, fattr, + &act, (flags & REQ_ATTRIB) ? &d : nil, &err)) == nil){ +errout: + r->respond(r, err); + goto out; + } + nfid = newfid(t, f); + amode = -1; + if(f->dacc & READMASK) + amode += 1; + if(f->dacc & WRITEMASK) + amode += 2; + if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2wwwllww__w______}#1w{}{.}", + xcmd, nfid, + !d ? 0 : dosfileattr(d), + !d ? 0 : d->mtime+tzoff, + !d ? 0 : filesize32(d->length), + !d ? 0 : amode, + !d ? 0 : f->rtype, + !d ? 0 : act, &r->rp)){ + delfid(t, nfid); + r->respond(r, STATUS_INVALID_SMB); + } else + smbcmd(r, xcmd, h, xp, e); +out: + free(name); + free(path); + putfile(f); + free(d); +} + +enum { + NT_CREATE_REQUEST_OPLOCK = 0x02, + NT_CREATE_REQUEST_OPBATCH = 0x04, + NT_CREATE_OPEN_TARGET_DIR = 0x08, +}; + +void +smbntcreatendx(Req *r, uchar *h, uchar *p, uchar *e) +{ + int err, nfid, xcmd, flags, rootfid, fattr, dacc, sacc, cdisp, copt, act; + char *name, *path; + vlong csize; + uchar *xp; + Tree *t; + File *f; + Dir *d; + + f = nil; + d = nil; + name = path = nil; + if(!unpack(h, p, e, "#0b{*2b_@2w___lllvllll_____}#1w{f}{?.}", + &xcmd, &flags, &rootfid, &dacc, &csize, &fattr, &sacc, &cdisp, &copt, + r->o->nameunpack, &name, &xp)){ + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + if(rootfid){ + if((f = getfile(r->tid, rootfid, &t, &err)) == nil) + goto errout; + path = conspath(f->path, name); + putfile(f); + } else if((path = getpath(r->tid, name, &t, &err)) == nil) + goto errout; + if((f = createfile(path, r->namecmp, dacc, sacc, cdisp, copt, csize, fattr, &act, &d, &err)) == nil){ +errout: + r->respond(r, err); + goto out; + } + nfid = newfid(t, f); + if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2wbwlvvvvlvvw__b}#1w{}{.}", + xcmd, 0, nfid, act, tofiletime(d->mtime), tofiletime(d->atime), + tofiletime(d->mtime), tofiletime(d->mtime), extfileattr(d), + allocsize(d->length, t->share->blocksize), + d->length, f->rtype, d->qid.type == QTDIR, &r->rp)){ + delfid(t, nfid); + r->respond(r, STATUS_INVALID_SMB); + } else + smbcmd(r, xcmd, h, xp, e); +out: + free(name); + free(path); + putfile(f); + free(d); +} + +void +smbreadandx(Req *r, uchar *h, uchar *p, uchar *e) +{ + int n, xcmd, fid, mincount, maxcount, loff, hoff; + uchar *rb, *rp, *re, *xp; + vlong off; + File *f; + + f = nil; + hoff = 0; + if((unpack(h, p, e, "#0b{*2b_@2wwlww______l}#1w{}{?.}", + &xcmd, &fid, &loff, &mincount, &maxcount, &hoff, &xp) == 0) && + (unpack(h, p, e, "#0b{*2b_@2wwlww______}#1w{}{?.}", + &xcmd, &fid, &loff, &mincount, &maxcount, &xp) == 0)){ + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + off = (vlong)hoff<<32 | loff; + if((f = getfile(r->tid, fid, nil, &n)) == nil){ + r->respond(r, n); + goto out; + } + if((f->fd < 0) || (f->dacc & READMASK) == 0){ + r->respond(r, STATUS_ACCESS_DENIED); + goto out; + } + /* dont really pack, just to get the pointer to the response data */ + if(!pack(r->rh, r->rp, r->re, "#0b{*2________________________}#1w{%2.}", &rb)){ +badsmb: + r->respond(r, STATUS_INVALID_SMB); + goto out; + } + re = rb + maxcount; + if(re > r->re) + re = r->re; + if((rb + mincount) > re) + goto badsmb; + n = 0; + rp = rb; + while(rp < re){ + if((n = pread(f->fd, rp, re - rp, off)) <= 0) + break; + off += n; + rp += n; + } + if(n < 0){ + r->respond(r, smbmkerror()); + goto out; + } + if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@3www__#2w@2w__________}#1w{%2[]}{.}", + xcmd, 0xFFFF, 0x0000, rb, rp, &r->rp)) + goto badsmb; + smbcmd(r, xcmd, h, xp, e); +out: + putfile(f); +} + +void +smbwriteandx(Req *r, uchar *h, uchar *p, uchar *e) +{ + int n, xcmd, fid, loff, hoff, bufoff, buflen; + uchar *d, *de, *xp; + vlong off; + File *f; + + f = nil; + hoff = 0; + if((unpack(h, p, e, "#0b{*2b_@2wwl__________wwl}#1w{}{?.}", + &xcmd, &fid, &loff, &buflen, &bufoff, &hoff, &xp) == 0) && + (unpack(h, p, e, "#0b{*2b_@2wwl__________ww}#1w{}{?.}", + &xcmd, &fid, &loff, &buflen, &bufoff, &xp) == 0)){ + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + d = h + bufoff; + de = d + buflen; + if(d < h || de > e){ +badsmb: + r->respond(r, STATUS_INVALID_SMB); + goto out; + } + off = (vlong)hoff<<32 | loff; + if((f = getfile(r->tid, fid, nil, &n)) == nil){ + r->respond(r, n); + goto out; + } + if((f->fd < 0) || (f->dacc & WRITEMASK) == 0){ + r->respond(r, STATUS_ACCESS_DENIED); + goto out; + } + if((n = pwrite(f->fd, d, de - d, off)) < 0){ + r->respond(r, smbmkerror()); + goto out; + } + if(!pack(r->rh, r->rp, r->re, "#0b{*2b_@2www____}#1w{}{.}", xcmd, n, 0xFFFF, &r->rp)) + goto badsmb; + smbcmd(r, xcmd, h, xp, e); +out: + putfile(f); +} + +void +smbwrite(Req *r, uchar *h, uchar *p, uchar *e) +{ + int n, fid, count, bf, off; + uchar *d, *de; + File *f; + + f = nil; + if(!unpack(h, p, e, "#0b{*2wwl__}#1w{b#2w[]}", &fid, &count, &off, &bf, &d, &de)){ +unsup: + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + if(bf != 0x1) + goto unsup; + if((f = getfile(r->tid, fid, nil, &n)) == nil){ + r->respond(r, n); + goto out; + } + if((f->fd < 0) || (f->dacc & WRITEMASK) == 0){ + r->respond(r, STATUS_ACCESS_DENIED); + goto out; + } + if(count != (de - d)){ + r->respond(r, STATUS_INVALID_SMB); + goto out; + } + if((n = pwrite(f->fd, d, count, off)) < 0){ + r->respond(r, smbmkerror()); + goto out; + } + if(!pack(r->rh, r->rp, r->re, "#0b{*2w}#1w{}.", n, &r->rp)) + r->respond(r, STATUS_INVALID_SMB); + else + r->respond(r, 0); +out: + putfile(f); +} + +void +smbcloseflush(Req *r, uchar *h, uchar *p, uchar *e) +{ + int err, fid; + Tree *t; + Find *s; + File *f; + + f = nil; + s = nil; + if(!unpack(h, p, e, "#0b{*2w}#1w{}", &fid)){ + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + switch(r->cmd){ + case 0x05: /* SMB_COM_FLUSH */ + if(fid == 0xFFFF){ + if(gettree(r->tid) == nil){ + r->respond(r, STATUS_SMB_BAD_TID); + goto out; + } + break; + } + /* no break */ + case 0x04: /* SMB_COM_CLOSE */ + if((f = getfile(r->tid, fid, &t, &err)) == nil){ + r->respond(r, err); + goto out; + } + if(r->cmd == 0x04) + delfid(t, fid); + break; + case 0x34: /* SMB_COM_FIND_CLOSE2 */ + if((s = getfind(r->tid, fid, &t, &err)) == nil){ + r->respond(r, err); + goto out; + } + delsid(t, fid); + break; + } + if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp)) + r->respond(r, STATUS_INVALID_SMB); + else + r->respond(r, 0); +out: + putfile(f); + putfind(s); +} + +void +smbcreatedirectory(Req *r, uchar *h, uchar *p, uchar *e) +{ + char *name, *path; + int err, fd; + + name = path = nil; + if(!unpack(h, p, e, "#0b{*2}#1w{_f}", r->o->nameunpack, &name)){ + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + if((path = getpath(r->tid, name, nil, &err)) == nil){ + r->respond(r, err); + goto out; + } + if(access(path, AEXIST) == 0){ + r->respond(r, STATUS_OBJECT_NAME_COLLISION); + goto out; + } + if((fd = create(path, OREAD, DMDIR | 0777)) < 0){ + r->respond(r, smbmkerror()); + goto out; + } + close(fd); + if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp)) + r->respond(r, STATUS_INVALID_SMB); + else + r->respond(r, 0); +out: + free(name); + free(path); +} + +void +smbrename(Req *r, uchar *h, uchar *p, uchar *e) +{ + char *name1, *name2, *path1, *path2, *x, *y; + int err, sattr; + Dir *d, nd; + + d = nil; + name1 = name2 = path1 = path2 = nil; + if(!unpack(h, p, e, "#0b{*2w}#1w{_f_f}", &sattr, + r->o->nameunpack, &name1, r->o->nameunpack, &name2)){ + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + if((path1 = getpath(r->tid, name1, nil, &err)) == nil){ + r->respond(r, err); + goto out; + } + if((path2 = getpath(r->tid, name2, nil, &err)) == nil){ + r->respond(r, err); + goto out; + } + if((d = xdirstat(&path1, r->namecmp)) == nil){ + r->respond(r, smbmkerror()); + goto out; + } + if(!matchattr(d, sattr) || (dosfileattr(d) & ATTR_READONLY)){ + r->respond(r, STATUS_NO_SUCH_FILE); + goto out; + } + + if(x = strrchr(path1, '/')){ + *x = 0; + } else { +badpath: + r->respond(r, STATUS_OBJECT_PATH_SYNTAX_BAD); + goto out; + } + if(y = strrchr(path2, '/')) + *y++ = 0; + else + goto badpath; + if(r->namecmp(path1, path2)){ + r->respond(r, STATUS_NOT_SAME_DEVICE); + goto out; + } + *x = '/'; + nulldir(&nd); + nd.name = y; + if(dirwstat(path1, &nd) < 0){ + r->respond(r, smbmkerror()); + goto out; + } + if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp)) + r->respond(r, STATUS_INVALID_SMB); + else + r->respond(r, 0); + xdirflush(path1, r->namecmp); +out: + free(name1); + free(name2); + free(path1); + free(path2); + free(d); +} + +void +smbdelete(Req *r, uchar *h, uchar *p, uchar *e) +{ + char *name, *path, *tmp; + int n, err, i, sattr; + Find *f; + Dir *d; + + f = nil; + name = path = nil; + if(!unpack(h, p, e, "#0b{*2w}#1w{_f}", &sattr, r->o->nameunpack, &name)){ + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + if((path = getpath(r->tid, name, nil, &err)) == nil){ +errout: + r->respond(r, err); + goto out; + } + if((f = openfind(path, r->namecmp, sattr, 0, &err)) == nil) + goto errout; + n = 0; + while((i = readfind(f, f->index, &d)) >= 0){ + tmp = conspath(f->base, d->name); + if(remove(tmp) < 0){ + err = smbmkerror(); + free(tmp); + goto errout; + } + free(tmp); + f->index = i+1; + n++; + } + if(n == 0){ + err = STATUS_NO_SUCH_FILE; + goto errout; + } + if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp)) + r->respond(r, STATUS_INVALID_SMB); + else + r->respond(r, 0); +out: + free(name); + free(path); + putfind(f); +} + +void +smbdeletedirectory(Req *r, uchar *h, uchar *p, uchar *e) +{ + char *name, *path; + Dir *d; + int err; + + name = path = nil; + if(!unpack(h, p, e, "#0b{*2}#1w{_f}", r->o->nameunpack, &name)){ + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + if((path = getpath(r->tid, name, nil, &err)) == nil){ + r->respond(r, err); + goto out; + } + if(remove(path) < 0){ + err = smbmkerror(); + if((d = xdirstat(&path, r->namecmp)) == nil){ + r->respond(r, err); + goto out; + } + free(d); + if(remove(path) < 0){ + r->respond(r, smbmkerror()); + goto out; + } + } + if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp)) + r->respond(r, STATUS_INVALID_SMB); + else + r->respond(r, 0); +out: + free(name); + free(path); +} + +void +smbecho(Req *r, uchar *h, uchar *p, uchar *e) +{ + uchar *t, *d, *de; + int i, n; + + if(!unpack(h, p, e, "#0b{*2w}#1w[]", &n, &d, &de)){ + r->respond(r, STATUS_NOT_SUPPORTED); + return; + } + if((r->tid != 0xFFFF) && (gettree(r->tid) == nil)){ + r->respond(r, STATUS_SMB_BAD_TID); + return; + } + t = r->rp; + for(i=0; i < n; i++){ + if(!pack(r->rh, r->rp, r->re, "#0b{*2w}#1w[].", i, d, de, &r->rp)){ + r->respond(r, STATUS_INVALID_SMB); + break; + } + r->respond(r, 0); + r->rp = t; + } +} + +void +smbdisconnecttree(Req *r, uchar *h, uchar *p, uchar *e) +{ + int err; + + if(!unpack(h, p, e, "#0b{*2}#1w{}")){ + r->respond(r, STATUS_NOT_SUPPORTED); + return; + } + if(err = disconnecttree(r->tid)){ + r->respond(r, err); + return; + } + if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp)) + r->respond(r, STATUS_INVALID_SMB); + else + r->respond(r, 0); +} + +void +smbqueryinformation(Req *r, uchar *h, uchar *p, uchar *e) +{ + char *name, *path; + int err, mtime; + Dir *d; + + d = nil; + name = path = nil; + if(!unpack(h, p, e, "#0b{*2}#1w{_f}", r->o->nameunpack, &name)){ + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + if((path = getpath(r->tid, name, nil, &err)) == nil){ + r->respond(r, err); + goto out; + } + if((d = xdirstat(&path, r->namecmp)) == nil){ + r->respond(r, smbmkerror()); + goto out; + } + mtime = d->mtime + tzoff; + if(!pack(r->rh, r->rp, r->re, "#0b{*2wll__________}#1w{}.", + dosfileattr(d), mtime, filesize32(d->length), &r->rp)) + r->respond(r, STATUS_INVALID_SMB); + else + r->respond(r, 0); +out: + free(name); + free(path); + free(d); +} + +void +smbsetinformation(Req *r, uchar *h, uchar *p, uchar *e) +{ + char *name, *path; + int err, attr, mtime; + Dir *d, nd; + + d = nil; + name = path = nil; + if(!unpack(h, p, e, "#0b{*2wl__________}#1w{_f}", &attr, &mtime, r->o->nameunpack, &name)){ + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + if((path = getpath(r->tid, name, nil, &err)) == nil){ + r->respond(r, err); + goto out; + } + if((d = xdirstat(&path, r->namecmp)) == nil){ + r->respond(r, smbmkerror()); + goto out; + } + nulldir(&nd); + if(mtime) + nd.mtime = mtime-tzoff; + if(attr & ATTR_READONLY){ + if(d->mode & 0222) + nd.mode = d->mode & ~0222; + } else { + if((d->mode & 0222) == 0) + nd.mode = d->mode | 0222; + } + if(dirwstat(path, &nd) < 0){ + r->respond(r, smbmkerror()); + goto out; + } + if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp)) + r->respond(r, STATUS_INVALID_SMB); + else + r->respond(r, 0); + xdirflush(path, r->namecmp); +out: + free(name); + free(path); + free(d); +} + +void +smbcheckdirectory(Req *r, uchar *h, uchar *p, uchar *e) +{ + char *name, *path; + int err; + Dir *d; + + d = nil; + name = path = nil; + if(!unpack(h, p, e, "#0b{*2}#1w{_f}", r->o->nameunpack, &name)){ + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + if((path = getpath(r->tid, name, nil, &err)) == nil){ + r->respond(r, err); + goto out; + } + if((d = xdirstat(&path, r->namecmp)) == nil){ + r->respond(r, smbmkerror()); + goto out; + } + if(d->qid.type != QTDIR){ + r->respond(r, STATUS_OBJECT_PATH_NOT_FOUND); + goto out; + } + if(!pack(r->rh, r->rp, r->re, "#0b{*2}#1w{}.", &r->rp)) + r->respond(r, STATUS_INVALID_SMB); + else + r->respond(r, 0); +out: + free(name); + free(path); + free(d); +} + +void +smbqueryinformation2(Req *r, uchar *h, uchar *p, uchar *e) +{ + int err, fid, adate, atime, mdate, mtime; + Tree *t; + File *f; + Dir *d; + + f = nil; + t = nil; + d = nil; + if(!unpack(h, p, e, "#0b{*2w}#1w{}", &fid)){ + r->respond(r, STATUS_NOT_SUPPORTED); + goto out; + } + if((f = getfile(r->tid, fid, &t, &err)) == nil){ + r->respond(r, err); + goto out; + } + if((d = statfile(f)) == nil){ + r->respond(r, smbmkerror()); + goto out; + } + todatetime(d->atime+tzoff, &adate, &atime); + todatetime(d->mtime+tzoff, &mdate, &mtime); + if(!pack(r->rh, r->rp, r->re, "#0b{*2wwwwwwllw}#1w{}.", + mdate, mtime, adate, atime, mdate, mtime, + filesize32(d->length), filesize32(allocsize(d->length, t->share->blocksize)), + dosfileattr(d), &r->rp)) + r->respond(r, STATUS_INVALID_SMB); + else + r->respond(r, 0); +out: + putfile(f); + free(d); +} + +void +smbqueryinformationdisk(Req *r, uchar *h, uchar *p, uchar *e) +{ + Tree *t; + Share *s; + + if(!unpack(h, p, e, "#0b{*2}#1w{}")){ + r->respond(r, STATUS_NOT_SUPPORTED); + return; + } + if((t = gettree(r->tid)) == nil){ + r->respond(r, STATUS_SMB_BAD_TID); + return; + } + s = t->share; + if(!pack(r->rh, r->rp, r->re, "#0b{*2wwww__}#1w{}.", + (int)(allocsize(s->allocsize + s->freesize, s->blocksize) / s->blocksize), + s->blocksize / s->sectorsize, s->sectorsize, + (int)(allocsize(s->freesize, s->blocksize) / s->blocksize), &r->rp)) + r->respond(r, STATUS_INVALID_SMB); + else + r->respond(r, 0); +} + +static int +fpackdir(Req *r, Dir *d, Tree *t, int i, int level, uchar *b, uchar *p, uchar *e, uchar **prevoff, uchar **nameoff) +{ + vlong atime, mtime, alen, dlen; + uchar shortname[2*12]; + uchar *namep; + Share *share; + int n; + + share = t->share; + dlen = d->length; + alen = allocsize(dlen, share->blocksize); + atime = tofiletime(d->atime); + mtime = tofiletime(d->mtime); + memset(shortname, 0, sizeof(shortname)); + + switch(level){ + case 0x0101: /* SMB_FIND_FILE_DIRECTORY_INFO */ + n = pack(b, p, e, "llvvvvvvl#0l{.f}%4", + 0, i, mtime, atime, mtime, mtime, dlen, alen, extfileattr(d), + &namep, r->o->untermnamepack, d->name); + break; + + case 0x0102: /* SMB_FIND_FILE_FULL_DIRECTORY_INFO */ + n = pack(b, p, e, "llvvvvvvl#0ll{.f}%4", + 0, i, mtime, atime, mtime, mtime, dlen, alen, extfileattr(d), 0, + &namep, r->o->untermnamepack, d->name); + break; + + case 0x0103: /* SMB_FIND_FILE_NAMES_INFO */ + n = pack(b, p, e, "ll#0l{.f}%4", + 0, i, &namep, r->o->untermnamepack, d->name); + break; + + case 0x0104: /* SMB_FIND_FILE_BOTH_DIRECTORY_INFO */ + n = pack(b, p, e, "llvvvvvvl#1l#2lb_[]{.f}{}____%4", + 0, i, mtime, atime, mtime, mtime, dlen, alen, extfileattr(d), + 0, shortname, shortname+sizeof(shortname), + &namep, r->o->untermnamepack, d->name); + break; + + default: + logit("[%.4x] unknown FIND infolevel", level); + return -1; + } + if(n <= 0) + return 0; + if(nameoff) + *nameoff = namep; + if(prevoff && *prevoff) + pack(b, *prevoff, e, "l", (int)(p - *prevoff)); + if(prevoff) + *prevoff = p; + return n; +} + +static int +qpackdir(Req *, Dir *d, Tree *t, File *f, int level, uchar *b, uchar *p, uchar *e) +{ + vlong atime, mtime, dlen, alen; + int link, delete, isdir; + Share *share; + + if(debug) + fprint(2, "QYERY level %.4x\n", level); + + share = t->share; + dlen = d->length; + alen = allocsize(dlen, share->blocksize); + atime = tofiletime(d->atime); + mtime = tofiletime(d->mtime); + isdir = d->qid.type == QTDIR; + delete = f && deletedfile(f); + link = !delete; + + switch(level){ + case 0x0101: /* SMB_QUERY_FILE_BASIC_INFO */ + return pack(b, p, e, "vvvvl____", mtime, atime, mtime, mtime, extfileattr(d)); + + case 0x0102: /* SMB_QUERY_FILE_STANDARD_INFO */ + return pack(b, p, e, "vvlbb", alen, dlen, link, delete, isdir); + + case 0x0103: /* SMB_QUERY_FILE_EA_INFO */ + return pack(b, p, e, "l", 0); + + case 0x0107: /* SMB_QUERY_FILE_ALL_INFO */ + return pack(b, p, e, "vvvvl____vvlbb__#1l#0l{f}{}", + mtime, atime, mtime, mtime, extfileattr(d), alen, dlen, link, delete, isdir, + smbuntermnamepack16, d->name); + + case 0x0109: /* SMB_QUERY_FILE_STREAM_INFO */ + if(isdir) + return 0; + return pack(b, p, e, "l#0lvv{f}", 0, dlen, alen, smbuntermstrpack16, "::$DATA"); + + default: + logit("[%.4x] unknown QUERY infolevel", level); + return -1; + } +} + +void +trans2querypathinformation(Trans *t) +{ + char *name, *path; + Tree *tree; + int n, level; + Dir *d; + + d = nil; + path = name = nil; + if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "w____f", + &level, t->o->nameunpack, &name)){ + t->respond(t, STATUS_NOT_SUPPORTED); + goto out; + } + if((path = getpath(t->r->tid, name, &tree, &n)) == nil){ + t->respond(t, n); + goto out; + } + if((d = xdirstat(&path, t->namecmp)) == nil){ + t->respond(t, smbmkerror()); + goto out; + } + pack(t->out.param.b, t->out.param.p, t->out.param.e, "__.", &t->out.param.p); + if((n = qpackdir(t->r, d, tree, nil, level, t->out.data.b, t->out.data.p, t->out.data.e)) < 0) + t->respond(t, STATUS_OS2_INVALID_LEVEL); + else { + t->out.data.p += n; + t->respond(t, 0); + } +out: + free(name); + free(path); + free(d); +} + +void +trans2queryfileinformation(Trans *t) +{ + int n, fid, level; + Tree *tree; + File *f; + Dir *d; + + f = nil; + d = nil; + if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "ww", &fid, &level)){ + t->respond(t, STATUS_NOT_SUPPORTED); + goto out; + } + if((f = getfile(t->r->tid, fid, &tree, &n)) == nil){ + t->respond(t, n); + goto out; + } + if((d = statfile(f)) == nil){ + t->respond(t, smbmkerror()); + goto out; + } + pack(t->out.param.b, t->out.param.p, t->out.param.e, "__.", &t->out.param.p); + if((n = qpackdir(t->r, d, tree, f, level, t->out.data.b, t->out.data.p, t->out.data.e)) < 0) + t->respond(t, STATUS_OS2_INVALID_LEVEL); + else { + t->out.data.p += n; + t->respond(t, 0); + } +out: + putfile(f); + free(d); +} + +static int +setfilepathinformation(Req *r, Dir *d, File *f, char *path, int level, uchar *b, uchar *p, uchar *e) +{ + int attr, adt, atm, mdt, mtm, delete; + vlong len, atime, mtime; + Dir nd; + + nulldir(&nd); + if(debug) + fprint(2, "SET level %.4x\n", level); + switch(level){ + case 0x0001: /* SMB_INFO_STANDARD */ + if(!unpack(b, p, e, "____wwww__________", &adt, &atm, &mdt, &mtm)) + goto unsup; + nd.atime = fromdatetime(adt, atm)-tzoff; + nd.mtime = fromdatetime(mdt, mtm)-tzoff; + break; + + case 0x0101: /* SMB_SET_FILE_BASIC_INFO */ + if(f == nil || !unpack(b, p, e, "________vv________l____", &atime, &mtime, &attr)) + goto unsup; + if(atime && atime != -1LL) + nd.atime = fromfiletime(atime); + if(mtime && mtime != -1LL) + nd.mtime = fromfiletime(mtime); + if(attr){ + if(attr & ATTR_READONLY){ + if(d->mode & 0222) + nd.mode = d->mode & ~0222; + } else { + if((d->mode & 0222) == 0) + nd.mode = d->mode | 0222; + } + } + break; + + case 0x0102: /* SMB_SET_FILE_DISPOSITION_INFO */ + if(f == nil || !unpack(b, p, e, "b", &delete)) + goto unsup; + if((f->dacc & FILE_DELETE) == 0) + return STATUS_ACCESS_DENIED; + deletefile(f, delete); + break; + + case 0x0103: /* SMB_SET_FILE_ALLOCATION_INFO */ + case 0x0104: /* SMB_SET_FILE_END_OF_FILE_INFO */ + if(f == nil || !unpack(b, p, e, "v", &len)) + goto unsup; + if(d->qid.type == QTDIR) + return STATUS_OS2_INVALID_ACCESS; + if(len != -1LL) + nd.length = len; + break; + + default: + logit("[%.4x] unknown SET infolevel", level); + return STATUS_OS2_INVALID_LEVEL; + unsup: + return STATUS_NOT_SUPPORTED; + } + if(debug) + fprint(2, "wstat\nmode %lo\natime %ld\nmtime %ld\nlength %llux\n", + nd.mode, nd.atime, nd.mtime, nd.length); + if(((f && f->fd >= 0) ? dirfwstat(f->fd, &nd) : dirwstat(path, &nd)) < 0) + return smbmkerror(); + xdirflush(path, r->namecmp); + return 0; +} + +void +trans2setpathinformation(Trans *t) +{ + int err, level; + Tree *tree; + char *name, *path; + Dir *d; + + d = nil; + name = path = nil; + if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "w____f", &level, + t->o->nameunpack, &name)){ + t->respond(t, STATUS_NOT_SUPPORTED); + goto out; + } + if((path = getpath(t->r->tid, name, &tree, &err)) == nil) + goto errout; + if((d = xdirstat(&path, t->namecmp)) == nil){ + t->respond(t, smbmkerror()); + goto out; + } + if(err = setfilepathinformation(t->r, d, nil, path, level, t->in.data.b, t->in.data.p, t->in.data.e)){ +errout: + t->respond(t, err); + goto out; + } + pack(t->out.param.b, t->out.param.p, t->out.param.e, "__.", &t->out.param.p); + t->respond(t, 0); +out: + free(name); + free(path); + free(d); +} + +void +trans2setfileinformation(Trans *t) +{ + int err, fid, level; + Tree *tree; + File *f; + Dir *d; + + f = nil; + d = nil; + if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "ww__", &fid, &level)){ + t->respond(t, STATUS_NOT_SUPPORTED); + goto out; + } + if((f = getfile(t->r->tid, fid, &tree, &err)) == nil) + goto errout; + if((d = statfile(f)) == nil){ + t->respond(t, smbmkerror()); + goto out; + } + if(err = setfilepathinformation(t->r, d, f, f->path, level, t->in.data.b, t->in.data.p, t->in.data.e)){ +errout: + t->respond(t, err); + goto out; + } + pack(t->out.param.b, t->out.param.p, t->out.param.e, "__.", &t->out.param.p); + t->respond(t, 0); +out: + putfile(f); + free(d); +} + +enum { + FILE_CASE_SENSITIVE_SEARCH = 1, + FILE_CASE_PRESERVED_NAMES = 2, + FILE_UNICODE_ON_DISK = 4, +}; + +void +trans2queryfsinformation(Trans *t) +{ + int n, level; + Share *share; + Tree *tree; + char *s; + + s = nil; + if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "w", &level)){ + t->respond(t, STATUS_NOT_SUPPORTED); + goto out; + } + if((tree = gettree(t->r->tid)) == nil){ + t->respond(t, STATUS_SMB_BAD_TID); + goto out; + } + share = tree->share; + if(debug) + fprint(2, "FS level %.4x\n", level); + switch(level){ + case 0x0001: /* SMB_INFO_ALLOCATION */ + n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "llllw", + 0x00000000, share->blocksize/share->sectorsize, + filesize32(allocsize(share->allocsize+share->freesize, share->blocksize)/share->blocksize), + filesize32(allocsize(share->freesize, share->blocksize)/share->blocksize), + share->sectorsize); + break; + + case 0x0002: /* SMB_INFO_VOLUME */ + s = smprint("%.12s", share->name); + n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "l#0b{f}", + (int)namehash(share->root), smbuntermstrpack8, s); + break; + + case 0x0102: /* SMB_QUERY_FS_VOLUME_INFO */ + s = smprint("%.12s", share->name); + n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "vl#0l__{f}", + tofiletime(starttime), (int)namehash(share->root), smbuntermstrpack16, s); + break; + + case 0x0103: /* SMB_QUERY_FS_SIZE_INFO */ + n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "vvll", + allocsize(share->allocsize+share->freesize, share->blocksize)/share->blocksize, + allocsize(share->freesize, share->blocksize)/share->blocksize, + share->blocksize/share->sectorsize, + share->sectorsize); + break; + + case 0x0105: /* SMB_QUERY_FS_ATTRIBUTE_INFO */ + n = pack(t->out.data.b, t->out.data.p, t->out.data.e, "ll#0l{f}", + FILE_CASE_SENSITIVE_SEARCH | + FILE_CASE_PRESERVED_NAMES | + FILE_UNICODE_ON_DISK, + share->namelen, smbuntermstrpack16, share->fsname); + break; + + default: + logit("[%.4x] unknown FS infolevel", level); + t->respond(t, STATUS_OS2_INVALID_LEVEL); + goto out; + } + if(n <= 0) + t->respond(t, STATUS_INVALID_SMB); + else { + t->out.data.p += n; + t->respond(t, 0); + } +out: + free(s); +} + +enum { + SMB_FIND_CLOSE_AFTER_REQUEST = 0x1, + SMB_FIND_CLOSE_AT_EOS = 0x2, + SMB_FIND_RETURN_RESUME_KEYS = 0x4, + SMB_FIND_CONTINUE_FROM_LAST = 0x8, +}; + +void +trans2findfirst2(Trans *t) +{ + int i, nsid, eos, n, attr, count, flags, level; + uchar *prevoff, *nameoff; + char *name, *path; + Tree *tree; + Find *f; + Dir *d; + + f = nil; + name = path = nil; + if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "wwww____f", + &attr, &count, &flags, &level, t->o->nameunpack, &name)){ + t->respond(t, STATUS_NOT_SUPPORTED); + goto out; + } + if(debug) + fprint(2, "FIND level %.4x\n", level); + if((path = getpath(t->r->tid, name, &tree, &n)) == nil){ + t->respond(t, n); + goto out; + } + if((f = openfind(path, t->namecmp, attr, 1, &n)) == nil){ + t->respond(t, n); + goto out; + } + n = eos = 0; + prevoff = nameoff = nil; + for(i = 0; i < count; i++){ + if((eos = readfind(f, f->index, &d)) < 0) + break; + if((n = fpackdir(t->r, d, tree, 0, level, + t->out.data.b, t->out.data.p, t->out.data.e, + &prevoff, &nameoff)) <= 0) + break; + t->out.data.p += n; + f->index = eos + 1; + } + if((n < 0) || (flags & SMB_FIND_CLOSE_AFTER_REQUEST) || + ((flags & SMB_FIND_CLOSE_AT_EOS) && (eos < 0))){ + if(n < 0){ + t->respond(t, STATUS_OS2_INVALID_LEVEL); + goto out; + } + eos = -1; + nsid = 0; + } else { + nsid = newsid(tree, f); + } + if(!i && (eos < 0)){ + t->respond(t, STATUS_NO_MORE_FILES); + goto out; + } + if(!pack(t->out.param.b, t->out.param.p, t->out.param.e, "wwwww.", + nsid, i, (eos < 0), 0, (int)(nameoff - t->out.data.b), &t->out.param.p)){ + t->respond(t, STATUS_INVALID_SMB); + delsid(tree, nsid); + } else + t->respond(t, 0); +out: + free(name); + free(path); + putfind(f); +} + +void +trans2findnext2(Trans *t) +{ + int i, n, eos, sid, count, level, index, flags; + uchar *prevoff, *nameoff; + char *name; + Tree *tree; + Find *f; + Dir *d; + + f = nil; + name = nil; + if(!unpack(t->in.param.b, t->in.param.p, t->in.param.e, "wwwlwf", + &sid, &count, &level, &index, &flags, t->o->nameunpack, &name)){ + t->respond(t, STATUS_NOT_SUPPORTED); + goto out; + } + if(debug) + fprint(2, "FIND level %.4x\n", level); + if((f = getfind(t->r->tid, sid, &tree, &n)) == nil){ + t->respond(t, n); + goto out; + } + n = eos = 0; + if((flags & SMB_FIND_CONTINUE_FROM_LAST) == 0){ + f->index = 0; + while((eos = readfind(f, f->index, &d)) >= 0){ + f->index = eos + 1; + if(strcmp(name, d->name) == 0) + break; + } + } + prevoff = nameoff = nil; + for(i = 0; i < count; i++){ + if((eos = readfind(f, f->index, &d)) < 0) + break; + if((n = fpackdir(t->r, d, tree, 0, level, + t->out.data.b, t->out.data.p, t->out.data.e, + &prevoff, &nameoff)) <= 0) + break; + t->out.data.p += n; + f->index = eos + 1; + } + if((flags & SMB_FIND_CLOSE_AFTER_REQUEST) || + ((flags & SMB_FIND_CLOSE_AT_EOS) && (eos < 0))){ + delsid(tree, sid); + eos = -1; + } + if(!i && (eos < 0)){ + t->respond(t, STATUS_NO_MORE_FILES); + goto out; + } + if(!pack(t->out.param.b, t->out.param.p, t->out.param.e, "wwww.", + i, (eos < 0), 0, (int)(nameoff - t->out.data.b), &t->out.param.p)) + t->respond(t, STATUS_INVALID_SMB); + else + t->respond(t, 0); +out: + free(name); + putfind(f); +} + +static void +transrespond(Trans *t, int err) +{ + Req *r; + + r = t->r; + t->r = nil; + t->respond = nil; + if(!err && !pack(r->rh, r->rp, r->re, + "#0b{*2ww__#3w@3ww#4w@4ww#1b_[*2]}#2w{%4[]%4[]}.", + t->out.param.p - t->out.param.b, + t->out.data.p - t->out.data.b, + 0, 0, + t->out.setup.b, t->out.setup.p, + t->out.param.b, t->out.param.p, + t->out.data.b, t->out.data.p, &r->rp)) + r->respond(r, STATUS_INVALID_SMB); + else + r->respond(r, err); + free(t->out.param.b); + free(t->out.data.b); + free(t->out.setup.b); +} + +struct { + char *name; + void (*fun)(Trans *t); +} transoptab[] = { + [0x0000] { "TRANS_RAP", transrap }, +}, trans2optab[] = { + [0x0001] { "TRANS2_FIND_FIRST2", trans2findfirst2 }, + [0x0002] { "TRANS2_FIND_NEXT2", trans2findnext2 }, + [0x0003] { "TRANS2_QUERY_FS_INFORMATION", trans2queryfsinformation }, + [0x0005] { "TRANS2_QUERY_PATH_INFORMATION", trans2querypathinformation }, + [0x0007] { "TRANS2_QUERY_FILE_INFORMATION", trans2queryfileinformation }, + [0x0006] { "TRANS2_SET_PATH_INFORMATION", trans2setpathinformation }, + [0x0008] { "TRANS2_SET_FILE_INFORMATION", trans2setfileinformation }, +}; + +void +smbtransaction(Req *r, uchar *h, uchar *p, uchar *e) +{ + int tpc, tdc, rpc, rdc, rsc; + uchar *sa, *se, *da, *de, *pa, *pe; + void (*fun)(Trans *t); + Trans t; + + t.r = r; + t.o = r->o; + t.namecmp = r->namecmp; + t.cmd = 0; + t.respond = transrespond; + if(!unpack(h, p, e, "#0b{*2wwwwb_w______#3w@3w#4w@4w#1b_[*2]}#2w{[?][?]}", + &tpc, &tdc, &rpc, &rdc, &rsc, &t.flags, &sa, &se, &pa, &pe, &da, &de)){ +unsup: + r->respond(r, STATUS_NOT_SUPPORTED); + return; + } + unpack(sa, sa, se, "w", &t.cmd); + + switch(r->cmd){ + case 0x25: /* SMB_COM_TRANSACTION */ + if((t.cmd >= nelem(transoptab)) || ((fun = transoptab[t.cmd].fun) == nil)){ + logit("[%.4x] transaction subcommand not implemented", t.cmd); + goto unsup; + } + t.name = transoptab[t.cmd].name; + break; + case 0x32: /* SMB_COM_TRANSACTION2 */ + if((t.cmd >= nelem(trans2optab)) || ((fun = trans2optab[t.cmd].fun) == nil)){ + logit("[%.4x] transaction2 subcommand not implemented", t.cmd); + goto unsup; + } + t.name = trans2optab[t.cmd].name; + break; + default: + goto unsup; + } + + if((tpc > (pe - pa)) || (tdc > (de - da))){ + logit("[%.4x] %s request truncated", t.cmd, t.name); + goto unsup; + } + if(57+((rsc+1)&~1)+((rpc+3)&~3)+((rdc+3)&~3) > remotebuffersize){ + logit("[%.4x] %s response doesnt fit in client buffer", t.cmd, t.name); + goto unsup; + } + + t.in.param.b = t.in.param.p = pa; t.in.param.e = pe; + t.in.data.b = t.in.data.p = da; t.in.data.e = de; + t.in.setup.b = t.in.setup.p = sa; t.in.setup.e = se; + + t.out.param.b = t.out.param.p = t.out.param.e = (rpc > 0) ? malloc(rpc) : nil; + t.out.param.e += rpc; + t.out.data.b = t.out.data.p = t.out.data.e = (rdc > 0) ? malloc(rdc) : nil; + t.out.data.e += rdc; + t.out.setup.b = t.out.setup.p = t.out.setup.e = (rsc > 0) ? malloc(rsc) : nil; + t.out.setup.e += rsc; + + if(debug) + fprint(2, "[%.4x] %s\n", t.cmd, t.name); + (*fun)(&t); +} + +void +smbnoandxcommand(Req *r, uchar *, uchar *, uchar *) +{ + r->respond(r, (r->cmd == 0xFF) ? STATUS_INVALID_SMB : 0); +} + +struct { + char *name; + void (*fun)(Req *, uchar *, uchar *, uchar *); +} optab[] = { + [0x00] { "SMB_COM_CREATE_DIRECTORY", smbcreatedirectory }, + [0x01] { "SMB_COM_DELETE_DIRECTORY", smbdeletedirectory }, + [0x04] { "SMB_COM_CLOSE", smbcloseflush }, + [0x05] { "SMB_COM_FLUSH", smbcloseflush }, + [0x06] { "SMB_COM_DELETE", smbdelete }, + [0x07] { "SMB_COM_RENAME", smbrename }, + [0x08] { "SMB_COM_QUERY_INFORMATION", smbqueryinformation }, + [0x09] { "SMB_COM_SET_INFORMATION", smbsetinformation }, + [0x10] { "SMB_CON_CHECK_DIRECTORY", smbcheckdirectory }, + [0x0b] { "SMB_COM_WRITE", smbwrite }, + [0x23] { "SMB_COM_QUERY_INFORMATION2", smbqueryinformation2 }, + [0x24] { "SMB_COM_LOCKING_ANDX", smblockingandx }, + [0x25] { "SMB_COM_TRANSACTION", smbtransaction }, + [0x2b] { "SMB_COM_ECHO", smbecho }, + [0x2d] { "SMB_COM_OPEN_ANDX", smbopenandx }, + [0x2e] { "SMB_COM_READ_ANDX", smbreadandx }, + [0x2f] { "SMB_COM_WRITE_ANDX", smbwriteandx }, + [0x32] { "SMB_COM_TRANSACTION2", smbtransaction }, + [0x34] { "SMB_COM_FIND_CLOSE2", smbcloseflush }, + [0x71] { "SMB_COM_DISCONNECT_TREE", smbdisconnecttree }, + [0x72] { "SMB_COM_NEGOTIATE", smbnegotiate }, + [0x73] { "SMB_COM_SESSION_SETUP_ANX", smbsessionsetupandx }, + [0x74] { "SMB_COM_LOGOFF_ANDX", smblogoffandx }, + [0x75] { "SMB_COM_TREE_CONNECT_ANDX", smbtreeconnectandx }, + [0x80] { "SMB_COM_QUERY_INFORMATION_DISK", smbqueryinformationdisk }, + [0xa2] { "SMB_COM_NT_CREATE_ANDX", smbntcreatendx }, + [0xFF] { "SMB_COM_NO_ANDX_COMMAND", smbnoandxcommand }, +}; + +void +smbcmd(Req *r, int cmd, uchar *h, uchar *p, uchar *e) +{ + if((cmd >= nelem(optab)) || (optab[cmd].fun == nil)){ + logit("[%.2x] command not implemented", cmd); + r->respond(r, STATUS_NOT_SUPPORTED); + return; + } + r->name = optab[cmd].name; + if(debug) + fprint(2, "[%.2x] %s\n", cmd, r->name); + if((!negotiated && cmd != 0x72) || (negotiated && cmd == 0x72)){ + r->respond(r, STATUS_INVALID_SMB); + return; + } + if(!remoteuser){ + switch(cmd){ + case 0x72: /* SMB_COM_NEGOTIATE */ + case 0x73: /* SMB_COM_SESSION_SETUP_ANX */ + case 0x74: /* SMB_COM_LOGOFF_ANDX */ + case 0xFF: /* SMB_COM_NO_ANDX_COMMAND */ + break; + default: + logit("auth not completed in %s request", r->name); + case 0x75: /* SMB_COM_TREE_CONNECT_ANDX */ + r->respond(r, STATUS_LOGON_FAILURE); + return; + } + } else if(r->uid != sessionuid){ + switch(cmd){ + case 0x73: /* SMB_COM_SESSION_SETUP_ANX */ + case 0x2b: /* SMB_COM_ECHO */ + break; + default: + logit("bad uid %.4x in %s request", r->uid, r->name); + r->respond(r, STATUS_SMB_BAD_UID); + return; + } + } + (*optab[cmd].fun)(r, h, p, e); +} diff --git a/sys/src/cmd/ip/cifsd/tree.c b/sys/src/cmd/ip/cifsd/tree.c new file mode 100644 index 000000000..18865fc4e --- /dev/null +++ b/sys/src/cmd/ip/cifsd/tree.c @@ -0,0 +1,233 @@ +#include +#include +#include "dat.h" +#include "fns.h" + +static void* +getid(void **a, int n, int i) +{ + void *p; + if(i <= 0 || i > n || (p = a[i-1]) == nil) + return nil; + return p; +} + +static void +setid(void **a, int n, int i, void *p) +{ + assert(i > 0 || i <= n); + a[i-1] = p; +} + +static int +newid(void ***pa, int *pn, void *p) +{ + int i; + for(i=0; i < *pn; i++) + if((*pa)[i] == nil) + break; + if(i == *pn){ + (*pn)++; + if((i % 8) == 0) + *pa = realloc(*pa, (i + 8) * sizeof(p)); + } + (*pa)[i] = p; + return i+1; +} + +static void **tree; +static int ntree; + +static void +freetree(Tree *t) +{ + int i; + + if(t == nil) + return; + for(i = 0; i < t->nfile; i++) + putfile(t->file[i]); + for(i = 0; i < t->nfind; i++) + putfile(t->find[i]); + free(t->file); + free(t->find); + free(t); +} + +Tree* +connecttree(char *service, char *path, int *perr) +{ + Share *s; + Tree *t; + int err; + + t = nil; + if((s = mapshare(path)) == nil){ + err = STATUS_BAD_NETWORK_NAME; + goto out; + } + if(strcmp(service, "?????") && cistrcmp(service, s->service)){ + err = STATUS_BAD_DEVICE_TYPE; + goto out; + } + t = mallocz(sizeof(*t), 1); + t->share = s; + t->tid = newid(&tree, &ntree, t); + err = 0; +out: + if(perr) + *perr = err; + return t; +} + +int +disconnecttree(int tid) +{ + Tree *t; + + if((t = gettree(tid)) == nil) + return STATUS_SMB_BAD_TID; + setid(tree, ntree, tid, nil); + freetree(t); + return 0; +} + +void +logoff(void) +{ + int i; + + for(i=0; ishare->root); + return t; +} + +int +newfid(Tree *t, File *f) +{ + f->ref++; + return newid(&t->file, &t->nfile, f); +} + +void +delfid(Tree *t, int fid) +{ + File *f; + + if(f = getid(t->file, t->nfile, fid)){ + setid(t->file, t->nfile, fid, nil); + putfile(f); + } +} + +File* +getfile(int tid, int fid, Tree **ptree, int *perr) +{ + Tree *t; + File *f; + int err; + + f = nil; + if((t = gettree(tid)) == nil){ + err = STATUS_SMB_BAD_TID; + goto out; + } + if((f = getid(t->file, t->nfile, fid)) == nil){ + err = STATUS_SMB_BAD_FID; + goto out; + } + f->ref++; + err = 0; + if(debug) + fprint(2, "file [%x] %s\n", fid, f->path); +out: + if(perr) + *perr = err; + if(ptree) + *ptree = t; + return f; +} + +char* +getpath(int tid, char *name, Tree **ptree, int *perr) +{ + Tree *t; + char *p; + int err; + + if(t = gettree(tid)){ + err = 0; + p = conspath(t->share->root, name); + if(debug) + fprint(2, "path %s\n", p); + } else { + err = STATUS_SMB_BAD_TID; + p = nil; + } + if(perr) + *perr = err; + if(ptree) + *ptree = t; + return p; +} + +int +newsid(Tree *t, Find *f) +{ + f->ref++; + return newid(&t->find, &t->nfind, f); +} + +void +delsid(Tree *t, int sid) +{ + Find *f; + + if(f = getid(t->find, t->nfind, sid)){ + setid(t->find, t->nfind, sid, nil); + putfind(f); + } +} + +Find* +getfind(int tid, int sid, Tree **ptree, int *perr) +{ + Tree *t; + Find *f; + int err; + + f = nil; + if((t = gettree(tid)) == nil){ + err = STATUS_SMB_BAD_TID; + goto out; + } + if((f = getid(t->find, t->nfind, sid)) == nil){ + err = STATUS_SMB_BAD_FID; + goto out; + } + f->ref++; + err = 0; + if(debug) + fprint(2, "find [%x] %s %s\n", sid, f->base, f->pattern); +out: + if(perr) + *perr = err; + if(ptree) + *ptree = t; + return f; +} + + diff --git a/sys/src/cmd/ip/cifsd/util.c b/sys/src/cmd/ip/cifsd/util.c new file mode 100644 index 000000000..e168eb1d6 --- /dev/null +++ b/sys/src/cmd/ip/cifsd/util.c @@ -0,0 +1,409 @@ +#include +#include +#include "dat.h" +#include "fns.h" + +void +logit(char *fmt, ...) +{ + char buf[8192]; + va_list arg; + + va_start(arg, fmt); + vseprint(buf, buf + sizeof(buf), fmt, arg); + va_end(arg); + if(debug) + fprint(2, "%s\n", buf); + syslog(0, progname, "(%s\\%s) %s", remotesys, remoteuser, buf); +} + +char* +getremote(char *dir) +{ + int fd, n; + char remfile[256]; + static char buf[256]; + + sprint(remfile, "%s/remote", dir); + fd = open(remfile, OREAD); + if(fd < 0) + return nil; + if((n = read(fd, buf, sizeof(buf)-1))>0) + buf[n-1] = 0; + else + strcpy(buf, remfile); + close(fd); + return buf; +} + +char* +conspath(char *base, char *name) +{ + return cleanname(smprint("%s/%s", base, name ? name : "")); +} + +int +splitpath(char *path, char **base, char **name) +{ + char *p, *b; + + b = strdup(path); + if((p = strrchr(b, '/')) == nil){ + free(b); + if(name) + *name = nil; + if(base) + *base = nil; + return 0; + } + if(p == b){ + if(name) *name = strdup(p+1); + p[1] = 0; + } else { + *p++ = 0; + if(name) *name = strdup(p); + } + if(base) + *base = b; + else + free(b); + return 1; +} + +void +dumphex(char *s, uchar *h, uchar *e) +{ + int i, n; + + n = e - h; + for(i=0; imday) | ((tm->mon + 1) << 5) | ((tm->year - 80) << 9); + if(ptime) + *ptime = (tm->sec >> 1) | (tm->min << 5) | (tm->hour << 11); +} + +long +fromdatetime(int date, int time) +{ + Tm tm; + + strcpy(tm.zone, "GMT"); + tm.mday = date & 0x1f; + tm.mon = ((date >> 5) & 0xf) - 1; + tm.year = (date >> 9) + 80; + tm.yday = 0; + tm.sec = (time & 0x1f) << 1; + tm.min = (time >> 5) & 0x3f; + tm.hour = time >> 11; + return tm2sec(&tm); +} + +vlong +tofiletime(long time) +{ + return ((vlong)time + 11644473600LL) * 10000000; +} + +long +fromfiletime(vlong filetime) +{ + return filetime / 10000000 - 11644473600LL; +} + + +int +filesize32(vlong size) +{ + if(size > 0xFFFFFFFFUL) + return 0xFFFFFFFF; + return size; +} + +vlong +allocsize(vlong size, int blocksize) +{ + return ((size + blocksize-1)/blocksize)*blocksize; +} + +int +extfileattr(Dir *d) +{ + int a; + + a = (d->qid.type == QTDIR) ? ATTR_DIRECTORY : ATTR_NORMAL; + if((d->mode & 0222) == 0) + a |= ATTR_READONLY; + if(d->name[0] == '.' && d->name[1] && d->name[1] != '.') + a |= ATTR_HIDDEN; + return a; +} + +int +dosfileattr(Dir *d) +{ + return extfileattr(d) & DOSMASK; +} + +ulong +namehash(char *s) +{ + ulong h, t; + Rune r; + + h = 0; + while(*s){ + s += chartorune(&r, s); + r = toupperrune(r); + t = h & 0xf8000000; + h <<= 5; + h ^= t>>27; + h ^= (ulong)r; + } + return h; +} + +char* +strtr(char *s, Rune (*tr)(Rune)) +{ + char buf[UTFmax], *p, *w; + Rune r; + int n; + + p = s; + w = s; + while(*p){ + p += chartorune(&r, p); + r = (*tr)(r); + n = runetochar(buf, &r); + if(w + n <= p){ + memmove(w, buf, n); + w += n; + } + } + *w = 0; + return s; +} + +char* +strchrs(char *s, char *c) +{ + Rune r; + int n; + + while(*s){ + n = chartorune(&r, s); + if(strchr(c, r)) + return s; + s += n; + } + return nil; +} + +static int +strpack8(uchar *, uchar *p, uchar *e, char *s, int term, Rune (*tr)(Rune)) +{ + uchar *t; + Rune r; + + t = p; + while((p < e) && *s){ + s += chartorune(&r, s); + r = tr(r); + *p++ = r & 0x7F; + } + if(p >= e) + return 0; + if(term) + *p++ = 0; + return p - t; +} + +static int +strpack16(uchar *b, uchar *p, uchar *e, char *s, int term, Rune (*tr)(Rune)) +{ + unsigned int rr; + uchar *t; + Rune r; + + t = p; + if((p - b) % 2){ + if(p >= e) + return 0; + *p++ = 0; + } + while((p+1 < e) && *s){ + s += chartorune(&r, s); + rr = tr(r); + if(rr > 0xFFFF){ + if(p+3 >= e) + break; + rr -= 0x10000; + *p++ = (rr>>10) & 0xFF; + *p++ = ((rr>>18)&3) + 0xD8; + *p++ = rr & 0xFF; + *p++ = ((rr>>8)&3) + 0xDC; + } else { + *p++ = rr & 0xFF; + *p++ = rr>>8; + } + } + if(p+1 >= e) + return 0; + if(term){ + *p++ = 0; + *p++ = 0; + } + return p - t; +} + +static int +strunpack8(uchar *, uchar *p, uchar *e, char **dp, int term, Rune (*tr)(Rune)) +{ + uchar *t; + char *d; + Rune r; + int n; + + t = p; + n = 0; + while((p < e) && (!term || *p)){ + p++; + n++; + } + if(term && ((p >= e) || *p)) + return 0; + p -= n; + *dp = d = malloc(n*UTFmax+1); + while(n--){ + r = *p & 0x7F; + r = tr(r); + d += runetochar(d, &r); + p++; + } + *d = 0; + if(term) + p++; + return p - t; +} + +static int +strunpack16(uchar *b, uchar *p, uchar *e, char **dp, int term, Rune (*tr)(Rune)) +{ + unsigned int rr; + uchar *t; + char *d; + Rune r; + int n; + + t = p; + if((p - b) % 2) + p++; + n = 0; + while((p+1 < e) && (!term || (p[0] || p[1]))){ + p += 2; + n++; + } + if(term && ((p+1 >= e) || p[0] || p[1])) + return 0; + p -= 2*n; + *dp = d = malloc(n*UTFmax+1); + while(n--){ + if(p[1] >= 0xD8 && p[1] <= 0xDB){ + if(!n--) + break; + rr = ((p[0]<<10) | ((p[1]-0xD8)<<18) | p[2] | ((p[3]-0xDC)<<8))+0x10000; + p += 2; + } else + rr = p[0] | (p[1]<<8); + r = tr(rr); + d += runetochar(d, &r); + p += 2; + } + *d = 0; + if(term) + p += 2; + return p - t; +} + +static Rune +notr(Rune r) +{ + return r; +} + +static Rune +fromnametr(Rune r) +{ + switch(r){ + case '\\': + return '/'; + case ' ': + if(trspaces) + return 0xa0; + } + return r; +} + +static Rune +tonametr(Rune r) +{ + switch(r){ + case '/': + return '\\'; + case 0xa0: + if(trspaces) + return ' '; + } + return r; +} + +int smbstrpack8(uchar *b, uchar *p, uchar *e, void *arg){ + return strpack8(b, p, e, (char*)arg, 1, notr); +} +int smbstrpack16(uchar *b, uchar *p, uchar *e, void *arg){ + return strpack16(b, p, e, (char*)arg, 1, notr); +} +int smbstrunpack8(uchar *b, uchar *p, uchar *e, void *arg){ + return strunpack8(b, p, e, (char**)arg, 1, notr); +} +int smbstrunpack16(uchar *b, uchar *p, uchar *e, void *arg){ + return strunpack16(b, p, e, (char**)arg, 1, notr); +} +int smbnamepack8(uchar *b, uchar *p, uchar *e, void *arg){ + return strpack8(b, p, e, (char*)arg, 1, tonametr); +} +int smbnamepack16(uchar *b, uchar *p, uchar *e, void *arg){ + return strpack16(b, p, e, (char*)arg, 1, tonametr); +} +int smbnameunpack8(uchar *b, uchar *p, uchar *e, void *arg){ + return strunpack8(b, p, e, (char**)arg, 1, fromnametr); +} +int smbnameunpack16(uchar *b, uchar *p, uchar *e, void *arg){ + return strunpack16(b, p, e, (char**)arg, 1, fromnametr); +} +int smbuntermstrpack8(uchar *b, uchar *p, uchar *e, void *arg){ + return strpack8(b, p, e, (char*)arg, 0, notr); +} +int smbuntermstrpack16(uchar *b, uchar *p, uchar *e, void *arg){ + return strpack16(b, p, e, (char*)arg, 0, notr); +} +int smbuntermnamepack8(uchar *b, uchar *p, uchar *e, void *arg){ + return strpack8(b, p, e, (char*)arg, 0, tonametr); +} +int smbuntermnamepack16(uchar *b, uchar *p, uchar *e, void *arg){ + return strpack16(b, p, e, (char*)arg, 0, tonametr); +}