837 lines
18 KiB
C
837 lines
18 KiB
C
#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;
|
|
|
|
if(s->mtu > MTU)
|
|
s->mtu = MTU;
|
|
|
|
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;
|
|
p->request = cmd; /* for debug */
|
|
|
|
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 reply, 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 < 32+NBHDRLEN){
|
|
werrstr("cifs packet too small (%d < %d)\n", got, 32+NBHDRLEN);
|
|
return -1;
|
|
}
|
|
|
|
gmem(p, m, nelem(magic));
|
|
if(memcmp(m, magic, nelem(magic)) != 0){
|
|
werrstr("cifsrpc: bad magic number in packet 0x%02ux%02ux%02ux%02ux",
|
|
m[0], m[1], m[2], m[3]);
|
|
return -1;
|
|
}
|
|
|
|
reply = g8(p); /* cmd */
|
|
err = gl32(p); /* errcode */
|
|
g8(p); /* flags */
|
|
p->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(reply != p->request){
|
|
fprint(2, "unexpected reply (cmd=%x/%x seq=%d/%d)\n",
|
|
reply, p->request, seq, p->seq);
|
|
return -1;
|
|
}
|
|
|
|
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){
|
|
werrstr("bad sequence number (%d != %d)\n", p->seq, seq);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
p->tid = tid;
|
|
if(p->s->uid == NO_UID)
|
|
p->s->uid = uid;
|
|
|
|
if(p->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;
|
|
|
|
/*
|
|
* This should not be necessary, however the XP seems to use
|
|
* Unicode strings in its Negoiate response, but not set the
|
|
* Flags2 UNICODE flag.
|
|
*
|
|
* It does however echo back the FL_UNICODE flag we set in the
|
|
* flags2 negoiate request.
|
|
*
|
|
* The bodge is to force FL_UNICODE for this single request,
|
|
* clearing it after. Later we set FL2_UNICODE if the server
|
|
* agrees to CAP_UNICODE as it "should" be done.
|
|
*/
|
|
s->flags2 |= FL2_UNICODE;
|
|
p = cifshdr(s, nil, SMB_COM_NEGOTIATE);
|
|
s->flags2 &= ~FL2_UNICODE;
|
|
|
|
pbytes(p);
|
|
for(i = 0; i < nelem(dialects); i++){
|
|
p8(p, STR_DIALECT);
|
|
pascii(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);
|
|
|
|
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;
|
|
}
|