summaryrefslogtreecommitdiff
path: root/sys/src/cmd/cifs
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/cifs
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/cifs')
-rwxr-xr-xsys/src/cmd/cifs/apinums.h222
-rwxr-xr-xsys/src/cmd/cifs/auth-testcase.c477
-rwxr-xr-xsys/src/cmd/cifs/auth.c416
-rwxr-xr-xsys/src/cmd/cifs/cifs.c806
-rwxr-xr-xsys/src/cmd/cifs/cifs.h638
-rwxr-xr-xsys/src/cmd/cifs/dfs.c407
-rwxr-xr-xsys/src/cmd/cifs/doserrstr.c187
-rwxr-xr-xsys/src/cmd/cifs/fs.c366
-rwxr-xr-xsys/src/cmd/cifs/info.c106
-rwxr-xr-xsys/src/cmd/cifs/main.c1283
-rwxr-xr-xsys/src/cmd/cifs/misc.c25
-rwxr-xr-xsys/src/cmd/cifs/mkfile20
-rwxr-xr-xsys/src/cmd/cifs/netbios.c475
-rwxr-xr-xsys/src/cmd/cifs/nterrstr.c1019
-rwxr-xr-xsys/src/cmd/cifs/pack.c463
-rwxr-xr-xsys/src/cmd/cifs/ping.c132
-rwxr-xr-xsys/src/cmd/cifs/raperrstr.c346
-rwxr-xr-xsys/src/cmd/cifs/remsmb.h466
-rwxr-xr-xsys/src/cmd/cifs/sid2name.c120
-rwxr-xr-xsys/src/cmd/cifs/trans.c785
-rwxr-xr-xsys/src/cmd/cifs/trans2.c539
-rwxr-xr-xsys/src/cmd/cifs/transnt.c167
22 files changed, 9465 insertions, 0 deletions
diff --git a/sys/src/cmd/cifs/apinums.h b/sys/src/cmd/cifs/apinums.h
new file mode 100755
index 000000000..6963d19c7
--- /dev/null
+++ b/sys/src/cmd/cifs/apinums.h
@@ -0,0 +1,222 @@
+/********************************************************************/
+/** Microsoft LAN Manager **/
+/** Copyright(c) Microsoft Corp., 1987-1991 **/
+/********************************************************************/
+
+#define API_WShareEnum 0
+#define API_WShareGetInfo 1
+#define API_WShareSetInfo 2
+#define API_WShareAdd 3
+#define API_WShareDel 4
+#define API_NetShareCheck 5
+#define API_WSessionEnum 6
+#define API_WSessionGetInfo 7
+#define API_WSessionDel 8
+#define API_WConnectionEnum 9
+#define API_WFileEnum 10
+#define API_WFileGetInfo 11
+#define API_WFileClose 12
+#define API_WServerGetInfo 13
+#define API_WServerSetInfo 14
+#define API_WServerDiskEnum 15
+#define API_WServerAdminCommand 16
+#define API_NetAuditOpen 17
+#define API_WAuditClear 18
+#define API_NetErrorLogOpen 19
+#define API_WErrorLogClear 20
+#define API_NetCharDevEnum 21
+#define API_NetCharDevGetInfo 22
+#define API_WCharDevControl 23
+#define API_NetCharDevQEnum 24
+#define API_NetCharDevQGetInfo 25
+#define API_WCharDevQSetInfo 26
+#define API_WCharDevQPurge 27
+#define API_WCharDevQPurgeSelf 28
+#define API_WMessageNameEnum 29
+#define API_WMessageNameGetInfo 30
+#define API_WMessageNameAdd 31
+#define API_WMessageNameDel 32
+#define API_WMessageNameFwd 33
+#define API_WMessageNameUnFwd 34
+#define API_WMessageBufferSend 35
+#define API_WMessageFileSend 36
+#define API_WMessageLogFileSet 37
+#define API_WMessageLogFileGet 38
+#define API_WServiceEnum 39
+#define API_WServiceInstall 40
+#define API_WServiceControl 41
+#define API_WAccessEnum 42
+#define API_WAccessGetInfo 43
+#define API_WAccessSetInfo 44
+#define API_WAccessAdd 45
+#define API_WAccessDel 46
+#define API_WGroupEnum 47
+#define API_WGroupAdd 48
+#define API_WGroupDel 49
+#define API_WGroupAddUser 50
+#define API_WGroupDelUser 51
+#define API_WGroupGetUsers 52
+#define API_WUserEnum 53
+#define API_WUserAdd 54
+#define API_WUserDel 55
+#define API_WUserGetInfo 56
+#define API_WUserSetInfo 57
+#define API_WUserPasswordSet 58
+#define API_WUserGetGroups 59
+#define API_DeadTableEntry 60
+/* This line and number replaced a Dead Entry */
+#define API_WWkstaSetUID 62
+#define API_WWkstaGetInfo 63
+#define API_WWkstaSetInfo 64
+#define API_WUseEnum 65
+#define API_WUseAdd 66
+#define API_WUseDel 67
+#define API_WUseGetInfo 68
+#define API_WPrintQEnum 69
+#define API_WPrintQGetInfo 70
+#define API_WPrintQSetInfo 71
+#define API_WPrintQAdd 72
+#define API_WPrintQDel 73
+#define API_WPrintQPause 74
+#define API_WPrintQContinue 75
+#define API_WPrintJobEnum 76
+#define API_WPrintJobGetInfo 77
+#define API_WPrintJobSetInfo_OLD 78
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+#define API_WPrintJobDel 81
+#define API_WPrintJobPause 82
+#define API_WPrintJobContinue 83
+#define API_WPrintDestEnum 84
+#define API_WPrintDestGetInfo 85
+#define API_WPrintDestControl 86
+#define API_WProfileSave 87
+#define API_WProfileLoad 88
+#define API_WStatisticsGet 89
+#define API_WStatisticsClear 90
+#define API_NetRemoteTOD 91
+#define API_WNetBiosEnum 92
+#define API_WNetBiosGetInfo 93
+#define API_NetServerEnum 94
+#define API_I_NetServerEnum 95
+#define API_WServiceGetInfo 96
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+#define API_WPrintQPurge 103
+#define API_NetServerEnum2 104
+#define API_WAccessGetUserPerms 105
+#define API_WGroupGetInfo 106
+#define API_WGroupSetInfo 107
+#define API_WGroupSetUsers 108
+#define API_WUserSetGroups 109
+#define API_WUserModalsGet 110
+#define API_WUserModalsSet 111
+#define API_WFileEnum2 112
+#define API_WUserAdd2 113
+#define API_WUserSetInfo2 114
+#define API_WUserPasswordSet2 115
+#define API_I_NetServerEnum2 116
+#define API_WConfigGet2 117
+#define API_WConfigGetAll2 118
+#define API_WGetDCName 119
+#define API_NetHandleGetInfo 120
+#define API_NetHandleSetInfo 121
+#define API_WStatisticsGet2 122
+#define API_WBuildGetInfo 123
+#define API_WFileGetInfo2 124
+#define API_WFileClose2 125
+#define API_WNetServerReqChallenge 126
+#define API_WNetServerAuthenticate 127
+#define API_WNetServerPasswordSet 128
+#define API_WNetAccountDeltas 129
+#define API_WNetAccountSync 130
+#define API_WUserEnum2 131
+#define API_WWkstaUserLogon 132
+#define API_WWkstaUserLogoff 133
+#define API_WLogonEnum 134
+#define API_WErrorLogRead 135
+#define API_WI_NetPathType 136
+#define API_WI_NetPathCanonicalize 137
+#define API_WI_NetPathCompare 138
+#define API_WI_NetNameValidate 139
+#define API_WI_NetNameCanonicalize 140
+#define API_WI_NetNameCompare 141
+#define API_WAuditRead 142
+#define API_WPrintDestAdd 143
+#define API_WPrintDestSetInfo 144
+#define API_WPrintDestDel 145
+#define API_WUserValidate2 146
+#define API_WPrintJobSetInfo 147
+#define API_TI_NetServerDiskEnum 148
+#define API_TI_NetServerDiskGetInfo 149
+#define API_TI_FTVerifyMirror 150
+#define API_TI_FTAbortVerify 151
+#define API_TI_FTGetInfo 152
+#define API_TI_FTSetInfo 153
+#define API_TI_FTLockDisk 154
+#define API_TI_FTFixError 155
+#define API_TI_FTAbortFix 156
+#define API_TI_FTDiagnoseError 157
+#define API_TI_FTGetDriveStats 158
+/* This line and number replaced a Dead Entry */
+#define API_TI_FTErrorGetInfo 160
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+#define API_NetAccessCheck 163
+#define API_NetAlertRaise 164
+#define API_NetAlertStart 165
+#define API_NetAlertStop 166
+#define API_NetAuditWrite 167
+#define API_NetIRemoteAPI 168
+#define API_NetServiceStatus 169
+#define API_I_NetServerRegister 170
+#define API_I_NetServerDeregister 171
+#define API_I_NetSessionEntryMake 172
+#define API_I_NetSessionEntryClear 173
+#define API_I_NetSessionEntryGetInfo 174
+#define API_I_NetSessionEntrySetInfo 175
+#define API_I_NetConnectionEntryMake 176
+#define API_I_NetConnectionEntryClear 177
+#define API_I_NetConnectionEntrySetInfo 178
+#define API_I_NetConnectionEntryGetInfo 179
+#define API_I_NetFileEntryMake 180
+#define API_I_NetFileEntryClear 181
+#define API_I_NetFileEntrySetInfo 182
+#define API_I_NetFileEntryGetInfo 183
+#define API_AltSrvMessageBufferSend 184
+#define API_AltSrvMessageFileSend 185
+#define API_wI_NetRplWkstaEnum 186
+#define API_wI_NetRplWkstaGetInfo 187
+#define API_wI_NetRplWkstaSetInfo 188
+#define API_wI_NetRplWkstaAdd 189
+#define API_wI_NetRplWkstaDel 190
+#define API_wI_NetRplProfileEnum 191
+#define API_wI_NetRplProfileGetInfo 192
+#define API_wI_NetRplProfileSetInfo 193
+#define API_wI_NetRplProfileAdd 194
+#define API_wI_NetRplProfileDel 195
+#define API_wI_NetRplProfileClone 196
+#define API_wI_NetRplBaseProfileEnum 197
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+#define API_WIServerSetInfo 201
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+/* This line and number replaced a Dead Entry */
+#define API_WPrintDriverEnum 205
+#define API_WPrintQProcessorEnum 206
+#define API_WPrintPortEnum 207
+#define API_WNetWriteUpdateLog 208
+#define API_WNetAccountUpdate 209
+#define API_WNetAccountConfirmUpdate 210
+#define API_WConfigSet 211
+#define API_WAccountsReplicate 212
+/* 213 is used by WfW */
+#define API_SamOEMChgPasswordUser2_P 214
+#define API_NetServerEnum3 215
+#define MAX_API 215
diff --git a/sys/src/cmd/cifs/auth-testcase.c b/sys/src/cmd/cifs/auth-testcase.c
new file mode 100755
index 000000000..0bc74a773
--- /dev/null
+++ b/sys/src/cmd/cifs/auth-testcase.c
@@ -0,0 +1,477 @@
+/*
+ * Beware the LM hash is easy to crack (google for l0phtCrack)
+ * and though NTLM is more secure it is still breakable.
+ * Ntlmv2 is better and seen as good enough by the Windows community.
+ * For real security use Kerberos.
+ */
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <auth.h>
+#include <libsec.h>
+#include <ctype.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "cifs.h"
+
+#define NTLMV2_TEST 1
+#define DEF_AUTH "ntlmv2"
+
+static enum {
+ MACkeylen = 40, /* MAC key len */
+ MAClen = 8, /* signature length */
+ MACoff = 14, /* sign. offset from start of SMB (not netbios) pkt */
+ Bliplen = 8, /* size of LMv2 client nonce */
+};
+
+static void
+dmp(char *s, int seq, void *buf, int n)
+{
+ int i;
+ char *p = buf;
+
+ print("%s %3d ", s, seq);
+ while(n > 0){
+ for(i = 0; i < 16 && n > 0; i++, n--)
+ print("%02x ", *p++ & 0xff);
+ if(n > 0)
+ print("\n");
+ }
+ print("\n");
+}
+
+static Auth *
+auth_plain(char *windom, char *keyp, uchar *chal, int len)
+{
+ UserPasswd *up;
+ static Auth *ap;
+
+ USED(chal, len);
+
+ up = auth_getuserpasswd(auth_getkey, "windom=%s proto=pass service=cifs %s",
+ windom, keyp);
+ if(! up)
+ sysfatal("cannot get key - %r");
+
+ ap = emalloc9p(sizeof(Auth));
+ memset(ap, 0, sizeof(ap));
+ ap->user = estrdup9p(up->user);
+ ap->windom = estrdup9p(windom);
+
+ ap->resp[0] = estrdup9p(up->passwd);
+ ap->len[0] = strlen(up->passwd);
+ memset(up->passwd, 0, strlen(up->passwd));
+ free(up);
+
+ return ap;
+}
+
+static Auth *
+auth_lm_and_ntlm(char *windom, char *keyp, uchar *chal, int len)
+{
+ int err;
+ Auth *ap;
+ char user[64];
+ MSchapreply mcr;
+
+ err = auth_respond(chal, len, user, sizeof user, &mcr, sizeof mcr,
+ auth_getkey, "windom=%s proto=mschap role=client service=cifs %s",
+ windom, keyp);
+ if(err == -1)
+ sysfatal("cannot get key - %r");
+
+ ap = emalloc9p(sizeof(Auth));
+ memset(ap, 0, sizeof(ap));
+ ap->user = estrdup9p(user);
+ ap->windom = estrdup9p(windom);
+
+ /* LM response */
+ ap->len[0] = sizeof(mcr.LMresp);
+ ap->resp[0] = emalloc9p(ap->len[0]);
+ memcpy(ap->resp[0], mcr.LMresp, ap->len[0]);
+
+ /* NTLM response */
+ ap->len[1] = sizeof(mcr.NTresp);
+ ap->resp[1] = emalloc9p(ap->len[1]);
+ memcpy(ap->resp[1], mcr.NTresp, ap->len[1]);
+
+ return ap;
+}
+
+/*
+ * NTLM response only, the LM response is a just
+ * copy of the NTLM one. We do this because the lm
+ * response is easily reversed - Google for l0pht for more info.
+ */
+static Auth *
+auth_ntlm(char *windom, char *keyp, uchar *chal, int len)
+{
+ Auth *ap;
+
+ if((ap = auth_lm_and_ntlm(windom, keyp, chal, len)) == nil)
+ return nil;
+
+ free(ap->resp[0]);
+ ap->len[0] = ap->len[1];
+ ap->resp[0] = emalloc9p(ap->len[0]);
+ memcpy(ap->resp[0], ap->resp[1], ap->len[0]);
+ return ap;
+}
+
+/*
+ * This is not really nescessary as all fields hmac_md5'ed
+ * in the ntlmv2 protocol are less than 64 bytes long, however
+ * I still do this for completeness.
+ */
+static DigestState *
+hmac_t64(uchar *data, ulong dlen, uchar *key, ulong klen, uchar *digest,
+ DigestState *state)
+{
+ if(klen > 64)
+ klen = 64;
+ return hmac_md5(data, dlen, key, klen, digest, state);
+}
+
+
+static int
+ntv2_blob(uchar *blob, int len, char *windom)
+{
+ int n;
+ uvlong nttime;
+ Rune r;
+ char *d;
+ uchar *p;
+ enum { /* name types */
+ Beof, /* end of name list */
+ Bnetbios, /* Netbios machine name */
+ Bdomain, /* Windows Domain name (NT) */
+ Bdnsfqdn, /* DNS Fully Qualified Domain Name */
+ Bdnsname, /* DNS machine name (win2k) */
+ };
+
+ p = blob;
+ *p++ = 1; /* response type */
+ *p++ = 1; /* max response type understood by client */
+
+ *p++ = 0;
+ *p++ = 0; /* 2 bytes reserved */
+
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0; /* 4 bytes unknown */
+
+#ifdef NTLMV2_TEST
+ *p++ = 0xf0;
+ *p++ = 0x20;
+ *p++ = 0xd0;
+ *p++ = 0xb6;
+ *p++ = 0xc2;
+ *p++ = 0x92;
+ *p++ = 0xbe;
+ *p++ = 0x01;
+#else
+ nttime = time(nil); /* nt time now */
+ nttime = nttime + 11644473600LL;
+ nttime = nttime * 10000000LL;
+ *p++ = nttime & 0xff;
+ *p++ = (nttime >> 8) & 0xff;
+ *p++ = (nttime >> 16) & 0xff;
+ *p++ = (nttime >> 24) & 0xff;
+ *p++ = (nttime >> 32) & 0xff;
+ *p++ = (nttime >> 40) & 0xff;
+ *p++ = (nttime >> 48) & 0xff;
+ *p++ = (nttime >> 56) & 0xff;
+#endif
+#ifdef NTLMV2_TEST
+ *p++ = 0x05;
+ *p++ = 0x83;
+ *p++ = 0x32;
+ *p++ = 0xec;
+ *p++ = 0xfa;
+ *p++ = 0xe4;
+ *p++ = 0xf3;
+ *p++ = 0x6d;
+#else
+ genrandom(p, 8);
+ p += 8; /* client nonce */
+#endif
+ *p++ = 0x6f;
+ *p++ = 0;
+ *p++ = 0x6e;
+ *p++ = 0; /* unknown data */
+
+ *p++ = Bdomain;
+ *p++ = 0; /* name type */
+
+ n = utflen(windom) * 2;
+ *p++ = n;
+ *p++ = n >> 8; /* name length */
+
+ d = windom;
+ while(*d && p - blob < len - 8){
+ d += chartorune(&r, d);
+ r = toupperrune(r);
+ *p++ = r;
+ *p++ = r >> 8;
+ }
+
+ *p++ = 0;
+ *p++ = Beof; /* name type */
+
+ *p++ = 0;
+ *p++ = 0; /* name length */
+
+ *p++ = 0x65;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0; /* unknown data */
+ return p - blob;
+}
+
+static Auth *
+auth_ntlmv2(char *windom, char *keyp, uchar *chal, int len)
+{
+ int i, n;
+ Rune r;
+ char *p, *u;
+ uchar c, lm_hmac[MD5dlen], nt_hmac[MD5dlen], nt_sesskey[MD5dlen];
+ uchar lm_sesskey[MD5dlen];
+ uchar v1hash[MD5dlen], blip[Bliplen], blob[1024], v2hash[MD5dlen];
+ DigestState *ds;
+ UserPasswd *up;
+ static Auth *ap;
+
+ up = auth_getuserpasswd(auth_getkey, "windom=%s proto=pass service=cifs-ntlmv2 %s",
+ windom, keyp);
+ if(! up)
+ sysfatal("cannot get key - %r");
+
+#ifdef NTLMV2_TEST
+{
+ static uchar srvchal[] = { 0x52, 0xaa, 0xc8, 0xe8, 0x2c, 0x06, 0x7f, 0xa1 };
+ up->user = "ADMINISTRATOR";
+ windom = "rocknroll";
+ chal = srvchal;
+}
+#endif
+ ap = emalloc9p(sizeof(Auth));
+ memset(ap, 0, sizeof(ap));
+
+ /* Standard says unlimited length, experience says 128 max */
+ if((n = strlen(up->passwd)) > 128)
+ n = 128;
+
+ ds = md4(nil, 0, nil, nil);
+ for(i = 0, p = up->passwd; i < n; i++) {
+ p += chartorune(&r, p);
+ c = r;
+ md4(&c, 1, nil, ds);
+ c = r >> 8;
+ md4(&c, 1, nil, ds);
+ }
+ md4(nil, 0, v1hash, ds);
+
+#ifdef NTLMV2_TEST
+{
+ uchar v1[] = {
+ 0x0c, 0xb6, 0x94, 0x88, 0x05, 0xf7, 0x97, 0xbf,
+ 0x2a, 0x82, 0x80, 0x79, 0x73, 0xb8, 0x95, 0x37
+ ;
+ memcpy(v1hash, v1, sizeof(v1));
+}
+#endif
+ /*
+ * Some documentation insists that the username must be forced to
+ * uppercase, but the domain name should not be. Other shows both
+ * being forced to uppercase. I am pretty sure this is irrevevant as
+ * the domain name passed from the remote server always seems to be in
+ * uppercase already.
+ */
+ ds = hmac_t64(nil, 0, v1hash, MD5dlen, nil, nil);
+ u = up->user;
+ while(*u){
+ u += chartorune(&r, u);
+ r = toupperrune(r);
+ c = r & 0xff;
+ hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+ c = r >> 8;
+ hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+ }
+ u = windom;
+
+ while(*u){
+ u += chartorune(&r, u);
+ c = r;
+ hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+ c = r >> 8;
+ hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+ }
+ hmac_t64(nil, 0, v1hash, MD5dlen, v2hash, ds);
+#ifdef NTLMV2_TEST
+ print("want: 40 e1 b3 24...\n");
+ dmp("v2hash==kr", 0, v2hash, MD5dlen);
+#endif
+ ap->user = estrdup9p(up->user);
+ ap->windom = estrdup9p(windom);
+
+ /* LM v2 */
+
+ genrandom(blip, Bliplen);
+#ifdef NTLMV2_TEST
+{
+ uchar t[] = { 0x05, 0x83, 0x32, 0xec, 0xfa, 0xe4, 0xf3, 0x6d };
+ memcpy(blip, t, 8);
+}
+#endif
+ ds = hmac_t64(chal, len, v2hash, MD5dlen, nil, nil);
+ hmac_t64(blip, Bliplen, v2hash, MD5dlen, lm_hmac, ds);
+ ap->len[0] = MD5dlen+Bliplen;
+ ap->resp[0] = emalloc9p(ap->len[0]);
+ memcpy(ap->resp[0], lm_hmac, MD5dlen);
+ memcpy(ap->resp[0]+MD5dlen, blip, Bliplen);
+#ifdef NTLMV2_TEST
+ print("want: 38 6b ae...\n");
+ dmp("lmv2 resp ", 0, lm_hmac, MD5dlen);
+#endif
+
+ /* LM v2 session key */
+ hmac_t64(lm_hmac, MD5dlen, v2hash, MD5dlen, lm_sesskey, nil);
+
+ /* LM v2 MAC key */
+ ap->mackey[0] = emalloc9p(MACkeylen);
+ memcpy(ap->mackey[0], lm_sesskey, MD5dlen);
+ memcpy(ap->mackey[0]+MD5dlen, ap->resp[0], MACkeylen-MD5dlen);
+
+ /* NTLM v2 */
+ n = ntv2_blob(blob, sizeof(blob), windom);
+ ds = hmac_t64(chal, len, v2hash, MD5dlen, nil, nil);
+ hmac_t64(blob, n, v2hash, MD5dlen, nt_hmac, ds);
+ ap->len[1] = MD5dlen+n;
+ ap->resp[1] = emalloc9p(ap->len[1]);
+ memcpy(ap->resp[1], nt_hmac, MD5dlen);
+ memcpy(ap->resp[1]+MD5dlen, blob, n);
+#ifdef NTLMV2_TEST
+ print("want: 1a ad 55...\n");
+ dmp("ntv2 resp ", 0, nt_hmac, MD5dlen);
+#endif
+
+ /* NTLM v2 session key */
+ hmac_t64(nt_hmac, MD5dlen, v2hash, MD5dlen, nt_sesskey, nil);
+
+ /* NTLM v2 MAC key */
+ ap->mackey[1] = emalloc9p(MACkeylen);
+ memcpy(ap->mackey[1], nt_sesskey, MD5dlen);
+ memcpy(ap->mackey[1]+MD5dlen, ap->resp[1], MACkeylen-MD5dlen);
+ free(up);
+
+ return ap;
+}
+
+struct {
+ char *name;
+ Auth *(*func)(char *, char *, uchar *, int);
+} methods[] = {
+ { "plain", auth_plain },
+ { "lm+ntlm", auth_lm_and_ntlm },
+ { "ntlm", auth_ntlm },
+ { "ntlmv2", auth_ntlmv2 },
+// { "kerberos", auth_kerberos },
+};
+
+void
+autherr(void)
+{
+ int i;
+
+ fprint(2, "supported auth methods:\t");
+ for(i = 0; i < nelem(methods); i++)
+ fprint(2, "%s ", methods[i].name);
+ fprint(2, "\n");
+ exits("usage");
+}
+
+Auth *
+getauth(char *name, char *windom, char *keyp, int secmode, uchar *chal, int len)
+{
+ int i;
+ Auth *ap;
+
+ if(name == nil){
+ name = DEF_AUTH;
+ if((secmode & SECMODE_PW_ENCRYPT) == 0)
+ sysfatal("plaintext authentication required, use '-a plain'");
+ }
+
+ ap = nil;
+ for(i = 0; i < nelem(methods); i++)
+ if(strcmp(methods[i].name, name) == 0){
+ ap = methods[i].func(windom, keyp, chal, len);
+ break;
+ }
+
+ if(! ap){
+ fprint(2, "%s: %s - unknown auth method\n", argv0, name);
+ autherr(); /* never returns */
+ }
+ return ap;
+}
+
+static int
+genmac(uchar *buf, int len, int seq, uchar key[MACkeylen], uchar mine[MAClen])
+{
+ DigestState *ds;
+ uchar *sig, digest[MD5dlen], their[MAClen];
+
+ sig = buf+MACoff;
+ memcpy(their, sig, MAClen);
+ memset(sig, 0, MAClen);
+ sig[0] = seq;
+ sig[1] = seq >> 8;
+ sig[2] = seq >> 16;
+ sig[3] = seq >> 24;
+
+ ds = md5(key, MACkeylen, nil, nil);
+ md5(buf, len, nil, ds);
+ md5(nil, 0, digest, ds);
+ memcpy(mine, digest, MAClen);
+
+ return memcmp(their, mine, MAClen);
+}
+
+int
+macsign(Pkt *p)
+{
+ int i, len;
+ uchar *sig, *buf, mac[MAClen], zeros[MACkeylen];
+
+ sig = p->buf + NBHDRLEN + MACoff;
+ buf = p->buf + NBHDRLEN;
+ len = (p->pos - p->buf) - NBHDRLEN;
+
+ for(i = -3; i < 4; i++){
+ memset(zeros, 0, sizeof(zeros));
+ if(genmac(buf, len, p->seq+i, zeros, mac) == 0){
+ dmp("got", 0, buf, len);
+ dmp("Zero OK", p->seq, mac, MAClen);
+ return 0;
+ }
+
+ if(genmac(buf, len, p->seq+i, p->s->auth->mackey[0], mac) == 0){
+ dmp("got", 0, buf, len);
+ dmp("LM-hash OK", p->seq, mac, MAClen);
+ return 0;
+ }
+
+ if(genmac(buf, len, p->seq+i, p->s->auth->mackey[1], mac) == 0){
+ dmp("got", 0, buf, len);
+ dmp("NT-hash OK", p->seq, mac, MAClen);
+ return 0;
+ }
+ }
+ genmac(buf, len, p->seq, p->s->auth->mackey[0], mac);
+
+ memcpy(sig, mac, MAClen);
+ return -1;
+}
diff --git a/sys/src/cmd/cifs/auth.c b/sys/src/cmd/cifs/auth.c
new file mode 100755
index 000000000..f72c03d2d
--- /dev/null
+++ b/sys/src/cmd/cifs/auth.c
@@ -0,0 +1,416 @@
+/*
+ * Beware the LM hash is easy to crack (google for l0phtCrack)
+ * and though NTLM is more secure it is still breakable.
+ * Ntlmv2 is better and seen as good enough by the windows community.
+ * For real security use kerberos.
+ */
+#include <u.h>
+#include <libc.h>
+#include <mp.h>
+#include <auth.h>
+#include <libsec.h>
+#include <ctype.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "cifs.h"
+
+#define DEF_AUTH "ntlmv2"
+
+static enum {
+ MACkeylen = 40, /* MAC key len */
+ MAClen = 8, /* signature length */
+ MACoff = 14, /* sign. offset from start of SMB (not netbios) pkt */
+ Bliplen = 8, /* size of LMv2 client nonce */
+};
+
+static void
+dmp(char *s, int seq, void *buf, int n)
+{
+ int i;
+ char *p = buf;
+
+ print("%s %3d ", s, seq);
+ while(n > 0){
+ for(i = 0; i < 16 && n > 0; i++, n--)
+ print("%02x ", *p++ & 0xff);
+ if(n > 0)
+ print("\n");
+ }
+ print("\n");
+}
+
+static Auth *
+auth_plain(char *windom, char *keyp, uchar *chal, int len)
+{
+ UserPasswd *up;
+ static Auth *ap;
+
+ USED(chal, len);
+
+ up = auth_getuserpasswd(auth_getkey, "windom=%s proto=pass service=cifs %s",
+ windom, keyp);
+ if(! up)
+ sysfatal("cannot get key - %r");
+
+ ap = emalloc9p(sizeof(Auth));
+ memset(ap, 0, sizeof(ap));
+ ap->user = estrdup9p(up->user);
+ ap->windom = estrdup9p(windom);
+
+ ap->resp[0] = estrdup9p(up->passwd);
+ ap->len[0] = strlen(up->passwd);
+ memset(up->passwd, 0, strlen(up->passwd));
+ free(up);
+
+ return ap;
+}
+
+static Auth *
+auth_lm_and_ntlm(char *windom, char *keyp, uchar *chal, int len)
+{
+ int err;
+ char user[64];
+ Auth *ap;
+ MSchapreply mcr;
+
+ err = auth_respond(chal, len, user, sizeof user, &mcr, sizeof mcr,
+ auth_getkey, "windom=%s proto=mschap role=client service=cifs %s",
+ windom, keyp);
+ if(err == -1)
+ sysfatal("cannot get key - %r");
+
+ ap = emalloc9p(sizeof(Auth));
+ memset(ap, 0, sizeof(ap));
+ ap->user = estrdup9p(user);
+ ap->windom = estrdup9p(windom);
+
+ /* LM response */
+ ap->len[0] = sizeof(mcr.LMresp);
+ ap->resp[0] = emalloc9p(ap->len[0]);
+ memcpy(ap->resp[0], mcr.LMresp, ap->len[0]);
+
+ /* NTLM response */
+ ap->len[1] = sizeof(mcr.NTresp);
+ ap->resp[1] = emalloc9p(ap->len[1]);
+ memcpy(ap->resp[1], mcr.NTresp, ap->len[1]);
+
+ return ap;
+}
+
+/*
+ * NTLM response only, the LM response is a just
+ * copy of the NTLM one. we do this because the lm
+ * response is easily reversed - Google for l0pht
+ * for more info.
+ */
+static Auth *
+auth_ntlm(char *windom, char *keyp, uchar *chal, int len)
+{
+ Auth *ap;
+
+ if((ap = auth_lm_and_ntlm(windom, keyp, chal, len)) == nil)
+ return nil;
+
+ free(ap->resp[0]);
+ ap->len[0] = ap->len[1];
+ ap->resp[0] = emalloc9p(ap->len[0]);
+ memcpy(ap->resp[0], ap->resp[1], ap->len[0]);
+ return ap;
+}
+
+/*
+ * This is not really nescessary as all fields hmac_md5'ed
+ * in the ntlmv2 protocol are less than 64 bytes long, however
+ * I still do this for completeness
+ */
+static DigestState *
+hmac_t64(uchar *data, ulong dlen, uchar *key, ulong klen, uchar *digest,
+ DigestState *state)
+{
+ if(klen > 64)
+ klen = 64;
+ return hmac_md5(data, dlen, key, klen, digest, state);
+}
+
+
+static int
+ntv2_blob(uchar *blob, int len, char *windom)
+{
+ int n;
+ uvlong nttime;
+ Rune r;
+ char *d;
+ uchar *p;
+ enum { /* name types */
+ Beof, /* end of name list */
+ Bnetbios, /* Netbios machine name */
+ Bdomain, /* Windows Domain name (NT) */
+ Bdnsfqdn, /* DNS Fully Qualified Domain Name */
+ Bdnsname, /* DNS machine name (win2k) */
+ };
+
+ p = blob;
+ *p++ = 1; /* response type */
+ *p++ = 1; /* max response type understood by client */
+
+ *p++ = 0;
+ *p++ = 0; /* 2 bytes reserved */
+
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0; /* 4 bytes unknown */
+
+ nttime = time(nil); /* nt time now */
+ nttime += 11644473600LL;
+ nttime *= 10000000LL;
+ *p++ = nttime;
+ *p++ = nttime >> 8;
+ *p++ = nttime >> 16;
+ *p++ = nttime >> 24;
+ *p++ = nttime >> 32;
+ *p++ = nttime >> 40;
+ *p++ = nttime >> 48;
+ *p++ = nttime >> 56;
+
+ genrandom(p, 8);
+ p += 8; /* client nonce */
+ *p++ = 0x6f;
+ *p++ = 0;
+ *p++ = 0x6e;
+ *p++ = 0; /* unknown data */
+
+ *p++ = Bdomain;
+ *p++ = 0; /* name type */
+
+ n = utflen(windom) * 2;
+ *p++ = n;
+ *p++ = n >> 8; /* name length */
+
+ d = windom;
+ while(*d && p-blob < (len-8)){
+ d += chartorune(&r, d);
+ r = toupperrune(r);
+ *p++ = r;
+ *p++ = r >> 8;
+ }
+
+ *p++ = 0;
+ *p++ = Beof; /* name type */
+
+ *p++ = 0;
+ *p++ = 0; /* name length */
+
+ *p++ = 0x65;
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0; /* unknown data */
+ return p - blob;
+}
+
+static Auth *
+auth_ntlmv2(char *windom, char *keyp, uchar *chal, int len)
+{
+ int i, n;
+ Rune r;
+ char *p, *u;
+ uchar v1hash[MD5dlen], blip[Bliplen], blob[1024], v2hash[MD5dlen];
+ uchar c, lm_hmac[MD5dlen], nt_hmac[MD5dlen], nt_sesskey[MD5dlen],
+ lm_sesskey[MD5dlen];
+ DigestState *ds;
+ UserPasswd *up;
+ static Auth *ap;
+
+ up = auth_getuserpasswd(auth_getkey, "windom=%s proto=pass service=cifs-ntlmv2 %s",
+ windom, keyp);
+ if(!up)
+ sysfatal("cannot get key - %r");
+
+ ap = emalloc9p(sizeof(Auth));
+ memset(ap, 0, sizeof(ap));
+
+ /* Standard says unlimited length, experience says 128 max */
+ if((n = strlen(up->passwd)) > 128)
+ n = 128;
+
+ ds = md4(nil, 0, nil, nil);
+ for(i=0, p=up->passwd; i < n; i++) {
+ p += chartorune(&r, p);
+ c = r;
+ md4(&c, 1, nil, ds);
+ c = r >> 8;
+ md4(&c, 1, nil, ds);
+ }
+ md4(nil, 0, v1hash, ds);
+
+ /*
+ * Some documentation insists that the username must be forced to
+ * uppercase, but the domain name should not be. Other shows both
+ * being forced to uppercase. I am pretty sure this is irrevevant as the
+ * domain name passed from the remote server always seems to be in
+ * uppercase already.
+ */
+ ds = hmac_t64(nil, 0, v1hash, MD5dlen, nil, nil);
+ u = up->user;
+ while(*u){
+ u += chartorune(&r, u);
+ r = toupperrune(r);
+ c = r;
+ hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+ c = r >> 8;
+ hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+ }
+ u = windom;
+
+ while(*u){
+ u += chartorune(&r, u);
+ c = r;
+ hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+ c = r >> 8;
+ hmac_t64(&c, 1, v1hash, MD5dlen, nil, ds);
+ }
+ hmac_t64(nil, 0, v1hash, MD5dlen, v2hash, ds);
+ ap->user = estrdup9p(up->user);
+ ap->windom = estrdup9p(windom);
+
+ /* LM v2 */
+
+ genrandom(blip, Bliplen);
+ ds = hmac_t64(chal, len, v2hash, MD5dlen, nil, nil);
+ hmac_t64(blip, Bliplen, v2hash, MD5dlen, lm_hmac, ds);
+ ap->len[0] = MD5dlen+Bliplen;
+ ap->resp[0] = emalloc9p(ap->len[0]);
+ memcpy(ap->resp[0], lm_hmac, MD5dlen);
+ memcpy(ap->resp[0]+MD5dlen, blip, Bliplen);
+
+ /* LM v2 session key */
+ hmac_t64(lm_hmac, MD5dlen, v2hash, MD5dlen, lm_sesskey, nil);
+
+ /* LM v2 MAC key */
+ ap->mackey[0] = emalloc9p(MACkeylen);
+ memcpy(ap->mackey[0], lm_sesskey, MD5dlen);
+ memcpy(ap->mackey[0]+MD5dlen, ap->resp[0], MACkeylen-MD5dlen);
+
+ /* NTLM v2 */
+ n = ntv2_blob(blob, sizeof(blob), windom);
+ ds = hmac_t64(chal, len, v2hash, MD5dlen, nil, nil);
+ hmac_t64(blob, n, v2hash, MD5dlen, nt_hmac, ds);
+ ap->len[1] = MD5dlen+n;
+ ap->resp[1] = emalloc9p(ap->len[1]);
+ memcpy(ap->resp[1], nt_hmac, MD5dlen);
+ memcpy(ap->resp[1]+MD5dlen, blob, n);
+
+ /*
+ * v2hash definitely OK by
+ * the time we get here.
+ */
+ /* NTLM v2 session key */
+ hmac_t64(nt_hmac, MD5dlen, v2hash, MD5dlen, nt_sesskey, nil);
+
+ /* NTLM v2 MAC key */
+ ap->mackey[1] = emalloc9p(MACkeylen);
+ memcpy(ap->mackey[1], nt_sesskey, MD5dlen);
+ memcpy(ap->mackey[1]+MD5dlen, ap->resp[1], MACkeylen-MD5dlen);
+ free(up);
+
+ return ap;
+}
+
+struct {
+ char *name;
+ Auth *(*func)(char *, char *, uchar *, int);
+} methods[] = {
+ { "plain", auth_plain },
+ { "lm+ntlm", auth_lm_and_ntlm },
+ { "ntlm", auth_ntlm },
+ { "ntlmv2", auth_ntlmv2 },
+// { "kerberos", auth_kerberos },
+};
+
+void
+autherr(void)
+{
+ int i;
+
+ fprint(2, "supported auth methods:\t");
+ for(i = 0; i < nelem(methods); i++)
+ fprint(2, "%s ", methods[i].name);
+ fprint(2, "\n");
+ exits("usage");
+}
+
+Auth *
+getauth(char *name, char *windom, char *keyp, int secmode, uchar *chal, int len)
+{
+ int i;
+ Auth *ap;
+
+ if(name == nil){
+ name = DEF_AUTH;
+ if((secmode & SECMODE_PW_ENCRYPT) == 0)
+ sysfatal("plaintext authentication required, use '-a plain'");
+ }
+
+ ap = nil;
+ for(i = 0; i < nelem(methods); i++)
+ if(strcmp(methods[i].name, name) == 0){
+ ap = methods[i].func(windom, keyp, chal, len);
+ break;
+ }
+
+ if(! ap){
+ fprint(2, "%s: %s - unknown auth method\n", argv0, name);
+ autherr(); /* never returns */
+ }
+ return ap;
+}
+
+static int
+genmac(uchar *buf, int len, int seq, uchar key[MACkeylen], uchar ours[MAClen])
+{
+ DigestState *ds;
+ uchar *sig, digest[MD5dlen], theirs[MAClen];
+
+ sig = buf+MACoff;
+ memcpy(theirs, sig, MAClen);
+
+ memset(sig, 0, MAClen);
+ sig[0] = seq;
+ sig[1] = seq >> 8;
+ sig[2] = seq >> 16;
+ sig[3] = seq >> 24;
+
+ ds = md5(key, MACkeylen, nil, nil);
+ md5(buf, len, digest, ds);
+ memcpy(ours, digest, MAClen);
+
+ return memcmp(theirs, ours, MAClen);
+}
+
+int
+macsign(Pkt *p, int seq)
+{
+ int rc, len;
+ uchar *sig, *buf, mac[MAClen];
+
+ sig = p->buf + NBHDRLEN + MACoff;
+ buf = p->buf + NBHDRLEN;
+ len = (p->pos - p->buf) - NBHDRLEN;
+
+#ifdef DEBUG_MAC
+ if(seq & 1)
+ dmp("rx", seq, sig, MAClen);
+#endif
+ rc = 0;
+ if(! p->s->seqrun)
+ memcpy(mac, "BSRSPYL ", 8); /* no idea, ask MS */
+ else
+ rc = genmac(buf, len, seq, p->s->auth->mackey[0], mac);
+#ifdef DEBUG_MAC
+ if(!(seq & 1))
+ dmp("tx", seq, mac, MAClen);
+#endif
+ memcpy(sig, mac, MAClen);
+ return rc;
+}
diff --git a/sys/src/cmd/cifs/cifs.c b/sys/src/cmd/cifs/cifs.c
new file mode 100755
index 000000000..68b6a636c
--- /dev/null
+++ b/sys/src/cmd/cifs/cifs.c
@@ -0,0 +1,806 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "cifs.h"
+
+static char magic[] = { 0xff, 'S', 'M', 'B' };
+
+Session *
+cifsdial(char *host, char *called, char *sysname)
+{
+ int nbt, fd;
+ char *addr;
+ Session *s;
+
+ if(Debug)
+ fprint(2, "cifsdial: host=%s called=%s sysname=%s\n", host, called, sysname);
+
+ if((addr = netmkaddr(host, "tcp", "cifs")) == nil)
+ return nil;
+
+ nbt = 0;
+ if((fd = dial(addr, nil, nil, nil)) == -1){
+ nbt = 1;
+ if((fd = nbtdial(host, called, sysname)) == -1)
+ return nil;
+ }
+
+ s = emalloc9p(sizeof(Session));
+ memset(s, 0, sizeof(Session));
+
+ s->fd = fd;
+ s->nbt = nbt;
+ s->mtu = MTU;
+ s->pid = getpid();
+ s->mid = time(nil) ^ getpid();
+ s->uid = NO_UID;
+ s->seq = 0;
+ s->seqrun = 0;
+ s->secmode = SECMODE_SIGN_ENABLED; /* hope for the best */
+ s->flags2 = FL2_KNOWS_LONG_NAMES | FL2_HAS_LONG_NAMES | FL2_PAGEING_IO;
+ s->macidx = -1;
+
+ return s;
+}
+
+void
+cifsclose(Session *s)
+{
+ if(s->fd)
+ close(s->fd);
+ free(s);
+}
+
+Pkt *
+cifshdr(Session *s, Share *sp, int cmd)
+{
+ Pkt *p;
+ int sign, tid, dfs;
+
+ dfs = 0;
+ tid = NO_TID;
+ Active = IDLE_TIME;
+ werrstr("");
+ sign = s->secmode & SECMODE_SIGN_ENABLED? FL2_PACKET_SIGNATURES: 0;
+
+ if(sp){
+ tid = sp->tid;
+// FIXME! if(sp->options & SMB_SHARE_IS_IN_DFS)
+// FIXME! dfs = FL2_DFS;
+ }
+
+ p = emalloc9p(sizeof(Pkt) + MTU);
+ memset(p, 0, sizeof(Pkt) +MTU);
+
+ p->buf = (uchar *)p + sizeof(Pkt);
+ p->s = s;
+
+ qlock(&s->seqlock);
+ if(s->seqrun){
+ p->seq = s->seq;
+ s->seq = (s->seq + 2) % 0x10000;
+ }
+ qunlock(&s->seqlock);
+
+ nbthdr(p);
+ pmem(p, magic, nelem(magic));
+ p8(p, cmd);
+ pl32(p, 0); /* status (error) */
+ p8(p, FL_CASELESS_NAMES | FL_CANNONICAL_NAMES); /* flags */
+ pl16(p, s->flags2 | dfs | sign); /* flags2 */
+ pl16(p, (s->pid >> 16) & 0xffff); /* PID MS bits */
+ pl32(p, p->seq); /* MAC / sequence number */
+ pl32(p, 0); /* MAC */
+ pl16(p, 0); /* padding */
+
+ pl16(p, tid);
+ pl16(p, s->pid & 0xffff);
+ pl16(p, s->uid);
+ pl16(p, s->mid);
+
+ p->wordbase = p8(p, 0); /* filled in by pbytes() */
+
+ return p;
+}
+
+void
+pbytes(Pkt *p)
+{
+ int n;
+
+ assert(p->wordbase != nil); /* cifshdr not called */
+ assert(p->bytebase == nil); /* called twice */
+
+ n = p->pos - p->wordbase;
+ assert(n % 2 != 0); /* even addr */
+ *p->wordbase = n / 2;
+
+ p->bytebase = pl16(p, 0); /* filled in by cifsrpc() */
+}
+
+static void
+dmp(int seq, uchar *buf)
+{
+ int i;
+
+ if(seq == 99)
+ print("\n ");
+ else
+ print("%+2d ", seq);
+ for(i = 0; i < 8; i++)
+ print("%02x ", buf[i] & 0xff);
+ print("\n");
+}
+
+int
+cifsrpc(Pkt *p)
+{
+ int flags2, got, err;
+ uint tid, uid, seq;
+ uchar *pos;
+ char m[nelem(magic)];
+
+ pos = p->pos;
+ if(p->bytebase){
+ p->pos = p->bytebase;
+ pl16(p, pos - (p->bytebase + 2)); /* 2 = sizeof bytecount */
+ }
+ p->pos = pos;
+
+ if(p->s->secmode & SECMODE_SIGN_ENABLED)
+ macsign(p, p->seq);
+
+ qlock(&p->s->rpclock);
+ got = nbtrpc(p);
+ qunlock(&p->s->rpclock);
+ if(got == -1)
+ return -1;
+
+ gmem(p, m, nelem(magic));
+ if(memcmp(m, magic, nelem(magic)) != 0){
+ werrstr("cifsrpc: bad magic number in packet %20ux%02ux%02ux%02ux",
+ m[0], m[1], m[2], m[3]);
+ return -1;
+ }
+
+ g8(p); /* cmd */
+ err = gl32(p); /* errcode */
+ g8(p); /* flags */
+ flags2 = gl16(p); /* flags2 */
+ gl16(p); /* PID MS bits */
+ seq = gl32(p); /* reserved */
+ gl32(p); /* MAC (if in use) */
+ gl16(p); /* Padding */
+ tid = gl16(p); /* TID */
+ gl16(p); /* PID lsbs */
+ uid = gl16(p); /* UID */
+ gl16(p); /* mid */
+ g8(p); /* word count */
+
+ if(p->s->secmode & SECMODE_SIGN_ENABLED){
+ if(macsign(p, p->seq+1) != 0 && p->s->seqrun){
+ werrstr("cifsrpc: invalid packet signature");
+print("MAC signature bad\n");
+// FIXME: for debug only return -1;
+ }
+ }else{
+ /*
+ * We allow the sequence number of zero as some old samba
+ * servers seem to fall back to this unexpectedly
+ * after reporting sequence numbers correctly for a while.
+ *
+ * Some other samba servers seem to always report a sequence
+ * number of zero if MAC signing is disabled, so we have to
+ * catch that too.
+ */
+ if(p->s->seqrun && seq != p->seq && seq != 0){
+ print("%ux != %ux bad sequence number\n", seq, p->seq);
+ return -1;
+ }
+ }
+
+ p->tid = tid;
+ if(p->s->uid == NO_UID)
+ p->s->uid = uid;
+
+ if(flags2 & FL2_NT_ERRCODES){
+ /* is it a real error rather than info/warning/chatter? */
+ if((err & 0xF0000000) == 0xC0000000){
+ werrstr("%s", nterrstr(err));
+ return -1;
+ }
+ }else{
+ if(err){
+ werrstr("%s", doserrstr(err));
+ return -1;
+ }
+ }
+ return got;
+}
+
+
+/*
+ * Some older servers (old samba) prefer to talk older
+ * dialects but if given no choice they will talk the
+ * more modern ones, so we don't give them the choice.
+ */
+int
+CIFSnegotiate(Session *s, long *svrtime, char *domain, int domlen, char *cname,
+ int cnamlen)
+{
+ int d, i;
+ char *ispeak = "NT LM 0.12";
+ static char *dialects[] = {
+// { "PC NETWORK PROGRAM 1.0"},
+// { "MICROSOFT NETWORKS 1.03"},
+// { "MICROSOFT NETWORKS 3.0"},
+// { "LANMAN1.0"},
+// { "LM1.2X002"},
+// { "NT LANMAN 1.0"},
+ { "NT LM 0.12" },
+ };
+ Pkt *p;
+
+ p = cifshdr(s, nil, SMB_COM_NEGOTIATE);
+ pbytes(p);
+ for(i = 0; i < nelem(dialects); i++){
+ p8(p, STR_DIALECT);
+ pstr(p, dialects[i]);
+ }
+
+ if(cifsrpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ d = gl16(p);
+ if(d < 0 || d > nelem(dialects)){
+ werrstr("no CIFS dialect in common");
+ free(p);
+ return -1;
+ }
+
+ if(strcmp(dialects[d], ispeak) != 0){
+ werrstr("%s dialect unsupported", dialects[d]);
+ free(p);
+ return -1;
+ }
+
+ s->secmode = g8(p); /* Security mode */
+
+ gl16(p); /* Max outstanding requests */
+ gl16(p); /* Max VCs */
+ s->mtu = gl32(p); /* Max buffer size */
+ gl32(p); /* Max raw buffer size (depricated) */
+ gl32(p); /* Session key */
+ s->caps = gl32(p); /* Server capabilities */
+ *svrtime = gvtime(p); /* fileserver time */
+ s->tz = (short)gl16(p) * 60; /* TZ in mins, is signed (SNIA doc is wrong) */
+ s->challen = g8(p); /* Encryption key length */
+ gl16(p);
+ gmem(p, s->chal, s->challen); /* Get the challenge */
+ gstr(p, domain, domlen); /* source domain */
+
+ { /* NetApp Filer seem not to report its called name */
+ char *cn = emalloc9p(cnamlen);
+
+ gstr(p, cn, cnamlen); /* their name */
+ if(strlen(cn) > 0)
+ memcpy(cname, cn, cnamlen);
+ free(cn);
+ }
+
+ if(s->caps & CAP_UNICODE)
+ s->flags2 |= FL2_UNICODE;
+
+ free(p);
+ return 0;
+}
+
+int
+CIFSsession(Session *s)
+{
+ char os[64], *q;
+ Rune r;
+ Pkt *p;
+ enum {
+ mycaps = CAP_UNICODE | CAP_LARGE_FILES | CAP_NT_SMBS |
+ CAP_NT_FIND | CAP_STATUS32,
+ };
+
+ s->seqrun = 1; /* activate the sequence number generation/checking */
+
+ p = cifshdr(s, nil, SMB_COM_SESSION_SETUP_ANDX);
+ p8(p, 0xFF); /* No secondary command */
+ p8(p, 0); /* Reserved (must be zero) */
+ pl16(p, 0); /* Offset to next command */
+ pl16(p, MTU); /* my max buffer size */
+ pl16(p, 1); /* my max multiplexed pending requests */
+ pl16(p, 0); /* Virtual connection # */
+ pl32(p, 0); /* Session key (if vc != 0) */
+
+
+ if((s->secmode & SECMODE_PW_ENCRYPT) == 0) {
+ pl16(p, utflen(Sess->auth->resp[0])*2 + 2); /* passwd size */
+ pl16(p, utflen(Sess->auth->resp[0])*2 + 2); /* passwd size (UPPER CASE) */
+ pl32(p, 0); /* Reserved */
+ pl32(p, mycaps);
+ pbytes(p);
+
+ for(q = Sess->auth->resp[0]; *q; ){
+ q += chartorune(&r, q);
+ pl16(p, toupperrune(r));
+ }
+ pl16(p, 0);
+
+ for(q = Sess->auth->resp[0]; *q; ){
+ q += chartorune(&r, q);
+ pl16(p, r);
+ }
+ pl16(p, 0);
+ }else{
+ pl16(p, Sess->auth->len[0]); /* LM passwd size */
+ pl16(p, Sess->auth->len[1]); /* NTLM passwd size */
+ pl32(p, 0); /* Reserved */
+ pl32(p, mycaps);
+ pbytes(p);
+
+ pmem(p, Sess->auth->resp[0], Sess->auth->len[0]);
+ pmem(p, Sess->auth->resp[1], Sess->auth->len[1]);
+ }
+
+ pstr(p, Sess->auth->user); /* Account name */
+ pstr(p, Sess->auth->windom); /* Primary domain */
+ pstr(p, "plan9"); /* Client OS */
+ pstr(p, argv0); /* Client LAN Manager type */
+
+ if(cifsrpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ g8(p); /* Reserved (0) */
+ gl16(p); /* Offset to next command wordcount */
+ Sess->isguest = gl16(p) & 1; /* logged in as guest */
+
+ gl16(p);
+ gl16(p);
+ /* no security blob here - we don't understand extended security anyway */
+ gstr(p, os, sizeof(os));
+ s->remos = estrdup9p(os);
+
+ free(p);
+ return 0;
+}
+
+
+CIFStreeconnect(Session *s, char *cname, char *tree, Share *sp)
+{
+ int len;
+ char *resp, *path;
+ char zeros[24];
+ Pkt *p;
+
+ resp = Sess->auth->resp[0];
+ len = Sess->auth->len[0];
+ if((s->secmode & SECMODE_USER) != SECMODE_USER){
+ memset(zeros, 0, sizeof(zeros));
+ resp = zeros;
+ len = sizeof(zeros);
+ }
+
+ p = cifshdr(s, nil, SMB_COM_TREE_CONNECT_ANDX);
+ p8(p, 0xFF); /* Secondary command */
+ p8(p, 0); /* Reserved */
+ pl16(p, 0); /* Offset to next Word Count */
+ pl16(p, 0); /* Flags */
+
+ if((s->secmode & SECMODE_PW_ENCRYPT) == 0){
+ pl16(p, len+1); /* password len, including null */
+ pbytes(p);
+ pascii(p, resp);
+ }else{
+ pl16(p, len);
+ pbytes(p);
+ pmem(p, resp, len);
+ }
+
+ path = smprint("//%s/%s", cname, tree);
+ strupr(path);
+ ppath(p, path); /* path */
+ free(path);
+
+ pascii(p, "?????"); /* service type any (so we can do RAP calls) */
+
+ if(cifsrpc(p) == -1){
+ free(p);
+ return -1;
+ }
+ g8(p); /* Secondary command */
+ g8(p); /* Reserved */
+ gl16(p); /* Offset to next command */
+ sp->options = g8(p); /* options supported */
+ sp->tid = p->tid; /* get received TID from packet header */
+ free(p);
+ return 0;
+}
+
+int
+CIFSlogoff(Session *s)
+{
+ int rc;
+ Pkt *p;
+
+ p = cifshdr(s, nil, SMB_COM_LOGOFF_ANDX);
+ p8(p, 0xFF); /* No ANDX command */
+ p8(p, 0); /* Reserved (must be zero) */
+ pl16(p, 0); /* offset ot ANDX */
+ pbytes(p);
+ rc = cifsrpc(p);
+
+ free(p);
+ return rc;
+}
+
+int
+CIFStreedisconnect(Session *s, Share *sp)
+{
+ int rc;
+ Pkt *p;
+
+ p = cifshdr(s, sp, SMB_COM_TREE_DISCONNECT);
+ pbytes(p);
+ rc = cifsrpc(p);
+
+ free(p);
+ return rc;
+}
+
+
+int
+CIFSdeletefile(Session *s, Share *sp, char *name)
+{
+ int rc;
+ Pkt *p;
+
+ p = cifshdr(s, sp, SMB_COM_DELETE);
+ pl16(p, ATTR_HIDDEN|ATTR_SYSTEM); /* search attributes */
+ pbytes(p);
+ p8(p, STR_ASCII); /* buffer format */
+ ppath(p, name);
+ rc = cifsrpc(p);
+
+ free(p);
+ return rc;
+}
+
+int
+CIFSdeletedirectory(Session *s, Share *sp, char *name)
+{
+ int rc;
+ Pkt *p;
+
+ p = cifshdr(s, sp, SMB_COM_DELETE_DIRECTORY);
+ pbytes(p);
+ p8(p, STR_ASCII); /* buffer format */
+ ppath(p, name);
+ rc = cifsrpc(p);
+
+ free(p);
+ return rc;
+}
+
+int
+CIFScreatedirectory(Session *s, Share *sp, char *name)
+{
+ int rc;
+ Pkt *p;
+
+ p = cifshdr(s, sp, SMB_COM_CREATE_DIRECTORY);
+ pbytes(p);
+ p8(p, STR_ASCII);
+ ppath(p, name);
+ rc = cifsrpc(p);
+
+ free(p);
+ return rc;
+}
+
+int
+CIFSrename(Session *s, Share *sp, char *old, char *new)
+{
+ int rc;
+ Pkt *p;
+
+ p = cifshdr(s, sp, SMB_COM_RENAME);
+ pl16(p, ATTR_HIDDEN|ATTR_SYSTEM|ATTR_DIRECTORY); /* search attributes */
+ pbytes(p);
+ p8(p, STR_ASCII);
+ ppath(p, old);
+ p8(p, STR_ASCII);
+ ppath(p, new);
+ rc = cifsrpc(p);
+
+ free(p);
+ return rc;
+}
+
+
+/* for NT4/Win2k/XP */
+int
+CIFS_NT_opencreate(Session *s, Share *sp, char *name, int flags, int options,
+ int attrs, int access, int share, int action, int *result, FInfo *fi)
+{
+ Pkt *p;
+ int fh;
+
+ p = cifshdr(s, sp, SMB_COM_NT_CREATE_ANDX);
+ p8(p, 0xFF); /* Secondary command */
+ p8(p, 0); /* Reserved */
+ pl16(p, 0); /* Offset to next command */
+ p8(p, 0); /* Reserved */
+ pl16(p, utflen(name) *2); /* file name len */
+ pl32(p, flags); /* Flags */
+ pl32(p, 0); /* fid of cwd, if relative path */
+ pl32(p, access); /* access desired */
+ pl64(p, 0); /* initial allocation size */
+ pl32(p, attrs); /* Extended attributes */
+ pl32(p, share); /* Share Access */
+ pl32(p, action); /* What to do on success/failure */
+ pl32(p, options); /* Options */
+ pl32(p, SECURITY_IMPERSONATION); /* Impersonation level */
+ p8(p, SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY); /* security flags */
+ pbytes(p);
+ p8(p, 0); /* FIXME: padding? */
+ ppath(p, name); /* filename */
+
+ if(cifsrpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ memset(fi, 0, sizeof(FInfo));
+ g8(p); /* Secondary command */
+ g8(p); /* Reserved */
+ gl16(p); /* Offset to next command */
+ g8(p); /* oplock granted */
+ fh = gl16(p); /* FID for opened object */
+ *result = gl32(p); /* create action taken */
+ gl64(p); /* creation time */
+ fi->accessed = gvtime(p); /* last access time */
+ fi->written = gvtime(p); /* last written time */
+ fi->changed = gvtime(p); /* change time */
+ fi->attribs = gl32(p); /* extended attributes */
+ gl64(p); /* bytes allocated */
+ fi->size = gl64(p); /* file size */
+
+ free(p);
+ return fh;
+}
+
+/* for Win95/98/ME */
+CIFS_SMB_opencreate(Session *s, Share *sp, char *name, int access,
+ int attrs, int action, int *result)
+{
+ Pkt *p;
+ int fh;
+
+ p = cifshdr(s, sp, SMB_COM_OPEN_ANDX);
+ p8(p, 0xFF); /* Secondary command */
+ p8(p, 0); /* Reserved */
+ pl16(p, 0); /* Offset to next command */
+ pl16(p, 0); /* Flags (0 == no stat(2) info) */
+ pl16(p, access); /* desired access */
+ pl16(p, ATTR_HIDDEN|ATTR_SYSTEM);/* search attributes */
+ pl16(p, attrs); /* file attribytes */
+ pdatetime(p, 0); /* creation time (0 == now) */
+ pl16(p, action); /* What to do on success/failure */
+ pl32(p, 0); /* allocation size */
+ pl32(p, 0); /* reserved */
+ pl32(p, 0); /* reserved */
+ pbytes(p);
+ ppath(p, name); /* filename */
+
+ if(cifsrpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ g8(p); /* Secondary command */
+ g8(p); /* Reserved */
+ gl16(p); /* Offset to next command */
+ fh = gl16(p); /* FID for opened object */
+ gl16(p); /* extended attributes */
+ gvtime(p); /* last written time */
+ gl32(p); /* file size */
+ gl16(p); /* file type (disk/fifo/printer etc) */
+ gl16(p); /* device status (for fifos) */
+ *result = gl16(p); /* access granted */
+
+ free(p);
+ return fh;
+}
+
+vlong
+CIFSwrite(Session *s, Share *sp, int fh, uvlong off, void *buf, vlong n)
+{
+ Pkt *p;
+ vlong got;
+
+ /* FIXME: Payload should be padded to long boundary */
+ assert((n & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
+ assert((off & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
+ assert(n < s->mtu - T2HDRLEN || s->caps & CAP_LARGE_WRITEX);
+
+ p = cifshdr(s, sp, SMB_COM_WRITE_ANDX);
+ p8(p, 0xFF); /* Secondary command */
+ p8(p, 0); /* Reserved */
+ pl16(p, 0); /* Offset to next command */
+ pl16(p, fh); /* File handle */
+ pl32(p, off & 0xffffffff); /* LSBs of Offset */
+ pl32(p, 0); /* Reserved (0) */
+ pl16(p, s->nocache); /* Write mode (0 - write through) */
+ pl16(p, 0); /* Bytes remaining */
+ pl16(p, n >> 16); /* MSBs of length */
+ pl16(p, n & 0xffffffff); /* LSBs of length */
+ pl16(p, T2HDRLEN); /* Offset to data, in bytes */
+ pl32(p, off >> 32); /* MSBs of offset */
+ pbytes(p);
+
+ p->pos = p->buf +T2HDRLEN +NBHDRLEN;
+ pmem(p, buf, n); /* Data */
+
+ if(cifsrpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ g8(p); /* Secondary command */
+ g8(p); /* Reserved */
+ gl16(p); /* Offset to next command */
+ got = gl16(p); /* LSWs of bytes written */
+ gl16(p); /* remaining (space ?) */
+ got |= (gl16(p) << 16); /* MSWs of bytes written */
+
+ free(p);
+ return got;
+}
+
+vlong
+CIFSread(Session *s, Share *sp, int fh, uvlong off, void *buf, vlong n,
+ vlong minlen)
+{
+ int doff;
+ vlong got;
+ Pkt *p;
+
+ assert((n & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
+ assert((off & 0xffffffff00000000LL) == 0 || s->caps & CAP_LARGE_FILES);
+ assert(n < s->mtu - T2HDRLEN || s->caps & CAP_LARGE_READX);
+
+ p = cifshdr(s, sp, SMB_COM_READ_ANDX);
+ p8(p, 0xFF); /* Secondary command */
+ p8(p, 0); /* Reserved */
+ pl16(p, 0); /* Offset to next command */
+ pl16(p, fh); /* File handle */
+ pl32(p, off & 0xffffffff); /* Offset to beginning of write */
+ pl16(p, n); /* Maximum number of bytes to return */
+ pl16(p, minlen); /* Minimum number of bytes to return */
+ pl32(p, (uint)n >> 16); /* MSBs of maxlen */
+ pl16(p, 0); /* Bytes remaining to satisfy request */
+ pl32(p, off >> 32); /* MS 32 bits of offset */
+ pbytes(p);
+
+ if(cifsrpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ g8(p); /* Secondary command */
+ g8(p); /* Reserved */
+ gl16(p); /* Offset to next command */
+ gl16(p); /* Remaining */
+ gl16(p); /* Compression mode */
+ gl16(p); /* Reserved */
+ got = gl16(p); /* length */
+ doff = gl16(p); /* Offset from header to data */
+ got |= gl16(p) << 16;
+
+ p->pos = p->buf + doff + NBHDRLEN;
+
+ gmem(p, buf, got); /* data */
+ free(p);
+ return got;
+}
+
+int
+CIFSflush(Session *s, Share *sp, int fh)
+{
+ int rc;
+ Pkt *p;
+
+ p = cifshdr(s, sp, SMB_COM_FLUSH);
+ pl16(p, fh); /* fid */
+ pbytes(p);
+ rc = cifsrpc(p);
+
+ free(p);
+ return rc;
+}
+
+/*
+ * Setting the time of last write to -1 gives "now" if the file
+ * was written and leaves it the same if the file wasn't written.
+ */
+int
+CIFSclose(Session *s, Share *sp, int fh)
+{
+ int rc;
+ Pkt *p;
+
+ p = cifshdr(s, sp, SMB_COM_CLOSE);
+ pl16(p, fh); /* fid */
+ pl32(p, ~0L); /* Time of last write (none) */
+ pbytes(p);
+ rc = cifsrpc(p);
+
+ free(p);
+ return rc;
+}
+
+
+int
+CIFSfindclose2(Session *s, Share *sp, int sh)
+{
+ int rc;
+ Pkt *p;
+
+ p = cifshdr(s, sp, SMB_COM_FIND_CLOSE2);
+ pl16(p, sh); /* sid */
+ pbytes(p);
+ rc = cifsrpc(p);
+
+ free(p);
+ return rc;
+}
+
+
+int
+CIFSecho(Session *s)
+{
+ Pkt *p;
+ int rc;
+
+ p = cifshdr(s, nil, SMB_COM_ECHO);
+ pl16(p, 1); /* number of replies */
+ pbytes(p);
+ pascii(p, "abcdefghijklmnopqrstuvwxyz"); /* data */
+
+ rc = cifsrpc(p);
+ free(p);
+ return rc;
+}
+
+
+int
+CIFSsetinfo(Session *s, Share *sp, char *path, FInfo *fip)
+{
+ int rc;
+ Pkt *p;
+
+ p = cifshdr(s, sp, SMB_COM_SET_INFORMATION);
+ pl16(p, fip->attribs);
+ pl32(p, time(nil) - s->tz); /* modified time */
+ pl64(p, 0); /* reserved */
+ pl16(p, 0); /* reserved */
+
+ pbytes(p);
+ p8(p, STR_ASCII); /* buffer format */
+ ppath(p, path);
+
+ rc = cifsrpc(p);
+ free(p);
+ return rc;
+}
diff --git a/sys/src/cmd/cifs/cifs.h b/sys/src/cmd/cifs/cifs.h
new file mode 100755
index 000000000..a600ed7d4
--- /dev/null
+++ b/sys/src/cmd/cifs/cifs.h
@@ -0,0 +1,638 @@
+/* cifs.h */
+
+enum {
+ Proot = 1, /* LSBs of qid.path for root dir */
+ Pinfo = 2, /* LSBs of qid.path for info files */
+ Pshare = 4, /* LSBs of qid.path for share dirs */
+ NBHDRLEN = 4, /* length of a netbios header */
+ T2HDRLEN = 64, /* Transaction2 header length */
+ NO_UID = 0xffff, /* initial UID on connect */
+ NO_TID = 0xffff, /* initial TID on connect */
+ MTU = 0xefff, /* our MTU */
+ CACHETIME = 2, /* seconds read-ahead is valid for */
+ CIFS_FNAME_MAX = 0xff, /* max file path component len */
+ OVERHEAD = 80, /* max packet overhead when reading & writing */
+ IDLE_TIME = 10, /* keepalive send rate in mins */
+ NBNSTOUT = 300, /* Netbios Name Service Timeout (300ms x 3retrys) */
+ NBRPCTOUT = 45*60*1000, /* Netbios RPC Timeout (45sec) */
+ MAX_SHARES = 4096, /* static table of shares attached */
+ RAP_ERR_MOREINFO= 234, /* non-error code, more info to be fetched */
+ MAX_DFS_PATH = 512, /* MS says never more than 250 chars... */
+};
+
+enum {
+ SMB_COM_CREATE_DIRECTORY = 0x00,
+ SMB_COM_DELETE_DIRECTORY = 0x01,
+ SMB_COM_OPEN = 0x02,
+ SMB_COM_CREATE = 0x03,
+ SMB_COM_CLOSE = 0x04,
+ SMB_COM_FLUSH = 0x05,
+ SMB_COM_DELETE = 0x06,
+ SMB_COM_RENAME = 0x07,
+ SMB_COM_QUERY_INFORMATION = 0x08,
+ SMB_COM_SET_INFORMATION = 0x09,
+ SMB_COM_READ = 0x0A,
+ SMB_COM_WRITE = 0x0B,
+ SMB_COM_LOCK_BYTE_RANGE = 0x0C,
+ SMB_COM_UNLOCK_BYTE_RANGE = 0x0D,
+ SMB_COM_CREATE_TEMPORARY = 0x0E,
+ SMB_COM_CREATE_NEW = 0x0F,
+ SMB_COM_CHECK_DIRECTORY = 0x10,
+ SMB_COM_PROCESS_EXIT = 0x11,
+ SMB_COM_SEEK = 0x12,
+ SMB_COM_LOCK_AND_READ = 0x13,
+ SMB_COM_WRITE_AND_UNLOCK = 0x14,
+ SMB_COM_READ_RAW = 0x1A,
+ SMB_COM_READ_MPX = 0x1B,
+ SMB_COM_READ_MPX_SECONDARY = 0x1C,
+ SMB_COM_WRITE_RAW = 0x1D,
+ SMB_COM_WRITE_MPX = 0x1E,
+ SMB_COM_WRITE_MPX_SECONDARY = 0x1F,
+ SMB_COM_WRITE_COMPLETE = 0x20,
+ SMB_COM_QUERY_SERVER = 0x21,
+ SMB_COM_SET_INFORMATION2 = 0x22,
+ SMB_COM_QUERY_INFORMATION2 = 0x23,
+ SMB_COM_LOCKING_ANDX = 0x24,
+ SMB_COM_TRANSACTION = 0x25,
+ SMB_COM_TRANSACTION_SECONDARY = 0x26,
+ SMB_COM_IOCTL = 0x27,
+ SMB_COM_IOCTL_SECONDARY = 0x28,
+ SMB_COM_COPY = 0x29,
+ SMB_COM_MOVE = 0x2A,
+ SMB_COM_ECHO = 0x2B,
+ SMB_COM_WRITE_AND_CLOSE = 0x2C,
+ SMB_COM_OPEN_ANDX = 0x2D,
+ SMB_COM_READ_ANDX = 0x2E,
+ SMB_COM_WRITE_ANDX = 0x2F,
+ SMB_COM_NEW_FILE_SIZE = 0x30,
+ SMB_COM_CLOSE_AND_TREE_DISC = 0x31,
+ SMB_COM_TRANSACTION2 = 0x32,
+ SMB_COM_TRANSACTION2_SECONDARY = 0x33,
+ SMB_COM_FIND_CLOSE2 = 0x34,
+ SMB_COM_FIND_NOTIFY_CLOSE = 0x35,
+ SMB_COM_TREE_CONNECT = 0x70,
+ SMB_COM_TREE_DISCONNECT = 0x71,
+ SMB_COM_NEGOTIATE = 0x72,
+ SMB_COM_SESSION_SETUP_ANDX = 0x73,
+ SMB_COM_LOGOFF_ANDX = 0x74,
+ SMB_COM_TREE_CONNECT_ANDX = 0x75,
+ SMB_COM_QUERY_INFORMATION_DISK = 0x80,
+ SMB_COM_SEARCH = 0x81,
+ SMB_COM_FIND = 0x82,
+ SMB_COM_FIND_UNIQUE = 0x83,
+ SMB_COM_FIND_CLOSE = 0x84,
+ SMB_COM_NT_TRANSACT = 0xA0,
+ SMB_COM_NT_TRANSACT_SECONDARY = 0xA1,
+ SMB_COM_NT_CREATE_ANDX = 0xA2,
+ SMB_COM_NT_CANCEL = 0xA4,
+ SMB_COM_NT_RENAME = 0xA5,
+ SMB_COM_OPEN_PRINT_FILE = 0xC0,
+ SMB_COM_WRITE_PRINT_FILE = 0xC1,
+ SMB_COM_CLOSE_PRINT_FILE = 0xC2,
+ SMB_COM_GET_PRINT_QUEUE = 0xC3,
+ SMB_COM_READ_BULK = 0xD8,
+ SMB_COM_WRITE_BULK = 0xD9,
+ SMB_COM_WRITE_BULK_DATA = 0xDA,
+
+ TRANS2_OPEN2 = 0x00,
+ TRANS2_FIND_FIRST2 = 0x01,
+ TRANS2_FIND_NEXT2 = 0x02,
+ TRANS2_QUERY_FS_INFORMATION = 0x03,
+ TRANS2_QUERY_PATH_INFORMATION = 0x05,
+ TRANS2_SET_PATH_INFORMATION = 0x06,
+ TRANS2_QUERY_FILE_INFORMATION = 0x07,
+ TRANS2_SET_FILE_INFORMATION = 0x08,
+ TRANS2_CREATE_DIRECTORY = 0x0D,
+ TRANS2_SESSION_SETUP = 0x0E,
+ TRANS2_GET_DFS_REFERRAL = 0x10,
+
+ NT_TRANSACT_CREATE = 0x01,
+ NT_TRANSACT_IOCTL = 0x02,
+ NT_TRANSACT_SET_SECURITY_DESC = 0x03,
+ NT_TRANSACT_NOTIFY_CHANGE = 0x04,
+ NT_TRANSACT_RENAME = 0x05,
+ NT_TRANSACT_QUERY_SECURITY_DESC = 0x06
+};
+
+enum { /* CIFS flags */
+ FL_CASELESS_NAMES = 1<<3,
+ FL_CANNONICAL_NAMES = 1<<4,
+
+ FL2_KNOWS_LONG_NAMES = 1<<0,
+ FL2_PACKET_SIGNATURES = 1<<2,
+ FL2_HAS_LONG_NAMES = 1<<6,
+ FL2_EXTENDED_SECURITY = 1<<11,
+ FL2_DFS = 1<<12,
+ FL2_PAGEING_IO = 1<<13, /* allow read of exec only files */
+ FL2_NT_ERRCODES = 1<<14,
+ FL2_UNICODE = 1<<15,
+};
+
+enum { /* Capabilities Negoiated */
+ CAP_RAW_MODE = 1,
+ CAP_MPX_MODE = 1<<1,
+ CAP_UNICODE = 1<<2,
+ CAP_LARGE_FILES = 1<<3, /* 64 bit files */
+ CAP_NT_SMBS = 1<<4,
+ CAP_RPC_REMOTE_APIS = 1<<5,
+ CAP_STATUS32 = 1<<6,
+ CAP_L2_OPLOCKS = 1<<7,
+ CAP_LOCK_READ = 1<<8,
+ CAP_NT_FIND = 1<<9,
+ CAP_DFS = 1<<12,
+ CAP_INFO_PASSTHRU = 1<<13,
+ CAP_LARGE_READX = 1<<14,
+ CAP_LARGE_WRITEX = 1<<15,
+ CAP_UNIX = 1<<23,
+ CAP_BULK_TRANSFER = 1<<29,
+ CAP_COMPRESSED = 1<<30,
+ CAP_EX_SECURE = 1<<31
+};
+
+enum { /* string prefixes */
+ STR_DIALECT = 2,
+ STR_PATH = 3,
+ STR_ASCII = 4,
+};
+
+enum { /* optional support bits in treeconnect */
+ SMB_SUPPROT_SEARCH_BITS = 1,
+ SMB_SHARE_IS_IN_DFS = 2,
+};
+
+enum { /* DFS referal header flags */
+ DFS_HEADER_ROOT = 1, /* Server type, returns root targets */
+ DFS_HEADER_STORAGE = 2, /* server has storage, no more referals */
+ DFS_HEADER_FAILBACK = 4, /* target failback enabled */
+};
+
+enum { /* DFS referal entry flags */
+ DFS_SERVER_ROOT = 1, /* Server type, returns root targets */
+ DFS_REFERAL_LIST = 0x200, /* reply is a list (v3 only) */
+ DFS_REFERAL_SET = 0x400, /* target is a member of a set */
+};
+
+enum { /* share types */
+ STYPE_DISKTREE = 0,
+ STYPE_PRINTQ = 1,
+ STYPE_DEVICE = 2,
+ STYPE_IPC = 3,
+ STYPE_SPECIAL = 4,
+ STYPE_TEMP = 5,
+};
+
+enum { /* Security */
+ SECMODE_USER = 0x01, /* i.e. not share level security */
+ SECMODE_PW_ENCRYPT = 0x02,
+ SECMODE_SIGN_ENABLED = 0x04,
+ SECMODE_SIGN_REQUIRED = 0x08,
+};
+
+enum { /* file access rights */
+ DELETE = 0x00010000,
+ SYNCHRONIZE = 0x00100000,
+
+ READ_CONTROL = 0x00020000,
+ GENERIC_ALL = 0x10000000,
+ GENERIC_EXECUTE = 0x20000000,
+ GENERIC_WRITE = 0x40000000,
+ GENERIC_READ = 0x80000000,
+
+ ATTR_READONLY = 0x0001,
+ ATTR_HIDDEN = 0x0002,
+ ATTR_SYSTEM = 0x0004,
+ ATTR_VOLUME = 0x0008,
+ ATTR_DIRECTORY = 0x0010,
+ ATTR_ARCHIVE = 0x0020,
+ ATTR_DEVICE = 0x0040,
+ ATTR_NORMAL = 0x0080,
+ ATTR_TEMPORARY = 0x0100,
+ ATTR_SPARSE = 0x0200,
+ ATTR_REPARSE = 0x0400,
+ ATTR_COMPRESSED = 0x0800,
+ ATTR_OFFLINE = 0x100, /* offline storage */
+ ATTR_NOT_CONTENT_INDEXED= 0x2000,
+ ATTR_ENCRYPTED = 0x4000,
+ ATTR_POSIX_SEMANTICS = 0x01000000,
+ ATTR_BACKUP_SEMANTICS = 0x02000000,
+ ATTR_DELETE_ON_CLOSE = 0x04000000,
+ ATTR_SEQUENTIAL_SCAN = 0x08000000,
+ ATTR_RANDOM_ACCESS = 0x10000000,
+ ATTR_NO_BUFFERING = 0x20000000,
+ ATTR_WRITE_THROUGH = 0x80000000,
+
+ /* ShareAccess flags */
+ FILE_NO_SHARE = 0,
+ FILE_SHARE_READ = 1,
+ FILE_SHARE_WRITE = 2,
+ FILE_SHARE_DELETE = 4,
+ FILE_SHARE_ALL = 7,
+
+ /* CreateDisposition flags */
+ FILE_SUPERSEDE = 0,
+ FILE_OPEN = 1,
+ FILE_CREATE = 2,
+ FILE_OPEN_IF = 3,
+ FILE_OVERWRITE = 4,
+ FILE_OVERWRITE_IF = 5,
+
+ /* CreateOptions */
+ FILE_DIRECTORY_FILE = 0x00000001,
+ FILE_WRITE_THROUGH = 0x00000002,
+ FILE_SEQUENTIAL_ONLY = 0x00000004,
+ FILE_NO_INTERMEDIATE_BUFFERING = 0x00000008,
+ FILE_SYNCHRONOUS_IO_ALERT = 0x00000010,
+ FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020,
+ FILE_NON_DIRECTORY_FILE = 0x00000040,
+ FILE_CREATE_TREE_CONNECTION = 0x00000080,
+ FILE_COMPLETE_IF_OPLOCKED = 0x00000100,
+ FILE_NO_EA_KNOWLEDGE = 0x00000200,
+ FILE_OPEN_FOR_RECOVERY = 0x00000400,
+ FILE_EIGHT_DOT_THREE_ONLY = 0x00000400, /* samba source says so... */
+ FILE_RANDOM_ACCESS = 0x00000800,
+ FILE_DELETE_ON_CLOSE = 0x00001000,
+ FILE_OPEN_BY_FILE_ID = 0x00002000,
+ FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000,
+ FILE_NO_COMPRESSION = 0x00008000,
+
+ /* open/create result codes */
+ FILE_WAS_OPENED = 1,
+ FILE_WAS_CREATED = 2,
+ FILE_WAS_OVERWRITTEN = 3,
+
+ /* ImpersonationLevel flags */
+ SECURITY_ANONYMOUS = 0,
+ SECURITY_IDENTIFICATION = 1,
+ SECURITY_IMPERSONATION = 2,
+ SECURITY_DELEGATION = 3,
+
+ /* SecurityFlags */
+ SECURITY_CONTEXT_TRACKING = 1,
+ SECURITY_EFFECTIVE_ONLY = 2,
+
+ /* security descriptor bitmask */
+ QUERY_OWNER_SECURITY_INFORMATION = 1,
+ QUERY_GROUP_SECURITY_INFORMATION = 2,
+ QUERY_DACL_SECURITY_INFORMATION = 4,
+ QUERY_SACL_SECURITY_INFORMATION = 8,
+
+};
+
+enum { /* PathInfo/FileInfo infolevels */
+ SMB_INFO_STANDARD = 0x1,
+ SMB_INFO_IS_NAME_VALID = 0x6,
+ SMB_QUERY_FILE_BASIC_INFO = 0x101,
+ SMB_QUERY_FILE_STANDARD_INFO = 0x102,
+ SMB_QUERY_FILE_NAME_INFO = 0x104,
+ SMB_QUERY_FILE_ALLOCATION_INFO = 0x105,
+ SMB_QUERY_FILE_END_OF_FILE_INFO = 0x106,
+ SMB_QUERY_FILE_ALL_INFO = 0x107,
+ SMB_QUERY_ALT_NAME_INFO = 0x108,
+ SMB_QUERY_FILE_STREAM_INFO = 0x109,
+ SMB_QUERY_FILE_COMPRESSION_INFO = 0x10b,
+ SMB_QUERY_FILE_UNIX_BASIC = 0x200,
+ SMB_QUERY_FILE_UNIX_LINK = 0x201,
+
+ SMB_SET_FILE_BASIC_INFO = 0x101,
+ SMB_SET_FILE_DISPOSITION_INFO = 0x102,
+ SMB_SET_FILE_ALLOCATION_INFO = 0x103,
+ SMB_SET_FILE_END_OF_FILE_INFO = 0x104,
+ SMB_SET_FILE_UNIX_BASIC = 0x200,
+ SMB_SET_FILE_UNIX_LINK = 0x201,
+ SMB_SET_FILE_UNIX_HLINK = 0x203,
+ SMB_SET_FILE_BASIC_INFO2 = 0x3ec,
+ SMB_SET_FILE_RENAME_INFORMATION = 0x3f2,
+ SMB_SET_FILE_ALLOCATION_INFO2 = 0x3fb,
+ SMB_SET_FILE_END_OF_FILE_INFO2 = 0x3fc,
+
+ /* Find File infolevels */
+ SMB_FIND_FILE_DIRECTORY_INFO = 0x101,
+ SMB_FIND_FILE_FULL_DIRECTORY_INFO= 0x102,
+ SMB_FIND_FILE_NAMES_INFO = 0x103,
+ SMB_FIND_FILE_BOTH_DIRECTORY_INFO= 0x104,
+ SMB_FIND_FILE_UNIX = 0x202,
+
+ /* Trans2 FindFirst & FindNext */
+ CIFS_SEARCH_CLOSE_ALWAYS = 0x0001,
+ CIFS_SEARCH_CLOSE_AT_END = 0x0002,
+ CIFS_SEARCH_RETURN_RESUME = 0x0004,
+ CIFS_SEARCH_CONTINUE_FROM_LAST = 0x0008,
+ CIFS_SEARCH_BACKUP_SEARCH = 0x0010,
+
+ /* Trans2 FsInfo */
+ SMB_INFO_ALLOCATION = 0x1,
+ SMB_INFO_VOLUME = 0x2,
+ SMB_QUERY_FS_VOLUME_INFO = 0x102,
+ SMB_QUERY_FS_SIZE_INFO = 0x103,
+ SMB_QUERY_FS_DEVICE_INFO = 0x104,
+ SMB_QUERY_FS_ATTRIBUTE_INFO = 0x105,
+ SMB_QUERY_CIFS_UNIX_INFO = 0x200,
+};
+
+enum { /* things to search for in server lookups */
+ LOCAL_AUTHORATIVE_ONLY = 0x40000000,
+ LIST_DOMAINS_ONLY = 0x80000000,
+ ALL_LEARNT_IN_DOMAIN = 0xFFFFFFFF
+};
+
+typedef struct {
+ char *user; /* username */
+ char *windom; /* remote server's domain name */
+ char *resp[2]; /* ASCII/Unicode or LM/NTLM keys */
+ int len[2]; /* length of above */
+ uchar *mackey[2]; /* Message Authentication key */
+} Auth;
+
+typedef struct {
+ int fd; /* File descriptor for I/O */
+ int nbt; /* am using cifs over netbios */
+ int trn; /* TRN (unique RPC) ID */
+ int uid; /* user (authentication) ID */
+ int seq; /* sequence number */
+ int seqrun; /* sequence numbering active */
+ int caps; /* server capabilities */
+ int support; /* support bits */
+ int flags; /* SMB flags */
+ int flags2; /* SMB flags 2 */
+ int nocache; /* disable write behind caching in server */
+ int pid; /* process ID */
+ int mid; /* multiplex ID */
+ int mtu; /* max size of packet */
+ int tz; /* Timezone, mins from UTC */
+ int isguest; /* logged in as guest */
+ int secmode; /* security mode */
+ int macidx; /* which MAC is in use, -1 is none */
+ uchar chal[0xff +1]; /* server's challange for authentication */
+ int challen; /* length of challange */
+ long slip; /* time difference between the server and us */
+ uvlong lastfind; /* nsec when last find peformed */
+ QLock seqlock; /* sequence number increment */
+ QLock rpclock; /* actual remote procedure call */
+ char *cname; /* remote hosts called name (for info) */
+ char *remos; /* remote hosts OS (for info) */
+ Auth *auth; /* authentication info */
+} Session;
+
+typedef struct {
+ Session *s;
+
+ int tid; /* tree ID received from server */
+ int seq; /* sequence number expected in reply */
+
+ uchar *seqbase; /* cifs: pos of sequence number in packet */
+ uchar *wordbase; /* cifs: base of words section of data */
+ uchar *bytebase; /* cifs: base of bytes section of data */
+ uchar *tbase; /* transactions: start of trans packet */
+ uchar *tsetup; /* transactions: start of setup section */
+ uchar *tparam; /* transactions: start of params section */
+ uchar *tdata; /* transactions: start of data section */
+
+ uchar *eop; /* received end of packet */
+ uchar *pos; /* current pos in packet */
+ uchar *buf; /* packet buffer, must be last entry in struct */
+} Pkt;
+
+typedef struct {
+ char *name;
+ int tid; /* not part of the protocol, housekeeping */
+ int options; /* not part of the protocol, housekeeping */
+} Share;
+
+typedef struct {
+ long created; /* last access time */
+ long accessed; /* last access time */
+ long written; /* last written time */
+ long changed; /* change time */
+ uvlong size; /* file size */
+ long attribs; /* attributes */
+ char name[CIFS_FNAME_MAX +1]; /* name */
+} FInfo;
+
+typedef struct {
+ char *wrkstn;
+ char *user;
+ long sesstime;
+ long idletime;
+} Sessinfo;
+
+typedef struct {
+ char *name;
+} Namelist;
+
+typedef struct {
+ char *user;
+ char *comment;
+ char *user_comment;
+ char *fullname;
+} Userinfo;
+
+typedef struct {
+ char *name;
+ int type;
+ char *comment;
+ int perms;
+ int maxusrs;
+ int activeusrs;
+ char *path;
+ char *passwd;
+} Shareinfo2;
+
+typedef struct {
+ char *name;
+ int major;
+ int minor;
+ int type;
+ char *comment;
+} Serverinfo;
+
+typedef struct {
+ int type; /* o=unknown, 1=CIFS, 2=netware 3=domain */
+ int flags; /* 1 == strip off consumed chars before resubmitting */
+ int ttl; /* time to live of this info in secs */
+ int prox; /* lower value is preferred */
+ char *path; /* new path */
+ char *addr; /* new server */
+} Refer;
+
+typedef struct {
+ char *node;
+ char *user;
+ char *langroup;
+ int major;
+ int minor;
+ char *pridom;
+ char *otherdoms;
+} Wrkstainfo;
+
+typedef struct {
+ int ident;
+ int perms;
+ int locks;
+ char *path;
+ char *user;
+} Fileinfo;
+
+extern int Active;
+extern int Checkcase;
+extern int Dfstout;
+extern char *Debug;
+extern char *Host;
+extern Session *Sess;
+extern Share Ipc;
+
+extern Share Shares[MAX_SHARES];
+extern int Nshares;
+
+/* main.c */
+Qid mkqid(char *, int, long, int, long);
+
+/* auth.c */
+Auth *getauth(char *, char *, char *, int, uchar *, int);
+void autherr(void);
+int macsign(Pkt *, int);
+
+/* cifs.c */
+Session *cifsdial(char *, char *, char *);
+void cifsclose(Session *);
+Pkt *cifshdr(Session *, Share *, int);
+void pbytes(Pkt *);
+int cifsrpc(Pkt *);
+int CIFSnegotiate(Session *, long *, char *, int, char *, int);
+int CIFSsession(Session *);
+int CIFStreeconnect(Session *, char *, char *, Share *);
+int CIFSlogoff(Session *);
+int CIFStreedisconnect(Session *, Share *);
+int CIFSdeletefile(Session *, Share *, char *);
+int CIFSdeletedirectory(Session *, Share *, char *);
+int CIFScreatedirectory(Session *, Share *, char *);
+int CIFSrename(Session *, Share *, char *, char *);
+int CIFS_NT_opencreate(Session *, Share *, char *, int, int, int, int, int, int, int *, FInfo *);
+int CIFS_SMB_opencreate(Session *, Share *, char *, int, int, int, int *);
+vlong CIFSwrite(Session *, Share *, int, uvlong, void *, vlong);
+vlong CIFSread(Session *, Share *, int, uvlong, void *, vlong, vlong);
+int CIFSflush(Session *, Share *, int);
+int CIFSclose(Session *, Share *, int);
+int CIFSfindclose2(Session *, Share *, int);
+int CIFSecho(Session *);
+int CIFSsetinfo(Session *, Share *, char *, FInfo *);
+void goff(Pkt *, uchar *, char *, int);
+
+/* dfs.c */
+char *mapfile(char *);
+int mapshare(char *, Share **);
+int redirect(Session *, Share *s, char *);
+int dfscacheinfo(Fmt *);
+char *trimshare(char *);
+
+/* doserrstr.c */
+char *doserrstr(uint);
+
+/* fs.c */
+int shareinfo(Fmt *);
+int conninfo(Fmt *);
+int sessioninfo(Fmt *);
+int userinfo(Fmt *);
+int groupinfo(Fmt *);
+int domaininfo(Fmt *);
+int workstationinfo(Fmt *);
+int dfsrootinfo(Fmt *);
+int openfileinfo(Fmt *);
+int dfsrootinfo(Fmt *);
+int filetableinfo(Fmt *); /* is in main.c due to C scope */
+
+/* info.c */
+int walkinfo(char *);
+int numinfo(void);
+int dirgeninfo(int, Dir *);
+int makeinfo(int);
+int readinfo(int, char *, int, int);
+void freeinfo(int);
+
+/* main.c */
+void usage(void);
+void dmpkey(char *, void *, int);
+void main(int, char **);
+
+/* misc.c */
+char *strupr(char *);
+char *strlwr(char *);
+
+/* netbios.c */
+void Gmem(uchar **, void *, int);
+int calledname(char *, char *);
+int nbtdial(char *, char *, char *);
+void nbthdr(Pkt *);
+int nbtrpc(Pkt *);
+void xd(char *, void *, int);
+
+/* nterrstr.c */
+char *nterrstr(uint);
+
+/* pack.c */
+void *pmem(Pkt *, void *, int);
+void *ppath(Pkt *, char *);
+void *pstr(Pkt *, char *);
+void *pascii(Pkt *, char *);
+void *pl64(Pkt *, uvlong);
+void *pb32(Pkt *, uint);
+void *pl32(Pkt *, uint);
+void *pb16(Pkt *, uint);
+void *pl16(Pkt *, uint);
+void *p8(Pkt *, uint);
+void *pname(Pkt *, char *, char);
+void *pvtime(Pkt *, uvlong);
+void *pdatetime(Pkt *, long);
+void gmem(Pkt *, void *, int);
+void gstr(Pkt *, char *, int);
+void gascii(Pkt *, char *, int);
+uvlong gl64(Pkt *);
+uvlong gb48(Pkt *);
+uint gb32(Pkt *);
+uint gl32(Pkt *);
+uint gb16(Pkt *);
+uint gl16(Pkt *);
+uint g8(Pkt *);
+long gdatetime(Pkt *);
+long gvtime(Pkt *);
+void gconv(Pkt *, int, char *, int);
+
+/* raperrstr.c */
+char *raperrstr(uint);
+
+/* sid2name.c */
+void upd_names(Session *, Share *, char *, Dir *);
+
+/* trans.c */
+int RAPshareenum(Session *, Share *, Share **);
+int RAPshareinfo(Session *, Share *, char *, Shareinfo2 *);
+
+int RAPsessionenum(Session *, Share *, Sessinfo **);
+
+int RAPgroupenum(Session *, Share *, Namelist **);
+int RAPgroupusers(Session *, Share *, char *, Namelist **);
+
+int RAPuserenum(Session *, Share *, Namelist **);
+int RAPuserenum2(Session *, Share *, Namelist **);
+int RAPuserinfo(Session *, Share *, char *, Userinfo *);
+
+int RAPServerenum2(Session *, Share *, char *, int, int *, Serverinfo **);
+int RAPServerenum3(Session *, Share *, char *, int, int, Serverinfo *);
+
+int RAPFileenum2(Session *, Share *, char *, char *, Fileinfo **);
+
+/* trans2.c */
+int T2findfirst(Session *, Share *, int, char *, int *, long *, FInfo *);
+int T2findnext(Session *, Share *, int, char *, int *, long *, FInfo *, int);
+int T2queryall(Session *, Share *, char *, FInfo *);
+int T2querystandard(Session *, Share *, char *, FInfo *);
+int T2setpathinfo(Session *, Share *, char *, FInfo *);
+int T2setfilelength(Session *, Share *, int, FInfo *);
+int T2fsvolumeinfo(Session *, Share *, long *, long *, char *, int);
+int T2fssizeinfo(Session *, Share *, uvlong *, uvlong *);
+int T2getdfsreferral(Session *, Share *, char *, int *, int *, Refer *, int);
+
+/* transnt.c */
+int TNTquerysecurity(Session *, Share *, int, char **, char **);
+
+/* ping.c */
+int ping(char *, int);
diff --git a/sys/src/cmd/cifs/dfs.c b/sys/src/cmd/cifs/dfs.c
new file mode 100755
index 000000000..73c9a3e01
--- /dev/null
+++ b/sys/src/cmd/cifs/dfs.c
@@ -0,0 +1,407 @@
+/*
+ * DNS referrals give two main fields: the path to connect to in
+ * /Netbios-host-name/share-name/path... form and a network
+ * address of how to find this path of the form domain.dom.
+ *
+ * The domain.dom is resolved in XP/Win2k etc using AD to do
+ * a lookup (this is a consensus view, I don't think anyone
+ * has proved it). I cannot do this as AD needs Kerberos and
+ * LDAP which I don't have.
+ *
+ * Instead I just use the NetBios names passed in the paths
+ * and assume that the servers are in the same DNS domain as me
+ * and have their DNS hostname set the same as their netbios
+ * called-name; thankfully this always seems to be the case (so far).
+ *
+ * I have not added support for starting another instance of
+ * cifs to connect to other servers referenced in DFS links,
+ * this is not a problem for me and I think it hides a load
+ * of problems of its own wrt plan9's private namespaces.
+ *
+ * The proximity of my test server (AD enabled) is always 0 but some
+ * systems may report more meaningful values. The expiry time is
+ * similarly zero, so I guess at 5 mins.
+ *
+ * If the redirection points to a "hidden" share (i.e., its name
+ * ends in a $) then the type of the redirection is 0 (unknown) even
+ * though it is a CIFS share.
+ *
+ * It would be nice to add a check for which subnet a server is on
+ * so our first choice is always the the server on the same subnet
+ * as us which replies to a ping (i.e., is up). This could short-
+ * circuit the tests as the a server on the same subnet will always
+ * be the fastest to get to.
+ *
+ * If I set Flags2_DFS then I don't see DFS links, I just get
+ * path not found (?!).
+ *
+ * If I do a QueryFileInfo of a DFS link point (IE when I'am doing a walk)
+ * Then I just see a directory, its not until I try to walk another level
+ * That I get "IO reparse tag not handled" error rather than
+ * "Path not covered".
+ *
+ * If I check the extended attributes of the QueryFileInfo in walk() then I can
+ * see this is a reparse point and so I can get the referral. The only
+ * problem here is that samba and the like may not support this.
+ */
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <libsec.h>
+#include <ctype.h>
+#include <9p.h>
+#include "cifs.h"
+
+enum {
+ Nomatch, /* not found in cache */
+ Exactmatch, /* perfect match found */
+ Badmatch /* matched but wrong case */
+};
+
+#define SINT_MAX 0x7fffffff
+
+typedef struct Dfscache Dfscache;
+struct Dfscache {
+ Dfscache*next; /* next entry */
+ char *src;
+ char *host;
+ char *share;
+ char *path;
+ long expiry; /* expiry time in sec */
+ long rtt; /* round trip time, nsec */
+ int prox; /* proximity, lower = closer */
+};
+
+Dfscache *Cache;
+
+int
+dfscacheinfo(Fmt *f)
+{
+ long ex;
+ Dfscache *cp;
+
+ for(cp = Cache; cp; cp = cp->next){
+ ex = cp->expiry - time(nil);
+ if(ex < 0)
+ ex = -1;
+ fmtprint(f, "%-42s %6ld %8.1f %4d %-16s %-24s %s\n",
+ cp->src, ex, (double)cp->rtt/1000.0L, cp->prox,
+ cp->host, cp->share, cp->path);
+ }
+ return 0;
+}
+
+char *
+trimshare(char *s)
+{
+ char *p;
+ static char name[128];
+
+ strncpy(name, s, sizeof(name));
+ name[sizeof(name)-1] = 0;
+ if((p = strrchr(name, '$')) != nil && p[1] == 0)
+ *p = 0;
+ return name;
+}
+
+static Dfscache *
+lookup(char *path, int *match)
+{
+ int len, n, m;
+ Dfscache *cp, *best;
+
+ if(match)
+ *match = Nomatch;
+
+ len = 0;
+ best = nil;
+ m = strlen(path);
+ for(cp = Cache; cp; cp = cp->next){
+ n = strlen(cp->src);
+ if(n < len)
+ continue;
+ if(strncmp(path, cp->src, n) != 0)
+ continue;
+ if(path[n] != 0 && path[n] != '/')
+ continue;
+ best = cp;
+ len = n;
+ if(n == m){
+ if(match)
+ *match = Exactmatch;
+ break;
+ }
+ }
+ return best;
+}
+
+char *
+mapfile(char *opath)
+{
+ int exact;
+ Dfscache *cp;
+ char *p, *path;
+ static char npath[MAX_DFS_PATH];
+
+ path = opath;
+ if((cp = lookup(path, &exact)) != nil){
+ snprint(npath, sizeof npath, "/%s%s%s%s", cp->share,
+ *cp->path? "/": "", cp->path, path + strlen(cp->src));
+ path = npath;
+ }
+
+ if((p = strchr(path+1, '/')) == nil)
+ p = "/";
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print("mapfile src=%q => dst=%q\n", opath, p);
+ return p;
+}
+
+int
+mapshare(char *path, Share **osp)
+{
+ int i;
+ Share *sp;
+ Dfscache *cp;
+ char *s, *try;
+ char *tail[] = { "", "$" };
+
+ if((cp = lookup(path, nil)) == nil)
+ return 0;
+
+ for(sp = Shares; sp < Shares+Nshares; sp++){
+ s = trimshare(sp->name);
+ if(cistrcmp(cp->share, s) != 0)
+ continue;
+ if(Checkcase && strcmp(cp->share, s) != 0)
+ continue;
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print("mapshare, already connected, src=%q => dst=%q\n", path, sp->name);
+ *osp = sp;
+ return 0;
+ }
+ /*
+ * Try to autoconnect to share if it is not known. Note even if you
+ * didn't specify any shares and let the system autoconnect you may
+ * not already have the share you need as RAP (which we use) throws
+ * away names > 12 chars long. If we where to use RPC then this block
+ * of code would be less important, though it would still be useful
+ * to catch Shares added since cifs(1) was started.
+ */
+ sp = Shares + Nshares;
+ for(i = 0; i < 2; i++){
+ try = smprint("%s%s", cp->share, tail[i]);
+ if(CIFStreeconnect(Sess, Sess->cname, try, sp) == 0){
+ sp->name = try;
+ *osp = sp;
+ Nshares++;
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print("mapshare connected, src=%q dst=%q\n",
+ path, cp->share);
+ return 0;
+ }
+ free(try);
+ }
+
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print("mapshare failed src=%s\n", path);
+ werrstr("not found");
+ return -1;
+}
+
+/*
+ * Rtt_tol is the fractional tollerance for RTT comparisons.
+ * If a later (further down the list) host's RTT is less than
+ * 1/Rtt_tol better than my current best then I don't bother
+ * with it. This biases me towards entries at the top of the list
+ * which Active Directory has already chosen for me and prevents
+ * noise in RTTs from pushing me to more distant machines.
+ */
+static int
+remap(Dfscache *cp, Refer *re)
+{
+ int n;
+ long rtt;
+ char *p, *a[4];
+ enum {
+ Hostname = 1,
+ Sharename = 2,
+ Pathname = 3,
+
+ Rtt_tol = 10
+ };
+
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print(" remap %s\n", re->addr);
+
+ for(p = re->addr; *p; p++)
+ if(*p == '\\')
+ *p = '/';
+
+ if(cp->prox < re->prox){
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print(" remap %d < %d\n", cp->prox, re->prox);
+ return -1;
+ }
+ if((n = getfields(re->addr, a, sizeof(a), 0, "/")) < 3){
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print(" remap nfields=%d\n", n);
+ return -1;
+ }
+ if((rtt = ping(a[Hostname], Dfstout)) == -1){
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print(" remap ping failed\n");
+ return -1;
+ }
+ if(cp->rtt < rtt && (rtt/labs(rtt-cp->rtt)) < Rtt_tol){
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print(" remap bad ping %ld < %ld && %ld < %d\n",
+ cp->rtt, rtt, (rtt/labs(rtt-cp->rtt)), Rtt_tol);
+ return -1;
+ }
+
+ if(n < 4)
+ a[Pathname] = "";
+ if(re->ttl == 0)
+ re->ttl = 60*5;
+
+ free(cp->host);
+ free(cp->share);
+ free(cp->path);
+ cp->rtt = rtt;
+ cp->prox = re->prox;
+ cp->expiry = time(nil)+re->ttl;
+ cp->host = estrdup9p(a[Hostname]);
+ cp->share = estrdup9p(trimshare(a[Sharename]));
+ cp->path = estrdup9p(a[Pathname]);
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print(" remap ping OK prox=%d host=%s share=%s path=%s\n",
+ cp->prox, cp->host, cp->share, cp->path);
+ return 0;
+}
+
+static int
+redir1(Session *s, char *path, Dfscache *cp, int level)
+{
+ Refer retab[16], *re;
+ int n, gflags, used, found;
+
+ if(level > 8)
+ return -1;
+
+ if((n = T2getdfsreferral(s, &Ipc, path, &gflags, &used, retab,
+ nelem(retab))) == -1)
+ return -1;
+
+ if(! (gflags & DFS_HEADER_ROOT))
+ used = SINT_MAX;
+
+ found = 0;
+ for(re = retab; re < retab+n; re++){
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print("referal level=%d prox=%d path=%q addr=%q\n",
+ level, re->prox, re->path, re->addr);
+
+ if(gflags & DFS_HEADER_STORAGE){
+ if(remap(cp, re) == 0)
+ found = 1;
+ } else{
+ if(redir1(s, re->addr, cp, level+1) != -1) /* ???? */
+ found = 1;
+ }
+ free(re->addr);
+ free(re->path);
+ }
+
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print("referal level=%d path=%q found=%d used=%d\n",
+ level, path, found, used);
+ if(!found)
+ return -1;
+ return used;
+}
+
+/*
+ * We can afford to ignore the used count returned by redir
+ * because of the semantics of 9p - we always walk to directories
+ * ome and we a time and we always walk before any other file operations
+ */
+int
+redirect(Session *s, Share *sp, char *path)
+{
+ int match;
+ char *unc;
+ Dfscache *cp;
+
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print("redirect name=%q path=%q\n", sp->name, path);
+
+ cp = lookup(path, &match);
+ if(match == Badmatch)
+ return -1;
+
+ if(cp && match == Exactmatch){
+ if(cp->expiry >= time(nil)){ /* cache hit */
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print("redirect cache=hit src=%q => share=%q path=%q\n",
+ cp->src, cp->share, cp->path);
+ return 0;
+
+ } else{ /* cache hit, but entry stale */
+ cp->rtt = SINT_MAX;
+ cp->prox = SINT_MAX;
+
+ unc = smprint("//%s/%s/%s%s%s", s->auth->windom,
+ cp->share, cp->path, *cp->path? "/": "",
+ path + strlen(cp->src) + 1);
+ if(unc == nil)
+ sysfatal("no memory: %r");
+ if(redir1(s, unc, cp, 1) == -1){
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print("redirect refresh failed unc=%q\n",
+ unc);
+ free(unc);
+ return -1;
+ }
+ free(unc);
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print("redirect refresh cache=stale src=%q => share=%q path=%q\n",
+ cp->src, cp->share, cp->path);
+ return 0;
+ }
+ }
+
+
+ /* in-exact match or complete miss */
+ if(cp)
+ unc = smprint("//%s/%s/%s%s%s", s->auth->windom, cp->share,
+ cp->path, *cp->path? "/": "", path + strlen(cp->src) + 1);
+ else
+ unc = smprint("//%s%s", s->auth->windom, path);
+ if(unc == nil)
+ sysfatal("no memory: %r");
+
+ cp = emalloc9p(sizeof(Dfscache));
+ memset(cp, 0, sizeof(Dfscache));
+ cp->rtt = SINT_MAX;
+ cp->prox = SINT_MAX;
+
+ if(redir1(s, unc, cp, 1) == -1){
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print("redirect new failed unc=%q\n", unc);
+ free(unc);
+ free(cp);
+ return -1;
+ }
+ free(unc);
+
+ cp->src = estrdup9p(path);
+ cp->next = Cache;
+ Cache = cp;
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print("redirect cache=miss src=%q => share=%q path=%q\n",
+ cp->src, cp->share, cp->path);
+ return 0;
+}
+
diff --git a/sys/src/cmd/cifs/doserrstr.c b/sys/src/cmd/cifs/doserrstr.c
new file mode 100755
index 000000000..c0b8c98af
--- /dev/null
+++ b/sys/src/cmd/cifs/doserrstr.c
@@ -0,0 +1,187 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * This file is derrived from nterr.h in the samba distribution
+ */
+
+/*
+ Unix SMB/CIFS implementation.
+ DOS error code constants
+ Copyright (C) Andrew Tridgell 1992-2000
+ Copyright (C) John H Terpstra 1996-2000
+ Copyright (C) Luke Kenneth Casson Leighton 1996-2000
+ Copyright (C) Paul Ashton 1998-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+static struct {
+ int err;
+ char *msg;
+} DOSerrs[] = {
+ /* smb x/open error codes for the errdos error class */
+ { (0<<16)|1, "no error" },
+ { (1<<16)|1, "invalid function" },
+ { (2<<16)|1, "file not found" },
+ { (3<<16)|1, "directory not found" },
+ { (4<<16)|1, "too many open files" },
+ { (5<<16)|1, "access denied" },
+ { (6<<16)|1, "invalid fid" },
+ { (7<<16)|1, "memory control blocks destroyed." },
+ { (8<<16)|1, "out of memory" },
+ { (9<<16)|1, "invalid memory block address" },
+ { (10<<16)|1, "invalid environment" },
+ { (12<<16)|1, "invalid open mode" },
+ { (13<<16)|1, "invalid data (only from ioctl call)" },
+ { (14<<16)|1, "reserved" },
+ { (15<<16)|1, "invalid drive" },
+ { (16<<16)|1, "attempt to delete current directory" },
+ { (17<<16)|1, "rename/move across filesystems" },
+ { (18<<16)|1, "no more files found" },
+ { (31<<16)|1, "general failure" },
+ { (32<<16)|1, "share mode conflict with open mode" },
+ { (33<<16)|1, "lock conflicts" },
+ { (50<<16)|1, "request unsupported" },
+ { (64<<16)|1, "network name not available" },
+ { (66<<16)|1, "ipc unsupported (guess)" },
+ { (67<<16)|1, "invalid share name" },
+ { (80<<16)|1, "file already exists" },
+ { (87<<16)|1, "invalid paramater" },
+ { (110<<16)|1, "cannot open" },
+ { (122<<16)|1, "insufficent buffer" },
+ { (123<<16)|1, "invalid name" },
+ { (124<<16)|1, "unknown level" },
+ { (158<<16)|1, "this region already locked" },
+
+ { (183<<16)|1, "rename failed" },
+
+ { (230<<16)|1, "named pipe invalid" },
+ { (231<<16)|1, "pipe busy" },
+ { (232<<16)|1, "close in progress" },
+ { (233<<16)|1, "no reader of named pipe" },
+ { (234<<16)|1, "more data to be returned" },
+ { (259<<16)|1, "no more items" },
+ { (267<<16)|1, "invalid directory name in a path" },
+ { (282<<16)|1, "extended attributes" },
+ { (1326<<16)|1, "authentication failed" },
+ { (2123<<16)|1, "buffer too small" },
+ { (2142<<16)|1, "unknown ipc" },
+ { (2151<<16)|1, "no such print job" },
+ { (2455<<16)|1, "invalid group" },
+
+ /* Error codes for the ERRSRV class */
+ { (1<<16)|2, "non specific error" },
+ { (2<<16)|2, "bad password" },
+ { (3<<16)|2, "reserved" },
+ { (4<<16)|2, "permission denied" },
+ { (5<<16)|2, "tid invalid" },
+ { (6<<16)|2, "invalid server name" },
+ { (7<<16)|2, "invalid device" },
+ { (22<<16)|2, "unknown smb" },
+ { (49<<16)|2, "print queue full" },
+ { (50<<16)|2, "queued item too big" },
+ { (52<<16)|2, "fid invalid in print file" },
+ { (64<<16)|2, "unrecognised command" },
+ { (65<<16)|2, "smb server internal error" },
+ { (67<<16)|2, "fid/pathname invalid" },
+ { (68<<16)|2, "reserved 68" },
+ { (69<<16)|2, "access is invalid" },
+ { (70<<16)|2, "reserved 70" },
+ { (71<<16)|2, "attribute mode invalid" },
+ { (81<<16)|2, "message server paused" },
+ { (82<<16)|2, "not receiving messages" },
+ { (83<<16)|2, "no room for message" },
+ { (87<<16)|2, "too many remote usernames" },
+ { (88<<16)|2, "operation timed out" },
+ { (89<<16)|2, "no resources" },
+ { (90<<16)|2, "too many userids" },
+ { (91<<16)|2, "bad userid" },
+ { (250<<16)|2, "retry with mpx mode" },
+ { (251<<16)|2, "retry with standard mode" },
+ { (252<<16)|2, "resume mpx mode" },
+ { (0xffff<<16)|2, "function not supported" },
+
+ /* Error codes for the ERRHRD class */
+ { (19<<16)|3, "read only media" },
+ { (20<<16)|3, "unknown device" },
+ { (21<<16)|3, "drive not ready" },
+ { (22<<16)|3, "unknown command" },
+ { (23<<16)|3, "data (CRC) error" },
+ { (24<<16)|3, "bad request length" },
+ { (25<<16)|3, "seek failed" },
+ { (26<<16)|3, "bad media" },
+ { (27<<16)|3, "bad sector" },
+ { (28<<16)|3, "no paper" },
+ { (29<<16)|3, "write fault" },
+ { (30<<16)|3, "read fault" },
+ { (31<<16)|3, "general hardware failure" },
+ { (34<<16)|3, "wrong disk" },
+ { (35<<16)|3, "FCB unavailable" },
+ { (36<<16)|3, "share buffer exceeded" },
+ { (39<<16)|3, "disk full" },
+
+};
+
+char *
+doserrstr(uint err)
+{
+ int i, match;
+ char *class;
+ static char buf[0xff];
+
+ switch(err & 0xff){
+ case 1:
+ class = "dos";
+ break;
+ case 2:
+ class = "network";
+ break;
+ case 3:
+ class = "hardware";
+ break;
+ case 4:
+ class = "Xos";
+ break;
+ case 0xe1:
+ class = "mx1";
+ break;
+ case 0xe2:
+ class = "mx2";
+ break;
+ case 0xe3:
+ class = "mx3";
+ break;
+ case 0xff:
+ class = "packet";
+ break;
+ default:
+ class = "unknown";
+ break;
+ }
+
+ match = -1;
+ for(i = 0; i < nelem(DOSerrs); i++)
+ if(DOSerrs[i].err == err)
+ match = i;
+
+ if(match != -1)
+ snprint(buf, sizeof(buf), "%s, %s", class, DOSerrs[match].msg);
+ else
+ snprint(buf, sizeof(buf), "%s, %ud/0x%ux - unknown error",
+ class, err >> 16, err >> 16);
+ return buf;
+}
diff --git a/sys/src/cmd/cifs/fs.c b/sys/src/cmd/cifs/fs.c
new file mode 100755
index 000000000..153937143
--- /dev/null
+++ b/sys/src/cmd/cifs/fs.c
@@ -0,0 +1,366 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "cifs.h"
+
+static char *period(long sec);
+
+int
+shareinfo(Fmt *f)
+{
+ int i, j, n;
+ char *type;
+ Shareinfo2 si2;
+ Share *sp, *sip;
+
+ if((n = RAPshareenum(Sess, &Ipc, &sip)) < 1){
+ fmtprint(f, "can't enumerate shares: %r\n");
+ return 0;
+ }
+
+ for(i = 0; i < n; i++){
+ fmtprint(f, "%-13q ", sip[i].name);
+
+ sp = &sip[i];
+ for(j = 0; j < Nshares; j++)
+ if(strcmp(Shares[j].name, sip[i].name) == 0){
+ sp = &Shares[j];
+ break;
+ }
+ if(j >= Nshares)
+ sp->tid = Ipc.tid;
+
+ if(RAPshareinfo(Sess, sp, sp->name, &si2) != -1){
+ switch(si2.type){
+ case STYPE_DISKTREE: type = "disk"; break;
+ case STYPE_PRINTQ: type = "printq"; break;
+ case STYPE_DEVICE: type = "device"; break;
+ case STYPE_IPC: type = "ipc"; break;
+ case STYPE_SPECIAL: type = "special"; break;
+ case STYPE_TEMP: type = "temp"; break;
+ default: type = "unknown"; break;
+ }
+
+ fmtprint(f, "%-8s %5d/%-5d %s", type,
+ si2.activeusrs, si2.maxusrs, si2.comment);
+ free(si2.name);
+ free(si2.comment);
+ free(si2.path);
+ free(si2.passwd);
+ }
+ fmtprint(f, "\n");
+
+ }
+ free(sip);
+ return 0;
+}
+
+int
+openfileinfo(Fmt *f)
+{
+ int got, i;
+ Fileinfo *fi;
+
+ fi = nil;
+ if((got = RAPFileenum2(Sess, &Ipc, "", "", &fi)) == -1){
+ fmtprint(f, "RAPfileenum: %r\n");
+ return 0;
+ }
+
+ for(i = 0; i < got; i++){
+ fmtprint(f, "%c%c%c %-4d %-24q %q ",
+ (fi[i].perms & 1)? 'r': '-',
+ (fi[i].perms & 2)? 'w': '-',
+ (fi[i].perms & 4)? 'c': '-',
+ fi[i].locks, fi[i].user, fi[i].path);
+ free(fi[i].path);
+ free(fi[i].user);
+ }
+ free(fi);
+ return 0;
+}
+
+int
+conninfo(Fmt *f)
+{
+ int i;
+ typedef struct {
+ int val;
+ char *name;
+ } Tab;
+ static Tab captab[] = {
+ { 1, "raw-mode" },
+ { 2, "mpx-mode" },
+ { 4, "unicode" },
+ { 8, "large-files" },
+ { 0x10, "NT-smbs" },
+ { 0x20, "rpc-remote-APIs" },
+ { 0x40, "status32" },
+ { 0x80, "l2-oplocks" },
+ { 0x100, "lock-read" },
+ { 0x200, "NT-find" },
+ { 0x1000, "Dfs" },
+ { 0x2000, "info-passthru" },
+ { 0x4000, "large-readx" },
+ { 0x8000, "large-writex" },
+ { 0x800000, "Unix" },
+ { 0x20000000, "bulk-transfer" },
+ { 0x40000000, "compressed" },
+ { 0x80000000, "extended-security" },
+ };
+ static Tab sectab[] = {
+ { 1, "user-auth" },
+ { 2, "challange-response" },
+ { 4, "signing-available" },
+ { 8, "signing-required" },
+ };
+
+ fmtprint(f, "%q %q %q %q %+ldsec %dmtu %s\n",
+ Sess->auth->user, Sess->cname,
+ Sess->auth->windom, Sess->remos,
+ Sess->slip, Sess->mtu, Sess->isguest? "as guest": "");
+
+ fmtprint(f, "caps: ");
+ for(i = 0; i < nelem(captab); i++)
+ if(Sess->caps & captab[i].val)
+ fmtprint(f, "%s ", captab[i].name);
+ fmtprint(f, "\n");
+
+ fmtprint(f, "security: ");
+ for(i = 0; i < nelem(sectab); i++)
+ if(Sess->secmode & sectab[i].val)
+ fmtprint(f, "%s ", sectab[i].name);
+ fmtprint(f, "\n");
+
+ if(Sess->nbt)
+ fmtprint(f, "transport: cifs over netbios\n");
+ else
+ fmtprint(f, "transport: cifs\n");
+ return 0;
+}
+
+int
+sessioninfo(Fmt *f)
+{
+ int got, i;
+ Sessinfo *si;
+
+ si = nil;
+ if((got = RAPsessionenum(Sess, &Ipc, &si)) == -1){
+ fmtprint(f, "RAPsessionenum: %r\n");
+ return 0;
+ }
+
+ for(i = 0; i < got; i++){
+ fmtprint(f, "%-24q %-24q ", si[i].user, si[i].wrkstn);
+ fmtprint(f, "%12s ", period(si[i].sesstime));
+ fmtprint(f, "%12s\n", period(si[i].idletime));
+ free(si[i].wrkstn);
+ free(si[i].user);
+ }
+ free(si);
+ return 0;
+}
+
+/*
+ * We request the domain referral for "" which gives the
+ * list of all the trusted domains in the clients forest, and
+ * other trusted forests.
+ *
+ * We then sumbit each of these names in turn which gives the
+ * names of the domain controllers for that domain.
+ *
+ * We get a DNS domain name for each domain controller as well as a
+ * netbios name. I THINK I am correct in saying that a name
+ * containing a dot ('.') must be a DNS name, as the NetBios
+ * name munging cannot encode one. Thus names which contain no
+ * dots must be netbios names.
+ *
+ */
+static void
+dfsredir(Fmt *f, char *path, int depth)
+{
+ Refer *re, retab[128];
+ int n, used, flags;
+
+ n = T2getdfsreferral(Sess, &Ipc, path, &flags, &used, retab, nelem(retab));
+ if(n == -1)
+ return;
+ for(re = retab; re < retab+n; re++){
+ if(strcmp(path, re->path) != 0)
+ dfsredir(f, re->path, depth+1);
+ else
+ fmtprint(f, "%-32q %q\n", re->path, re->addr);
+
+ free(re->addr);
+ free(re->path);
+ }
+}
+
+int
+dfsrootinfo(Fmt *f)
+{
+ dfsredir(f, "", 0);
+ return 0;
+}
+
+
+int
+userinfo(Fmt *f)
+{
+ int got, i;
+ Namelist *nl;
+ Userinfo ui;
+
+ nl = nil;
+ if((got = RAPuserenum2(Sess, &Ipc, &nl)) == -1)
+ if((got = RAPuserenum(Sess, &Ipc, &nl)) == -1){
+ fmtprint(f, "RAPuserenum: %r\n");
+ return 0;
+ }
+
+ for(i = 0; i < got; i++){
+ fmtprint(f, "%-24q ", nl[i].name);
+
+ if(RAPuserinfo(Sess, &Ipc, nl[i].name, &ui) != -1){
+ fmtprint(f, "%-48q %q", ui.fullname, ui.comment);
+ free(ui.user);
+ free(ui.comment);
+ free(ui.fullname);
+ free(ui.user_comment);
+ }
+ free(nl[i].name);
+ fmtprint(f, "\n");
+ }
+ free(nl);
+ return 0;
+}
+
+int
+groupinfo(Fmt *f)
+{
+ int got1, got2, i, j;
+ Namelist *grps, *usrs;
+
+ grps = nil;
+ if((got1 = RAPgroupenum(Sess, &Ipc, &grps)) == -1){
+ fmtprint(f, "RAPgroupenum: %r\n");
+ return 0;
+ }
+
+ for(i = 0; i < got1; i++){
+ fmtprint(f, "%q ", grps[i].name);
+ usrs = nil;
+ if((got2 = RAPgroupusers(Sess, &Ipc, grps[i].name, &usrs)) != -1){
+ for(j = 0; j < got2; j++){
+ fmtprint(f, "%q ", usrs[j].name);
+ free(usrs[j].name);
+ }
+ free(usrs);
+ }
+ free(grps[i].name);
+ fmtprint(f, "\n");
+ }
+ free(grps);
+ return 0;
+}
+
+static int
+nodelist(Fmt *f, int type)
+{
+ int more, got, i, j;
+ Serverinfo *si;
+ static char *types[] = {
+ [0] "workstation",
+ [1] "server",
+ [2] "SQL server",
+ [3] "DC",
+ [4] "backup DC",
+ [5] "time source",
+ [6] "Apple server",
+ [7] "Novell server",
+ [8] "domain member",
+ [9] "printer server",
+ [10] "dial-up server",
+ [11] "Unix",
+ [12] "NT",
+ [13] "WFW",
+ [14] "MFPN (?)",
+ [15] "NT server",
+ [16] "potential browser",
+ [17] "backup browser",
+ [18] "LMB",
+ [19] "DMB",
+ [20] "OSF Unix",
+ [21] "VMS",
+ [22] "Win95",
+ [23] "DFS",
+ [24] "NT cluster",
+ [25] "Terminal server",
+ [26] "[26]",
+ [27] "[27]",
+ [28] "IBM DSS",
+ };
+
+ si = nil;
+ if((got = RAPServerenum2(Sess, &Ipc, Sess->auth->windom, type, &more,
+ &si)) == -1){
+ fmtprint(f, "RAPServerenum2: %r\n");
+ return 0;
+ }
+ if(more)
+ if((got = RAPServerenum3(Sess, &Ipc, Sess->auth->windom, type,
+ got-1, si)) == -1){
+ fmtprint(f, "RAPServerenum3: %r\n");
+ return 0;
+ }
+
+ for(i = 0; i < got; i++){
+ fmtprint(f, "%-16q %-16q ", si[i].name, si[i].comment);
+ if(type != LIST_DOMAINS_ONLY){
+ fmtprint(f, "v%d.%d ", si[i].major, si[i].minor);
+ for(j = 0; j < nelem(types); j++)
+ if(si[i].type & (1 << j) && types[j])
+ fmtprint(f, "%s,", types[j]);
+ }
+ fmtprint(f, "\n");
+ free(si[i].name);
+ free(si[i].comment);
+ }
+ free(si);
+ return 0;
+}
+
+int
+domaininfo(Fmt *f)
+{
+ return nodelist(f, LIST_DOMAINS_ONLY);
+}
+
+int
+workstationinfo(Fmt *f)
+{
+ return nodelist(f, ALL_LEARNT_IN_DOMAIN);
+}
+
+static char *
+period(long sec)
+{
+ int days, hrs, min;
+ static char when[32];
+
+ days = sec / (60L * 60L * 24L);
+ sec -= days * (60L * 60L * 24L);
+ hrs = sec / (60L * 60L);
+ sec -= hrs * (60L * 60L);
+ min = sec / 60L;
+ sec -= min * 60L;
+ if(days)
+ snprint(when, sizeof(when), "%d %d:%d:%ld ", days, hrs, min, sec);
+ else
+ snprint(when, sizeof(when), "%d:%d:%ld ", hrs, min, sec);
+ return when;
+}
diff --git a/sys/src/cmd/cifs/info.c b/sys/src/cmd/cifs/info.c
new file mode 100755
index 000000000..f6feb5f3b
--- /dev/null
+++ b/sys/src/cmd/cifs/info.c
@@ -0,0 +1,106 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "cifs.h"
+
+
+struct {
+ char *name;
+ int (*func)(Fmt *f);
+ char *buf;
+ int len;
+} Infdir[] = {
+ { "Users", userinfo },
+ { "Groups", groupinfo },
+ { "Shares", shareinfo },
+ { "Connection", conninfo },
+ { "Sessions", sessioninfo },
+ { "Dfsroot", dfsrootinfo },
+ { "Dfscache", dfscacheinfo },
+ { "Domains", domaininfo },
+ { "Openfiles", openfileinfo },
+ { "Workstations", workstationinfo },
+ { "Filetable", filetableinfo },
+};
+
+int
+walkinfo(char *name)
+{
+ int i;
+
+ for(i = 0; i < nelem(Infdir); i++)
+ if(strcmp(Infdir[i].name, name) == 0)
+ return(i);
+ return -1;
+}
+
+int
+numinfo(void)
+{
+ return nelem(Infdir);
+}
+
+int
+dirgeninfo(int slot, Dir *d)
+{
+ if(slot < 0 || slot > nelem(Infdir))
+ return -1;
+
+ memset(d, 0, sizeof(Dir));
+ d->type = 'N';
+ d->dev = 99;
+ d->name = estrdup9p(Infdir[slot].name);
+ d->uid = estrdup9p("other");
+ d->muid = estrdup9p("other");
+ d->gid = estrdup9p("other");
+ d->mode = 0666;
+ d->atime = time(0);
+ d->mtime = d->atime;
+ d->qid = mkqid(Infdir[slot].name, 0, 1, Pinfo, slot);
+ d->qid.vers = 1;
+ d->qid.path = slot;
+ d->qid.type = 0;
+ return 0;
+}
+
+int
+makeinfo(int path)
+{
+ Fmt f;
+
+ if(path < 0 || path > nelem(Infdir))
+ return -1;
+ if(Infdir[path].buf != nil)
+ return 0;
+ fmtstrinit(&f);
+ if((*Infdir[path].func)(&f) == -1l)
+ return -1;
+ Infdir[path].buf = fmtstrflush(&f);
+ Infdir[path].len = strlen(Infdir[path].buf);
+ return 0;
+}
+
+int
+readinfo(int path, char *buf, int len, int off)
+{
+ if(path < 0 || path > nelem(Infdir))
+ return -1;
+ if(off > Infdir[path].len)
+ return 0;
+ if(len + off > Infdir[path].len)
+ len = Infdir[path].len - off;
+ memmove(buf, Infdir[path].buf + off, len);
+ return len;
+}
+
+void
+freeinfo(int path)
+{
+ if(path < 0 || path > nelem(Infdir))
+ return;
+ free(Infdir[path].buf);
+ Infdir[path].buf = nil;
+}
diff --git a/sys/src/cmd/cifs/main.c b/sys/src/cmd/cifs/main.c
new file mode 100755
index 000000000..4849c5495
--- /dev/null
+++ b/sys/src/cmd/cifs/main.c
@@ -0,0 +1,1283 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <libsec.h>
+#include <9p.h>
+#include "cifs.h"
+
+#define max(a,b) (((a) > (b))? (a): (b))
+#define min(a,b) (((a) < (b))? (a): (b))
+
+typedef struct Aux Aux;
+struct Aux {
+ Aux *next;
+ Aux *prev;
+ char *path; /* full path fo file */
+ Share *sp; /* this share's info */
+ long expire; /* expiration time of cache */
+ long off; /* file pos of start of cache */
+ long end; /* file pos of end of cache */
+ char *cache;
+ int fh; /* file handle */
+ int sh; /* search handle */
+ long srch; /* find first's internal state */
+};
+
+extern int chatty9p;
+
+int Checkcase = 1; /* enforce case significance on filenames */
+int Dfstout = 100; /* timeout (in ms) for ping of dfs servers (assume they are local) */
+int Billtrog = 1; /* enable file owner/group resolution */
+int Attachpid; /* pid of proc that attaches (ugh !) */
+char *Debug = nil; /* messages */
+Qid Root; /* root of remote system */
+Share Ipc; /* Share info of IPC$ share */
+Session *Sess; /* current session */
+int Active = IDLE_TIME; /* secs until next keepalive is sent */
+static int Keeppid; /* process ID of keepalive thread */
+Share Shares[MAX_SHARES]; /* table of connected shares */
+int Nshares = 0; /* number of Shares connected */
+Aux *Auxroot = nil; /* linked list of Aux structs */
+char *Host = nil; /* host we are connected to */
+
+static char *Ipcname = "IPC$";
+
+#define ptype(x) (((x) & 0xf))
+#define pindex(x) (((x) & 0xff0) >> 4)
+
+void
+setup(void)
+{
+ int fd;
+ char buf[32];
+
+ /*
+ * This is revolting but I cannot see any other way to get
+ * the pid of the server. We need this as Windows doesn't
+ * drop the TCP connection when it closes a connection.
+ * Thus we keepalive() to detect when/if we are thrown off.
+ */
+ Attachpid = getpid();
+
+ snprint(buf, sizeof buf, "#p/%d/args", getpid());
+ if((fd = open(buf, OWRITE)) >= 0){
+ fprint(fd, "%s network", Host);
+ close(fd);
+ }
+}
+
+int
+filetableinfo(Fmt *f)
+{
+ Aux *ap;
+ char *type;
+
+ if((ap = Auxroot) != nil)
+ do{
+ type = "walked";
+ if(ap->sh != -1)
+ type = "opendir";
+ if(ap->fh != -1)
+ type = "openfile";
+ fmtprint(f, "%-9s %s\n", type, ap->path);
+ ap = ap->next;
+ }while(ap != Auxroot);
+ return 0;
+}
+
+Qid
+mkqid(char *s, int is_dir, long vers, int subtype, long path)
+{
+ Qid q;
+ union { /* align digest suitably */
+ uchar digest[SHA1dlen];
+ uvlong uvl;
+ } u;
+
+ sha1((uchar *)s, strlen(s), u.digest, nil);
+ q.type = (is_dir)? QTDIR: 0;
+ q.vers = vers;
+ if(subtype){
+ q.path = *((uvlong *)u.digest) & ~0xfffL;
+ q.path |= ((path & 0xff) << 4);
+ q.path |= (subtype & 0xf);
+ }
+ else
+ q.path = *((uvlong *)u.digest) & ~0xfL;
+ return q;
+}
+
+/*
+ * used only for root dir and shares
+ */
+static void
+V2D(Dir *d, Qid qid, char *name)
+{
+ memset(d, 0, sizeof(Dir));
+ d->type = 'C';
+ d->dev = 1;
+ d->name = estrdup9p(name);
+ d->uid = estrdup9p("bill");
+ d->muid = estrdup9p("boyd");
+ d->gid = estrdup9p("trog");
+ d->mode = 0755 | DMDIR;
+ d->atime = time(nil);
+ d->mtime = d->atime;
+ d->length = 0;
+ d->qid = qid;
+}
+
+static void
+I2D(Dir *d, Share *sp, char *path, FInfo *fi)
+{
+ char *name;
+
+ if((name = strrchr(fi->name, '\\')) != nil)
+ name++;
+ else
+ name = fi->name;
+ d->name = estrdup9p(name);
+ d->type = 'C';
+ d->dev = sp->tid;
+ d->uid = estrdup9p("bill");
+ d->gid = estrdup9p("trog");
+ d->muid = estrdup9p("boyd");
+ d->atime = fi->accessed;
+ d->mtime = fi->written;
+
+ if(fi->attribs & ATTR_READONLY)
+ d->mode = 0444;
+ else
+ d->mode = 0666;
+
+ d->length = fi->size;
+ d->qid = mkqid(path, fi->attribs & ATTR_DIRECTORY, fi->changed, 0, 0);
+
+ if(fi->attribs & ATTR_DIRECTORY){
+ d->length = 0;
+ d->mode |= DMDIR|0111;
+ }
+}
+
+static void
+responderrstr(Req *r)
+{
+ char e[ERRMAX];
+
+ *e = 0;
+ rerrstr(e, sizeof e);
+ respond(r, e);
+}
+
+static char *
+newpath(char *path, char *name)
+{
+ char *p, *q;
+
+ assert((p = strrchr(path, '/')) != nil);
+
+ if(strcmp(name, "..") == 0){
+ if(p == path)
+ return estrdup9p("/");
+ q = emalloc9p((p-path)+1);
+ strecpy(q, q+(p-path)+1, path);
+ return q;
+ }
+ if(strcmp(path, "/") == 0)
+ return smprint("/%s", name);
+ return smprint("%s/%s", path, name);
+}
+
+static int
+dirgen(int slot, Dir *d, void *aux)
+{
+ long off;
+ FInfo *fi;
+ int rc, got;
+ Aux *a = aux;
+ char *npath;
+ int numinf = numinfo();
+ int slots = min(Sess->mtu, MTU) / sizeof(FInfo);
+
+ if(strcmp(a->path, "/") == 0){
+ if(slot < numinf){
+ dirgeninfo(slot, d);
+ return 0;
+ } else
+ slot -= numinf;
+
+ if(slot >= Nshares)
+ return -1;
+ V2D(d, mkqid(Shares[slot].name, 1, 1, Pshare, slot),
+ Shares[slot].name);
+ return 0;
+ }
+
+ off = slot * sizeof(FInfo);
+ if(off >= a->off && off < a->end && time(nil) < a->expire)
+ goto from_cache;
+
+ if(off == 0){
+ fi = (FInfo *)a->cache;
+ npath = smprint("%s/*", mapfile(a->path));
+ a->sh = T2findfirst(Sess, a->sp, slots, npath, &got, &a->srch,
+ (FInfo *)a->cache);
+ free(npath);
+ if(a->sh == -1)
+ return -1;
+
+ a->off = 0;
+ a->end = got * sizeof(FInfo);
+
+ if(got >= 2 && strcmp(fi[0].name, ".") == 0 &&
+ strcmp(fi[1].name, "..") == 0){
+ a->end = (got - 2) * sizeof(FInfo);
+ memmove(a->cache, a->cache + sizeof(FInfo)*2,
+ a->end - a->off);
+ }
+ }
+
+ while(off >= a->end && a->sh != -1){
+ fi = (FInfo *)(a->cache + (a->end - a->off) - sizeof(FInfo));
+ a->off = a->end;
+ npath = smprint("%s/%s", mapfile(a->path), fi->name);
+ rc = T2findnext(Sess, a->sp, slots, npath,
+ &got, &a->srch, (FInfo *)a->cache, a->sh);
+ free(npath);
+ if(rc == -1 || got == 0)
+ break;
+ a->end = a->off + got * sizeof(FInfo);
+ }
+ a->expire = time(nil) + CACHETIME;
+
+ if(got < slots){
+ if(a->sh != -1)
+ CIFSfindclose2(Sess, a->sp, a->sh);
+ a->sh = -1;
+ }
+
+ if(off >= a->end)
+ return -1;
+
+from_cache:
+ fi = (FInfo *)(a->cache + (off - a->off));
+ npath = smprint("%s/%s", mapfile(a->path), fi->name);
+ I2D(d, a->sp, npath, fi);
+ if(Billtrog == 0)
+ upd_names(Sess, a->sp, npath, d);
+ free(npath);
+ return 0;
+}
+
+static void
+fsattach(Req *r)
+{
+ Aux *a;
+ static int first = 1;
+ char *spec = r->ifcall.aname;
+
+ if(first)
+ setup();
+
+ if(spec && *spec){
+ respond(r, "invalid attach specifier");
+ return;
+ }
+
+ r->ofcall.qid = mkqid("/", 1, 1, Proot, 0);
+ r->fid->qid = r->ofcall.qid;
+
+ a = r->fid->aux = emalloc9p(sizeof(Aux));
+ memset(a, 0, sizeof(Aux));
+ a->path = estrdup9p("/");
+ a->sp = nil;
+ a->fh = -1;
+ a->sh = -1;
+
+ if(Auxroot){
+ a->prev = Auxroot;
+ a->next = Auxroot->next;
+ Auxroot->next->prev = a;
+ Auxroot->next = a;
+ } else {
+ Auxroot = a;
+ a->next = a;
+ a->prev = a;
+ }
+ respond(r, nil);
+}
+
+static char*
+fsclone(Fid *ofid, Fid *fid)
+{
+ Aux *oa = ofid->aux;
+ Aux *a = emalloc9p(sizeof(Aux));
+
+ fid->aux = a;
+
+ memset(a, 0, sizeof(Aux));
+ a->sh = -1;
+ a->fh = -1;
+ a->sp = oa->sp;
+ a->path = estrdup9p(oa->path);
+
+ if(Auxroot){
+ a->prev = Auxroot;
+ a->next = Auxroot->next;
+ Auxroot->next->prev = a;
+ Auxroot->next = a;
+ } else {
+ Auxroot = a;
+ a->next = a;
+ a->prev = a;
+ }
+ return nil;
+}
+
+/*
+ * for some weird reason T2queryall() returns share names
+ * in lower case so we have to do an extra test against
+ * our share table to validate filename case.
+ *
+ * on top of this here (snell & Wilcox) most of our
+ * redirections point to a share of the same name,
+ * but some do not, thus the tail of the filename
+ * returned by T2queryall() is not the same as
+ * the name we wanted.
+ *
+ * We work around this by not validating the names
+ * or files which resolve to share names as they must
+ * be correct, having been enforced in the dfs layer.
+ */
+static int
+validfile(char *found, char *want, char *winpath, Share *sp)
+{
+ char *share;
+
+ if(strcmp(want, "..") == 0)
+ return 1;
+ if(strcmp(winpath, "/") == 0){
+ share = trimshare(sp->name);
+ if(cistrcmp(want, share) == 0)
+ return strcmp(want, share) == 0;
+ /*
+ * OK, a DFS redirection points us from a directory XXX
+ * to a share named YYY. There is no case checking we can
+ * do so we allow either case - it's all we can do.
+ */
+ return 1;
+ }
+ if(cistrcmp(found, want) != 0)
+ return 0;
+ if(!Checkcase)
+ return 1;
+ if(strcmp(found, want) == 0)
+ return 1;
+ return 0;
+}
+
+
+static char*
+fswalk1(Fid *fid, char *name, Qid *qid)
+{
+ FInfo fi;
+ int rc, n, i;
+ Aux *a = fid->aux;
+ static char e[ERRMAX];
+ char *p, *npath, *winpath;
+
+ *e = 0;
+ npath = newpath(a->path, name);
+ if(strcmp(npath, "/") == 0){ /* root dir */
+ *qid = mkqid("/", 1, 1, Proot, 0);
+ free(a->path);
+ a->path = npath;
+ fid->qid = *qid;
+ return nil;
+ }
+
+ if(strrchr(npath, '/') == npath){ /* top level dir */
+ if((n = walkinfo(name)) != -1){ /* info file */
+ *qid = mkqid(npath, 0, 1, Pinfo, n);
+ }
+ else { /* volume name */
+ for(i = 0; i < Nshares; i++){
+ n = strlen(Shares[i].name);
+ if(cistrncmp(npath+1, Shares[i].name, n) != 0)
+ continue;
+ if(Checkcase && strncmp(npath+1, Shares[i].name, n) != 0)
+ continue;
+ if(npath[n+1] != 0 && npath[n+1] != '/')
+ continue;
+ break;
+ }
+ if(i >= Nshares){
+ free(npath);
+ return "not found";
+ }
+ a->sp = Shares+i;
+ *qid = mkqid(npath, 1, 1, Pshare, i);
+ }
+ free(a->path);
+ a->path = npath;
+ fid->qid = *qid;
+ return nil;
+ }
+
+ /* must be a vanilla file or directory */
+again:
+ if(mapshare(npath, &a->sp) == -1){
+ rerrstr(e, sizeof(e));
+ free(npath);
+ return e;
+ }
+
+ winpath = mapfile(npath);
+ memset(&fi, 0, sizeof fi);
+ if(Sess->caps & CAP_NT_SMBS)
+ rc = T2queryall(Sess, a->sp, winpath, &fi);
+ else
+ rc = T2querystandard(Sess, a->sp, winpath, &fi);
+
+ if(rc == -1){
+ rerrstr(e, sizeof(e));
+ free(npath);
+ return e;
+ }
+
+ if((a->sp->options & SMB_SHARE_IS_IN_DFS) != 0 &&
+ (fi.attribs & ATTR_REPARSE) != 0){
+ if(redirect(Sess, a->sp, npath) != -1)
+ goto again;
+ }
+
+ if((p = strrchr(fi.name, '/')) == nil && (p = strrchr(fi.name, '\\')) == nil)
+ p = fi.name;
+ else
+ p++;
+
+ if(! validfile(p, name, winpath, a->sp)){
+ free(npath);
+ return "not found";
+
+ }
+ *qid = mkqid(npath, fi.attribs & ATTR_DIRECTORY, fi.changed, 0, 0);
+
+ free(a->path);
+ a->path = npath;
+ fid->qid = *qid;
+ return nil;
+}
+
+static void
+fsstat(Req *r)
+{
+ int rc;
+ FInfo fi;
+ Aux *a = r->fid->aux;
+
+ if(ptype(r->fid->qid.path) == Proot)
+ V2D(&r->d, r->fid->qid, "");
+ else if(ptype(r->fid->qid.path) == Pinfo)
+ dirgeninfo(pindex(r->fid->qid.path), &r->d);
+ else if(ptype(r->fid->qid.path) == Pshare)
+ V2D(&r->d, r->fid->qid, a->path +1);
+ else{
+ memset(&fi, 0, sizeof fi);
+ if(Sess->caps & CAP_NT_SMBS)
+ rc = T2queryall(Sess, a->sp, mapfile(a->path), &fi);
+ else
+ rc = T2querystandard(Sess, a->sp, mapfile(a->path), &fi);
+ if(rc == -1){
+ responderrstr(r);
+ return;
+ }
+ I2D(&r->d, a->sp, a->path, &fi);
+ if(Billtrog == 0)
+ upd_names(Sess, a->sp, mapfile(a->path), &r->d);
+ }
+ respond(r, nil);
+}
+
+static int
+smbcreateopen(Aux *a, char *path, int mode, int perm, int is_create,
+ int is_dir, FInfo *fip)
+{
+ int rc, action, attrs, access, result;
+
+ if(is_create && is_dir){
+ if(CIFScreatedirectory(Sess, a->sp, path) == -1)
+ return -1;
+ return 0;
+ }
+
+ if(mode & DMAPPEND) {
+ werrstr("filesystem does not support DMAPPEND");
+ return -1;
+ }
+
+ if(is_create)
+ action = 0x12;
+ else if(mode & OTRUNC)
+ action = 0x02;
+ else
+ action = 0x01;
+
+ if(perm & 0222)
+ attrs = ATTR_NORMAL;
+ else
+ attrs = ATTR_NORMAL|ATTR_READONLY;
+
+ switch (mode & OMASK){
+ case OREAD:
+ access = 0;
+ break;
+ case OWRITE:
+ access = 1;
+ break;
+ case ORDWR:
+ access = 2;
+ break;
+ case OEXEC:
+ access = 3;
+ break;
+ default:
+ werrstr("%d bad open mode", mode & OMASK);
+ return -1;
+ break;
+ }
+
+ if(mode & DMEXCL == 0)
+ access |= 0x10;
+ else
+ access |= 0x40;
+
+ if((a->fh = CIFS_SMB_opencreate(Sess, a->sp, path, access, attrs,
+ action, &result)) == -1)
+ return -1;
+
+ if(Sess->caps & CAP_NT_SMBS)
+ rc = T2queryall(Sess, a->sp, mapfile(a->path), fip);
+ else
+ rc = T2querystandard(Sess, a->sp, mapfile(a->path), fip);
+ if(rc == -1){
+ fprint(2, "internal error: stat of newly open/created file failed\n");
+ return -1;
+ }
+
+ if((mode & OEXCL) && (result & 0x8000) == 0){
+ werrstr("%d bad open mode", mode & OMASK);
+ return -1;
+ }
+ return 0;
+}
+
+/* Uncle Bill, you have a lot to answer for... */
+static int
+ntcreateopen(Aux *a, char *path, int mode, int perm, int is_create,
+ int is_dir, FInfo *fip)
+{
+ int options, result, attrs, flags, access, action, share;
+
+ if(mode & DMAPPEND){
+ werrstr("CIFSopen, DMAPPEND not supported");
+ return -1;
+ }
+
+ if(is_create){
+ if(mode & OEXCL)
+ action = FILE_OPEN;
+ else if(mode & OTRUNC)
+ action = FILE_CREATE;
+ else
+ action = FILE_OVERWRITE_IF;
+ } else {
+ if(mode & OTRUNC)
+ action = FILE_OVERWRITE_IF;
+ else
+ action = FILE_OPEN_IF;
+ }
+
+ flags = 0; /* FIXME: really not sure */
+
+ if(mode & OEXCL)
+ share = FILE_NO_SHARE;
+ else
+ share = FILE_SHARE_ALL;
+
+ switch (mode & OMASK){
+ case OREAD:
+ access = GENERIC_READ;
+ break;
+ case OWRITE:
+ access = GENERIC_WRITE;
+ break;
+ case ORDWR:
+ access = GENERIC_ALL;
+ break;
+ case OEXEC:
+ access = GENERIC_EXECUTE;
+ break;
+ default:
+ werrstr("%d bad open mode", mode & OMASK);
+ return -1;
+ break;
+ }
+
+ if(is_dir){
+ action = FILE_CREATE;
+ options = FILE_DIRECTORY_FILE;
+ if(perm & 0222)
+ attrs = ATTR_DIRECTORY;
+ else
+ attrs = ATTR_DIRECTORY|ATTR_READONLY;
+ } else {
+ options = FILE_NON_DIRECTORY_FILE;
+ if(perm & 0222)
+ attrs = ATTR_NORMAL;
+ else
+ attrs = ATTR_NORMAL|ATTR_READONLY;
+ }
+
+ if(mode & ORCLOSE){
+ options |= FILE_DELETE_ON_CLOSE;
+ attrs |= ATTR_DELETE_ON_CLOSE;
+ }
+
+ if((a->fh = CIFS_NT_opencreate(Sess, a->sp, path, flags, options,
+ attrs, access, share, action, &result, fip)) == -1)
+ return -1;
+
+ if((mode & OEXCL) && (result & 0x8000) == 0){
+ werrstr("%d bad open mode", mode & OMASK);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+fscreate(Req *r)
+{
+ FInfo fi;
+ int rc, is_dir;
+ char *npath;
+ Aux *a = r->fid->aux;
+
+ a->end = a->off = 0;
+ a->cache = emalloc9p(max(Sess->mtu, MTU));
+
+ is_dir = (r->ifcall.perm & DMDIR) == DMDIR;
+ npath = smprint("%s/%s", a->path, r->ifcall.name);
+
+ if(Sess->caps & CAP_NT_SMBS)
+ rc = ntcreateopen(a, mapfile(npath), r->ifcall.mode,
+ r->ifcall.perm, 1, is_dir, &fi);
+ else
+ rc = smbcreateopen(a, mapfile(npath), r->ifcall.mode,
+ r->ifcall.perm, 1, is_dir, &fi);
+ if(rc == -1){
+ free(npath);
+ responderrstr(r);
+ return;
+ }
+
+ r->fid->qid = mkqid(npath, fi.attribs & ATTR_DIRECTORY, fi.changed, 0, 0);
+
+ r->ofcall.qid = r->fid->qid;
+ free(a->path);
+ a->path = npath;
+
+ respond(r, nil);
+}
+
+static void
+fsopen(Req *r)
+{
+ int rc;
+ FInfo fi;
+ Aux *a = r->fid->aux;
+
+ a->end = a->off = 0;
+ a->cache = emalloc9p(max(Sess->mtu, MTU));
+
+ if(ptype(r->fid->qid.path) == Pinfo){
+ if(makeinfo(pindex(r->fid->qid.path)) != -1)
+ respond(r, nil);
+ else
+ respond(r, "cannot generate info");
+ return;
+ }
+
+ if(r->fid->qid.type & QTDIR){
+ respond(r, nil);
+ return;
+ }
+
+ if(Sess->caps & CAP_NT_SMBS)
+ rc = ntcreateopen(a, mapfile(a->path), r->ifcall.mode, 0777,
+ 0, 0, &fi);
+ else
+ rc = smbcreateopen(a, mapfile(a->path), r->ifcall.mode, 0777,
+ 0, 0, &fi);
+ if(rc == -1){
+ responderrstr(r);
+ return;
+ }
+ respond(r, nil);
+}
+
+static void
+fswrite(Req *r)
+{
+ vlong n, m, got;
+ Aux *a = r->fid->aux;
+ vlong len = r->ifcall.count;
+ vlong off = r->ifcall.offset;
+ char *buf = r->ifcall.data;
+
+ got = 0;
+ n = Sess->mtu -OVERHEAD;
+ do{
+ if(len - got < n)
+ n = len - got;
+ m = CIFSwrite(Sess, a->sp, a->fh, off + got, buf + got, n);
+ if(m != -1)
+ got += m;
+ } while(got < len && m >= n);
+
+ r->ofcall.count = got;
+ if(m == -1)
+ responderrstr(r);
+ else
+ respond(r, nil);
+}
+
+static void
+fsread(Req *r)
+{
+ vlong n, m, got;
+ Aux *a = r->fid->aux;
+ char *buf = r->ofcall.data;
+ vlong len = r->ifcall.count;
+ vlong off = r->ifcall.offset;
+
+ if(ptype(r->fid->qid.path) == Pinfo){
+ r->ofcall.count = readinfo(pindex(r->fid->qid.path), buf, len,
+ off);
+ respond(r, nil);
+ return;
+ }
+
+ if(r->fid->qid.type & QTDIR){
+ dirread9p(r, dirgen, a);
+ respond(r, nil);
+ return;
+ }
+
+ got = 0;
+ n = Sess->mtu -OVERHEAD;
+ do{
+ if(len - got < n)
+ n = len - got;
+ m = CIFSread(Sess, a->sp, a->fh, off + got, buf + got, n, len);
+ if(m != -1)
+ got += m;
+ } while(got < len && m >= n);
+
+ r->ofcall.count = got;
+ if(m == -1)
+ responderrstr(r);
+ else
+ respond(r, nil);
+}
+
+static void
+fsdestroyfid(Fid *f)
+{
+ Aux *a = f->aux;
+
+ if(ptype(f->qid.path) == Pinfo)
+ freeinfo(pindex(f->qid.path));
+ f->omode = -1;
+ if(! a)
+ return;
+ if(a->fh != -1)
+ if(CIFSclose(Sess, a->sp, a->fh) == -1)
+ fprint(2, "%s: close failed fh=%d %r\n", argv0, a->fh);
+ if(a->sh != -1)
+ if(CIFSfindclose2(Sess, a->sp, a->sh) == -1)
+ fprint(2, "%s: findclose failed sh=%d %r\n",
+ argv0, a->sh);
+ if(a->path)
+ free(a->path);
+ if(a->cache)
+ free(a->cache);
+
+ if(a == Auxroot)
+ Auxroot = a->next;
+ a->prev->next = a->next;
+ a->next->prev = a->prev;
+ if(a->next == a->prev)
+ Auxroot = nil;
+ if(a)
+ free(a);
+}
+
+int
+rdonly(Session *s, Share *sp, char *path, int rdonly)
+{
+ int rc;
+ FInfo fi;
+
+ if(Sess->caps & CAP_NT_SMBS)
+ rc = T2queryall(s, sp, path, &fi);
+ else
+ rc = T2querystandard(s, sp, path, &fi);
+ if(rc == -1)
+ return -1;
+
+ if((rdonly && !(fi.attribs & ATTR_READONLY)) ||
+ (!rdonly && (fi.attribs & ATTR_READONLY))){
+ fi.attribs &= ~ATTR_READONLY;
+ fi.attribs |= rdonly? ATTR_READONLY: 0;
+ rc = CIFSsetinfo(s, sp, path, &fi);
+ }
+ return rc;
+}
+
+static void
+fsremove(Req *r)
+{
+ int try, rc;
+ char e[ERRMAX];
+ Aux *ap, *a = r->fid->aux;
+
+ *e = 0;
+ if(ptype(r->fid->qid.path) == Proot ||
+ ptype(r->fid->qid.path) == Pshare){
+ respond(r, "illegal operation");
+ return;
+ }
+
+ /* close all instences of this file/dir */
+ if((ap = Auxroot) != nil)
+ do{
+ if(strcmp(ap->path, a->path) == 0){
+ if(ap->sh != -1)
+ CIFSfindclose2(Sess, ap->sp, ap->sh);
+ ap->sh = -1;
+ if(ap->fh != -1)
+ CIFSclose(Sess, ap->sp, ap->fh);
+ ap->fh = -1;
+ }
+ ap = ap->next;
+ }while(ap != Auxroot);
+ try = 0;
+again:
+ if(r->fid->qid.type & QTDIR)
+ rc = CIFSdeletedirectory(Sess, a->sp, mapfile(a->path));
+ else
+ rc = CIFSdeletefile(Sess, a->sp, mapfile(a->path));
+
+ rerrstr(e, sizeof(e));
+ if(rc == -1 && try++ == 0 && strcmp(e, "permission denied") == 0 &&
+ rdonly(Sess, a->sp, mapfile(a->path), 0) == 0)
+ goto again;
+ if(rc == -1)
+ responderrstr(r);
+ else
+ respond(r, nil);
+}
+
+static void
+fswstat(Req *r)
+{
+ int fh, result, rc;
+ FInfo fi, tmpfi;
+ char *p, *from, *npath;
+ Aux *a = r->fid->aux;
+
+ if(ptype(r->fid->qid.path) == Proot ||
+ ptype(r->fid->qid.path) == Pshare){
+ respond(r, "illegal operation");
+ return;
+ }
+
+ if((r->d.uid && r->d.uid[0]) || (r->d.gid && r->d.gid[0])){
+ respond(r, "cannot change ownership");
+ return;
+ }
+
+ /*
+ * get current info
+ */
+ if(Sess->caps & CAP_NT_SMBS)
+ rc = T2queryall(Sess, a->sp, mapfile(a->path), &fi);
+ else
+ rc = T2querystandard(Sess, a->sp, mapfile(a->path), &fi);
+ if(rc == -1){
+ werrstr("(query) - %r");
+ responderrstr(r);
+ return;
+ }
+
+ /*
+ * always clear the readonly attribute if set,
+ * before trying to set any other fields.
+ * wstat() fails if the file/dir is readonly
+ * and this function is so full of races - who cares about one more?
+ */
+ rdonly(Sess, a->sp, mapfile(a->path), 0);
+
+ /*
+ * rename - one piece of joy, renaming open files
+ * is legal (sharing permitting).
+ */
+ if(r->d.name && r->d.name[0]){
+ if((p = strrchr(a->path, '/')) == nil){
+ respond(r, "illegal path");
+ return;
+ }
+ npath = emalloc9p((p-a->path)+strlen(r->d.name)+2);
+ strecpy(npath, npath+(p- a->path)+2, a->path);
+ strcat(npath, r->d.name);
+
+ from = estrdup9p(mapfile(a->path));
+ if(CIFSrename(Sess, a->sp, from, mapfile(npath)) == -1){
+ werrstr("(rename) - %r");
+ responderrstr(r);
+ free(npath);
+ free(from);
+ return;
+ }
+ free(from);
+ free(a->path);
+ a->path = npath;
+ }
+
+ /*
+ * set the files length, do this before setting
+ * the file times as open() will alter them
+ */
+ if(~r->d.length){
+ fi.size = r->d.length;
+
+ if(Sess->caps & CAP_NT_SMBS){
+ if((fh = CIFS_NT_opencreate(Sess, a->sp, mapfile(a->path),
+ 0, FILE_NON_DIRECTORY_FILE,
+ ATTR_NORMAL, GENERIC_WRITE, FILE_SHARE_ALL,
+ FILE_OPEN_IF, &result, &tmpfi)) == -1){
+ werrstr("(set length, open) - %r");
+ responderrstr(r);
+ return;
+ }
+ rc = T2setfilelength(Sess, a->sp, fh, &fi);
+ CIFSclose(Sess, a->sp, fh);
+ if(rc == -1){
+ werrstr("(set length), set) - %r");
+ responderrstr(r);
+ return;
+ }
+ } else {
+ if((fh = CIFS_SMB_opencreate(Sess, a->sp, mapfile(a->path),
+ 1, ATTR_NORMAL, 1, &result)) == -1){
+ werrstr("(set length, open) failed - %r");
+ responderrstr(r);
+ return;
+ }
+ rc = CIFSwrite(Sess, a->sp, fh, fi.size, 0, 0);
+ CIFSclose(Sess, a->sp, fh);
+ if(rc == -1){
+ werrstr("(set length, write) - %r");
+ responderrstr(r);
+ return;
+ }
+ }
+ }
+
+ /*
+ * This doesn't appear to set length or
+ * attributes, no idea why, so I do those seperately
+ */
+ if(~r->d.mtime || ~r->d.atime){
+ if(~r->d.mtime)
+ fi.written = r->d.mtime;
+ if(~r->d.atime)
+ fi.accessed = r->d.atime;
+ if(T2setpathinfo(Sess, a->sp, mapfile(a->path), &fi) == -1){
+ werrstr("(set path info) - %r");
+ responderrstr(r);
+ return;
+ }
+ }
+
+ /*
+ * always update the readonly flag as
+ * we may have cleared it above.
+ */
+ if(~r->d.mode){
+ if(r->d.mode & 0222)
+ fi.attribs &= ~ATTR_READONLY;
+ else
+ fi.attribs |= ATTR_READONLY;
+ }
+ if(rdonly(Sess, a->sp, mapfile(a->path), fi.attribs & ATTR_READONLY) == -1){
+ werrstr("(set info) - %r");
+ responderrstr(r);
+ return;
+ }
+
+ /*
+ * Win95 has a broken write-behind cache for metadata
+ * on open files (writes go to the cache, reads bypass
+ * the cache), so we must flush the file.
+ */
+ if(r->fid->omode != -1 && CIFSflush(Sess, a->sp, a->fh) == -1){
+ werrstr("(flush) %r");
+ responderrstr(r);
+ return;
+ }
+ respond(r, nil);
+}
+
+static void
+fsend(Srv *srv)
+{
+ int i;
+ USED(srv);
+
+ for(i = 0; i < Nshares; i++)
+ CIFStreedisconnect(Sess, Shares+i);
+ CIFSlogoff(Sess);
+ postnote(PNPROC, Keeppid, "die");
+}
+
+Srv fs = {
+ .destroyfid = fsdestroyfid,
+ .attach= fsattach,
+ .open= fsopen,
+ .create= fscreate,
+ .read= fsread,
+ .write= fswrite,
+ .remove= fsremove,
+ .stat= fsstat,
+ .wstat= fswstat,
+ .clone= fsclone,
+ .walk1= fswalk1,
+ .end= fsend,
+};
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-d name] [-Dvb] [-a auth-method] [-s srvname] "
+ "[-n called-name] [-k factotum-params] [-m mntpnt] "
+ "host [share...]\n", argv0);
+ exits("usage");
+}
+
+/*
+ * SMBecho looks like the function to use for keepalives,
+ * sadly the echo packet does not seem to reload the
+ * idle timer in Microsoft's servers. Instead we use
+ * "get file system size" on each share until we get one that succeeds.
+ */
+static void
+keepalive(void)
+{
+ char buf[32];
+ uvlong tot, fre;
+ int fd, i, slot, rc;
+
+ snprint(buf, sizeof buf, "#p/%d/args", getpid());
+ if((fd = open(buf, OWRITE)) >= 0){
+ fprint(fd, "%s keepalive", Host);
+ close(fd);
+ }
+
+ rc = 0;
+ slot = 0;
+ do{
+ sleep(6000);
+ if(Active-- != 0)
+ continue;
+ for(i = 0; i < Nshares; i++){
+ if((rc = T2fssizeinfo(Sess, &Shares[slot], &tot, &fre)) == 0)
+ break;
+ if(++slot >= Nshares)
+ slot = 0;
+ }
+ }while(rc != -1);
+ postnote(PNPROC, Attachpid, "die");
+}
+
+
+static void
+ding(void *u, char *msg)
+{
+ USED(u);
+ if(strstr(msg, "alarm") != nil)
+ noted(NCONT);
+ noted(NDFLT);
+}
+
+void
+dmpkey(char *s, void *v, int n)
+{
+ int i;
+ unsigned char *p = (unsigned char *)v;
+
+ print("%s", s);
+ for(i = 0; i < n; i++)
+ print("%02ux ", *p++);
+ print("\n");
+}
+
+void
+main(int argc, char **argv)
+{
+ int i, n;
+ long svrtime;
+ char windom[64], cname[64];
+ char *method, *sysname, *keyp, *mtpt, *svs;
+ static char *sh[1024];
+
+ *cname = 0;
+ keyp = "";
+ method = nil;
+ strcpy(windom, "unknown");
+ mtpt = svs = nil;
+
+ notify(ding);
+
+ ARGBEGIN{
+ case 'a':
+ method = EARGF(autherr());
+ break;
+ case 'b':
+ Billtrog ^= 1;
+ break;
+ case 'D':
+ chatty9p++;
+ break;
+ case 'd':
+ Debug = EARGF(usage());
+ break;
+ case 'i':
+ Checkcase = 0;
+ break;
+ case 'k':
+ keyp = EARGF(usage());
+ break;
+ case 'm':
+ mtpt = EARGF(usage());
+ break;
+ case 'n':
+ strncpy(cname, EARGF(usage()), sizeof(cname));
+ cname[sizeof(cname) - 1] = 0;
+ break;
+ case 's':
+ svs = EARGF(usage());
+ break;
+ case 't':
+ Dfstout = atoi(EARGF(usage()));
+ break;
+ default:
+ usage();
+ break;
+ }ARGEND
+
+ if(argc < 1)
+ usage();
+
+ Host = argv[0];
+
+ if(mtpt == nil && svs == nil)
+ mtpt = smprint("/n/%s", Host);
+
+ if((sysname = getenv("sysname")) == nil)
+ sysname = "unknown";
+
+ if(*cname && (Sess = cifsdial(Host, cname, sysname)) != nil)
+ goto connected;
+
+ if(calledname(Host, cname) == 0 &&
+ (Sess = cifsdial(Host, cname, sysname)) != nil)
+ goto connected;
+
+ strcpy(cname, Host);
+ if((Sess = cifsdial(Host, Host, sysname)) != nil ||
+ (Sess = cifsdial(Host, "*SMBSERVER", sysname)) != nil)
+ goto connected;
+
+ sysfatal("%s - cannot dial, %r\n", Host);
+connected:
+ if(CIFSnegotiate(Sess, &svrtime, windom, sizeof windom, cname, sizeof cname) == -1)
+ sysfatal("%s - cannot negioate common protocol, %r\n", Host);
+
+#ifndef DEBUG_MAC
+ Sess->secmode &= ~SECMODE_SIGN_ENABLED;
+#endif
+
+ Sess->auth = getauth(method, windom, keyp, Sess->secmode, Sess->chal,
+ Sess->challen);
+
+ if(CIFSsession(Sess) < 0)
+ sysfatal("session authentication failed, %r\n");
+
+ Sess->slip = svrtime - time(nil);
+ Sess->cname = estrdup9p(cname);
+
+ if(CIFStreeconnect(Sess, cname, Ipcname, &Ipc) == -1)
+ fprint(2, "%s, %r - can't connect\n", Ipcname);
+
+ Nshares = 0;
+ if(argc == 1){
+ Share *sip;
+
+ if((n = RAPshareenum(Sess, &Ipc, &sip)) < 1)
+ sysfatal("can't enumerate shares: %r - specify share "
+ "names on command line\n");
+
+ for(i = 0; i < n; i++){
+#ifdef NO_HIDDEN_SHARES
+ int l = strlen(sip[i].name);
+
+ if(l > 1 && sip[i].name[l-1] == '$'){
+ free(sip[i].name);
+ continue;
+ }
+#endif
+ memcpy(Shares+Nshares, sip+i, sizeof(Share));
+ if(CIFStreeconnect(Sess, Sess->cname,
+ Shares[Nshares].name, Shares+Nshares) == -1){
+ free(Shares[Nshares].name);
+ continue;
+ }
+ Nshares++;
+ }
+ free(sip);
+ } else
+ for(i = 1; i < argc; i++){
+ if(CIFStreeconnect(Sess, Sess->cname, argv[i],
+ Shares+Nshares) == -1){
+ fprint(2, "%s: %s %q - can't connect to share"
+ ", %r\n", argv0, Host, argv[i]);
+ continue;
+ }
+ Shares[Nshares].name = strlwr(estrdup9p(argv[i]));
+ Nshares++;
+ }
+
+ if(Nshares == 0)
+ fprint(2, "no available shares\n");
+
+ if((Keeppid = rfork(RFPROC|RFMEM|RFNOTEG|RFFDG|RFNAMEG)) == 0){
+ keepalive();
+ exits(nil);
+ }
+ postmountsrv(&fs, svs, mtpt, MREPL|MCREATE);
+ exits(nil);
+}
diff --git a/sys/src/cmd/cifs/misc.c b/sys/src/cmd/cifs/misc.c
new file mode 100755
index 000000000..c82cc7a82
--- /dev/null
+++ b/sys/src/cmd/cifs/misc.c
@@ -0,0 +1,25 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+
+char*
+strupr(char *s)
+{
+ char *p;
+
+ for(p = s; *p; p++)
+ if(*p >= 0 && islower(*p))
+ *p = toupper(*p);
+ return s;
+}
+
+char*
+strlwr(char *s)
+{
+ char *p;
+
+ for(p = s; *p; p++)
+ if(*p >= 0 && isupper(*p))
+ *p = tolower(*p);
+ return s;
+}
diff --git a/sys/src/cmd/cifs/mkfile b/sys/src/cmd/cifs/mkfile
new file mode 100755
index 000000000..b53660103
--- /dev/null
+++ b/sys/src/cmd/cifs/mkfile
@@ -0,0 +1,20 @@
+</$objtype/mkfile
+
+# CFLAGS=$CFLAGS -DDEBUG_MAC
+
+TARG=cifs
+HFILES=cifs.h
+BIN=/$objtype/bin
+OFILES= main.$O transnt.$O trans2.$O trans.$O cifs.$O \
+ netbios.$O pack.$O info.$O fs.$O sid2name.$O \
+ misc.$O nterrstr.$O doserrstr.$O raperrstr.$O \
+ auth.$O dfs.$O ping.$O
+
+</sys/src/cmd/mkone
+
+install:V:
+ # cp $TARG.man /sys/man/4/$TARG
+
+
+test:V: 8.out
+ 8.out se-00 root && ls /n/se-00/root/eng/design/conversion/* && p /n/se-00/Shares
diff --git a/sys/src/cmd/cifs/netbios.c b/sys/src/cmd/cifs/netbios.c
new file mode 100755
index 000000000..708dd41a1
--- /dev/null
+++ b/sys/src/cmd/cifs/netbios.c
@@ -0,0 +1,475 @@
+/*
+ * netbios dial, read, write
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "cifs.h"
+
+enum {
+ MAXNBPKT = 8096, /* max netbios packet size */
+ NBquery = 0, /* packet type - query */
+
+ NBAdapterStatus = 0x21, /* get host interface info */
+ NBInternet = 1, /* scope for info */
+
+ NBmessage = 0x00, /* Netbios packet types */
+ NBrequest = 0x81,
+ NBpositive,
+ NBnegative,
+ NBretarget,
+ NBkeepalive,
+
+ ISgroup = 0x8000,
+};
+
+
+static char *NBerr[] = {
+ [0] "not listening on called name",
+ [1] "not listening for calling name",
+ [2] "called name not present",
+ [3] "insufficient resources",
+ [15] "unspecified error"
+};
+
+
+static ulong
+GL32(uchar **p)
+{
+ ulong n;
+
+ n = *(*p)++;
+ n |= *(*p)++ << 8;
+ n |= *(*p)++ << 16;
+ n |= *(*p)++ << 24;
+ return n;
+}
+
+static ushort
+GL16(uchar **p)
+{
+ ushort n;
+
+ n = *(*p)++;
+ n |= *(*p)++ << 8;
+ return n;
+}
+
+void
+Gmem(uchar **p, void *v, int n)
+{
+ uchar *str = v;
+
+ while(n--)
+ *str++ = *(*p)++;
+}
+
+
+static ulong
+GB32(uchar **p)
+{
+ ulong n;
+
+ n = *(*p)++ << 24;
+ n |= *(*p)++ << 16;
+ n |= *(*p)++ << 8;
+ n |= *(*p)++;
+ return n;
+}
+
+static ushort
+GB16(uchar **p)
+{
+ ushort n;
+
+ n = *(*p)++ << 8;
+ n |= *(*p)++;
+ return n;
+}
+
+static uchar
+G8(uchar **p)
+{
+ return *(*p)++;
+}
+
+static void
+PB16(uchar **p, uint n)
+{
+ *(*p)++ = n >> 8;
+ *(*p)++ = n;
+}
+
+static void
+P8(uchar **p, uint n)
+{
+ *(*p)++ = n;
+}
+
+
+static void
+nbname(uchar **p, char *name, char pad)
+{
+ char c;
+ int i;
+ int done = 0;
+
+ *(*p)++ = 0x20;
+ for(i = 0; i < 16; i++) {
+ c = pad;
+ if(!done && name[i] == '\0')
+ done = 1;
+ if(!done)
+ c = toupper(name[i]);
+ *(*p)++ = ((uchar)c >> 4) + 'A';
+ *(*p)++ = (c & 0xf) + 'A';
+ }
+ *(*p)++ = 0;
+}
+
+int
+calledname(char *host, char *name)
+{
+ char *addr;
+ uchar buf[1024], *p;
+ static char tmp[20];
+ int num, flg, svs, j, i, fd, trn;
+
+ trn = (getpid() ^ time(0)) & 0xffff;
+ if((addr = netmkaddr(host, "udp", "137")) == nil)
+ return -1;
+
+ if((fd = dial(addr, "137", 0, 0)) < 0)
+ return -1;
+ p = buf;
+
+ PB16(&p, trn); /* TRNid */
+ P8(&p, 0); /* flags */
+ P8(&p, 0x10); /* type */
+ PB16(&p, 1); /* # questions */
+ PB16(&p, 0); /* # answers */
+ PB16(&p, 0); /* # authority RRs */
+ PB16(&p, 0); /* # Aditional RRs */
+ nbname(&p, "*", 0);
+ PB16(&p, NBAdapterStatus);
+ PB16(&p, NBInternet);
+
+ if(Debug && strstr(Debug, "dump"))
+ xd(nil, buf, p-buf);
+
+ if(write(fd, buf, p-buf) != p-buf)
+ return -1;
+
+ p = buf;
+ for(i = 0; i < 3; i++){
+ memset(buf, 0, sizeof(buf));
+ alarm(NBNSTOUT);
+ read(fd, buf, sizeof(buf));
+ alarm(0);
+ if(GB16(&p) == trn)
+ break;
+ }
+ close(fd);
+ if(i >= 3)
+ return -1;
+
+ p = buf +56;
+ num = G8(&p); /* number of names */
+
+ for(i = 0; i < num; i++){
+ memset(tmp, 0, sizeof(tmp));
+ Gmem(&p, tmp, 15);
+ svs = G8(&p);
+ flg = GB16(&p);
+ for(j = 14; j >= 0 && tmp[j] == ' '; j--)
+ tmp[j] = 0;
+ if(svs == 0 && !(flg & ISgroup))
+ strcpy(name, tmp);
+ }
+ return 0;
+}
+
+
+int
+nbtdial(char *addr, char *called, char *sysname)
+{
+ char redir[20];
+ uchar *p, *lenp, buf[1024];
+ int type, len, err, fd, nkeepalive, nretarg;
+
+ nretarg = 0;
+ nkeepalive = 0;
+Redial:
+ if((addr = netmkaddr(addr, "tcp", "139")) == nil ||
+ (fd = dial(addr, 0, 0, 0)) < 0)
+ return -1;
+
+ memset(buf, 0, sizeof(buf));
+
+ p = buf;
+ P8(&p, NBrequest); /* type */
+ P8(&p, 0); /* flags */
+ lenp = p; PB16(&p, 0); /* length placeholder */
+ nbname(&p, called, ' '); /* remote NetBios name */
+ nbname(&p, sysname, ' '); /* our machine name */
+ PB16(&lenp, p-lenp -2); /* length re-write */
+
+ if(Debug && strstr(Debug, "dump"))
+ xd(nil, buf, p-buf);
+ if(write(fd, buf, p-buf) != p-buf)
+ goto Error;
+Reread:
+ p = buf;
+ memset(buf, 0, sizeof(buf));
+ if(readn(fd, buf, 4) < 4)
+ goto Error;
+
+ type = G8(&p);
+ G8(&p); /* flags */
+ len = GB16(&p);
+
+ if(readn(fd, buf +4, len -4) < len -4)
+ goto Error;
+
+ if(Debug && strstr(Debug, "dump"))
+ xd(nil, buf, len+4);
+
+ switch(type) {
+ case NBpositive:
+ return fd;
+ case NBnegative:
+ if(len < 1) {
+ werrstr("nbdial: bad error pkt");
+ goto Error;
+ }
+ err = G8(&p);
+ if(err < 0 || err > nelem(NBerr) || NBerr[err] == nil)
+ werrstr("NBT: %d - unknown error", err);
+ else
+ werrstr("NBT: %s", NBerr[err]);
+
+ goto Error;
+ case NBkeepalive:
+ if(++nkeepalive >= 16){
+ werrstr("nbdial: too many keepalives");
+ goto Error;
+ }
+ goto Reread;
+
+ case NBretarget:
+ if(++nretarg >= 16) {
+ werrstr("nbdial: too many redirects");
+ goto Error;
+ }
+ if(len < 4) {
+ werrstr("nbdial: bad redirect pkt");
+ goto Error;
+ }
+ sprint(redir, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+ addr = redir;
+ goto Redial;
+
+ default:
+ werrstr("nbdial: 0x%x - unknown packet in netbios handshake", type);
+ goto Error;
+ }
+Error:
+ close(fd);
+ return -1;
+}
+
+void
+nbthdr(Pkt *p)
+{
+ p->pos = p->buf;
+ memset(p->buf, 0xa5, MTU);
+
+ p8(p, NBmessage); /* type */
+ p8(p, 0); /* flags */
+ pb16(p, 0); /* length (filled in later) */
+}
+
+int
+nbtrpc(Pkt *p)
+{
+ int len, got, type, nkeep;
+
+ len = p->pos - p->buf;
+
+ p->pos = p->buf +2;
+ pb16(p, len - NBHDRLEN); /* length */
+
+ if(Debug && strstr(Debug, "dump"))
+ xd("tx", p->buf, len);
+
+ alarm(NBRPCTOUT);
+ if(write(p->s->fd, p->buf, len) != len){
+ werrstr("nbtrpc: write failed - %r");
+ alarm(0);
+ return -1;
+ }
+
+ nkeep = 0;
+retry:
+ p->pos = p->buf;
+ memset(p->buf, 0xa5, MTU);
+
+ got = readn(p->s->fd, p->buf, NBHDRLEN);
+
+ if(got < NBHDRLEN){
+ werrstr("nbtrpc: short read - %r");
+ alarm(0);
+ return -1;
+ }
+ p->eop = p->buf + got;
+
+ type = g8(p); /* NBT type (session) */
+ if(type == NBkeepalive){
+ if(++nkeep > 16) {
+ werrstr("nbtrpc: too many keepalives (%d attempts)", nkeep);
+ alarm(0);
+ return -1;
+ }
+ goto retry;
+ }
+
+ g8(p); /* NBT flags (none) */
+
+ len = gb16(p); /* NBT payload length */
+ if((len +NBHDRLEN) > MTU){
+ werrstr("nbtrpc: packet bigger than MTU, (%d > %d)", len, MTU);
+ alarm(0);
+ return -1;
+ }
+
+ got = readn(p->s->fd, p->buf +NBHDRLEN, len);
+ alarm(0);
+
+ if(Debug && strstr(Debug, "dump"))
+ xd("rx", p->buf, got +NBHDRLEN);
+
+ if(got < 0)
+ return -1;
+ p->eop = p->buf + got +NBHDRLEN;
+ return got+NBHDRLEN;
+}
+
+
+void
+xd(char *str, void *buf, int n)
+{
+ int fd, flg, flags2, cmd;
+ uint sum;
+ long err;
+ uchar *p, *end;
+
+ if(n == 0)
+ return;
+
+ p = buf;
+ end = (uchar *)buf +n;
+
+ if(Debug && strstr(Debug, "log") != nil){
+ if((fd = open("pkt.log", ORDWR)) == -1)
+ return;
+ seek(fd, 0, 2);
+ fprint(fd, "%d ", 0);
+ while(p < end)
+ fprint(fd, "%02x ", *p++);
+ fprint(fd, "\n");
+ close(fd);
+ return;
+ }
+
+ if(!str)
+ goto Raw;
+
+ p = (uchar *)buf + 4;
+ if(GL32(&p) == 0x424d53ff){
+ buf = (uchar *)buf + 4;
+ n -= 4;
+ }
+ end = (uchar *)buf + n;
+
+ sum = 0;
+ p = buf;
+ while(p < end)
+ sum += *p++;
+ p = buf;
+
+ fprint(2, "%s : len=%ud sum=%d\n", str, n, sum);
+
+ fprint(2, "mag=0x%ulx ", GL32(&p));
+ fprint(2, "cmd=0x%ux ", cmd = G8(&p));
+ fprint(2, "err=0x%ulx ", err=GL32(&p));
+ fprint(2, "flg=0x%02ux ", flg = G8(&p));
+ fprint(2, "flg2=0x%04ux\n", flags2= GL16(&p));
+ fprint(2, "dfs=%s\n", (flags2 & FL2_DFS)? "y": "n");
+
+ fprint(2, "pidl=%ud ", GL16(&p));
+ fprint(2, "res=%uld ", GL32(&p));
+ fprint(2, "sid=%ud ", GL16(&p));
+ fprint(2, "seq=0x%ux ", GL16(&p));
+ fprint(2, "pad=%ud ", GL16(&p));
+
+ fprint(2, "tid=%ud ", GL16(&p));
+ fprint(2, "pid=%ud ", GL16(&p));
+ fprint(2, "uid=%ud ", GL16(&p));
+ fprint(2, "mid=%ud\n", GL16(&p));
+
+ if(cmd == 0x32 && (flg & 0x80) == 0){ /* TRANS 2, TX */
+ fprint(2, "words=%ud ", G8(&p));
+ fprint(2, "totparams=%ud ", GL16(&p));
+ fprint(2, "totdata=%ud ", GL16(&p));
+ fprint(2, "maxparam=%ud ", GL16(&p));
+ fprint(2, "maxdata=%ud\n", GL16(&p));
+ fprint(2, "maxsetup=%ud ", G8(&p));
+ fprint(2, "reserved=%ud ", G8(&p));
+ fprint(2, "flags=%ud ", GL16(&p));
+ fprint(2, "timeout=%uld\n", GL32(&p));
+ fprint(2, "reserved=%ud ", GL16(&p));
+ fprint(2, "paramcnt=%ud ", GL16(&p));
+ fprint(2, "paramoff=%ud ", GL16(&p));
+ fprint(2, "datacnt=%ud ", GL16(&p));
+ fprint(2, "dataoff=%ud ", GL16(&p));
+ fprint(2, "setupcnt=%ud ", G8(&p));
+ fprint(2, "reserved=%ud\n", G8(&p));
+ fprint(2, "trans2=0x%02x ", GL16(&p));
+ fprint(2, "data-words=%d ", G8(&p));
+ fprint(2, "padding=%d\n", G8(&p));
+ }
+ if(cmd == 0x32 && (flg & 0x80) == 0x80){ /* TRANS 2, RX */
+ fprint(2, "words=%ud ", G8(&p));
+ fprint(2, "totparams=%ud ", GL16(&p));
+ fprint(2, "totdata=%ud ", GL16(&p));
+ fprint(2, "reserved=%ud ", GL16(&p));
+ fprint(2, "paramcnt=%ud\n", GL16(&p));
+ fprint(2, "paramoff=%ud ", GL16(&p));
+ fprint(2, "paramdisp=%ud ", GL16(&p));
+ fprint(2, "datacnt=%ud\n", GL16(&p));
+ fprint(2, "dataoff=%ud ", GL16(&p));
+ fprint(2, "datadisp=%ud ", GL16(&p));
+ fprint(2, "setupcnt=%ud ", G8(&p));
+ fprint(2, "reserved=%ud\n", G8(&p));
+ }
+ if(err)
+ if(flags2 & FL2_NT_ERRCODES)
+ fprint(2, "err=%s\n", nterrstr(err));
+ else
+ fprint(2, "err=%s\n", doserrstr(err));
+Raw:
+ fprint(2, "\n");
+ for(; p < end; p++){
+ if((p - (uchar *)buf) % 16 == 0)
+ fprint(2, "\n%06lx\t", p - (uchar *)buf);
+ if(isprint((char)*p))
+ fprint(2, "%c ", (char )*p);
+ else
+ fprint(2, "%02ux ", *p);
+ }
+ fprint(2, "\n");
+}
diff --git a/sys/src/cmd/cifs/nterrstr.c b/sys/src/cmd/cifs/nterrstr.c
new file mode 100755
index 000000000..8ec9c8ba1
--- /dev/null
+++ b/sys/src/cmd/cifs/nterrstr.c
@@ -0,0 +1,1019 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * some lines commented 4APE have been changed to
+ * make them the same as plan9 error messages. This is not
+ * a problem for native programs but those built on APE
+ * will give unhelpful errors if this is not done
+ */
+
+static struct {
+ char *msg;
+ int err;
+} NTerrs[] = {
+ { "success", 0x0 },
+ { "wait 1", 0x1 },
+ { "wait 2", 0x2 },
+ { "wait 3", 0x3 },
+ { "wait 63", 0x3f },
+ { "abandoned", 0x80 },
+ { "abandoned wait 63", 0xbf },
+ { "user apc", 0xc0 },
+ { "kernel apc", 0x100 },
+ { "alerted", 0x101 },
+ { "timeout", 0x102 },
+ { "pending", 0x103 },
+ { "reparse", 0x104 },
+ { "more entries", 0x105 },
+ { "not all assigned", 0x106 },
+ { "some not mapped", 0x107 },
+ { "oplock break in progress", 0x108 },
+ { "volume mounted", 0x109 },
+ { "rxact committed", 0x10a },
+ { "notify cleanup", 0x10b },
+ { "notify enum dir", 0x10c },
+ { "no quotas for account", 0x10d },
+ { "primary transport connect failed", 0x10e },
+ { "page fault transition", 0x110 },
+ { "page fault demand zero", 0x111 },
+ { "page fault copy on write", 0x112 },
+ { "page fault guard page", 0x113 },
+ { "page fault paging file", 0x114 },
+ { "cache page locked", 0x115 },
+ { "crash dump", 0x116 },
+ { "buffer all zeros", 0x117 },
+ { "reparse object", 0x118 },
+ { "resource requirements changed", 0x119 },
+ { "translation complete", 0x120 },
+ { "ds membership evaluated locally", 0x121 },
+ { "nothing to terminate", 0x122 },
+ { "process not in job", 0x123 },
+ { "process in job", 0x124 },
+ { "wait for oplock", 0x367 },
+ { "object name exists", 0x40000000 },
+ { "thread was suspended", 0x40000001 },
+ { "working set limit range", 0x40000002 },
+ { "image not at base", 0x40000003 },
+ { "rxact state created", 0x40000004 },
+ { "segment notification", 0x40000005 },
+ { "local user session key", 0x40000006 },
+ { "bad current directory", 0x40000007 },
+ { "serial more writes", 0x40000008 },
+ { "registry recovered", 0x40000009 },
+ { "ft read recovery from backup", 0x4000000a },
+ { "ft write recovery", 0x4000000b },
+ { "serial counter timeout", 0x4000000c },
+ { "null LM password", 0x4000000d },
+ { "image machine type mismatch", 0x4000000e },
+ { "receive partial", 0x4000000f },
+ { "receive expedited", 0x40000010 },
+ { "receive partial expedited", 0x40000011 },
+ { "event done", 0x40000012 },
+ { "event pending", 0x40000013 },
+ { "checking file system", 0x40000014 },
+ { "fatal app exit", 0x40000015 },
+ { "predefined handle", 0x40000016 },
+ { "was unlocked", 0x40000017 },
+ { "service notification", 0x40000018 },
+ { "was locked", 0x40000019 },
+ { "log hard error", 0x4000001a },
+ { "already win32", 0x4000001b },
+ { "wx86 unsimulate", 0x4000001c },
+ { "wx86 continue", 0x4000001d },
+ { "wx86 single step", 0x4000001e },
+ { "wx86 breakpoint", 0x4000001f },
+ { "wx86 exception continue", 0x40000020 },
+ { "wx86 exception lastchance", 0x40000021 },
+ { "wx86 exception chain", 0x40000022 },
+ { "image machine type mismatch exe", 0x40000023 },
+ { "no yield performed", 0x40000024 },
+ { "timer resume ignored", 0x40000025 },
+ { "arbitration unhandled", 0x40000026 },
+ { "cardbus not supported", 0x40000027 },
+ { "wx86 createwx86tib", 0x40000028 },
+ { "MP processor mismatch", 0x40000029 },
+ { "hibernated", 0x4000002a },
+ { "resume hibernation", 0x4000002b },
+ { "wake system", 0x40000294 },
+ { "ds shutting down", 0x40000370 },
+ { "CTX cdm connect", 0x400a0004 },
+ { "CTX cdm disconnect", 0x400a0005 },
+ { "SXS release activation context", 0x4015000d },
+ { "guard page violation", 0x80000001 },
+ { "datatype misalignment", 0x80000002 },
+ { "breakpoint", 0x80000003 },
+ { "single step", 0x80000004 },
+ { "buffer overflow", 0x80000005 },
+ { "no more files", 0x80000006 },
+ { "wake system debugger", 0x80000007 },
+ { "handles closed", 0x8000000a },
+ { "no inheritance", 0x8000000b },
+ { "GUID substitution made", 0x8000000c },
+ { "partial copy", 0x8000000d },
+ { "device paper empty", 0x8000000e },
+ { "device powered off", 0x8000000f },
+ { "device off line", 0x80000010 },
+ { "device busy", 0x80000011 },
+ { "no more EAs", 0x80000012 },
+ { "invalid EA name", 0x80000013 },
+ { "ea list inconsistent", 0x80000014 },
+ { "invalid ea flag", 0x80000015 },
+ { "verify required", 0x80000016 },
+ { "extraneous information", 0x80000017 },
+ { "rxact commit necessary", 0x80000018 },
+ { "no more entries", 0x8000001a },
+ { "filemark detected", 0x8000001b },
+ { "media changed", 0x8000001c },
+ { "bus reset", 0x8000001d },
+ { "end of media", 0x8000001e },
+ { "beginning of media", 0x8000001f },
+ { "media check", 0x80000020 },
+ { "setmark detected", 0x80000021 },
+ { "no data detected", 0x80000022 },
+ { "redirector has open handles", 0x80000023 },
+ { "server has open handles", 0x80000024 },
+ { "already disconnected", 0x80000025 },
+ { "longjump", 0x80000026 },
+ { "cleaner cartridge installed", 0x80000027 },
+ { "plugplay query vetoed", 0x80000028 },
+ { "unwind consolidate", 0x80000029 },
+ { "device requires cleaning", 0x80000288 },
+ { "device door open", 0x80000289 },
+ { "cluster node already up", 0x80130001 },
+ { "cluster node already down", 0x80130002 },
+ { "cluster network already online", 0x80130003 },
+ { "cluster network already offline", 0x80130004 },
+ { "cluster node already member", 0x80130005 },
+ { "unsuccessful", 0xc0000001 },
+ { "not implemented", 0xc0000002 },
+ { "invalid info class", 0xc0000003 },
+ { "info length mismatch", 0xc0000004 },
+ { "access violation", 0xc0000005 },
+ { "in page error", 0xc0000006 },
+ { "pagefile quota", 0xc0000007 },
+ { "invalid handle", 0xc0000008 },
+ { "bad initial stack", 0xc0000009 },
+ { "bad initial PC", 0xc000000a },
+ { "invalid CID", 0xc000000b },
+ { "timer not canceled", 0xc000000c },
+ { "invalid parameter", 0xc000000d },
+ { "no such device", 0xc000000e },
+ { "no such file", 0xc000000f },
+ { "invalid device request", 0xc0000010 },
+ { "end of file", 0xc0000011 },
+ { "wrong volume", 0xc0000012 },
+ { "no media in device", 0xc0000013 },
+ { "unrecognized media", 0xc0000014 },
+ { "nonexistent sector", 0xc0000015 },
+ { "more processing required", 0xc0000016 },
+ { "no memory", 0xc0000017 },
+ { "conflicting addresses", 0xc0000018 },
+ { "not mapped view", 0xc0000019 },
+ { "unable to free VM", 0xc000001a },
+ { "unable to delete section", 0xc000001b },
+ { "invalid system service", 0xc000001c },
+ { "illegal instruction", 0xc000001d },
+ { "invalid lock sequence", 0xc000001e },
+ { "invalid view size", 0xc000001f },
+ { "invalid file for section", 0xc0000020 },
+ { "already committed", 0xc0000021 },
+ { "permission denied", 0xc0000022 },
+//4APE { "access denied", 0xc0000022 },
+ { "buffer too small", 0xc0000023 },
+ { "object type mismatch", 0xc0000024 },
+ { "noncontinuable exception", 0xc0000025 },
+ { "invalid disposition", 0xc0000026 },
+ { "unwind", 0xc0000027 },
+ { "bad stack", 0xc0000028 },
+ { "invalid unwind target", 0xc0000029 },
+ { "not locked", 0xc000002a },
+ { "parity error", 0xc000002b },
+ { "unable to decommit VM", 0xc000002c },
+ { "not committed", 0xc000002d },
+ { "invalid port attributes", 0xc000002e },
+ { "port message too long", 0xc000002f },
+ { "invalid parameter mix", 0xc0000030 },
+ { "invalid quota lower", 0xc0000031 },
+ { "disk corrupt error", 0xc0000032 },
+ { "file name syntax", 0xc0000033 },
+//4APE { "object name invalid", 0xc0000033 },
+ { "does not exist", 0xc0000034 },
+//4APE { "object name not found", 0xc0000034 },
+ { "create -- file exists", 0xc0000035 },
+//4APE { "object name collision", 0xc0000035 },
+ { "port disconnected", 0xc0000037 },
+ { "device already attached", 0xc0000038 },
+ { "does not exist", 0xc0000039 },
+//4APE { "object path invalid", 0xc0000039 },
+ { "does not exist", 0xc000003a },
+//4APE { "object path not found", 0xc000003a },
+ { "file name syntax", 0xc000003b },
+//4APE { "object path syntax bad", 0xc000003b },
+ { "data overrun", 0xc000003c },
+ { "data late error", 0xc000003d },
+ { "data error", 0xc000003e },
+ { "crc error", 0xc000003f },
+ { "section too big", 0xc0000040 },
+ { "port connection refused", 0xc0000041 },
+ { "invalid port handle", 0xc0000042 },
+ { "sharing violation", 0xc0000043 },
+ { "quota exceeded", 0xc0000044 },
+ { "invalid page protection", 0xc0000045 },
+ { "mutant not owned", 0xc0000046 },
+ { "semaphore limit exceeded", 0xc0000047 },
+ { "port already set", 0xc0000048 },
+ { "section not image", 0xc0000049 },
+ { "suspend count exceeded", 0xc000004a },
+ { "thread is terminating", 0xc000004b },
+ { "bad working set limit", 0xc000004c },
+ { "incompatible file map", 0xc000004d },
+ { "section protection", 0xc000004e },
+ { "EAs not supported", 0xc000004f },
+ { "EA too large", 0xc0000050 },
+ { "nonexistent ea entry", 0xc0000051 },
+ { "no EAs on file", 0xc0000052 },
+ { "EA corrupt error", 0xc0000053 },
+ { "file lock conflict", 0xc0000054 },
+ { "lock not granted", 0xc0000055 },
+ { "delete pending", 0xc0000056 },
+ { "ctl file not supported", 0xc0000057 },
+ { "unknown revision", 0xc0000058 },
+ { "revision mismatch", 0xc0000059 },
+ { "invalid owner", 0xc000005a },
+ { "invalid primary group", 0xc000005b },
+ { "no impersonation token", 0xc000005c },
+ { "cant disable mandatory", 0xc000005d },
+ { "no logon servers", 0xc000005e },
+ { "no such logon session", 0xc000005f },
+ { "no such privilege", 0xc0000060 },
+ { "privilege not held", 0xc0000061 },
+ { "invalid account name", 0xc0000062 },
+ { "user exists", 0xc0000063 },
+ { "no such user", 0xc0000064 },
+ { "group exists", 0xc0000065 },
+ { "no such group", 0xc0000066 },
+ { "member in group", 0xc0000067 },
+ { "member not in group", 0xc0000068 },
+ { "last admin", 0xc0000069 },
+ { "wrong password", 0xc000006a },
+ { "ill-formed password", 0xc000006b },
+ { "password restriction", 0xc000006c },
+ { "logon failure", 0xc000006d },
+ { "account restriction", 0xc000006e },
+ { "invalid logon hours", 0xc000006f },
+ { "invalid workstation", 0xc0000070 },
+ { "password expired", 0xc0000071 },
+ { "account disabled", 0xc0000072 },
+ { "none mapped", 0xc0000073 },
+ { "too many luids requested", 0xc0000074 },
+ { "luids exhausted", 0xc0000075 },
+ { "invalid sub authority", 0xc0000076 },
+ { "invalid ACL", 0xc0000077 },
+ { "invalid SID", 0xc0000078 },
+ { "invalid security descr", 0xc0000079 },
+ { "procedure not found", 0xc000007a },
+ { "invalid image format", 0xc000007b },
+ { "no token", 0xc000007c },
+ { "bad inheritance ACL", 0xc000007d },
+ { "range not locked", 0xc000007e },
+ { "disk full", 0xc000007f },
+ { "server disabled", 0xc0000080 },
+ { "server not disabled", 0xc0000081 },
+ { "too many guids requested", 0xc0000082 },
+ { "guids exhausted", 0xc0000083 },
+ { "invalid id authority", 0xc0000084 },
+ { "agents exhausted", 0xc0000085 },
+ { "invalid volume label", 0xc0000086 },
+ { "section not extended", 0xc0000087 },
+ { "not mapped data", 0xc0000088 },
+ { "resource data not found", 0xc0000089 },
+ { "resource type not found", 0xc000008a },
+ { "resource name not found", 0xc000008b },
+ { "array bounds exceeded", 0xc000008c },
+ { "float denormal operand", 0xc000008d },
+ { "float divide by zero", 0xc000008e },
+ { "float inexact result", 0xc000008f },
+ { "float invalid operation", 0xc0000090 },
+ { "float overflow", 0xc0000091 },
+ { "float stack check", 0xc0000092 },
+ { "float underflow", 0xc0000093 },
+ { "integer divide by zero", 0xc0000094 },
+ { "integer overflow", 0xc0000095 },
+ { "privileged instruction", 0xc0000096 },
+ { "too many paging files", 0xc0000097 },
+ { "file invalid", 0xc0000098 },
+ { "allotted space exceeded", 0xc0000099 },
+ { "insufficient resources", 0xc000009a },
+ { "dfs exit path found", 0xc000009b },
+ { "device data error", 0xc000009c },
+ { "device not connected", 0xc000009d },
+ { "device power failure", 0xc000009e },
+ { "free VM not at base", 0xc000009f },
+ { "memory not allocated", 0xc00000a0 },
+ { "working set quota", 0xc00000a1 },
+ { "media write protected", 0xc00000a2 },
+ { "device not ready", 0xc00000a3 },
+ { "invalid group attributes", 0xc00000a4 },
+ { "bad impersonation level", 0xc00000a5 },
+ { "cant open anonymous", 0xc00000a6 },
+ { "bad validation class", 0xc00000a7 },
+ { "bad token type", 0xc00000a8 },
+ { "bad master boot record", 0xc00000a9 },
+ { "instruction misalignment", 0xc00000aa },
+ { "instance not available", 0xc00000ab },
+ { "pipe not available", 0xc00000ac },
+ { "invalid pipe state", 0xc00000ad },
+ { "pipe busy", 0xc00000ae },
+ { "illegal function", 0xc00000af },
+ { "pipe disconnected", 0xc00000b0 },
+ { "pipe closing", 0xc00000b1 },
+ { "pipe connected", 0xc00000b2 },
+ { "pipe listening", 0xc00000b3 },
+ { "invalid read mode", 0xc00000b4 },
+ { "IO timeout", 0xc00000b5 },
+ { "file forced closed", 0xc00000b6 },
+ { "profiling not started", 0xc00000b7 },
+ { "profiling not stopped", 0xc00000b8 },
+ { "could not interpret", 0xc00000b9 },
+ { "file is a directory", 0xc00000ba },
+ { "not supported", 0xc00000bb },
+ { "remote not listening", 0xc00000bc },
+ { "duplicate name", 0xc00000bd },
+ { "bad network path", 0xc00000be },
+ { "network busy", 0xc00000bf },
+ { "device does not exist", 0xc00000c0 },
+ { "too many commands", 0xc00000c1 },
+ { "adapter hardware error", 0xc00000c2 },
+ { "invalid network response", 0xc00000c3 },
+ { "unexpected network error", 0xc00000c4 },
+ { "bad remote adapter", 0xc00000c5 },
+ { "print queue full", 0xc00000c6 },
+ { "no spool space", 0xc00000c7 },
+ { "print cancelled", 0xc00000c8 },
+ { "network name deleted", 0xc00000c9 },
+ { "network access denied", 0xc00000ca },
+ { "bad device type", 0xc00000cb },
+ { "bad network name", 0xc00000cc },
+ { "too many names", 0xc00000cd },
+ { "too many sessions", 0xc00000ce },
+ { "sharing paused", 0xc00000cf },
+ { "request not accepted", 0xc00000d0 },
+ { "redirector paused", 0xc00000d1 },
+ { "net write fault", 0xc00000d2 },
+ { "profiling at limit", 0xc00000d3 },
+ { "not same device", 0xc00000d4 },
+ { "file renamed", 0xc00000d5 },
+ { "virtual circuit closed", 0xc00000d6 },
+ { "no security on object", 0xc00000d7 },
+ { "cant wait", 0xc00000d8 },
+ { "pipe empty", 0xc00000d9 },
+ { "cant access domain info", 0xc00000da },
+ { "cant terminate self", 0xc00000db },
+ { "invalid server state", 0xc00000dc },
+ { "invalid domain state", 0xc00000dd },
+ { "invalid domain role", 0xc00000de },
+ { "no such domain", 0xc00000df },
+ { "domain exists", 0xc00000e0 },
+ { "domain limit exceeded", 0xc00000e1 },
+ { "oplock not granted", 0xc00000e2 },
+ { "invalid oplock protocol", 0xc00000e3 },
+ { "internal DB corruption", 0xc00000e4 },
+ { "internal error", 0xc00000e5 },
+ { "generic not mapped", 0xc00000e6 },
+ { "bad descriptor format", 0xc00000e7 },
+ { "invalid user buffer", 0xc00000e8 },
+ { "unexpected io error", 0xc00000e9 },
+ { "unexpected MM create err", 0xc00000ea },
+ { "unexpected MM map error", 0xc00000eb },
+ { "unexpected MM extend err", 0xc00000ec },
+ { "not logon process", 0xc00000ed },
+ { "logon session exists", 0xc00000ee },
+ { "invalid parameter 1", 0xc00000ef },
+ { "invalid parameter 2", 0xc00000f0 },
+ { "invalid parameter 3", 0xc00000f1 },
+ { "invalid parameter 4", 0xc00000f2 },
+ { "invalid parameter 5", 0xc00000f3 },
+ { "invalid parameter 6", 0xc00000f4 },
+ { "invalid parameter 7", 0xc00000f5 },
+ { "invalid parameter 8", 0xc00000f6 },
+ { "invalid parameter 9", 0xc00000f7 },
+ { "invalid parameter 10", 0xc00000f8 },
+ { "invalid parameter 11", 0xc00000f9 },
+ { "invalid parameter 12", 0xc00000fa },
+ { "redirector not started", 0xc00000fb },
+ { "redirector started", 0xc00000fc },
+ { "stack overflow", 0xc00000fd },
+ { "no such package", 0xc00000fe },
+ { "bad function table", 0xc00000ff },
+ { "variable not found", 0xc0000100 },
+ { "directory not empty", 0xc0000101 },
+ { "file corrupt error", 0xc0000102 },
+ { "not a directory", 0xc0000103 },
+ { "bad logon session state", 0xc0000104 },
+ { "logon session collision", 0xc0000105 },
+ { "name too long", 0xc0000106 },
+ { "files open", 0xc0000107 },
+ { "connection in use", 0xc0000108 },
+ { "message not found", 0xc0000109 },
+ { "process is terminating", 0xc000010a },
+ { "invalid logon type", 0xc000010b },
+ { "no guid translation", 0xc000010c },
+ { "cannot impersonate", 0xc000010d },
+ { "image already loaded", 0xc000010e },
+ { "abios not present", 0xc000010f },
+ { "abios lid not exist", 0xc0000110 },
+ { "abios lid already owned", 0xc0000111 },
+ { "abios not lid owner", 0xc0000112 },
+ { "abios invalid command", 0xc0000113 },
+ { "abios invalid lid", 0xc0000114 },
+ { "abios selector not available", 0xc0000115 },
+ { "abios invalid selector", 0xc0000116 },
+ { "no LDT", 0xc0000117 },
+ { "invalid LDT size", 0xc0000118 },
+ { "invalid LDT offset", 0xc0000119 },
+ { "invalid LDT descriptor", 0xc000011a },
+ { "invalid image NE format", 0xc000011b },
+ { "rxact invalid state", 0xc000011c },
+ { "rxact commit failure", 0xc000011d },
+ { "mapped file size zero", 0xc000011e },
+ { "too many opened files", 0xc000011f },
+ { "cancelled", 0xc0000120 },
+ { "permission denied", 0xc0000121 },
+// { "cannot delete", 0xc0000121 },
+ { "invalid computer name", 0xc0000122 },
+ { "file deleted", 0xc0000123 },
+ { "special account", 0xc0000124 },
+ { "special group", 0xc0000125 },
+ { "special user", 0xc0000126 },
+ { "members primary group", 0xc0000127 },
+ { "file closed", 0xc0000128 },
+ { "too many threads", 0xc0000129 },
+ { "thread not in process", 0xc000012a },
+ { "token already in use", 0xc000012b },
+ { "pagefile quota exceeded", 0xc000012c },
+ { "commitment limit", 0xc000012d },
+ { "invalid image le format", 0xc000012e },
+ { "invalid image not MZ", 0xc000012f },
+ { "invalid image protect", 0xc0000130 },
+ { "invalid image win 16", 0xc0000131 },
+ { "logon server conflict", 0xc0000132 },
+ { "time difference at DC", 0xc0000133 },
+ { "synchronization required", 0xc0000134 },
+ { "DLL not found", 0xc0000135 },
+ { "open failed", 0xc0000136 },
+ { "IO privilege failed", 0xc0000137 },
+ { "ordinal not found", 0xc0000138 },
+ { "entrypoint not found", 0xc0000139 },
+ { "control-C exit", 0xc000013a },
+ { "local disconnect", 0xc000013b },
+ { "remote disconnect", 0xc000013c },
+ { "remote resources", 0xc000013d },
+ { "link failed", 0xc000013e },
+ { "link timeout", 0xc000013f },
+ { "invalid connection", 0xc0000140 },
+ { "invalid address", 0xc0000141 },
+ { "DLL init failed", 0xc0000142 },
+ { "missing systemfile", 0xc0000143 },
+ { "unhandled exception", 0xc0000144 },
+ { "application init failure", 0xc0000145 },
+ { "pagefile create failed", 0xc0000146 },
+ { "no pagefile", 0xc0000147 },
+ { "invalid level", 0xc0000148 },
+ { "wrong password core", 0xc0000149 },
+ { "illegal float context", 0xc000014a },
+ { "pipe broken", 0xc000014b },
+ { "registry corrupt", 0xc000014c },
+ { "registry io failed", 0xc000014d },
+ { "no event pair", 0xc000014e },
+ { "unrecognized volume", 0xc000014f },
+ { "serial no device inited", 0xc0000150 },
+ { "no such alias", 0xc0000151 },
+ { "member not in alias", 0xc0000152 },
+ { "member in alias", 0xc0000153 },
+ { "alias exists", 0xc0000154 },
+ { "logon not granted", 0xc0000155 },
+ { "too many secrets", 0xc0000156 },
+ { "secret too long", 0xc0000157 },
+ { "internal db error", 0xc0000158 },
+ { "fullscreen mode", 0xc0000159 },
+ { "too many context IDs", 0xc000015a },
+ { "logon type not granted", 0xc000015b },
+ { "not registry file", 0xc000015c },
+ { "NT cross encryption required", 0xc000015d },
+ { "domain ctrlr config error", 0xc000015e },
+ { "ft missing member", 0xc000015f },
+ { "ill formed service entry", 0xc0000160 },
+ { "illegal character", 0xc0000161 },
+ { "unmappable character", 0xc0000162 },
+ { "undefined character", 0xc0000163 },
+ { "floppy volume", 0xc0000164 },
+ { "floppy id mark not found", 0xc0000165 },
+ { "floppy wrong cylinder", 0xc0000166 },
+ { "floppy unknown error", 0xc0000167 },
+ { "floppy bad registers", 0xc0000168 },
+ { "disk recalibrate failed", 0xc0000169 },
+ { "disk operation failed", 0xc000016a },
+ { "disk reset failed", 0xc000016b },
+ { "shared IRQ busy", 0xc000016c },
+ { "FT orphaning", 0xc000016d },
+ { "BIOS failed to connect interrupt", 0xc000016e },
+ { "partition failure", 0xc0000172 },
+ { "invalid block length", 0xc0000173 },
+ { "device not partitioned", 0xc0000174 },
+ { "unable to lock media", 0xc0000175 },
+ { "unable to unload media", 0xc0000176 },
+ { "eom overflow", 0xc0000177 },
+ { "no media", 0xc0000178 },
+ { "no such member", 0xc000017a },
+ { "invalid member", 0xc000017b },
+ { "key deleted", 0xc000017c },
+ { "no log space", 0xc000017d },
+ { "too many SIDs", 0xc000017e },
+ { "LM cross encryption required", 0xc000017f },
+ { "key has children", 0xc0000180 },
+ { "child must be volatile", 0xc0000181 },
+ { "device configuration error", 0xc0000182 },
+ { "driver internal error", 0xc0000183 },
+ { "invalid device state", 0xc0000184 },
+ { "io device error", 0xc0000185 },
+ { "device protocol error", 0xc0000186 },
+ { "backup controller", 0xc0000187 },
+ { "log file full", 0xc0000188 },
+ { "too late", 0xc0000189 },
+ { "no trust LSA secret", 0xc000018a },
+ { "no trust SAM account", 0xc000018b },
+ { "trusted domain failure", 0xc000018c },
+ { "trusted relationship failure", 0xc000018d },
+ { "eventlog file corrupt", 0xc000018e },
+ { "eventlog cant start", 0xc000018f },
+ { "trust failure", 0xc0000190 },
+ { "mutant limit exceeded", 0xc0000191 },
+ { "netlogon not started", 0xc0000192 },
+ { "account expired", 0xc0000193 },
+ { "possible deadlock", 0xc0000194 },
+ { "network credential conflict", 0xc0000195 },
+ { "remote session limit", 0xc0000196 },
+ { "eventlog file changed", 0xc0000197 },
+ { "nologon interdomain trust account", 0xc0000198 },
+ { "nologon workstation trust account", 0xc0000199 },
+ { "nologon server trust account", 0xc000019a },
+ { "domain trust inconsistent", 0xc000019b },
+ { "fs driver required", 0xc000019c },
+ { "no user session key", 0xc0000202 },
+ { "user session deleted", 0xc0000203 },
+ { "resource lang not found", 0xc0000204 },
+ { "insuff server resources", 0xc0000205 },
+ { "invalid buffer size", 0xc0000206 },
+ { "invalid address component", 0xc0000207 },
+ { "invalid address wildcard", 0xc0000208 },
+ { "too many addresses", 0xc0000209 },
+ { "address already exists", 0xc000020a },
+ { "address closed", 0xc000020b },
+ { "connection disconnected", 0xc000020c },
+ { "connection reset", 0xc000020d },
+ { "too many nodes", 0xc000020e },
+ { "transaction aborted", 0xc000020f },
+ { "transaction timed out", 0xc0000210 },
+ { "transaction no release", 0xc0000211 },
+ { "transaction no match", 0xc0000212 },
+ { "transaction responded", 0xc0000213 },
+ { "transaction invalid id", 0xc0000214 },
+ { "transaction invalid type", 0xc0000215 },
+ { "not server session", 0xc0000216 },
+ { "not client session", 0xc0000217 },
+ { "cannot load registry file", 0xc0000218 },
+ { "debug attach failed", 0xc0000219 },
+ { "system process terminated", 0xc000021a },
+ { "data not accepted", 0xc000021b },
+ { "no browser servers found", 0xc000021c },
+ { "VDM hard error", 0xc000021d },
+ { "driver cancel timeout", 0xc000021e },
+ { "reply message mismatch", 0xc000021f },
+ { "mapped alignment", 0xc0000220 },
+ { "image checksum mismatch", 0xc0000221 },
+ { "lost writebehind data", 0xc0000222 },
+ { "client server parameters invalid", 0xc0000223 },
+ { "password must change", 0xc0000224 },
+ { "not found", 0xc0000225 },
+ { "not tiny stream", 0xc0000226 },
+ { "recovery failure", 0xc0000227 },
+ { "stack overflow read", 0xc0000228 },
+ { "fail check", 0xc0000229 },
+ { "duplicate objectid", 0xc000022a },
+ { "objectid exists", 0xc000022b },
+ { "convert to large", 0xc000022c },
+ { "retry", 0xc000022d },
+ { "found out of scope", 0xc000022e },
+ { "allocate bucket", 0xc000022f },
+ { "propset not found", 0xc0000230 },
+ { "marshall overflow", 0xc0000231 },
+ { "invalid variant", 0xc0000232 },
+ { "domain controller not found", 0xc0000233 },
+ { "account locked out", 0xc0000234 },
+ { "handle not closable", 0xc0000235 },
+ { "connection refused", 0xc0000236 },
+ { "graceful disconnect", 0xc0000237 },
+ { "address already associated", 0xc0000238 },
+ { "address not associated", 0xc0000239 },
+ { "connection invalid", 0xc000023a },
+ { "connection active", 0xc000023b },
+ { "network unreachable", 0xc000023c },
+ { "host unreachable", 0xc000023d },
+ { "protocol unreachable", 0xc000023e },
+ { "port unreachable", 0xc000023f },
+ { "request aborted", 0xc0000240 },
+ { "connection aborted", 0xc0000241 },
+ { "bad compression buffer", 0xc0000242 },
+ { "user mapped file", 0xc0000243 },
+ { "audit failed", 0xc0000244 },
+ { "timer resolution not set", 0xc0000245 },
+ { "connection count limit", 0xc0000246 },
+ { "login time restriction", 0xc0000247 },
+ { "login wkstation restriction", 0xc0000248 },
+ { "image mp up mismatch", 0xc0000249 },
+ { "insufficient logon info", 0xc0000250 },
+ { "bad DLL entrypoint", 0xc0000251 },
+ { "bad service entrypoint", 0xc0000252 },
+ { "lpc reply lost", 0xc0000253 },
+ { "IP address conflict1", 0xc0000254 },
+ { "IP address conflict2", 0xc0000255 },
+ { "registry quota limit", 0xc0000256 },
+ { "path not covered", 0xc0000257 },
+ { "no callback active", 0xc0000258 },
+ { "license quota exceeded", 0xc0000259 },
+ { "password too short", 0xc000025a },
+ { "password too recent", 0xc000025b },
+ { "password history conflict", 0xc000025c },
+ { "plugplay no device", 0xc000025e },
+ { "unsupported compression", 0xc000025f },
+ { "invalid hw profile", 0xc0000260 },
+ { "invalid plugplay device path", 0xc0000261 },
+ { "driver ordinal not found", 0xc0000262 },
+ { "driver entrypoint not found", 0xc0000263 },
+ { "resource not owned", 0xc0000264 },
+ { "too many links", 0xc0000265 },
+ { "quota list inconsistent", 0xc0000266 },
+ { "file is offline", 0xc0000267 },
+ { "evaluation expiration", 0xc0000268 },
+ { "illegal DLL relocation", 0xc0000269 },
+ { "license violation", 0xc000026a },
+ { "DLL init failed logoff", 0xc000026b },
+ { "driver unable to load", 0xc000026c },
+ { "dfs unavailable", 0xc000026d },
+ { "volume dismounted", 0xc000026e },
+ { "wx86 internal error", 0xc000026f },
+ { "wx86 float stack check", 0xc0000270 },
+ { "validate continue", 0xc0000271 },
+ { "no match", 0xc0000272 },
+ { "no more matches", 0xc0000273 },
+ { "not a reparse point", 0xc0000275 },
+ { "IO reparse tag invalid", 0xc0000276 },
+ { "IO reparse tag mismatch", 0xc0000277 },
+ { "IO reparse data invalid", 0xc0000278 },
+ { "IO reparse tag not handled", 0xc0000279 },
+ { "reparse point not resolved", 0xc0000280 },
+ { "directory is a reparse point", 0xc0000281 },
+ { "range list conflict", 0xc0000282 },
+ { "source element empty", 0xc0000283 },
+ { "destination element full", 0xc0000284 },
+ { "illegal element address", 0xc0000285 },
+ { "magazine not present", 0xc0000286 },
+ { "reinitialization needed", 0xc0000287 },
+ { "encryption failed", 0xc000028a },
+ { "decryption failed", 0xc000028b },
+ { "range not found", 0xc000028c },
+ { "no recovery policy", 0xc000028d },
+ { "no EFS", 0xc000028e },
+ { "wrong EFS", 0xc000028f },
+ { "no user keys", 0xc0000290 },
+ { "file not encrypted", 0xc0000291 },
+ { "not export format", 0xc0000292 },
+ { "file encrypted", 0xc0000293 },
+ { "WMI guid not found", 0xc0000295 },
+ { "WMI instance not found", 0xc0000296 },
+ { "WMI itemid not found", 0xc0000297 },
+ { "WMI try again", 0xc0000298 },
+ { "shared policy", 0xc0000299 },
+ { "policy object not found", 0xc000029a },
+ { "policy only in DS", 0xc000029b },
+ { "volume not upgraded", 0xc000029c },
+ { "remote storage not active", 0xc000029d },
+ { "remote storage media error", 0xc000029e },
+ { "no tracking service", 0xc000029f },
+ { "server SID mismatch", 0xc00002a0 },
+ { "DS no attribute or value", 0xc00002a1 },
+ { "DS invalid attribute syntax", 0xc00002a2 },
+ { "DS attribute type undefined", 0xc00002a3 },
+ { "DS attribute or value exists", 0xc00002a4 },
+ { "DS busy", 0xc00002a5 },
+ { "DS unavailable", 0xc00002a6 },
+ { "DS no RIDs allocated", 0xc00002a7 },
+ { "DS no more RIDs", 0xc00002a8 },
+ { "DS incorrect role owner", 0xc00002a9 },
+ { "DS ridmgr init error", 0xc00002aa },
+ { "DS obj class violation", 0xc00002ab },
+ { "DS cant on non leaf", 0xc00002ac },
+ { "DS cant on RDN", 0xc00002ad },
+ { "DS cant mod obj class", 0xc00002ae },
+ { "DS cross dom move failed", 0xc00002af },
+ { "DS GC not available", 0xc00002b0 },
+ { "directory service required", 0xc00002b1 },
+ { "reparse attribute conflict", 0xc00002b2 },
+ { "cant enable deny only", 0xc00002b3 },
+ { "float multiple faults", 0xc00002b4 },
+ { "float multiple traps", 0xc00002b5 },
+ { "device removed", 0xc00002b6 },
+ { "journal delete in progress", 0xc00002b7 },
+ { "journal not active", 0xc00002b8 },
+ { "nointerface", 0xc00002b9 },
+ { "DS admin limit exceeded", 0xc00002c1 },
+ { "driver failed sleep", 0xc00002c2 },
+ { "mutual authentication failed", 0xc00002c3 },
+ { "corrupt system file", 0xc00002c4 },
+ { "datatype misalignment error", 0xc00002c5 },
+ { "WMI read only", 0xc00002c6 },
+ { "WMI set failure", 0xc00002c7 },
+ { "commitment minimum", 0xc00002c8 },
+ { "reg NAT consumption", 0xc00002c9 },
+ { "transport full", 0xc00002ca },
+ { "DS SAM init failure", 0xc00002cb },
+ { "only if connected", 0xc00002cc },
+ { "DS sensitive group violation", 0xc00002cd },
+ { "PNP restart enumeration", 0xc00002ce },
+ { "journal entry deleted", 0xc00002cf },
+ { "DS cant mod primarygroupid", 0xc00002d0 },
+ { "system image bad signature", 0xc00002d1 },
+ { "PNP reboot required", 0xc00002d2 },
+ { "power state invalid", 0xc00002d3 },
+ { "DS invalid group type", 0xc00002d4 },
+ { "DS no nest globalgroup in mixeddomain", 0xc00002d5 },
+ { "DS no nest localgroup in mixeddomain", 0xc00002d6 },
+ { "DS global can't have local member", 0xc00002d7 },
+ { "DS global can't have universal member", 0xc00002d8 },
+ { "DS universal can't have local member", 0xc00002d9 },
+ { "DS global can't have crossdomain member", 0xc00002da },
+ { "DS local can't have crossdomain local member",0xc00002db },
+ { "DS have primary members", 0xc00002dc },
+ { "WMI not supported", 0xc00002dd },
+ { "insufficient power", 0xc00002de },
+ { "SAM need bootkey password", 0xc00002df },
+ { "SAM need bootkey floppy", 0xc00002e0 },
+ { "DS cant start", 0xc00002e1 },
+ { "DS init failure", 0xc00002e2 },
+ { "SAM init failure", 0xc00002e3 },
+ { "DS gc required", 0xc00002e4 },
+ { "DS local member of local only", 0xc00002e5 },
+ { "DS no FPO in universal groups", 0xc00002e6 },
+ { "DS machine account quota exceeded", 0xc00002e7 },
+ { "multiple fault violation", 0xc00002e8 },
+ { "current domain not allowed", 0xc00002e9 },
+ { "cannot make", 0xc00002ea },
+ { "system shutdown", 0xc00002eb },
+ { "DS init failure console", 0xc00002ec },
+ { "DS sam init failure console", 0xc00002ed },
+ { "unfinished context deleted", 0xc00002ee },
+ { "no TGT reply", 0xc00002ef },
+ { "objectid not found", 0xc00002f0 },
+ { "no IP addresses", 0xc00002f1 },
+ { "wrong credential handle", 0xc00002f2 },
+ { "crypto system invalid", 0xc00002f3 },
+ { "max referrals exceeded", 0xc00002f4 },
+ { "must be kdc", 0xc00002f5 },
+ { "strong crypto not supported", 0xc00002f6 },
+ { "too many principals", 0xc00002f7 },
+ { "no PA data", 0xc00002f8 },
+ { "pkinit name mismatch", 0xc00002f9 },
+ { "smartcard logon required", 0xc00002fa },
+ { "KDC invalid request", 0xc00002fb },
+ { "KDC unable to refer", 0xc00002fc },
+ { "KDC unknown etype", 0xc00002fd },
+ { "shutdown in progress", 0xc00002fe },
+ { "server shutdown in progress", 0xc00002ff },
+ { "not supported on sbs", 0xc0000300 },
+ { "WMI GUID disconnected", 0xc0000301 },
+ { "WMI already disabled", 0xc0000302 },
+ { "WMI already enabled", 0xc0000303 },
+ { "mft too fragmented", 0xc0000304 },
+ { "copy protection failure", 0xc0000305 },
+ { "CSS authentication failure", 0xc0000306 },
+ { "CSS key not present", 0xc0000307 },
+ { "CSS key not established", 0xc0000308 },
+ { "CSS scrambled sector", 0xc0000309 },
+ { "CSS region mismatch", 0xc000030a },
+ { "CSS resets exhausted", 0xc000030b },
+ { "pkinit failure", 0xc0000320 },
+ { "smartcard subsystem failure", 0xc0000321 },
+ { "no kerb key", 0xc0000322 },
+ { "host down", 0xc0000350 },
+ { "unsupported preauth", 0xc0000351 },
+ { "EFS alg blob too big", 0xc0000352 },
+ { "port not set", 0xc0000353 },
+ { "debugger inactive", 0xc0000354 },
+ { "ds version check failure", 0xc0000355 },
+ { "auditing disabled", 0xc0000356 },
+ { "prent4 machine account", 0xc0000357 },
+ { "DS AG can't have universal member", 0xc0000358 },
+ { "invalid image Win 32", 0xc0000359 },
+ { "invalid image Win 64", 0xc000035a },
+ { "bad bindings", 0xc000035b },
+ { "network session expired", 0xc000035c },
+ { "apphelp block", 0xc000035d },
+ { "all SIDs filtered", 0xc000035e },
+ { "not safe mode driver", 0xc000035f },
+ { "access disabled by policy default", 0xc0000361 },
+ { "access disabled by policy path", 0xc0000362 },
+ { "access disabled by policy publisher", 0xc0000363 },
+ { "access disabled by policy other", 0xc0000364 },
+ { "failed driver entry", 0xc0000365 },
+ { "device enumeration error", 0xc0000366 },
+ { "mount point not resolved", 0xc0000368 },
+ { "invalid device object parameter", 0xc0000369 },
+ { "mca occured", 0xc000036a },
+ { "driver blocked critical", 0xc000036b },
+ { "driver blocked", 0xc000036c },
+ { "driver database error", 0xc000036d },
+ { "system hive too large", 0xc000036e },
+ { "invalid import of non DLL", 0xc000036f },
+ { "smartcard wrong pin", 0xc0000380 },
+ { "smartcard card blocked", 0xc0000381 },
+ { "smartcard card not authenticated", 0xc0000382 },
+ { "smartcard no card", 0xc0000383 },
+ { "smartcard no key container", 0xc0000384 },
+ { "smartcard no certificate", 0xc0000385 },
+ { "smartcard no keyset", 0xc0000386 },
+ { "smartcard io error", 0xc0000387 },
+ { "downgrade detected", 0xc0000388 },
+ { "smartcard cert revoked", 0xc0000389 },
+ { "issuing CA untrusted", 0xc000038a },
+ { "revocation offline c", 0xc000038b },
+ { "pkinit client failure", 0xc000038c },
+ { "smartcard cert expired", 0xc000038d },
+ { "driver failed prior unload", 0xc000038e },
+ { "wow assertion", 0xc0009898 },
+ { "PNP bad MPS table", 0xc0040035 },
+ { "PNP translation failed", 0xc0040036 },
+ { "PNP IRQ translation failed", 0xc0040037 },
+ { "CTX winstation name invalid", 0xc00a0001 },
+ { "CTX invalid PD", 0xc00a0002 },
+ { "CTX PD not found", 0xc00a0003 },
+ { "CTX close pending", 0xc00a0006 },
+ { "CTX no outbuf", 0xc00a0007 },
+ { "CTX modem inf not found", 0xc00a0008 },
+ { "CTX invalid modemname", 0xc00a0009 },
+ { "CTX response error", 0xc00a000a },
+ { "CTX modem response timeout", 0xc00a000b },
+ { "CTX modem response no carrier", 0xc00a000c },
+ { "CTX modem response no dialtone", 0xc00a000d },
+ { "CTX modem response busy", 0xc00a000e },
+ { "CTX modem response voice", 0xc00a000f },
+ { "CTX TD error", 0xc00a0010 },
+ { "CTX license client invalid", 0xc00a0012 },
+ { "CTX license not available", 0xc00a0013 },
+ { "CTX license expired", 0xc00a0014 },
+ { "CTX winstation not found", 0xc00a0015 },
+ { "CTX winstation name collision", 0xc00a0016 },
+ { "CTX winstation busy", 0xc00a0017 },
+ { "CTX bad video mode", 0xc00a0018 },
+ { "CTX graphics invalid", 0xc00a0022 },
+ { "CTX not console", 0xc00a0024 },
+ { "CTX client query timeout", 0xc00a0026 },
+ { "CTX console disconnect", 0xc00a0027 },
+ { "CTX console connect", 0xc00a0028 },
+ { "CTX shadow denied", 0xc00a002a },
+ { "CTX winstation access denied", 0xc00a002b },
+ { "CTX invalid wd", 0xc00a002e },
+ { "CTX WD not found", 0xc00a002f },
+ { "CTX shadow invalid", 0xc00a0030 },
+ { "CTX shadow disabled", 0xc00a0031 },
+ { "RDP protocol error", 0xc00a0032 },
+ { "CTX client license not set", 0xc00a0033 },
+ { "CTX client license in use", 0xc00a0034 },
+ { "CTX shadow ended by mode change", 0xc00a0035 },
+ { "CTX shadow not running", 0xc00a0036 },
+ { "cluster invalid node", 0xc0130001 },
+ { "cluster node exists", 0xc0130002 },
+ { "cluster join in progress", 0xc0130003 },
+ { "cluster node not found", 0xc0130004 },
+ { "cluster local node not found", 0xc0130005 },
+ { "cluster network exists", 0xc0130006 },
+ { "cluster network not found", 0xc0130007 },
+ { "cluster netinterface exists", 0xc0130008 },
+ { "cluster netinterface not found", 0xc0130009 },
+ { "cluster invalid request", 0xc013000a },
+ { "cluster invalid network provider", 0xc013000b },
+ { "cluster node down", 0xc013000c },
+ { "cluster node unreachable", 0xc013000d },
+ { "cluster node not member", 0xc013000e },
+ { "cluster join not in progress", 0xc013000f },
+ { "cluster invalid network", 0xc0130010 },
+ { "cluster no net adapters", 0xc0130011 },
+ { "cluster node up", 0xc0130012 },
+ { "cluster node paused", 0xc0130013 },
+ { "cluster node not paused", 0xc0130014 },
+ { "cluster no security context", 0xc0130015 },
+ { "cluster network not internal", 0xc0130016 },
+ { "cluster poisoned", 0xc0130017 },
+ { "ACPI invalid opcode", 0xc0140001 },
+ { "ACPI stack overflow", 0xc0140002 },
+ { "ACPI assert failed", 0xc0140003 },
+ { "ACPI invalid index", 0xc0140004 },
+ { "ACPI invalid argument", 0xc0140005 },
+ { "ACPI fatal", 0xc0140006 },
+ { "ACPI invalid supername", 0xc0140007 },
+ { "ACPI invalid argtype", 0xc0140008 },
+ { "ACPI invalid objtype", 0xc0140009 },
+ { "ACPI invalid targettype", 0xc014000a },
+ { "ACPI incorrect argument count", 0xc014000b },
+ { "ACPI address not mapped", 0xc014000c },
+ { "ACPI invalid eventtype", 0xc014000d },
+ { "ACPI handler collision", 0xc014000e },
+ { "ACPI invalid data", 0xc014000f },
+ { "ACPI invalid region", 0xc0140010 },
+ { "ACPI invalid access size", 0xc0140011 },
+ { "ACPI acquire global lock", 0xc0140012 },
+ { "ACPI already initialized", 0xc0140013 },
+ { "ACPI not initialized", 0xc0140014 },
+ { "ACPI invalid mutex level", 0xc0140015 },
+ { "ACPI mutex not owned", 0xc0140016 },
+ { "ACPI mutex not owner", 0xc0140017 },
+ { "ACPI rs access", 0xc0140018 },
+ { "ACPI invalid table", 0xc0140019 },
+ { "ACPI reg handler failed", 0xc0140020 },
+ { "ACPI power request failed", 0xc0140021 },
+ { "SXS section not found", 0xc0150001 },
+ { "SXS cant gen actctx", 0xc0150002 },
+ { "SXS invalid actctx data format", 0xc0150003 },
+ { "SXS assembly not found", 0xc0150004 },
+ { "SXS manifest format error", 0xc0150005 },
+ { "SXS manifest parse error", 0xc0150006 },
+ { "SXS activation context disabled", 0xc0150007 },
+ { "SXS key not found", 0xc0150008 },
+ { "SXS version conflict", 0xc0150009 },
+ { "SXS wrong section type", 0xc015000a },
+ { "SXS thread queries disabled", 0xc015000b },
+ { "SXS assembly missing", 0xc015000c },
+ { "SXS process default already set", 0xc015000e },
+ { "SXS early deactivation", 0xc015000f },
+ { "SXS invalid deactivation", 0xc0150010 },
+ { "SXS multiple deactivation", 0xc0150011 },
+ { "SXS system default activation context empty",0xc0150012 },
+ { "SXS process termination requested", 0xc0150013 },
+};
+
+char *
+nterrstr(uint err)
+{
+ int i, f, match;
+ char *why, *facility, tmp[32];
+ static char buf[0xff];
+
+ f = (err >> 16) & 0x7ff;
+ switch(f){
+ case 0:
+ facility = "";
+ break;
+ case 1:
+ facility = " (hardware), ";
+ break;
+ case 2:
+ facility = " (dispatch), ";
+ break;
+ case 3:
+ facility = " (storage), ";
+ break;
+ case 4:
+ facility = " (itf), ";
+ break;
+ case 7:
+ facility = " (win32), ";
+ break;
+ case 8:
+ facility = " (windows), ";
+ break;
+ case 0x0a:
+ facility = " (control), ";
+ break;
+ default:
+ snprint(tmp, sizeof(tmp), " (facility=%d), ", f);
+ facility = tmp;
+ break;
+ }
+
+ match = -1;
+ for(i = 0; i < nelem(NTerrs); i++)
+ if(NTerrs[i].err == err)
+ match = i;
+
+ why = "";
+ if(!(err & 0x80000000))
+ why = "warning, ";
+
+ if(match != -1)
+ snprint(buf, sizeof buf, "%s%s%s", why, facility,
+ NTerrs[match].msg);
+ else
+ snprint(buf, sizeof buf, "%s%s%d/0x%ux - unknown NT error",
+ why, facility, err, err);
+ return buf;
+}
diff --git a/sys/src/cmd/cifs/pack.c b/sys/src/cmd/cifs/pack.c
new file mode 100755
index 000000000..1f0230220
--- /dev/null
+++ b/sys/src/cmd/cifs/pack.c
@@ -0,0 +1,463 @@
+/* packet packing and unpacking */
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include "cifs.h"
+
+void *
+pmem(Pkt *p, void *v, int len)
+{
+ uchar *str = v;
+ void *s = p->pos;
+
+ if(!len || !v)
+ return s;
+ while(len--)
+ *p->pos++ = *str++;
+ return s;
+}
+
+void *
+ppath(Pkt *p, char *str)
+{
+ char c;
+ Rune r;
+ void *s = p->pos;
+
+ if(!str)
+ return s;
+
+ if(p->s->caps & CAP_UNICODE){
+ if(((p->pos - p->buf) % 2) != 0) /* pad to even offset */
+ p8(p, 0);
+ while(*str){
+ str += chartorune(&r, str);
+ if(r == L'/')
+ r = L'\\';
+ pl16(p, r);
+ }
+ pl16(p, 0);
+ } else {
+ while((c = *str++) != 0){
+ if(c == '/')
+ c = '\\';
+ *p->pos++ = c;
+ }
+ *p->pos++ = 0;
+ }
+ return s;
+}
+
+void *
+pstr(Pkt *p, char *str)
+{
+ void *s = p->pos;
+ Rune r;
+
+ if(!str)
+ return s;
+
+ if(p->s->caps & CAP_UNICODE){
+ if(((p->pos - p->buf) % 2) != 0)
+ p8(p, 0); /* pad to even offset */
+ while(*str){
+ str += chartorune(&r, str);
+ pl16(p, r);
+ }
+ pl16(p, 0);
+ } else {
+ while(*str)
+ *p->pos++ = *str++;
+ *p->pos++ = 0;
+ }
+ return s;
+}
+
+void *
+pascii(Pkt *p, char *str)
+{
+ void *s = p->pos;
+
+ while(*str)
+ *p->pos++ = *str++;
+ *p->pos++ = 0;
+ return s;
+}
+
+
+void *
+pl64(Pkt *p, uvlong n)
+{
+ void *s = p->pos;
+
+ *p->pos++ = n;
+ *p->pos++ = n >> 8;
+ *p->pos++ = n >> 16;
+ *p->pos++ = n >> 24;
+ *p->pos++ = n >> 32;
+ *p->pos++ = n >> 40;
+ *p->pos++ = n >> 48;
+ *p->pos++ = n >> 56;
+ return s;
+}
+
+void *
+pb32(Pkt *p, uint n)
+{
+ void *s = p->pos;
+
+ *p->pos++ = n >> 24;
+ *p->pos++ = n >> 16;
+ *p->pos++ = n >> 8;
+ *p->pos++ = n;
+ return s;
+}
+
+void *
+pl32(Pkt *p, uint n)
+{
+ void *s = p->pos;
+
+ *p->pos++ = n;
+ *p->pos++ = n >> 8;
+ *p->pos++ = n >> 16;
+ *p->pos++ = n >> 24;
+ return s;
+}
+
+void *
+pb16(Pkt *p, uint n)
+{
+ void *s = p->pos;
+
+ *p->pos++ = n >> 8;
+ *p->pos++ = n;
+ return s;
+}
+
+void *
+pl16(Pkt *p, uint n)
+{
+ void *s = p->pos;
+
+ *p->pos++ = n;
+ *p->pos++ = n >> 8;
+ return s;
+}
+
+void *
+p8(Pkt *p, uint n)
+{
+ void *s = p->pos;
+
+ *p->pos++ = n;
+ return s;
+}
+
+/*
+ * Encode a Netbios name
+ */
+void *
+pname(Pkt *p, char *name, char pad)
+{
+ int i, done = 0;
+ char c;
+ void *s = p->pos;
+
+ *p->pos++ = ' ';
+ for(i = 0; i < 16; i++) {
+ c = pad;
+ if(!done && name[i] == '\0')
+ done = 1;
+ if(!done)
+ c = islower(name[i])? toupper(name[i]): name[i];
+ *p->pos++ = ((uchar)c >> 4) + 'A';
+ *p->pos++ = (c & 0xf) + 'A';
+ }
+ *p->pos++ = '\0';
+ return s;
+}
+
+void *
+pvtime(Pkt *p, uvlong n)
+{
+ void *s = p->pos;
+
+ n += 11644473600LL;
+ n *= 10000000LL;
+
+ pl32(p, n);
+ pl32(p, n >> 32);
+ return s;
+}
+
+void *
+pdatetime(Pkt *p, long utc)
+{
+ void *s = p->pos;
+ Tm *tm = localtime(utc);
+ int t = tm->hour << 11 | tm->min << 5 | (tm->sec / 2);
+ int d = (tm->year - 80) << 9 | (tm->mon + 1) << 5 | tm->mday;
+
+ /*
+ * bug in word swapping in Win95 requires this
+ */
+ if(p->s->caps & CAP_NT_SMBS){
+ pl16(p, d);
+ pl16(p, t);
+ } else{
+ pl16(p, t);
+ pl16(p, d);
+ }
+ return s;
+}
+
+
+void
+gmem(Pkt *p, void *v, int n)
+{
+ uchar *str = v;
+
+ if(!n || !v)
+ return;
+ while(n-- && p->pos < p->eop)
+ *str++ = *p->pos++;
+}
+
+/*
+ * note len is the length of the source string in
+ * in runes or bytes, in ASCII mode this is also the size
+ * of the output buffer but this is not so in Unicode mode!
+ */
+void
+gstr(Pkt *p, char *str, int n)
+{
+ int i;
+ Rune r;
+
+ if(!n || !str)
+ return;
+
+ if(p->s->caps & CAP_UNICODE){
+ i = 0;
+ while(*p->pos && n && p->pos < p->eop){
+ r = gl16(p);
+ i += runetochar(str +i, &r);
+ n -= 2;
+ }
+ *(str + i) = 0;
+
+ while(*p->pos && p->pos < p->eop)
+ gl16(p);
+ /*
+ * some versions of windows terminate a rune string
+ * with a single nul so we do a dangerous hack...
+ */
+ if(p->pos[1])
+ g8(p);
+ else
+ gl16(p);
+ } else {
+ while(*p->pos && n-- && p->pos < p->eop)
+ *str++ = *p->pos++;
+ *str = 0;
+ while(*p->pos++ && p->pos < p->eop)
+ continue;
+ }
+}
+
+void
+gascii(Pkt *p, char *str, int n)
+{
+ if(!n || !str)
+ return;
+
+ while(*p->pos && n-- && p->pos < p->eop)
+ *str++ = *p->pos++;
+ *str = 0;
+ while(*p->pos++ && p->pos < p->eop)
+ continue;
+}
+
+
+uvlong
+gl64(Pkt *p)
+{
+ uvlong n;
+
+ if(p->pos + 8 > p->eop)
+ return 0;
+
+ n = (uvlong)*p->pos++;
+ n |= (uvlong)*p->pos++ << 8;
+ n |= (uvlong)*p->pos++ << 16;
+ n |= (uvlong)*p->pos++ << 24;
+ n |= (uvlong)*p->pos++ << 32;
+ n |= (uvlong)*p->pos++ << 40;
+ n |= (uvlong)*p->pos++ << 48;
+ n |= (uvlong)*p->pos++ << 56;
+ return n;
+}
+
+uvlong
+gb48(Pkt *p)
+{
+ uvlong n;
+
+ if(p->pos + 6 > p->eop)
+ return 0;
+
+ n = (uvlong)*p->pos++ << 40;
+ n |= (uvlong)*p->pos++ << 24;
+ n |= (uvlong)*p->pos++ << 32;
+ n |= (uvlong)*p->pos++ << 16;
+ n |= (uvlong)*p->pos++ << 8;
+ n |= (uvlong)*p->pos++;
+ return n;
+}
+
+uint
+gb32(Pkt *p)
+{
+ uint n;
+
+ if(p->pos + 4 > p->eop)
+ return 0;
+
+ n = (uint)*p->pos++ << 24;
+ n |= (uint)*p->pos++ << 16;
+ n |= (uint)*p->pos++ << 8;
+ n |= (uint)*p->pos++;
+ return n;
+}
+
+uint
+gl32(Pkt *p)
+{
+ uint n;
+
+ if(p->pos + 4 > p->eop)
+ return 0;
+
+ n = (uint)*p->pos++;
+ n |= (uint)*p->pos++ << 8;
+ n |= (uint)*p->pos++ << 16;
+ n |= (uint)*p->pos++ << 24;
+ return n;
+}
+
+uint
+gb16(Pkt *p)
+{
+ uint n;
+
+ if(p->pos + 2 > p->eop)
+ return 0;
+ n = (uint)*p->pos++ << 8;
+ n |= (uint)*p->pos++;
+ return n;
+}
+
+uint
+gl16(Pkt *p)
+{
+ uint n;
+
+ if(p->pos + 2 > p->eop)
+ return 0;
+ n = (uint)*p->pos++;
+ n |= (uint)*p->pos++ << 8;
+ return n;
+}
+
+uint
+g8(Pkt *p)
+{
+ if(p->pos + 1 > p->eop)
+ return 0;
+ return (uint)*p->pos++;
+}
+
+long
+gdatetime(Pkt *p)
+{
+ Tm tm;
+ uint d, t;
+
+ if(p->pos + 4 > p->eop)
+ return 0;
+
+ /*
+ * bug in word swapping in Win95 requires this
+ */
+ if(p->s->caps & CAP_NT_SMBS){
+ d = gl16(p);
+ t = gl16(p);
+ }else{
+ t = gl16(p);
+ d = gl16(p);
+ }
+
+ tm.year = 80 + (d >> 9);
+ tm.mon = ((d >> 5) & 017) - 1;
+ tm.mday = d & 037;
+ tm.zone[0] = 0;
+ tm.tzoff = p->s->tz;
+
+ tm.hour = t >> 11;
+ tm.min = (t >> 5) & 63;
+ tm.sec = (t & 31) << 1;
+
+ return tm2sec(&tm);
+}
+
+long
+gvtime(Pkt *p)
+{
+ uvlong vl;
+
+ if(p->pos + 8 > p->eop)
+ return 0;
+
+ vl = (uvlong)gl32(p);
+ vl |= (uvlong)gl32(p) << 32;
+
+ vl /= 10000000LL;
+ vl -= 11644473600LL;
+ return vl;
+}
+
+void
+gconv(Pkt *p, int conv, char *str, int n)
+{
+ int off;
+ uchar *pos;
+
+ off = gl32(p) & 0xffff;
+ if(off == 0 || p->tdata - conv + off > p->eop){
+ memset(str, 0, n);
+ return;
+ }
+
+ pos = p->pos;
+ p->pos = p->tdata - conv + off;
+ gascii(p, str, n);
+ p->pos = pos;
+}
+
+void
+goff(Pkt *p, uchar *base, char *str, int n)
+{
+ int off;
+ uchar *pos;
+
+ off = gl16(p);
+ if(off == 0 || base + off > p->eop){
+ memset(str, 0, n);
+ return;
+ }
+ pos = p->pos;
+ p->pos = base + off;
+ gstr(p, str, n);
+ p->pos = pos;
+}
diff --git a/sys/src/cmd/cifs/ping.c b/sys/src/cmd/cifs/ping.c
new file mode 100755
index 000000000..0aaaaa7e8
--- /dev/null
+++ b/sys/src/cmd/cifs/ping.c
@@ -0,0 +1,132 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <libsec.h>
+#include <9p.h>
+
+extern char *Debug;
+
+typedef struct Pingcache Pingcache;
+struct Pingcache {
+ Pingcache*next;
+ long rtt;
+ char *host;
+ long expire;
+};
+
+typedef struct {
+ uchar vihl; /* Version and header length */
+ uchar tos; /* Type of service */
+ uchar length[2]; /* packet length */
+ uchar id[2]; /* Identification */
+ uchar frag[2]; /* Fragment information */
+ uchar ttl; /* Time to live */
+ uchar proto; /* Protocol */
+ uchar ipcksum[2]; /* Header checksum */
+ uchar src[4]; /* Ip source */
+ uchar dst[4]; /* Ip destination */
+ uchar type;
+ uchar code;
+ uchar cksum[2];
+ uchar icmpid[2];
+ uchar seq[2];
+ uchar data[1];
+} Icmp;
+
+enum { /* Packet Types */
+ EchoReply = 0,
+ Unreachable = 3,
+ SrcQuench = 4,
+ EchoRequest = 8,
+ TimeExceed = 11,
+ Timestamp = 13,
+ TimestampReply = 14,
+ InfoRequest = 15,
+ InfoReply = 16,
+
+ ICMP_IPSIZE = 20,
+ ICMP_HDRSIZE = 8,
+
+ Npings = 8,
+ Payload = 32,
+
+ Cachetime = 60,
+};
+
+static Pingcache *Cache;
+
+/*
+ * We ignore the first result as that is probably bigger
+ * than expected due to IP sorting out the routing to the host
+ */
+int
+ping(char *host, int timeout)
+{
+ int rtt, fd, i, seq;
+ long now;
+ vlong then;
+ uchar buf[128];
+ Icmp *ip;
+ Pingcache *c;
+
+ now = time(nil);
+ for(c = Cache; c; c = c->next)
+ if(strcmp(c->host, host) == 0 && now < c->expire){
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print("\t\tping host=%s timeout=%d - cache hit\n",
+ host, timeout);
+ return c->rtt;
+ }
+
+ rtt = -1;
+ ip = (Icmp*)buf;
+
+ if((fd = dial(netmkaddr(host, "icmp", "1"), 0, 0, 0)) == -1)
+ goto fail;
+
+ for(seq = 0; seq < Npings; seq++){
+ then = nsec();
+ for(i = Payload; i < sizeof buf; i++)
+ buf[i] = i + seq;
+ ip->type = EchoRequest;
+ ip->code = 0;
+ ip->seq[0] = seq;
+ ip->seq[1] = seq;
+ alarm(timeout);
+ if(write(fd, ip, sizeof buf) != sizeof buf ||
+ read(fd, ip, sizeof buf) != sizeof buf)
+ goto fail;
+ alarm(0);
+ if(ip->type != EchoReply || ip->code != 0 ||
+ ip->seq[0] != seq || ip->seq[1] != seq)
+ goto fail;
+ for(i = Payload; i < sizeof buf; i++)
+ if((uchar)buf[i] != (uchar)(i + seq))
+ goto fail;
+ rtt = (rtt + nsec() - then) / 2;
+ }
+fail:
+ if(fd != -1)
+ close(fd);
+
+ if(Debug && strstr(Debug, "dfs") != nil)
+ print("\t\tping host=%s timeout=%d rtt=%d - failed\n",
+ host, timeout, rtt);
+
+ /*
+ * failures get cached too
+ */
+ for(c = Cache; c; c = c->next)
+ if(strcmp(c->host, host) == 0)
+ break;
+ if(c == nil){
+ c = emalloc9p(sizeof(Pingcache));
+ c->host = estrdup9p(host);
+ c->next = Cache;
+ Cache = c;
+ }
+ c->rtt = rtt;
+ c->expire = now+Cachetime;
+ return rtt;
+}
diff --git a/sys/src/cmd/cifs/raperrstr.c b/sys/src/cmd/cifs/raperrstr.c
new file mode 100755
index 000000000..ed944f0aa
--- /dev/null
+++ b/sys/src/cmd/cifs/raperrstr.c
@@ -0,0 +1,346 @@
+#include <u.h>
+#include <libc.h>
+
+static struct {
+ int err;
+ char *msg;
+} RAPerrs[] = {
+ { 0, "ok" },
+ { 5, "permission denied" },
+ { 50, "request not supported" },
+ { 65, "access denied" },
+ { 86, "password invalid" },
+ { 128, "not listening on called name" },
+ { 129, "not listening for calling name" },
+ { 130, "called name not present" },
+ { 131, "called name present, but insufficient resources" },
+ { 234, "more data" },
+
+ { 2102, "workstation driver is not installed" },
+ { 2103, "server could not be located" },
+ { 2104, "network cannot access a shared memory segment" },
+ { 2105, "A network resource shortage occurred" },
+ { 2106, "This operation is not supported on workstations" },
+ { 2107, "device is not connected" },
+ { 2114, "Server service is not started" },
+ { 2115, "queue is empty" },
+ { 2116, "device or directory does not exist" },
+ { 2117, "operation is invalid on a redirected resource" },
+ { 2118, "name has already been shared" },
+ { 2119, "server is currently out of the requested resource" },
+ { 2121, "Requested addition of items exceeds the maximum allowed" },
+ { 2122, "Peer service supports only two simultaneous users" },
+ { 2123, "API return buffer is too small" },
+ { 2127, "remote API error occurred" },
+ { 2131, "cannot open or read the configuration file" },
+ { 2136, "general network error occurred" },
+ { 2137, "Workstation service is corrupted" },
+ { 2138, "Workstation service has not been started" },
+ { 2139, "requested information is not available" },
+ { 2140, "internal error" },
+ { 2141, "server is not configured for transactions" },
+ { 2142, "requested API is not supported on the remote server" },
+ { 2143, "event name is invalid" },
+ { 2144, "computer name already exists on the network" },
+ { 2146, "specified component could not be found in the config info" },
+ { 2147, "specified parameter could not be found in the config info" },
+ { 2149, "A line in the configuration file is too long" },
+ { 2150, "printer does not exist" },
+ { 2151, "print job does not exist" },
+ { 2152, "printer destination cannot be found" },
+ { 2153, "printer destination already exists" },
+ { 2154, "printer queue already exists" },
+ { 2155, "no more printers can be added" },
+ { 2156, "no more print jobs can be added" },
+ { 2157, "no more printer destinations can be added" },
+ { 2158, "printer idle and cannot accept control operations" },
+ { 2159, "invalid control function in printer request" },
+ { 2160, "print processor not responding" },
+ { 2161, "spooler not running" },
+ { 2162, "operation cannot be performed on the print destination in its current state" },
+ { 2163, "operation cannot be performed on the printer queue in its current state" },
+ { 2164, "operation cannot be performed on the print job in its current state" },
+ { 2165, "spooler - no memory" },
+ { 2166, "device driver does not exist" },
+ { 2167, "data type not supported by the print processor" },
+ { 2168, "print processor is not installed" },
+ { 2180, "service database is locked" },
+ { 2181, "service table full" },
+ { 2182, "requested service already started" },
+ { 2183, "service does not responding" },
+ { 2184, "service not started" },
+ { 2185, "service name invalid" },
+ { 2186, "service is not responding to the control function" },
+ { 2187, "service control is busy" },
+ { 2188, "configuration file contains an invalid service program name" },
+ { 2189, "service could not be controlled in its present state" },
+ { 2190, "service ended abnormally" },
+ { 2191, "requested pause or stop is not valid for this service" },
+ { 2192, "could not find the service name in the dispatch table" },
+ { 2193, "service control dispatcher pipe read failed" },
+ { 2194, "thread create for the new service could not be created" },
+ { 2200, "This workstation is already logged on to the local-area network" },
+ { 2201, "workstation is not logged on to the local-area network" },
+ { 2202, "user name or group name parameter is invalid" },
+ { 2203, "password parameter is invalid" },
+ { 2204, "logon processor did not add the message alias" },
+ { 2205, "logon processor did not add the message alias" },
+ { 2206, "logoff processor did not delete the message alias" },
+ { 2207, "logoff processor did not delete the message alias" },
+ { 2209, "Network logons are paused" },
+ { 2210, "a centralized logon-server conflict occurred" },
+ { 2211, "server is configured without a valid user path" },
+ { 2212, "cannot run logon script" },
+ { 2214, "logon server was not specified. computer will be logged on as STANDALONE" },
+ { 2215, "logon server could not be found" },
+ { 2216, "already a logon domain for this computer" },
+ { 2217, "logon server could not validate the logon" },
+ { 2219, "security database could not be found" },
+ { 2220, "group name could not be found" },
+ { 2221, "user name could not be found" },
+ { 2222, "resource name could not be found" },
+ { 2223, "group already exists" },
+ { 2224, "user account already exists" },
+ { 2225, "resource permission list already exists" },
+ { 2226, "This operation is only allowed on the PDC of the domain" },
+ { 2227, "security database has not been started" },
+ { 2228, "There are too many names in the user accounts database" },
+ { 2229, "disk I/O failure occurred" },
+ { 2230, "limit of 64 entries per resource was exceeded" },
+ { 2231, "Deleting a user with a session is not allowed" },
+ { 2232, "parent directory could not be located" },
+ { 2233, "Unable to add to the security database session cache segment" },
+ { 2234, "This operation is not allowed on this special group" },
+ { 2235, "This user is not cached in user accounts database session cache" },
+ { 2236, "user already belongs to this group" },
+ { 2237, "user does not belong to this group" },
+ { 2238, "This user account is undefined" },
+ { 2239, "This user account has expired" },
+ { 2240, "user is not allowed to log on from this workstation" },
+ { 2241, "user is not allowed to log on at this time" },
+ { 2242, "password of this user has expired" },
+ { 2243, "password of this user cannot change" },
+ { 2244, "This password cannot be used now" },
+ { 2245, "password does not meet the password policy requirements" },
+ { 2246, "password of this user is too recent to change" },
+ { 2247, "security database is corrupted" },
+ { 2248, "No updates are necessary to this replicant network/local security database" },
+ { 2249, "This replicant database is outdated; synchronization is required" },
+ { 2250, "network connection could not be found" },
+ { 2251, "This asg_type is invalid" },
+ { 2252, "This device is currently being shared" },
+ { 2270, "computer name cannot be added - name may already exist" },
+ { 2271, "Messenger service is already started" },
+ { 2272, "Messenger service failed to start" },
+ { 2273, "message alias could not be found on the network" },
+ { 2274, "This message alias has already been forwarded" },
+ { 2275, "This message alias has been added but is still forwarded" },
+ { 2276, "This message alias already exists locally" },
+ { 2277, "maximum number of added message aliases has been exceeded" },
+ { 2278, "computer name could not be deleted" },
+ { 2279, "Messages cannot be forwarded back to the same workstation" },
+ { 2280, "An error occurred in the domain message processor" },
+ { 2281, "message was sent, but the recipient has paused the Messenger service" },
+ { 2282, "message was sent but not received" },
+ { 2283, "message alias is currently in use. Try again later" },
+ { 2284, "Messenger service has not been started" },
+ { 2285, "name is not on the local computer" },
+ { 2286, "forwarded message alias could not be found on the network" },
+ { 2287, "message alias table on the remote station is full" },
+ { 2288, "Messages for this alias are not currently being forwarded" },
+ { 2289, "broadcast message was truncated" },
+ { 2294, "This is an invalid device name" },
+ { 2295, "A write fault occurred" },
+ { 2297, "A duplicate message alias exists on the network" },
+ { 2298, "This message alias will be deleted later" },
+ { 2299, "message alias was not successfully deleted from all networks" },
+ { 2300, "This operation is not supported on computers with multiple networks" },
+ { 2310, "This shared resource does not exist" },
+ { 2311, "This device is not shared" },
+ { 2312, "A session does not exist with that computer name" },
+ { 2314, "There is not an open file with that identification number" },
+ { 2315, "A failure occurred when executing a remote administration command" },
+ { 2316, "A failure occurred when opening a remote temporary file" },
+ { 2317, "Data returned from a RAP command has been truncated to 64K" },
+ { 2318, "This device cannot be shared as both a spooled and a non-spooled resource" },
+ { 2319, "information in the list of servers may be incorrect" },
+ { 2320, "computer is not active in this domain" },
+ { 2321, "share must be removed from DFS before it can be deleted" },
+ { 2331, "operation is invalid for this device" },
+ { 2332, "This device cannot be shared" },
+ { 2333, "This device was not open" },
+ { 2334, "This device name list is invalid" },
+ { 2335, "queue priority is invalid" },
+ { 2337, "There are no shared communication devices" },
+ { 2338, "queue you specified does not exist" },
+ { 2340, "This list of devices is invalid" },
+ { 2341, "requested device is invalid" },
+ { 2342, "This device is already in use by the spooler" },
+ { 2343, "This device is already in use as a communication device" },
+ { 2351, "This computer name is invalid" },
+ { 2354, "string and prefix specified are too long" },
+ { 2356, "This path component is invalid" },
+ { 2357, "Could not determine the type of input" },
+ { 2362, "buffer for types is not big enough" },
+ { 2370, "Profile files cannot exceed 64K" },
+ { 2371, "start offset is out of range" },
+ { 2372, "system cannot delete current connections to network resources" },
+ { 2373, "system was unable to parse the command line in this file" },
+ { 2374, "An error occurred while loading the profile file" },
+ { 2375, "Errors occurred while saving the profile file" },
+ { 2377, "Log file %1 is full" },
+ { 2378, "This log file has changed between reads" },
+ { 2379, "Log file %1 is corrupt" },
+ { 2380, "source path cannot be a directory" },
+ { 2381, "source path is illegal" },
+ { 2382, "destination path is illegal" },
+ { 2383, "source and destination paths are on different servers" },
+ { 2385, "Run server you requested is paused" },
+ { 2389, "An error occurred when communicating with a Run server" },
+ { 2391, "An error occurred when starting a background process" },
+ { 2392, "shared resource you are connected to could not be found" },
+ { 2400, "LAN adapter number is invalid" },
+ { 2401, "There are open files on the connection" },
+ { 2402, "Active connections still exist" },
+ { 2403, "This share name or password is invalid" },
+ { 2404, "device is being accessed by an active process" },
+ { 2405, "drive letter is in use locally" },
+ { 2430, "specified client is already registered for the specified event" },
+ { 2431, "alert table is full" },
+ { 2432, "An invalid or nonexistent alert name was raised" },
+ { 2433, "alert recipient is invalid" },
+ { 2434, "A user's session with this server has been deleted" },
+ { 2440, "log file does not contain the requested record number" },
+ { 2450, "user accounts database is not configured correctly" },
+ { 2451, "This operation is not permitted when the Netlogon service is running" },
+ { 2452, "This operation is not allowed on the last administrative account" },
+ { 2453, "Could not find DC for this domain" },
+ { 2454, "Could not set logon information for this user" },
+ { 2455, "Netlogon service has not been started" },
+ { 2456, "Unable to add to the user accounts database" },
+ { 2457, "This server's clock is not synchronized with the PDC's clock" },
+ { 2458, "A password mismatch has been detected" },
+ { 2460, "server identification does not specify a valid server" },
+ { 2461, "session identification does not specify a valid session" },
+ { 2462, "connection identification does not specify a valid connection" },
+ { 2463, "There is no space for another entry in the table of available servers" },
+ { 2464, "server has reached the maximum number of sessions it supports" },
+ { 2465, "server has reached the maximum number of connections it supports" },
+ { 2466, "server cannot open more files because it has reached its maximum number" },
+ { 2467, "There are no alternate servers registered on this server" },
+ { 2470, "Try down-level (remote admin protocol) version of API instead" },
+ { 2480, "UPS driver could not be accessed by the UPS service" },
+ { 2481, "UPS service is not configured correctly" },
+ { 2482, "UPS service could not access the specified Comm Port" },
+ { 2483, "UPS indicated a line fail or low battery situation. Service not started" },
+ { 2484, "UPS service failed to perform a system shut down" },
+ { 2500, "program below returned an MS-DOS error code" },
+ { 2501, "program below needs more memory" },
+ { 2502, "program below called an unsupported MS-DOS function" },
+ { 2503, "workstation failed to boot" },
+ { 2504, "file below is corrupt" },
+ { 2505, "No loader is specified in the boot-block definition file" },
+ { 2506, "NetBIOS returned an error: The NCB and SMB are dumped above" },
+ { 2507, "A disk I/O error occurred" },
+ { 2508, "Image parameter substitution failed" },
+ { 2509, "Too many image parameters cross disk sector boundaries" },
+ { 2510, "image was not generated from an MS-DOS diskette formatted with /S" },
+ { 2511, "Remote boot will be restarted later" },
+ { 2512, "call to the Remoteboot server failed" },
+ { 2513, "Cannot connect to the Remoteboot server" },
+ { 2514, "Cannot open image file on the Remoteboot server" },
+ { 2515, "Connecting to the Remoteboot server..." },
+ { 2516, "Connecting to the Remoteboot server..." },
+ { 2517, "Remote boot service was stopped" },
+ { 2518, "Remote boot startup failed; check the error log" },
+ { 2519, "A second connection to a Remoteboot resource is not allowed" },
+ { 2550, "browser service was configured with MaintainServerList=No" },
+ { 2610, "Service failed to startas no network adapters started with this service" },
+ { 2611, "Service failed to start due to bad startup information in the registry" },
+ { 2612, "Service failed to start because its database is absent or corrupt" },
+ { 2613, "Service failed to start because RPLFILES share is absent" },
+ { 2614, "Service failed to start because RPLUSER group is absent" },
+ { 2615, "Cannot enumerate service records" },
+ { 2616, "Workstation record information has been corrupted" },
+ { 2617, "Workstation record was not found" },
+ { 2618, "Workstation name is in use by some other workstation" },
+ { 2619, "Profile record information has been corrupted" },
+ { 2620, "Profile record was not found" },
+ { 2621, "Profile name is in use by some other profile" },
+ { 2622, "There are workstations using this profile" },
+ { 2623, "Configuration record information has been corrupted" },
+ { 2624, "Configuration record was not found" },
+ { 2625, "Adapter ID record information has been corrupted" },
+ { 2626, "An internal service error has occurred" },
+ { 2627, "Vendor ID record information has been corrupted" },
+ { 2628, "Boot block record information has been corrupted" },
+ { 2629, "user account for this workstation record is missing" },
+ { 2630, "RPLUSER local group could not be found" },
+ { 2631, "Boot block record was not found" },
+ { 2632, "Chosen profile is incompatible with this workstation" },
+ { 2633, "Chosen network adapter ID is in use by some other workstation" },
+ { 2634, "There are profiles using this configuration" },
+ { 2635, "There are workstations, profiles, or configurations using this boot block" },
+ { 2636, "Service failed to backup Remoteboot database" },
+ { 2637, "Adapter record was not found" },
+ { 2638, "Vendor record was not found" },
+ { 2639, "Vendor name is in use by some other vendor record" },
+ { 2640, "(boot name, vendor ID) is in use by some other boot block record" },
+ { 2641, "Configuration name is in use by some other configuration" },
+ { 2660, "internal database maintained by the Dfs service is corrupt" },
+ { 2661, "One of the records in the internal Dfs database is corrupt" },
+ { 2662, "There is no DFS name whose entry path matches the input Entry Path" },
+ { 2663, "A root or link with the given name already exists" },
+ { 2664, "server share specified is already shared in the Dfs" },
+ { 2665, "indicated server share does not support the indicated DFS namespace" },
+ { 2666, "operation is not valid on this portion of the namespace" },
+ { 2667, "operation is not valid on this portion of the namespace" },
+ { 2668, "operation is ambiguous because the link has multiple servers" },
+ { 2669, "Unable to create a link" },
+ { 2670, "server is not Dfs Aware" },
+ { 2671, "specified rename target path is invalid" },
+ { 2672, "specified DFS link is offline" },
+ { 2673, "specified server is not a server for this link" },
+ { 2674, "A cycle in the Dfs name was detected" },
+ { 2675, "operation is not supported on a server-based Dfs" },
+ { 2676, "This link is already supported by the specified server-share" },
+ { 2677, "Can't remove the last server-share supporting this root or link" },
+ { 2678, "operation is not supported for an Inter-DFS link" },
+ { 2679, "internal state of the Dfs Service has become inconsistent" },
+ { 2680, "Dfs Service has been installed on the specified server" },
+ { 2681, "Dfs data being reconciled is identical" },
+ { 2682, "DFS root cannot be deleted. Uninstall DFS if required" },
+ { 2683, "A child or parent directory of the share is already in a Dfs" },
+ { 2690, "Dfs internal error" },
+ { 2691, "This machine is already joined to a domain" },
+ { 2692, "This machine is not currently joined to a domain" },
+ { 2693, "This machine is a DC and cannot be unjoined from a domain" },
+ { 2694, "destination DC does not support creating machine accounts in OUs" },
+ { 2695, "specified workgroup name is invalid" },
+ { 2696, "specified computer name is incompatible with the default language used on the DC" },
+ { 2697, "specified computer account could not be found" },
+ { 2698, "This version of Windows cannot be joined to a domain" },
+ { 2701, "password must change at the next logon" },
+ { 2702, "account is locked out" },
+ { 2703, "password is too long" },
+ { 2704, "password does not meet the complexity policy" },
+ { 2705, "password does not meet the requirements of the password filter DLLs" },
+};
+
+char *
+raperrstr(uint err)
+{
+ int i, match;
+ static char buf[0xff];
+
+ match = -1;
+ for(i = 0; i < nelem(RAPerrs); i++)
+ if(RAPerrs[i].err == err)
+ match = i;
+ if(match != -1)
+ snprint(buf, sizeof buf, "rap: %s", RAPerrs[match].msg);
+ else
+ snprint(buf, sizeof buf, "rap: %ud/0x%ux - unknown error",
+ err, err);
+ return buf;
+}
diff --git a/sys/src/cmd/cifs/remsmb.h b/sys/src/cmd/cifs/remsmb.h
new file mode 100755
index 000000000..ee01f4b9c
--- /dev/null
+++ b/sys/src/cmd/cifs/remsmb.h
@@ -0,0 +1,466 @@
+/*++
+
+Copyright (c) 1991-1992 Microsoft Corporation
+
+Module Name:
+
+ RemSmb.h
+
+Abstract:
+
+ Definition of descriptor strings for Net API remote calls.
+ Names defined in this file follow the format:
+
+ RemSmb_RemDescriptor
+
+ RemDescriptor follows one of the following formats:
+
+ StructureName_level - info structures
+ StructureName_level_suffix - special info structures
+ ApiName_P - parameter descriptors
+
+Notes:
+
+ 1. While the above formats should be followed, the equate names
+ cannot exceed 32 characters, and abbreviated forms should be used.
+
+ 2. The remote API mechanism requires that the return parameter length
+ is less than or equal to the send parameter length. This assumption
+ is made in order to reduce the overhead in the buffer management
+ required for the API call. This restriction is not unreasonable
+ as the APIs were designed to return data in the data buffer and just
+ use return parameters for data lengths & file handles etc.
+ HOWEVER, if it has been spec'ed to return a large parameter field, it
+ is possible to pad the size of the send parameter using a REM_FILL_BYTES
+ field to meet the above restriction.
+
+Author:
+
+Environment:
+
+ Portable to just about anything.
+ Requires ANSI C extensions: slash-slash comments, long external
+names.
+
+Revision History:
+
+--*/
+
+#ifndef _REMDEF_
+#define _REMDEF_
+/*
+ * ====================================================================
+ * SMB XACT message descriptors.
+ * ====================================================================
+ */
+
+#define REMSmb_share_info_0 "B13"
+#define REMSmb_share_info_1 "B13BWz"
+#define REMSmb_share_info_2 "B13BWzWWWzB9B"
+
+#define REMSmb_share_info_90 "B13BWz"
+#define REMSmb_share_info_92 "zzz"
+#define REMSmb_share_info_93 "zzz"
+
+#define REMSmb_share_info_0_setinfo "B13"
+#define REMSmb_share_info_1_setinfo "B13BWz"
+#define REMSmb_share_info_2_setinfo "B13BWzWWOB9B"
+
+#define REMSmb_share_info_90_setinfo "B13BWz"
+#define REMSmb_share_info_91_setinfo "B13BWzWWWOB9BB9BWzWWzWW"
+
+#define REMSmb_NetShareEnum_P "WrLeh"
+#define REMSmb_NetShareGetInfo_P "zWrLh"
+#define REMSmb_NetShareSetInfo_P "zWsTP"
+#define REMSmb_NetShareAdd_P "WsT"
+#define REMSmb_NetShareDel_P "zW"
+#define REMSmb_NetShareCheck_P "zh"
+
+#define REMSmb_session_info_0 "z"
+#define REMSmb_session_info_1 "zzWWWDDD"
+#define REMSmb_session_info_2 "zzWWWDDDz"
+#define REMSmb_session_info_10 "zzDD"
+
+#define REMSmb_NetSessionEnum_P "WrLeh"
+#define REMSmb_NetSessionGetInfo_P "zWrLh"
+#define REMSmb_NetSessionDel_P "zW"
+
+#define REMSmb_connection_info_0 "W"
+#define REMSmb_connection_info_1 "WWWWDzz"
+
+#define REMSmb_NetConnectionEnum_P "zWrLeh"
+
+#define REMSmb_file_info_0 "W"
+#define REMSmb_file_info_1 "WWWzz"
+#define REMSmb_file_info_2 "D"
+#define REMSmb_file_info_3 "DWWzz"
+
+#define REMSmb_NetFileEnum_P "zWrLeh"
+#define REMSmb_NetFileEnum2_P "zzWrLehb8g8"
+#define REMSmb_NetFileGetInfo_P "WWrLh"
+#define REMSmb_NetFileGetInfo2_P "DWrLh"
+#define REMSmb_NetFileClose_P "W"
+#define REMSmb_NetFileClose2_P "D"
+
+#define REMSmb_server_info_0 "B16"
+#define REMSmb_server_info_1 "B16BBDz"
+#define REMSmb_server_info_2 "B16BBDzDDDWWzWWWWWWWB21BzWWWWWWWWWWWWWWWWWWWWWWz"
+#define REMSmb_server_info_3 "B16BBDzDDDWWzWWWWWWWB21BzWWWWWWWWWWWWWWWWWWWWWWzDWz"
+
+#define REMSmb_server_info_1_setinfo "B16BBDz"
+#define REMSmb_server_info_2_setinfo "B16BBDzDDDWWzWWWWWWWB21BOWWWWWWWWWWWWWWWWWWWWWWz"
+
+#define REMSmb_server_admin_command "B"
+
+#define REMSmb_server_diskenum_0 "B3"
+
+#define REMSmb_authenticator_info_0 "B8D"
+
+#define REMSmb_server_diskft_100 "B"
+#define REMSmb_server_diskft_101 "BBWWWWDW"
+#define REMSmb_server_diskft_102 "BBWWWWDN"
+#define REMSmb_server_diskfterr_0 "DWWDDW"
+#define REMSmb_ft_info_0 "WWW"
+#define REMSmb_ft_drivestats_0 "BBWDDDDDDD"
+#define REMSmb_ft_error_info_1 "DWWDDWBBDD"
+
+#define REMSmb_I_NetServerDiskEnum_P "WrLeh"
+#define REMSmb_I_NetServerDiskGetInfo_P "WWrLh"
+#define REMSmb_I_FTVerifyMirror_P "Wz"
+#define REMSmb_I_FTAbortVerify_P "W"
+#define REMSmb_I_FTGetInfo_P "WrLh"
+#define REMSmb_I_FTSetInfo_P "WsTP"
+#define REMSmb_I_FTLockDisk_P "WWh"
+#define REMSmb_I_FTFixError_P "Dzhh2"
+#define REMSmb_I_FTAbortFix_P "D"
+#define REMSmb_I_FTDiagnoseError_P "Dhhhh"
+#define REMSmb_I_FTGetDriveStats_P "WWrLh"
+#define REMSmb_I_FTErrorGetInfo_P "DWrLh"
+
+#define REMSmb_NetServerEnum_P "WrLeh"
+#define REMSmb_I_NetServerEnum_P "WrLeh"
+#define REMSmb_NetServerEnum2_P "WrLehDz"
+#define REMSmb_I_NetServerEnum2_P "WrLehDz"
+#define REMSmb_NetServerEnum3_P "WrLehDzz"
+#define REMSmb_NetServerGetInfo_P "WrLh"
+#define REMSmb_NetServerSetInfo_P "WsTP"
+#define REMSmb_NetServerDiskEnum_P "WrLeh"
+#define REMSmb_NetServerAdminCommand_P "zhrLeh"
+#define REMSmb_NetServerReqChalleng_P "zb8g8"
+#define REMSmb_NetServerAuthenticat_P "zb8g8"
+#define REMSmb_NetServerPasswordSet_P "zb12g12b16"
+
+#define REMSmb_NetAuditOpen_P "h"
+#define REMSmb_NetAuditClear_P "zz"
+#define REMSmb_NetAuditRead_P "zb16g16DhDDrLeh"
+
+#define REMSmb_AuditLogReturnBuf "K"
+
+#define REMSmb_NetErrorLogOpen_P "h"
+#define REMSmb_NetErrorLogClear_P "zz"
+#define REMSmb_NetErrorLogRead_P "zb16g16DhDDrLeh"
+
+#define REMSmb_ErrorLogReturnBuf "K"
+
+#define REMSmb_chardev_info_0 "B9"
+#define REMSmb_chardev_info_1 "B10WB22D"
+#define REMSmb_chardevQ_info_0 "B13"
+#define REMSmb_chardevQ_info_1 "B14WzWW"
+
+#define REMSmb_NetCharDevEnum_P "WrLeh"
+#define REMSmb_NetCharDevGetInfo_P "zWrLh"
+#define REMSmb_NetCharDevControl_P "zW"
+#define REMSmb_NetCharDevQEnum_P "zWrLeh"
+#define REMSmb_NetCharDevQGetInfo_P "zzWrLh"
+#define REMSmb_NetCharDevQSetInfo_P "zWsTP"
+#define REMSmb_NetCharDevQPurge_P "z"
+#define REMSmb_NetCharDevQPurgeSelf_P "zz"
+
+#define REMSmb_msg_info_0 "B16"
+#define REMSmb_msg_info_1 "B16BBB16"
+#define REMSmb_send_struct "K"
+
+#define REMSmb_NetMessageNameEnum_P "WrLeh"
+#define REMSmb_NetMessageNameGetInfo_P "zWrLh"
+#define REMSmb_NetMessageNameAdd_P "zW"
+#define REMSmb_NetMessageNameDel_P "zW"
+#define REMSmb_NetMessageNameFwd_P "zzW"
+#define REMSmb_NetMessageNameUnFwd_P "z"
+#define REMSmb_NetMessageBufferSend_P "zsT"
+#define REMSmb_NetMessageFileSend_P "zz"
+#define REMSmb_NetMessageLogFileSet_P "zW"
+#define REMSmb_NetMessageLogFileGet_P "rLh"
+
+#define REMSmb_service_info_0 "B16"
+#define REMSmb_service_info_1 "B16WDW"
+#define REMSmb_service_info_2 "B16WDWB64"
+#define REMSmb_service_cmd_args "K"
+
+#define REMSmb_NetServiceEnum_P "WrLeh"
+#define REMSmb_NetServiceControl_P "zWWrL"
+#define REMSmb_NetServiceInstall_P "zF88sg88T" /* See NOTE 2 */
+#define REMSmb_NetServiceGetInfo_P "zWrLh"
+
+#define REMSmb_access_info_0 "z"
+#define REMSmb_access_info_0_setinfo "z"
+#define REMSmb_access_info_1 "zWN"
+#define REMSmb_access_info_1_setinfo "OWN"
+#define REMSmb_access_list "B21BW"
+
+#define REMSmb_NetAccessEnum_P "zWWrLeh"
+#define REMSmb_NetAccessGetInfo_P "zWrLh"
+#define REMSmb_NetAccessSetInfo_P "zWsTP"
+#define REMSmb_NetAccessAdd_P "WsT"
+#define REMSmb_NetAccessDel_P "z"
+#define REMSmb_NetAccessGetUserPerms_P "zzh"
+
+#define REMSmb_group_info_0 "B21"
+#define REMSmb_group_info_1 "B21Bz"
+#define REMSmb_group_users_info_0 "B21"
+#define REMSmb_group_users_info_1 "B21BN"
+
+#define REMSmb_NetGroupEnum_P "WrLeh"
+#define REMSmb_NetGroupAdd_P "WsT"
+#define REMSmb_NetGroupDel_P "z"
+#define REMSmb_NetGroupAddUser_P "zz"
+#define REMSmb_NetGroupDelUser_P "zz"
+#define REMSmb_NetGroupGetUsers_P "zWrLeh"
+#define REMSmb_NetGroupSetUsers_P "zWsTW"
+#define REMSmb_NetGroupGetInfo_P "zWrLh"
+#define REMSmb_NetGroupSetInfo_P "zWsTP"
+
+#define REMSmb_user_info_0 "B21"
+#define REMSmb_user_info_1 "B21BB16DWzzWz"
+#define REMSmb_user_info_2 "B21BB16DWzzWzDzzzzDDDDWb21WWzWW"
+#define REMSmb_user_info_10 "B21Bzzz"
+#define REMSmb_user_info_11 "B21BzzzWDDzzDDWWzWzDWb21W"
+
+#define REMSmb_user_info_100 "DWW"
+#define REMSmb_user_info_101 "B60"
+#define REMSmb_user_modals_info_0 "WDDDWW"
+#define REMSmb_user_modals_info_1 "Wz"
+#define REMSmb_user_modals_info_100 "B50"
+#define REMSmb_user_modals_info_101 "zDDzDD"
+#define REMSmb_user_logon_info_0 "B21B"
+#define REMSmb_user_logon_info_1 "WB21BWDWWDDDDDDDzzzD"
+#define REMSmb_user_logon_info_2 "B21BzzzD"
+#define REMSmb_user_logoff_info_1 "WDW"
+
+#define REMSmb_NetUserEnum_P "WrLeh"
+#define REMSmb_NetUserAdd_P "WsTW"
+#define REMSmb_NetUserAdd2_P "WsTWW"
+#define REMSmb_NetUserDel_P "z"
+#define REMSmb_NetUserGetInfo_P "zWrLh"
+#define REMSmb_NetUserSetInfo_P "zWsTPW"
+#define REMSmb_NetUserSetInfo2_P "zWsTPWW"
+#define REMSmb_NetUserPasswordSet_P "zb16b16W"
+#define REMSmb_NetUserPasswordSet2_P "zb16b16WW"
+#define REMSmb_NetUserGetGroups_P "zWrLeh"
+#define REMSmb_NetUserSetGroups_P "zWsTW"
+#define REMSmb_NetUserModalsGet_P "WrLh"
+#define REMSmb_NetUserModalsSet_P "WsTP"
+#define REMSmb_NetUserEnum2_P "WrLDieh"
+#define REMSmb_NetUserValidate2_P "Wb62WWrLhWW"
+
+#define REMSmb_wksta_info_0 "WDzzzzBBDWDWWWWWWWWWWWWWWWWWWWzzW"
+#define REMSmb_wksta_info_0_setinfo "WDOOOOBBDWDWWWWWWWWWWWWWWWWWWWzzW"
+#define REMSmb_wksta_info_1 "WDzzzzBBDWDWWWWWWWWWWWWWWWWWWWzzWzzW"
+#define REMSmb_wksta_info_1_setinfo "WDOOOOBBDWDWWWWWWWWWWWWWWWWWWWzzWzzW"
+#define REMSmb_wksta_info_10 "zzzBBzz"
+#define REMSmb_wksta_annc_info "K"
+
+#define REMSmb_NetWkstaLogon_P "zzirL"
+#define REMSmb_NetWkstaLogoff_P "zD"
+#define REMSmb_NetWkstaSetUID_P "zzzW"
+#define REMSmb_NetWkstaGetInfo_P "WrLh"
+#define REMSmb_NetWkstaSetInfo_P "WsTP"
+#define REMSmb_NetWkstaUserLogon_P "zzWb54WrLh"
+#define REMSmb_NetWkstaUserLogoff_P "zzWb38WrLh"
+
+#define REMSmb_use_info_0 "B9Bz"
+#define REMSmb_use_info_1 "B9BzzWWWW"
+
+#define REMSmb_use_info_2 "B9BzzWWWWWWWzB16"
+
+#define REMSmb_NetUseEnum_P "WrLeh"
+#define REMSmb_NetUseAdd_P "WsT"
+#define REMSmb_NetUseDel_P "zW"
+#define REMSmb_NetUseGetInfo_P "zWrLh"
+
+#define REMSmb_printQ_0 "B13"
+#define REMSmb_printQ_1 "B13BWWWzzzzzWW"
+#define REMSmb_printQ_2 "B13BWWWzzzzzWN"
+#define REMSmb_printQ_3 "zWWWWzzzzWWzzl"
+#define REMSmb_printQ_4 "zWWWWzzzzWNzzl"
+#define REMSmb_printQ_5 "z"
+
+#define REMSmb_DosPrintQEnum_P "WrLeh"
+#define REMSmb_DosPrintQGetInfo_P "zWrLh"
+#define REMSmb_DosPrintQSetInfo_P "zWsTP"
+#define REMSmb_DosPrintQAdd_P "WsT"
+#define REMSmb_DosPrintQDel_P "z"
+#define REMSmb_DosPrintQPause_P "z"
+#define REMSmb_DosPrintQPurge_P "z"
+#define REMSmb_DosPrintQContinue_P "z"
+
+#define REMSmb_print_job_0 "W"
+#define REMSmb_print_job_1 "WB21BB16B10zWWzDDz"
+#define REMSmb_print_job_2 "WWzWWDDzz"
+#define REMSmb_print_job_3 "WWzWWDDzzzzzzzzzzlz"
+
+#define REMSmb_print_job_info_1_setinfo "WB21BB16B10zWWODDz"
+#define REMSmb_print_job_info_3_setinfo "WWzWWDDzzzzzOzzzzlO"
+
+#define REMSmb_DosPrintJobEnum_P "zWrLeh"
+#define REMSmb_DosPrintJobGetInfo_P "WWrLh"
+#define REMSmb_DosPrintJobSetInfo_P "WWsTP"
+#define REMSmb_DosPrintJobAdd_P "zsTF129g129h" /* See note 2 */
+#define REMSmb_DosPrintJobSchedule_P "W"
+#define REMSmb_DosPrintJobDel_P "W"
+#define REMSmb_DosPrintJobPause_P "W"
+#define REMSmb_DosPrintJobContinue_P "W"
+
+#define REMSmb_print_dest_0 "B9"
+#define REMSmb_print_dest_1 "B9B21WWzW"
+#define REMSmb_print_dest_2 "z"
+#define REMSmb_print_dest_3 "zzzWWzzzWW"
+#define REMSmb_print_dest_info_3_setinfo "zOzWWOzzWW"
+
+#define REMSmb_DosPrintDestEnum_P "WrLeh"
+#define REMSmb_DosPrintDestGetInfo_P "zWrLh"
+#define REMSmb_DosPrintDestControl_P "zW"
+#define REMSmb_DosPrintDestAdd_P "WsT"
+#define REMSmb_DosPrintDestSetInfo_P "zWsTP"
+#define REMSmb_DosPrintDestDel_P "z"
+
+#define REMSmb_NetProfileSave_P "zDW"
+#define REMSmb_NetProfileLoad_P "zDrLD"
+
+#define REMSmb_profile_load_info "WDzD"
+
+#define REMSmb_statistics_info "B"
+
+#define REMSmb_statistics2_info_W "B120"
+#define REMSmb_stat_workstation_0 "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
+#define REMSmb_statistics2_info_S "B68"
+#define REMSmb_stat_server_0 "DDDDDDDDDDDDDDDDD"
+
+#define REMSmb_NetStatisticsGet_P "rLeh"
+#define REMSmb_NetStatisticsClear_P ""
+
+#define REMSmb_NetStatisticsGet2_P "zDWDrLh"
+
+#define REMSmb_NetRemoteTOD_P "rL"
+
+#define REMSmb_time_of_day_info "DDBBBBWWBBWB"
+
+#define REMSmb_netbios_info_0 "B17"
+#define REMSmb_netbios_info_1 "B17B9BBWWDWWW"
+
+#define REMSmb_NetBiosEnum_P "WrLeh"
+#define REMSmb_NetBiosGetInfo_P "zWrLh"
+
+#define REMSmb_Spl_open_data "zzlzzzzzz"
+#define REMSmb_plain_data "K"
+
+#define REMSmb_NetSplQmAbort_P "Di"
+#define REMSmb_NetSplQmClose_P "Di"
+#define REMSmb_NetSplQmEndDoc_P "Dhi"
+#define REMSmb_NetSplQmOpen_P "zTsWii"
+#define REMSmb_NetSplQmStartDoc_P "Dzi"
+#define REMSmb_NetSplQmWrite_P "DTsi"
+
+#define REMSmb_configgetall_info "B"
+#define REMSmb_configget_info "B"
+#define REMSmb_configset_info_0 "zz"
+
+#define REMSmb_NetConfigGetAll_P "zzrLeh"
+#define REMSmb_NetConfigGet_P "zzzrLe"
+#define REMSmb_NetConfigSet_P "zzWWsTD"
+
+#define REMSmb_NetBuildGetInfo_P "DWrLh"
+#define REMSmb_build_info_0 "WD"
+
+#define REMSmb_NetGetDCName_P "zrL"
+#define REMSmb_dc_name "B18"
+
+#define REMSmb_challenge_info_0 "B8"
+#define REMSmb_account_delta_info_0 "K"
+#define REMSmb_account_sync_info_0 "K"
+
+#define REMSmb_NetAccountDeltas_P "zb12g12b24WWrLehg24"
+#define REMSmb_NetAccountSync_P "zb12g12DWrLehig24"
+
+#define REMSmb_NetLogonEnum_P "WrLeh"
+
+#define REMSmb_I_NetPathType_P "ziD"
+#define REMSmb_I_NetPathCanonicalize_P "zrLziDD"
+#define REMSmb_I_NetPathCompare_P "zzDD"
+#define REMSmb_I_NetNameValidate_P "zWD"
+#define REMSmb_I_NetNameCanonicalize_P "zrLWD"
+#define REMSmb_I_NetNameCompare_P "zzWD"
+
+#define REMSmb_LocalOnlyCall ""
+
+/*
+ * The following definitions exist for DOS LANMAN--Windows 3.0.
+ * Normally, there is a const char far * servername
+ * as the first parameter, but this will be ignored (sort of).
+ */
+#define REMSmb_DosPrintJobGetId_P "WrL"
+#define REMSmb_GetPrintId "WB16B13B"
+#define REMSmb_NetRemoteCopy_P "zzzzWWrL"
+#define REMSmb_copy_info "WB1"
+#define REMSmb_NetRemoteMove_P "zzzzWWrL"
+#define REMSmb_move_info "WB1"
+#define REMSmb_NetHandleGetInfo_P "WWrLh"
+#define REMSmb_NetHandleSetInfo_P "WWsTP"
+#define REMSmb_handle_info_1 "DW"
+#define REMSmb_handle_info_2 "z"
+#define REMSmb_WWkstaGetInfo_P "WrLhOW"
+
+/* The following strings are defined for RIPL APIs */
+
+#define REMSmb_RplWksta_info_0 "z"
+#define REMSmb_RplWksta_info_1 "zz"
+#define REMSmb_RplWksta_info_2 "b13b16b15b15zN"
+#define REMSmb_RplWksta_info_3 "b16b49"
+
+#define REMSmb_RplWkstaEnum_P "WzWrLehb4g4"
+#define REMSmb_RplWkstaGetInfo_P "zWrLh"
+#define REMSmb_RplWkstaSetInfo_P "zWsTPW"
+#define REMSmb_RplWkstaAdd_P "WsTW"
+#define REMSmb_RplWkstaDel_P "zW"
+
+#define REMSmb_RplProfile_info_0 "z"
+#define REMSmb_RplProfile_info_1 "zz"
+#define REMSmb_RplProfile_info_2 "b16b47"
+#define REMSmb_RplProfile_info_3 "b16b47b16"
+
+#define REMSmb_RplProfileEnum_P "WzWrLehb4g4"
+#define REMSmb_RplProfileGetInfo_P "zWrLh"
+#define REMSmb_RplProfileSetInfo_P "zWsTP"
+#define REMSmb_RplProfileAdd_P "WzsTW"
+#define REMSmb_RplProfileDel_P "zW"
+#define REMSmb_RplProfileClone_P "WzsTW"
+#define REMSmb_RplBaseProfileEnum_P "WrLehb4g4"
+
+
+/* LAN Manager 3.0 API strings go here */
+
+#define REMSmb_I_GuidGetAgent_P "g6i"
+#define REMSmb_I_GuidSetAgent_P "b6D"
+
+
+/* update support */
+
+#define REMSmb_NetAccountUpdate_P "b12g12WWrLh"
+#define REMSmb_NetAccountConfirmUpd_P "b12g12D"
+#define REMSmb_update_info_0 "K"
+
+/*
+ * SamrOemChangePasswordUser2 api support
+ */
+#define REMSmb_SamOEMChgPasswordUser2 "B516B16" /* data that is passed */
+
+#endif /* ndef _REMDEF_ */
diff --git a/sys/src/cmd/cifs/sid2name.c b/sys/src/cmd/cifs/sid2name.c
new file mode 100755
index 000000000..694ff317f
--- /dev/null
+++ b/sys/src/cmd/cifs/sid2name.c
@@ -0,0 +1,120 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "cifs.h"
+
+struct { /* Well known security IDs */
+ char *name;
+ char *auth;
+ char *rid;
+} known[] = {
+ /* default local users */
+ { "lu.dialup", "S-1-5-1", nil },
+ { "lu.network", "S-1-5-2", nil },
+ { "lu.batch", "S-1-5-3", nil },
+ { "lu.interactive", "S-1-5-4", nil },
+ { "lu.service", "S-1-5-6", nil },
+ { "lu.anon", "S-1-5-7", nil },
+ { "lu.DC", "S-1-5-8", nil },
+ { "lu.enterprise-domain", "S-1-5-9", nil },
+ { "lu.self", "S-1-5-10", nil },
+ { "lu.authenticated", "S-1-5-11", nil },
+ { "lu.restricted", "S-1-5-12", nil },
+ { "lu.terminal-services", "S-1-5-13", nil },
+ { "lu.remote-desktop", "S-1-5-14", nil },
+ { "lu.local-system", "S-1-5-18", nil },
+ { "lu.local-service", "S-1-5-19", nil },
+ { "lu.network-service", "S-1-5-20", nil },
+ { "lu.builtin", "S-1-5-32", nil },
+
+ /* default local groups */
+ { "lg.null", "S-1-0-0", nil },
+ { "lg.world", "S-1-1-0", nil },
+ { "lg.local", "S-1-2-0", nil },
+ { "lg.creator-owner", "S-1-3-0", nil },
+ { "lg.creator-group", "S-1-3-1", nil },
+ { "lg.creator-owner-server", "S-1-3-2", nil },
+ { "lg.creator-group-server", "S-1-3-3", nil },
+
+ /* default domain users */
+ { "du.admin", "S-1-5", "500" },
+ { "du.guest", "S-1-5", "501" },
+ { "du.kerberos", "S-1-5", "502" },
+
+ /* default domain groups */
+ { "dg.admins", "S-1-5-21", "512" },
+ { "dg.users", "S-1-5-21", "513" },
+ { "dg.guests", "S-1-5", "514" },
+ { "dg.computers", "S-1-5", "515" },
+ { "dg.controllers", "S-1-5", "516" },
+ { "dg.cert-admins", "S-1-5", "517" },
+ { "dg.schema-admins", "S-1-5", "518" },
+ { "dg.enterprise-admins", "S-1-5", "519" },
+ { "dg.group-policy-admins", "S-1-5", "520" },
+ { "dg.remote-access", "S-1-5", "553" },
+
+ /* default domain aliases */
+ { "da.admins", "S-1-5", "544" },
+ { "da.users", "S-1-5", "545" },
+ { "da.guests", "S-1-5", "546" },
+ { "da.power-users", "S-1-5", "547" },
+ { "da.account-operators", "S-1-5", "548" },
+ { "da.server-operators", "S-1-5", "549" },
+ { "da.print-operators", "S-1-5", "550" },
+ { "da.backup-operators", "S-1-5", "551" },
+ { "da.replicator", "S-1-5", "552" },
+ { "da.RAS-servers", "S-1-5", "553" },
+
+};
+
+static char *
+sid2name(char *sid)
+{
+ int i;
+ char *rid;
+
+ if(sid == nil || (rid = strrchr(sid, '-')) == nil || *++rid == 0)
+ return estrdup9p("-");
+
+ for(i = 0; i < nelem(known); i++){
+ if(strcmp(known[i].auth, sid) == 0 && known[i].rid == nil)
+ return estrdup9p(known[i].name);
+
+ if(strlen(known[i].auth) < strlen(sid) &&
+ strncmp(known[i].auth, sid, strlen(known[i].auth)) == 0 &&
+ known[i].rid && strcmp(known[i].rid, rid) == 0)
+ return estrdup9p(known[i].name);
+ }
+
+ return estrdup9p(rid);
+}
+
+void
+upd_names(Session *s, Share *sp, char *path, Dir *d)
+{
+ int fh, result;
+ char *usid, *gsid;
+ FInfo fi;
+
+ if(d->uid)
+ free(d->uid);
+ if(d->gid)
+ free(d->gid);
+
+ if((fh = CIFS_NT_opencreate(s, sp, path, 0, 0, 0, READ_CONTROL,
+ FILE_SHARE_ALL, FILE_OPEN, &result, &fi)) == -1){
+ d->uid = estrdup9p("unknown");
+ d->gid = estrdup9p("unknown");
+ return;
+ }
+ usid = nil;
+ gsid = nil;
+ TNTquerysecurity(s, sp, fh, &usid, &gsid);
+ d->uid = sid2name(usid);
+ d->gid = sid2name(gsid);
+ if(fh != -1)
+ CIFSclose(s, sp, fh);
+}
diff --git a/sys/src/cmd/cifs/trans.c b/sys/src/cmd/cifs/trans.c
new file mode 100755
index 000000000..d6daa2857
--- /dev/null
+++ b/sys/src/cmd/cifs/trans.c
@@ -0,0 +1,785 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "cifs.h"
+#include "remsmb.h"
+#include "apinums.h"
+
+static Pkt *
+thdr(Session *s, Share *sp)
+{
+ Pkt *p;
+
+ p = cifshdr(s, sp, SMB_COM_TRANSACTION);
+ p->tbase = pl16(p, 0); /* 0 Total parameter bytes to be sent, filled later */
+ pl16(p, 0); /* 2 Total data bytes to be sent, filled later */
+ pl16(p, 64); /* 4 Max parameter to return */
+ pl16(p, MTU - T2HDRLEN - 128); /* 6 Max data to return */
+ pl16(p, 1); /* 8 Max setup count to return */
+ pl16(p, 0); /* 10 Flags */
+ pl32(p, 1000); /* 12 Timeout (ms) */
+ pl16(p, 0); /* 16 Reserved */
+ pl16(p, 0); /* 18 Parameter count, filled later */
+ pl16(p, 0); /* 20 Parameter offset, filled later */
+ pl16(p, 0); /* 22 Data count, filled later */
+ pl16(p, 0); /* 24 Data offset, filled later */
+ pl16(p, 0); /* 26 Setup count (in words) */
+ pbytes(p); /* end of cifs words section */
+ return p;
+}
+
+static void
+ptparam(Pkt *p)
+{
+ uchar *pos;
+
+ if(((p->pos - p->tbase) % 2) != 0)
+ p8(p, 0); /* pad to word boundry */
+ pos = p->pos;
+ p->pos = p->tbase + 20;
+ pl16(p, pos - p->buf - NBHDRLEN); /* param offset */
+ p->tparam = p->pos = pos;
+}
+
+static void
+ptdata(Pkt *p)
+{
+ uchar *pos = p->pos;
+
+ assert(p->tparam != 0);
+ if(((p->pos - p->tbase) % 2) != 0)
+ p8(p, 0); /* pad to word boundry */
+
+ p->pos = p->tbase + 0;
+ pl16(p, pos - p->tparam); /* total param count */
+
+ p->pos = p->tbase + 18;
+ pl16(p, pos - p->tparam); /* param count */
+
+ p->pos = p->tbase + 24;
+ pl16(p, pos - p->buf - NBHDRLEN); /* data offset */
+
+ p->tdata = p->pos = pos;
+}
+
+static int
+trpc(Pkt *p)
+{
+ int got;
+ uchar *pos = p->pos;
+
+ assert(p->tbase != 0);
+ assert(p->tdata != 0);
+
+ p->pos = p->tbase + 2;
+ pl16(p, pos - p->tdata); /* total data count */
+
+ p->pos = p->tbase + 22;
+ pl16(p, pos - p->tdata); /* data count */
+
+ p->pos = pos;
+ if((got = cifsrpc(p)) == -1)
+ return -1;
+
+ gl16(p); /* Total parameter count */
+ gl16(p); /* Total data count */
+ gl16(p); /* Reserved */
+ gl16(p); /* Parameter count in this buffer */
+ p->tparam = p->buf + NBHDRLEN + gl16(p); /* Parameter offset */
+ gl16(p); /* Parameter displacement */
+ gl16(p); /* Data count (this buffer); */
+ p->tdata = p->buf + NBHDRLEN + gl16(p); /* Data offset */
+ gl16(p); /* Data displacement */
+ g8(p); /* Setup count */
+ g8(p); /* Reserved */
+ return got;
+}
+
+static void
+gtparam(Pkt *p)
+{
+ p->pos = p->tparam;
+}
+
+static void
+gtdata(Pkt *p)
+{
+ p->pos = p->tdata;
+}
+
+
+int
+RAPshareenum(Session *s, Share *sp, Share **ent)
+{
+ int ngot = 0, err, navail, nret;
+ char tmp[1024];
+ Pkt *p;
+ Share *q;
+
+ p = thdr(s, sp);
+ pstr(p, "\\PIPE\\LANMAN");
+ ptparam(p);
+
+ pl16(p, API_WShareEnum);
+ pascii(p, REMSmb_NetShareEnum_P); /* request descriptor */
+ pascii(p, REMSmb_share_info_0); /* reply descriptor */
+ pl16(p, 0); /* detail level */
+ pl16(p, MTU - 200); /* receive buffer length */
+ ptdata(p);
+
+ if(trpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ gtparam(p);
+ err = gl16(p); /* error code */
+ gl16(p); /* rx buffer offset */
+ nret = gl16(p); /* number of entries returned */
+ navail = gl16(p); /* number of entries available */
+
+ if(err && err != RAP_ERR_MOREINFO){
+ werrstr("%s", raperrstr(err));
+ free(p);
+ return -1;
+ }
+
+ if(ngot == 0){
+ *ent = emalloc9p(sizeof(Share) * navail);
+ memset(*ent, 0, sizeof(Share) * navail);
+ }
+
+ q = *ent + ngot;
+ for (; ngot < navail && nret--; ngot++){
+ gmem(p, tmp, 13); /* name */
+ tmp[13] = 0;
+ q->name = estrdup9p(tmp);
+ q++;
+ }
+
+ if(ngot < navail)
+ fprint(2, "%s: %d/%d - share list incomplete\n", argv0, ngot, navail);
+
+ free(p);
+ return ngot;
+}
+
+
+int
+RAPshareinfo(Session *s, Share *sp, char *share, Shareinfo2 *si2p)
+{
+ int conv, err;
+ char tmp[1024];
+ Pkt *p;
+
+ p = thdr(s, sp);
+ pstr(p, "\\PIPE\\LANMAN");
+
+ ptparam(p);
+ pl16(p, API_WShareGetInfo);
+ pascii(p, REMSmb_NetShareGetInfo_P); /* request descriptor */
+ pascii(p, REMSmb_share_info_2); /* reply descriptor */
+ pascii(p, share);
+ pl16(p, 1); /* detail level */
+ pl16(p, MTU - 200); /* receive buffer length */
+
+ ptdata(p);
+
+ if(trpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ gtparam(p);
+ err = gl16(p); /* error code */
+ conv = gl16(p); /* rx buffer offset */
+ gl16(p); /* number of entries returned */
+ gl16(p); /* number of entries available */
+
+ if(err){
+ werrstr("%s", raperrstr(err));
+ free(p);
+ return -1;
+ }
+
+ memset(si2p, 0, sizeof(Shareinfo2));
+
+ gmem(p, tmp, 13);
+ tmp[13] = 0;
+ g8(p); /* padding */
+ si2p->name = estrdup9p(tmp);
+ si2p->type = gl16(p);
+ gconv(p, conv, tmp, sizeof tmp);
+ si2p->comment = estrdup9p(tmp);
+ gl16(p); /* comment offset high (unused) */
+ si2p->perms = gl16(p);
+ si2p->maxusrs = gl16(p);
+ si2p->activeusrs = gl16(p);
+ gconv(p, conv, tmp, sizeof tmp);
+ si2p->path = estrdup9p(tmp);
+ gl16(p); /* path offset high (unused) */
+ gmem(p, tmp, 9);
+ tmp[9] = 0;
+ si2p->passwd = estrdup9p(tmp);
+
+ free(p);
+ return 0;
+}
+
+/*
+ * Tried to split sessionenum into two passes, one getting the names
+ * of the connected workstations and the other collecting the detailed info,
+ * however API_WSessionGetInfo doesn't seem to work agains win2k3 for infolevel
+ * ten and infolevel one and two are priviledged calls. This means this code
+ * will work for small numbers of sessions agains win2k3 and fail for samba 3.0
+ * as it supports info levels zero and two only.
+ */
+int
+RAPsessionenum(Session *s, Share *sp, Sessinfo **sip)
+{
+ int ngot = 0, conv, err, navail, nret;
+ char tmp[1024];
+ Pkt *p;
+ Sessinfo *q;
+
+ p = thdr(s, sp);
+ pstr(p, "\\PIPE\\LANMAN");
+ ptparam(p);
+
+ pl16(p, API_WSessionEnum);
+ pascii(p, REMSmb_NetSessionEnum_P); /* request descriptor */
+ pascii(p, REMSmb_session_info_10); /* reply descriptor */
+ pl16(p, 10); /* detail level */
+ pl16(p, MTU - 200); /* receive buffer length */
+ ptdata(p);
+
+ if(trpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ gtparam(p);
+ err = gl16(p); /* error code */
+ conv = gl16(p); /* rx buffer offset */
+ nret = gl16(p); /* number of entries returned */
+ navail = gl16(p); /* number of entries available */
+
+ if(err && err != RAP_ERR_MOREINFO){
+ werrstr("%s", raperrstr(err));
+ free(p);
+ return -1;
+ }
+
+ if(ngot == 0){
+ *sip = emalloc9p(sizeof(Sessinfo) * navail);
+ memset(*sip, 0, sizeof(Sessinfo) * navail);
+ }
+
+ q = *sip + ngot;
+ while(nret-- != 0){
+ gconv(p, conv, tmp, sizeof tmp);
+ q->wrkstn = estrdup9p(tmp);
+ gconv(p, conv, tmp, sizeof tmp);
+ q->user = estrdup9p(tmp);
+ q->sesstime = gl32(p);
+ q->idletime = gl32(p);
+ ngot++;
+ q++;
+ }
+ if(ngot < navail)
+ fprint(2, "warning: %d/%d - session list incomplete\n", ngot, navail);
+ free(p);
+ return ngot;
+}
+
+
+int
+RAPgroupenum(Session *s, Share *sp, Namelist **nlp)
+{
+ int ngot, err, navail, nret;
+ char tmp[1024];
+ Pkt *p;
+ Namelist *q;
+
+ ngot = 0;
+ p = thdr(s, sp);
+ pstr(p, "\\PIPE\\LANMAN");
+ ptparam(p);
+
+ pl16(p, API_WGroupEnum);
+ pascii(p, REMSmb_NetGroupEnum_P); /* request descriptor */
+ pascii(p, REMSmb_group_info_0); /* reply descriptor */
+ pl16(p, 0); /* detail level */
+ pl16(p, MTU - 200); /* receive buffer length */
+ ptdata(p);
+
+ if(trpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ gtparam(p);
+ err = gl16(p); /* error code */
+ gl16(p); /* rx buffer offset */
+ nret = gl16(p); /* number of entries returned */
+ navail = gl16(p); /* number of entries available */
+
+ if(err && err != RAP_ERR_MOREINFO){
+ werrstr("%s", raperrstr(err));
+ free(p);
+ return -1;
+ }
+
+ *nlp = emalloc9p(sizeof(Namelist) * navail);
+ memset(*nlp, 0, sizeof(Namelist) * navail);
+
+ q = *nlp + ngot;
+ while(ngot < navail && nret--){
+ gmem(p, tmp, 21);
+ tmp[21] = 0;
+ q->name = estrdup9p(tmp);
+ ngot++;
+ q++;
+ if(p->pos >= p->eop) /* Windows seems to lie somtimes */
+ break;
+ }
+ free(p);
+ return ngot;
+}
+
+
+int
+RAPgroupusers(Session *s, Share *sp, char *group, Namelist **nlp)
+{
+ int ngot, err, navail, nret;
+ char tmp[1024];
+ Pkt *p;
+ Namelist *q;
+
+ ngot = 0;
+ p = thdr(s, sp);
+ pstr(p, "\\PIPE\\LANMAN");
+ ptparam(p);
+
+ pl16(p, API_WGroupGetUsers);
+ pascii(p, REMSmb_NetGroupGetUsers_P); /* request descriptor */
+ pascii(p, REMSmb_user_info_0); /* reply descriptor */
+ pascii(p, group); /* group name for list */
+ pl16(p, 0); /* detail level */
+ pl16(p, MTU - 200); /* receive buffer length */
+ ptdata(p);
+
+ if(trpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ gtparam(p);
+ err = gl16(p); /* error code */
+ gl16(p); /* rx buffer offset */
+ nret = gl16(p); /* number of entries returned */
+ navail = gl16(p); /* number of entries available */
+
+ if(err && err != RAP_ERR_MOREINFO){
+ werrstr("%s", raperrstr(err));
+ free(p);
+ return -1;
+ }
+
+ *nlp = emalloc9p(sizeof(Namelist) * navail);
+ memset(*nlp, 0, sizeof(Namelist) * navail);
+
+ q = *nlp + ngot;
+ while(ngot < navail && nret--){
+ gmem(p, tmp, 21);
+ tmp[21] = 0;
+ q->name = estrdup9p(tmp);
+ ngot++;
+ q++;
+ if(p->pos >= p->eop) /* Windows seems to lie somtimes */
+ break;
+ }
+ free(p);
+ return ngot;
+}
+
+int
+RAPuserenum(Session *s, Share *sp, Namelist **nlp)
+{
+ int ngot, err, navail, nret;
+ char tmp[1024];
+ Pkt *p;
+ Namelist *q;
+
+ ngot = 0;
+ p = thdr(s, sp);
+ pstr(p, "\\PIPE\\LANMAN");
+ ptparam(p);
+
+ pl16(p, API_WUserEnum);
+ pascii(p, REMSmb_NetUserEnum_P); /* request descriptor */
+ pascii(p, REMSmb_user_info_0); /* reply descriptor */
+ pl16(p, 0); /* detail level */
+ pl16(p, MTU - 200); /* receive buffer length */
+ ptdata(p);
+
+ if(trpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ gtparam(p);
+ err = gl16(p); /* error code */
+ gl16(p); /* rx buffer offset */
+ nret = gl16(p); /* number of entries returned */
+ navail = gl16(p); /* number of entries available */
+
+ if(err && err != RAP_ERR_MOREINFO){
+ werrstr("%s", raperrstr(err));
+ free(p);
+ return -1;
+ }
+
+ *nlp = emalloc9p(sizeof(Namelist) * navail);
+ memset(*nlp, 0, sizeof(Namelist) * navail);
+
+ q = *nlp + ngot;
+ while(ngot < navail && nret--){
+ gmem(p, tmp, 21);
+ tmp[21] = 0;
+ q->name = estrdup9p(tmp);
+ ngot++;
+ q++;
+ if(p->pos >= p->eop) /* Windows seems to lie somtimes */
+ break;
+ }
+ free(p);
+ return ngot;
+}
+
+int
+RAPuserenum2(Session *s, Share *sp, Namelist **nlp)
+{
+ int ngot, resume, err, navail, nret;
+ char tmp[1024];
+ Pkt *p;
+ Namelist *q;
+
+ ngot = 0;
+ resume = 0;
+more:
+ p = thdr(s, sp);
+ pstr(p, "\\PIPE\\LANMAN");
+ ptparam(p);
+
+ pl16(p, API_WUserEnum2);
+ pascii(p, REMSmb_NetUserEnum2_P); /* request descriptor */
+ pascii(p, REMSmb_user_info_0); /* reply descriptor */
+ pl16(p, 0); /* detail level */
+ pl16(p, MTU - 200); /* receive buffer length */
+ pl32(p, resume); /* resume key to allow multiple fetches */
+ ptdata(p);
+
+ if(trpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ gtparam(p);
+ err = gl16(p); /* error code */
+ gl16(p); /* rx buffer offset */
+ resume = gl32(p); /* resume key returned */
+ nret = gl16(p); /* number of entries returned */
+ navail = gl16(p); /* number of entries available */
+
+ if(err && err != RAP_ERR_MOREINFO){
+ werrstr("%s", raperrstr(err));
+ free(p);
+ return -1;
+ }
+
+ if(ngot == 0){
+ *nlp = emalloc9p(sizeof(Namelist) * navail);
+ memset(*nlp, 0, sizeof(Namelist) * navail);
+ }
+ q = *nlp + ngot;
+ while(ngot < navail && nret--){
+ gmem(p, tmp, 21);
+ tmp[21] = 0;
+ q->name = estrdup9p(tmp);
+ ngot++;
+ q++;
+ if(p->pos >= p->eop) /* Windows seems to lie somtimes */
+ break;
+ }
+ free(p);
+ if(ngot < navail)
+ goto more;
+ return ngot;
+}
+
+int
+RAPuserinfo(Session *s, Share *sp, char *user, Userinfo *uip)
+{
+ int conv, err;
+ char tmp[1024];
+ Pkt *p;
+
+ p = thdr(s, sp);
+ pstr(p, "\\PIPE\\LANMAN");
+ ptparam(p);
+
+ pl16(p, API_WUserGetInfo);
+ pascii(p, REMSmb_NetUserGetInfo_P); /* request descriptor */
+ pascii(p, REMSmb_user_info_10); /* reply descriptor */
+ pascii(p, user); /* username */
+ pl16(p, 10); /* detail level */
+ pl16(p, MTU - 200); /* receive buffer length */
+ ptdata(p);
+
+ if(trpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ gtparam(p);
+ err = gl16(p); /* error code */
+ conv = gl16(p); /* rx buffer offset */
+ gl16(p); /* number of entries returned */
+ gl16(p); /* number of entries available */
+
+ if(err && err != RAP_ERR_MOREINFO){
+ werrstr("%s", raperrstr(err));
+ free(p);
+ return -1;
+ }
+
+ gmem(p, tmp, 21);
+ tmp[21] = 0;
+ uip->user = estrdup9p(tmp);
+ g8(p); /* padding */
+ gconv(p, conv, tmp, sizeof tmp);
+ uip->comment = estrdup9p(tmp);
+ gconv(p, conv, tmp, sizeof tmp);
+ uip->user_comment = estrdup9p(tmp);
+ gconv(p, conv, tmp, sizeof tmp);
+ uip->fullname = estrdup9p(tmp);
+
+ free(p);
+ return 0;
+}
+
+/*
+ * This works agains win2k3 but fails
+ * against XP with the undocumented error 71/0x47
+ */
+int
+RAPServerenum2(Session *s, Share *sp, char *workgroup, int type, int *more,
+ Serverinfo **si)
+{
+ int ngot = 0, conv, err, nret, navail;
+ char tmp[1024];
+ Pkt *p;
+ Serverinfo *q;
+
+ p = thdr(s, sp);
+ pstr(p, "\\PIPE\\LANMAN");
+
+ ptparam(p);
+ pl16(p, API_NetServerEnum2);
+ pascii(p, REMSmb_NetServerEnum2_P); /* request descriptor */
+ pascii(p, REMSmb_server_info_1); /* reply descriptor */
+ pl16(p, 1); /* detail level */
+ pl16(p, MTU - 200); /* receive buffer length */
+ pl32(p, type);
+ pascii(p, workgroup);
+
+ ptdata(p);
+
+ if(trpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ gtparam(p);
+ err = gl16(p); /* error code */
+ conv = gl16(p); /* rx buffer offset */
+ nret = gl16(p); /* number of entries returned */
+ navail = gl16(p); /* number of entries available */
+
+ if(err && err != RAP_ERR_MOREINFO){
+ werrstr("%s", raperrstr(err));
+ free(p);
+ return -1;
+ }
+
+ *si = emalloc9p(sizeof(Serverinfo) * navail);
+ memset(*si, 0, sizeof(Serverinfo) * navail);
+
+ q = *si;
+ for (; nret-- != 0 && ngot < navail; ngot++){
+ gmem(p, tmp, 16);
+ tmp[16] = 0;
+ q->name = estrdup9p(tmp);
+ q->major = g8(p);
+ q->minor = g8(p);
+ q->type = gl32(p);
+ gconv(p, conv, tmp, sizeof tmp);
+ q->comment = estrdup9p(tmp);
+ q++;
+ }
+ free(p);
+ *more = err == RAP_ERR_MOREINFO;
+ return ngot;
+}
+
+int
+RAPServerenum3(Session *s, Share *sp, char *workgroup, int type, int last,
+ Serverinfo *si)
+{
+ int conv, err, ngot, nret, navail;
+ char *first, tmp[1024];
+ Pkt *p;
+ Serverinfo *q;
+
+ ngot = last +1;
+ first = si[last].name;
+more:
+ p = thdr(s, sp);
+ pstr(p, "\\PIPE\\LANMAN");
+
+ ptparam(p);
+ pl16(p, API_NetServerEnum3);
+ pascii(p, REMSmb_NetServerEnum3_P); /* request descriptor */
+ pascii(p, REMSmb_server_info_1); /* reply descriptor */
+ pl16(p, 1); /* detail level */
+ pl16(p, MTU - 200); /* receive buffer length */
+ pl32(p, type);
+ pascii(p, workgroup);
+ pascii(p, first);
+
+ ptdata(p);
+
+ if(trpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ gtparam(p);
+ err = gl16(p); /* error code */
+ conv = gl16(p); /* rx buffer offset */
+ nret = gl16(p); /* number of entries returned */
+ navail = gl16(p); /* number of entries available */
+
+ if(err && err != RAP_ERR_MOREINFO){
+ werrstr("%s", raperrstr(err));
+ free(p);
+ return -1;
+ }
+
+ if(nret < 2){ /* paranoia */
+ free(p);
+ return ngot;
+ }
+
+ q = si+ngot;
+ while(nret-- != 0 && ngot < navail){
+ gmem(p, tmp, 16);
+ tmp[16] = 0;
+ q->name = estrdup9p(tmp);
+ q->major = g8(p);
+ q->minor = g8(p);
+ q->type = gl32(p);
+ gconv(p, conv, tmp, sizeof tmp);
+ tmp[sizeof tmp - 1] = 0;
+ q->comment = estrdup9p(tmp);
+ if(strcmp(first, tmp) == 0){ /* 1st one thru _may_ be a repeat */
+ free(q->name);
+ free(q->comment);
+ continue;
+ }
+ ngot++;
+ q++;
+ }
+ free(p);
+ if(ngot < navail)
+ goto more;
+ return ngot;
+}
+
+/* Only the Administrator has permission to do this */
+int
+RAPFileenum2(Session *s, Share *sp, char *user, char *path, Fileinfo **fip)
+{
+ int conv, err, ngot, resume, nret, navail;
+ char tmp[1024];
+ Pkt *p;
+ Fileinfo *q;
+
+ ngot = 0;
+ resume = 0;
+more:
+ p = thdr(s, sp);
+ pstr(p, "\\PIPE\\LANMAN");
+
+ ptparam(p);
+ pl16(p, API_WFileEnum2);
+ pascii(p, REMSmb_NetFileEnum2_P); /* request descriptor */
+ pascii(p, REMSmb_file_info_1); /* reply descriptor */
+ pascii(p, path);
+ pascii(p, user);
+ pl16(p, 1); /* detail level */
+ pl16(p, MTU - 200); /* receive buffer length */
+ pl32(p, resume); /* resume key */
+/* FIXME: maybe the padding and resume key are the wrong way around? */
+ pl32(p, 0); /* padding ? */
+
+ ptdata(p);
+
+ if(trpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ gtparam(p);
+ err = gl16(p); /* error code */
+ conv = gl16(p); /* rx buffer offset */
+ resume = gl32(p); /* resume key returned */
+ nret = gl16(p); /* number of entries returned */
+ navail = gl16(p); /* number of entries available */
+
+ if(err && err != RAP_ERR_MOREINFO){
+ werrstr("%s", raperrstr(err));
+ free(p);
+ return -1;
+ }
+
+ if(nret < 2){ /* paranoia */
+ free(p);
+ return ngot;
+ }
+
+ if(ngot == 0){
+ *fip = emalloc9p(sizeof(Fileinfo) * navail);
+ memset(*fip, 0, sizeof(Fileinfo) * navail);
+ }
+ q = *fip + ngot;
+ for(; nret-- && ngot < navail; ngot++){
+ q->ident = gl16(p);
+ q->perms = gl16(p);
+ q->locks = gl16(p);
+ gconv(p, conv, tmp, sizeof tmp);
+ tmp[sizeof tmp - 1] = 0;
+ q->path = estrdup9p(tmp);
+ gconv(p, conv, tmp, sizeof tmp);
+ tmp[sizeof tmp - 1] = 0;
+ q->user = estrdup9p(tmp);
+ q++;
+ }
+ free(p);
+ if(ngot < navail)
+ goto more;
+ return ngot;
+}
diff --git a/sys/src/cmd/cifs/trans2.c b/sys/src/cmd/cifs/trans2.c
new file mode 100755
index 000000000..090054096
--- /dev/null
+++ b/sys/src/cmd/cifs/trans2.c
@@ -0,0 +1,539 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "cifs.h"
+
+static Pkt *
+t2hdr(Session *s, Share *sp, int cmd)
+{
+ Pkt *p;
+
+ p = cifshdr(s, sp, SMB_COM_TRANSACTION2);
+
+ p->tbase = pl16(p, 0); /* 0 Total parameter bytes to be sent, filled later */
+ pl16(p, 0); /* 2 Total data bytes to be sent, filled later */
+ pl16(p, 64); /* 4 Max parameter to return */
+ pl16(p, (MTU - T2HDRLEN)-64); /* 6 Max data to return */
+ p8(p, 0); /* 8 Max setup count to return */
+ p8(p, 0); /* 9 Reserved */
+ pl16(p, 0); /* 10 Flags */
+ pl32(p, 1000); /* 12 Timeout (ms) */
+ pl16(p, 0); /* 16 Reserved */
+ pl16(p, 0); /* 18 Parameter count, filled later */
+ pl16(p, 0); /* 20 Parameter offset, filled later */
+ pl16(p, 0); /* 22 Data count, filled later */
+ pl16(p, 0); /* 24 Data offset, filled later */
+ p8(p, 1); /* 26 Setup count (in words) */
+ p8(p, 0); /* 27 Reserved */
+ pl16(p, cmd); /* setup[0] */
+ pbytes(p);
+ p8(p, 0); /* padding ??!?!? */
+
+ return p;
+}
+
+static void
+pt2param(Pkt *p)
+{
+ uchar *pos = p->pos;
+
+ assert(p->tbase != 0);
+ p->pos = p->tbase + 20;
+ pl16(p, (pos - p->buf) - NBHDRLEN); /* param offset */
+
+ p->tparam = p->pos = pos;
+}
+
+static void
+pt2data(Pkt *p)
+{
+ uchar *pos = p->pos;
+
+ assert(p->tbase != 0);
+ assert(p->tparam != 0);
+
+ p->pos = p->tbase +0;
+ pl16(p, pos - p->tparam); /* total param count */
+
+ p->pos = p->tbase +18;
+ pl16(p, pos - p->tparam); /* param count */
+
+ p->pos = p->tbase +24;
+ pl16(p, (pos - p->buf) - NBHDRLEN); /* data offset */
+
+ p->tdata = p->pos = pos;
+}
+
+static int
+t2rpc(Pkt *p)
+{
+ int got;
+ uchar *pos;
+
+ assert(p->tbase != 0);
+ assert(p->tdata != 0);
+
+ pos = p->pos;
+
+ p->pos = p->tbase +2;
+ pl16(p, pos - p->tdata); /* total data count */
+
+ p->pos = p->tbase +22;
+ pl16(p, pos - p->tdata); /* data count */
+
+ p->pos = pos;
+ if((got = cifsrpc(p)) == -1)
+ return -1;
+
+ gl16(p); /* Total parameter count */
+ gl16(p); /* Total data count */
+ gl16(p); /* Reserved */
+ gl16(p); /* Parameter count in this buffer */
+ p->tparam = p->buf +NBHDRLEN +gl16(p); /* Parameter offset */
+ gl16(p); /* Parameter displacement */
+ gl16(p); /* Data count (this buffer); */
+ p->tdata = p->buf +NBHDRLEN +gl16(p); /* Data offset */
+ gl16(p); /* Data displacement */
+ g8(p); /* Setup count */
+ g8(p); /* Reserved */
+
+ return got;
+}
+
+static void
+gt2param(Pkt *p)
+{
+ p->pos = p->tparam;
+}
+
+static void
+gt2data(Pkt *p)
+{
+ p->pos = p->tdata;
+}
+
+
+int
+T2findfirst(Session *s, Share *sp, int slots, char *path, int *got,
+ long *resume, FInfo *fip)
+{
+ int pktlen, i, n, sh;
+ uchar *next;
+ Pkt *p;
+
+ p = t2hdr(s, sp, TRANS2_FIND_FIRST2);
+ p8(p, 'D'); /* OS/2 */
+ p8(p, ' '); /* OS/2 */
+
+ pt2param(p);
+ pl16(p, ATTR_HIDDEN|ATTR_SYSTEM|ATTR_DIRECTORY); /* Search attributes */
+ pl16(p, slots); /* Search count */
+ pl16(p, CIFS_SEARCH_RETURN_RESUME); /* Flags */
+ pl16(p, SMB_FIND_FILE_FULL_DIRECTORY_INFO); /* Information level */
+ pl32(p, 0); /* SearchStorage type (?) */
+ ppath(p, path); /* path */
+
+ pt2data(p);
+ if((pktlen = t2rpc(p)) == -1){
+ free(p);
+ return -1;
+ }
+
+ s->lastfind = nsec();
+ gt2param(p);
+
+ sh = gl16(p); /* Sid (search handle) */
+ *got = gl16(p); /* number of slots received */
+ gl16(p); /* End of search flag */
+ gl16(p); /* Offset into EA list if EA error */
+ gl16(p); /* Offset into data to file name of last entry */
+
+ gt2data(p);
+ memset(fip, 0, slots * sizeof(FInfo));
+ for(i = 0; i < *got; i++){
+ next = p->pos;
+ next += gl32(p); /* offset to next entry */
+ /*
+ * bug in Windows - somtimes it lies about how many
+ * directory entries it has put in the packet
+ */
+ if(next - p->buf > pktlen){
+ *got = i;
+ break;
+ }
+
+ *resume = gl32(p); /* resume key for search */
+ fip[i].created = gvtime(p); /* creation time */
+ fip[i].accessed = gvtime(p); /* last access time */
+ fip[i].written = gvtime(p); /* last written time */
+ fip[i].changed = gvtime(p); /* change time */
+ fip[i].size = gl64(p); /* file size */
+ gl64(p); /* bytes allocated */
+ fip[i].attribs = gl32(p); /* extended attributes */
+ n = gl32(p); /* name length */
+ gl32(p); /* EA size */
+ gstr(p, fip[i].name, n); /* name */
+ p->pos = next;
+ }
+
+ free(p);
+ return sh;
+
+}
+
+int
+T2findnext(Session *s, Share *sp, int slots, char *path, int *got,
+ long *resume, FInfo *fip, int sh)
+{
+ Pkt *p;
+ int i, n;
+ uchar *next;
+
+ /*
+ * So I believe from comp.protocols.smb if you send
+ * TRANS2_FIND_NEXT2 requests too quickly to windows 95, it can
+ * get confused and fail to reply, so we slow up a bit in these
+ * circumstances.
+ */
+ if(!(s->caps & CAP_NT_SMBS) && nsec() - s->lastfind < 200000000LL)
+ sleep(200);
+
+ p = t2hdr(s, sp, TRANS2_FIND_NEXT2);
+ p8(p, 'D'); /* OS/2 */
+ p8(p, ' '); /* OS/2 */
+
+ pt2param(p);
+ pl16(p, sh); /* search handle */
+ pl16(p, slots); /* Search count */
+ pl16(p, SMB_FIND_FILE_FULL_DIRECTORY_INFO); /* Information level */
+ pl32(p, *resume); /* resume key */
+ pl16(p, CIFS_SEARCH_CONTINUE_FROM_LAST); /* Flags */
+ ppath(p, path); /* file+path to resume */
+
+ pt2data(p);
+ if(t2rpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ s->lastfind = nsec();
+
+ gt2param(p);
+ *got = gl16(p); /* number of slots received */
+ gl16(p); /* End of search flag */
+ gl16(p); /* Offset into EA list if EA error */
+ gl16(p); /* Offset into data to file name of last entry */
+
+ gt2data(p);
+ memset(fip, 0, slots * sizeof(FInfo));
+ for(i = 0; i < *got; i++){
+ next = p->pos;
+ next += gl32(p); /* offset to next entry */
+ *resume = gl32(p); /* resume key for search */
+ fip[i].created = gvtime(p); /* creation time */
+ fip[i].accessed = gvtime(p); /* last access time */
+ fip[i].written = gvtime(p); /* last written time */
+ fip[i].changed = gvtime(p); /* change time */
+ fip[i].size = gl64(p); /* file size */
+ gl64(p); /* bytes allocated */
+ fip[i].attribs = gl32(p); /* extended attributes */
+ n = gl32(p); /* name length */
+ gl32(p); /* EA size */
+ gstr(p, fip[i].name, n); /* name */
+ p->pos = next;
+ }
+ free(p);
+ return 0;
+}
+
+
+/* supported by 2k/XP/NT4 */
+int
+T2queryall(Session *s, Share *sp, char *path, FInfo *fip)
+{
+ int n;
+ Pkt *p;
+
+ p = t2hdr(s, sp, TRANS2_QUERY_PATH_INFORMATION);
+ pt2param(p);
+ pl16(p, SMB_QUERY_FILE_ALL_INFO); /* Information level */
+ pl32(p, 0); /* reserved */
+ ppath(p, path); /* path */
+
+ pt2data(p);
+ if(t2rpc(p) == -1){
+ free(p);
+ return -1;
+ }
+ gt2data(p);
+
+ /*
+ * The layout of this struct is wrong in the SINA
+ * document, this layout gained by inspection.
+ */
+ memset(fip, 0, sizeof(FInfo));
+ fip->created = gvtime(p); /* creation time */
+ fip->accessed = gvtime(p); /* last access time */
+ fip->written = gvtime(p); /* last written time */
+ fip->changed = gvtime(p); /* change time */
+ fip->attribs = gl32(p); /* attributes */
+ gl32(p); /* reserved */
+ gl64(p); /* bytes allocated */
+ fip->size = gl64(p); /* file size */
+ gl32(p); /* number of hard links */
+ g8(p); /* delete pending */
+ g8(p); /* is a directory */
+ gl16(p); /* reserved */
+ gl32(p); /* EA size */
+
+ n = gl32(p);
+ if(n >= sizeof fip->name)
+ n = sizeof fip->name - 1;
+ gstr(p, fip->name, n);
+
+ free(p);
+ return 0;
+}
+
+/* supported by 95/98/ME */
+int
+T2querystandard(Session *s, Share *sp, char *path, FInfo *fip)
+{
+ Pkt *p;
+
+ p = t2hdr(s, sp, TRANS2_QUERY_PATH_INFORMATION);
+ pt2param(p);
+ pl16(p, SMB_INFO_STANDARD); /* Information level */
+ pl32(p, 0); /* reserved */
+ ppath(p, path); /* path */
+
+ pt2data(p);
+ if(t2rpc(p) == -1){
+ free(p);
+ return -1;
+ }
+ gt2data(p);
+ memset(fip, 0, sizeof(FInfo));
+ fip->created = gdatetime(p); /* creation time */
+ fip->accessed = gdatetime(p); /* last access time */
+ fip->written = gdatetime(p); /* last written time */
+ fip->changed = fip->written; /* change time */
+ fip->size = gl32(p); /* file size */
+ gl32(p); /* bytes allocated */
+ fip->attribs = gl16(p); /* attributes */
+ gl32(p); /* EA size */
+
+ free(p);
+ return 0;
+}
+
+int
+T2setpathinfo(Session *s, Share *sp, char *path, FInfo *fip)
+{
+ int rc;
+ Pkt *p;
+
+ p = t2hdr(s, sp, TRANS2_SET_PATH_INFORMATION);
+ pt2param(p);
+ pl16(p, SMB_INFO_STANDARD); /* Information level */
+ pl32(p, 0); /* reserved */
+ ppath(p, path); /* path */
+
+ pt2data(p);
+ pdatetime(p, fip->created); /* created */
+ pdatetime(p, fip->accessed); /* accessed */
+ pdatetime(p, fip->written); /* written */
+ pl32(p, fip->size); /* size */
+ pl32(p, 0); /* allocated */
+ pl16(p, fip->attribs); /* attributes */
+ pl32(p, 0); /* EA size */
+ pl16(p, 0); /* reserved */
+
+ rc = t2rpc(p);
+ free(p);
+ return rc;
+
+}
+
+int
+T2setfilelength(Session *s, Share *sp, int fh, FInfo *fip) /* FIXME: maybe broken, needs testing */
+{
+ int rc;
+ Pkt *p;
+
+ p = t2hdr(s, sp, TRANS2_SET_FILE_INFORMATION);
+ pt2param(p);
+ pl16(p, fh); /* file handle */
+ pl16(p, SMB_SET_FILE_END_OF_FILE_INFO); /* Information level */
+ pl16(p, 0); /* reserved */
+
+ pt2data(p);
+ pl64(p, fip->size);
+ pl32(p, 0); /* padding ?! */
+ pl16(p, 0);
+
+ rc = t2rpc(p);
+ free(p);
+ return rc;
+}
+
+
+int
+T2fsvolumeinfo(Session *s, Share *sp, long *created, long *serialno,
+ char *label, int labellen)
+{
+ Pkt *p;
+ long ct, sn, n;
+
+ p = t2hdr(s, sp, TRANS2_QUERY_FS_INFORMATION);
+ pt2param(p);
+ pl16(p, SMB_QUERY_FS_VOLUME_INFO); /* Information level */
+
+ pt2data(p);
+
+ if(t2rpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ gt2data(p);
+ ct = gvtime(p); /* creation time */
+ sn = gl32(p); /* serial number */
+ n = gl32(p); /* label name length */
+ g8(p); /* reserved */
+ g8(p); /* reserved */
+
+ memset(label, 0, labellen);
+ if(n < labellen && n > 0)
+ gstr(p, label, n); /* file system label */
+ if(created)
+ *created = ct;
+ if(serialno)
+ *serialno = sn;
+
+ free(p);
+ return 0;
+}
+
+int
+T2fssizeinfo(Session *s, Share *sp, uvlong *total, uvlong *unused)
+{
+ Pkt *p;
+ uvlong t, f, n, b;
+
+ p = t2hdr(s, sp, TRANS2_QUERY_FS_INFORMATION);
+ pt2param(p);
+ pl16(p, SMB_QUERY_FS_SIZE_INFO); /* Information level */
+
+ pt2data(p);
+
+ if(t2rpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ gt2data(p);
+ t = gl64(p); /* total blocks */
+ f = gl64(p); /* free blocks */
+ n = gl32(p); /* sectors per block */
+ b = gl32(p); /* bytes per sector */
+
+ if(free)
+ *unused = f * n * b;
+ if(total)
+ *total = t * n * b;
+
+ free(p);
+ return 0;
+}
+
+int
+T2getdfsreferral(Session *s, Share *sp, char *path, int *gflags, int *used,
+ Refer *re, int nent)
+{
+ int i, vers, nret, len;
+ char tmp[1024];
+ uchar *base;
+ Pkt *p;
+
+ p = t2hdr(s, sp, TRANS2_GET_DFS_REFERRAL);
+ pt2param(p);
+ pl16(p, 3); /* max info level we understand, must be >= 3 for domain requests */
+ ppath(p, path);
+
+ pt2data(p);
+
+ if(t2rpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ memset(re, 0, sizeof *re * nent);
+ gt2data(p);
+
+ *used = gl16(p) / 2; /* length used (/2 as Windows counts in runes) */
+ nret = gl16(p); /* number of referrals returned */
+ *gflags = gl32(p); /* global flags */
+
+ for(i = 0; i < nret && i < nent && i < 16; i++){
+ base = p->pos;
+ vers = gl16(p); /* version of records */
+ len = gl16(p); /* length of records */
+ re[i].type = gl16(p); /* server type */
+ re[i].flags = gl16(p); /* referal flags */
+ switch(vers){
+ case 1:
+ re[i].prox = 0; /* nearby */
+ re[i].ttl = 5*60; /* 5 mins */
+ gstr(p, tmp, sizeof tmp);
+ re[i].addr = estrdup9p(tmp);
+ re[i].path = estrdup9p(tmp);
+ break;
+ case 2:
+ re[i].prox = gl32(p); /* not implemented in v2 */
+ re[i].ttl = gl32(p);
+ goff(p, base, re[i].path, sizeof tmp);
+ re[i].path = estrdup9p(tmp);
+ goff(p, base, re[i].path, sizeof tmp);/* spurious 8.3 path */
+ goff(p, base, tmp, sizeof tmp);
+ re[i].addr = estrdup9p(tmp);
+ break;
+ case 3:
+ if(re[i].flags & DFS_REFERAL_LIST){
+ re[i].prox = 0;
+ re[i].ttl = gl32(p);
+ goff(p, base, tmp, sizeof tmp);
+ re[i].path = estrdup9p(tmp);
+ gl16(p);
+ goff(p, base, tmp, sizeof tmp);
+ re[i].addr = estrdup9p(tmp);
+ }
+ else{
+ re[i].prox = 0;
+ re[i].ttl = gl32(p);
+ goff(p, base, tmp, sizeof tmp);
+ re[i].path = estrdup9p(tmp);
+ gl16(p); /* spurious 8.3 path */
+ goff(p, base, tmp, sizeof tmp);
+ re[i].addr = estrdup9p(tmp);
+ gl16(p); /* GUID (historic) */
+ }
+ break;
+ default:
+ /*
+ * this should never happen as we specify our maximum
+ * understood level in the request (above)
+ */
+ fprint(2, "%d - unsupported DFS infolevel\n", vers);
+ re[i].path = estrdup9p(tmp);
+ re[i].addr = estrdup9p(tmp);
+ break;
+ }
+ p->pos = base+len;
+ }
+
+ free(p);
+ return i;
+}
diff --git a/sys/src/cmd/cifs/transnt.c b/sys/src/cmd/cifs/transnt.c
new file mode 100755
index 000000000..453beaa74
--- /dev/null
+++ b/sys/src/cmd/cifs/transnt.c
@@ -0,0 +1,167 @@
+#include <u.h>
+#include <libc.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+#include "cifs.h"
+
+static Pkt *
+tnthdr(Session *s, Share *sp, int cmd)
+{
+ Pkt *p;
+
+ p = cifshdr(s, sp, SMB_COM_NT_TRANSACT);
+ p->tbase = p8(p, 0); /* 0 Max setup count to return */
+ pl16(p, 0); /* 1 reserved */
+ pl32(p, 0); /* 3 Total parameter count */
+ pl32(p, 0); /* 7 Total data count */
+ pl32(p, 64); /* 11 Max parameter count to return */
+ pl32(p, (MTU - T2HDRLEN)-64); /* 15 Max data count to return */
+ pl32(p, 0); /* 19 Parameter count (in this buffer) */
+ pl32(p, 0); /* 23 Offset to parameters (in this buffer) */
+ pl32(p, 0); /* 27 Count of data in this buffer */
+ pl32(p, 0); /* 31 Offset to data in this buffer */
+ p8(p, 1); /* 35 Count of setup words */
+ pl16(p, cmd); /* 37 setup[0] */
+ pl16(p, 0); /* padding ??!?!? */
+ pbytes(p);
+ return p;
+}
+
+static void
+ptntparam(Pkt *p)
+{
+ uchar *pos = p->pos;
+ assert(p->tbase != 0);
+
+ p->pos = p->tbase +23;
+ pl32(p, (pos - p->buf) - NBHDRLEN); /* param offset */
+
+ p->tparam = p->pos = pos;
+}
+
+static void
+ptntdata(Pkt *p)
+{
+ uchar *pos = p->pos;
+ assert(p->tbase != 0);
+ assert(p->tparam != 0);
+
+ p->pos = p->tbase +3;
+ pl32(p, pos - p->tparam); /* total param count */
+
+ p->pos = p->tbase +19;
+ pl32(p, pos - p->tparam); /* param count */
+
+ p->pos = p->tbase +31;
+ pl32(p, (pos - p->buf) - NBHDRLEN); /* data offset */
+ p->tdata = p->pos = pos;
+}
+
+static int
+tntrpc(Pkt *p)
+{
+ int got;
+ uchar *pos;
+ assert(p->tbase != 0);
+ assert(p->tdata != 0);
+
+ pos = p->pos;
+
+ p->pos = p->tbase +7;
+ pl32(p, pos - p->tdata); /* total data count */
+
+ p->pos = p->tbase +27;
+ pl32(p, pos - p->tdata); /* data count */
+
+ p->pos = pos;
+ if((got = cifsrpc(p)) == -1)
+ return -1;
+
+ g8(p); /* Reserved */
+ g8(p); /* Reserved */
+ g8(p); /* Reserved */
+ gl32(p); /* Total parameter count */
+ gl32(p); /* Total data count */
+ gl32(p); /* Parameter count in this buffer */
+ p->tparam = p->buf +NBHDRLEN +gl32(p); /* Parameter offset */
+ gl32(p); /* Parameter displacement */
+ gl32(p); /* Data count (this buffer); */
+ p->tdata = p->buf +NBHDRLEN +gl32(p); /* Data offset */
+ gl32(p); /* Data displacement */
+ g8(p); /* Setup count */
+ gl16(p); /* padding ??? */
+
+ return got;
+}
+
+static void
+gtntparam(Pkt *p)
+{
+ p->pos = p->tparam;
+}
+
+static void
+gtntdata(Pkt *p)
+{
+ p->pos = p->tdata;
+}
+
+
+int
+TNTquerysecurity(Session *s, Share *sp, int fh, char **usid, char **gsid)
+{
+ Pkt *p;
+ uchar *base;
+ Fmt fmt, *f = &fmt;
+ int n, i, off2owner, off2group;
+
+ p = tnthdr(s, sp, NT_TRANSACT_QUERY_SECURITY_DESC);
+ ptntparam(p);
+
+ pl16(p, fh); /* File handle */
+ pl16(p, 0); /* Reserved */
+ pl32(p, QUERY_OWNER_SECURITY_INFORMATION |
+ QUERY_GROUP_SECURITY_INFORMATION);
+
+ ptntdata(p);
+
+ if(tntrpc(p) == -1){
+ free(p);
+ return -1;
+ }
+
+ gtntdata(p);
+
+ base = p->pos;
+ gl16(p); /* revision */
+ gl16(p); /* type */
+ off2owner = gl32(p); /* offset to owner */
+ off2group = gl32(p); /* offset to group */
+ gl32(p);
+ gl32(p);
+
+ if(off2owner){
+ p->pos = base + off2owner;
+ fmtstrinit(f);
+ fmtprint(f, "S-%ud", g8(p)); /* revision */
+ n = g8(p); /* num auth */
+ fmtprint(f, "-%llud", gb48(p)); /* authority */
+ for(i = 0; i < n; i++)
+ fmtprint(f, "-%ud", gl32(p)); /* sub-authorities */
+ *usid = fmtstrflush(f);
+ }
+
+ if(off2group){
+ p->pos = base + off2group;
+ fmtstrinit(f);
+ fmtprint(f, "S-%ud", g8(p)); /* revision */
+ n = g8(p); /* num auth */
+ fmtprint(f, "-%llud", gb48(p)); /* authority */
+ for(i = 0; i < n; i++)
+ fmtprint(f, "-%ud", gl32(p)); /* sub-authorities */
+ *gsid = fmtstrflush(f);
+ }
+ free(p);
+ return 0;
+}