ref: 4b550911d969d0c5930d1eac5a9c8dc15c0595a6
parent: 7dfb8b6ec5832d48da20a91c41540c9dd0eeeda1
author: cinap_lenrek <cinap_lenrek@centraldogma>
date: Mon May 9 06:42:20 EDT 2011
add cifsd
--- /dev/null
+++ b/sys/man/8/cifsd
@@ -1,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)
--- /dev/null
+++ b/sys/src/cmd/ip/cifsd/README
@@ -1,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
--- /dev/null
+++ b/sys/src/cmd/ip/cifsd/dat.h
@@ -1,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,
+};
--- /dev/null
+++ b/sys/src/cmd/ip/cifsd/dir.c
@@ -1,0 +1,218 @@
+#include <u.h>
+#include <libc.h>
+#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<n; i++){
+ p += strlen(d[i].name)+1;
+ if(d[i].uid) p += strlen(d[i].uid)+1;
+ if(d[i].gid) p += strlen(d[i].gid)+1;
+ if(d[i].muid) p += strlen(d[i].muid)+1;
+ }
+ o = malloc(n*sizeof(*d) + (int)p);
+ memmove(o, d, n*sizeof(*d));
+ p = (char*)&o[n];
+ for(i=0; i<n; i++){
+ o[i].name = append(&p, d[i].name);
+ o[i].uid = append(&p, d[i].uid);
+ o[i].gid = append(&p, d[i].gid);
+ o[i].muid = append(&p, d[i].muid);
+ }
+ return o;
+}
+
+static int xdirread0(char **path, int (*namecmp)(char *, char *), Dir **d);
+
+int
+xdirread(char **path, int (*namecmp)(char *, char *), Dir **d)
+{
+ Dir *t;
+ int n;
+
+ if((n = xdirread0(path, namecmp, &t)) > 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; i<n; i++){
+ if(namecmp(t[i].name, name))
+ continue;
+ free(*path); *path = conspath(base, t[i].name);
+ d = xdirdup(&t[i], 1);
+ goto out;
+ }
+ werrstr("%s", err);
+out:
+ free(base);
+ free(name);
+ return d;
+}
+
+Dir*
+xdirstat(char **path, int (*namecmp)(char *, char *))
+{
+ return xdirstat0(path, namecmp, "name not found");
+}
+
+typedef struct XDir XDir;
+struct XDir
+{
+ Qid qid;
+ char *path;
+ int ndir;
+ Dir *dir;
+ XDir *next;
+};
+
+static void
+freexdir(XDir *x)
+{
+ free(x->path);
+ 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);
+}
--- /dev/null
+++ b/sys/src/cmd/ip/cifsd/error.c
@@ -1,0 +1,129 @@
+#include <u.h>
+#include <libc.h>
+#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;
+}
--- /dev/null
+++ b/sys/src/cmd/ip/cifsd/file.c
@@ -1,0 +1,319 @@
+#include <u.h>
+#include <libc.h>
+#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);
+}
+
--- /dev/null
+++ b/sys/src/cmd/ip/cifsd/find.c
@@ -1,0 +1,191 @@
+#include <u.h>
+#include <libc.h>
+#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);
+}
--- /dev/null
+++ b/sys/src/cmd/ip/cifsd/fns.h
@@ -1,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 *));
--- /dev/null
+++ b/sys/src/cmd/ip/cifsd/main.c
@@ -1,0 +1,214 @@
+#include <u.h>
+#include <libc.h>
+#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);
+}
--- /dev/null
+++ b/sys/src/cmd/ip/cifsd/mkfile
@@ -1,0 +1,21 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin/ip
+TARG=cifsd
+
+HFILES=dat.h fns.h
+
+OFILES=\
+ pack.$O \
+ util.$O \
+ error.$O \
+ smb.$O \
+ rap.$O \
+ share.$O \
+ tree.$O \
+ dir.$O \
+ file.$O \
+ find.$O \
+ main.$O \
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/sys/src/cmd/ip/cifsd/pack.c
@@ -1,0 +1,305 @@
+#include <u.h>
+#include <libc.h>
+#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;
+}
+
--- /dev/null
+++ b/sys/src/cmd/ip/cifsd/rap.c
@@ -1,0 +1,143 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#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;
+}
--- /dev/null
+++ b/sys/src/cmd/ip/cifsd/share.c
@@ -1,0 +1,124 @@
+#include <u.h>
+#include <libc.h>
+#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;
+}
--- /dev/null
+++ b/sys/src/cmd/ip/cifsd/smb.c
@@ -1,0 +1,1691 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#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; i<nunlock+nlock; i++){
+ if(tol & LARGE_FILES){
+ if(!unpack(d, d, de, "w__llll[]", &pid, &hoff, &loff, &hlen, &llen, &d, nil))
+ goto unsup;
+ } else {
+ if(!unpack(d, d, de, "wll[]", &pid, &loff, &llen, &d, nil))
+ goto unsup;
+ hoff = hlen = 0;
+ }
+ off = (vlong)hoff<<32 | loff;
+ len = (vlong)hlen<<32 | llen;
+ if(debug)
+ fprint(2, "%s %x %llux %llux\n", (i < nunlock) ? "unlock" : "lock", pid, off, len);
+ }
+ if(!pack(r->rh, 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);
+}
--- /dev/null
+++ b/sys/src/cmd/ip/cifsd/tree.c
@@ -1,0 +1,233 @@
+#include <u.h>
+#include <libc.h>
+#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; i<ntree; i++)
+ freetree(tree[i]);
+ free(tree);
+ tree = nil;
+ ntree = 0;
+}
+
+Tree*
+gettree(int tid)
+{
+ Tree *t;
+
+ if(t = getid(tree, ntree, tid))
+ if(debug)
+ fprint(2, "tree [%.4x] %s\n", tid, t->share->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;
+}
+
+
--- /dev/null
+++ b/sys/src/cmd/ip/cifsd/util.c
@@ -1,0 +1,409 @@
+#include <u.h>
+#include <libc.h>
+#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; i<n; i++){
+ if((i % 16) == 0)
+ fprint(2, "%s%s: [%.4x] ", i ? "\n" : "", s, i);
+ fprint(2, "%.2x ", (int)h[i]);
+ }
+ fprint(2, "\n");
+}
+
+void
+todatetime(long time, int *pdate, int *ptime)
+{
+ Tm *tm;
+
+ tm = gmtime(time);
+ if(pdate)
+ *pdate = (tm->mday) | ((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);
+}