Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include "headers.h"

static void
smblogprintattr(int cmd, ushort attr)
{
        if (attr & SMB_ATTR_READ_ONLY)
                smblogprint(cmd, " readonly");
        if (attr & SMB_ATTR_HIDDEN)
                smblogprint(cmd, " hidden");
        if (attr & SMB_ATTR_SYSTEM)
                smblogprint(cmd, " system");
        if (attr & SMB_ATTR_DIRECTORY)
                smblogprint(cmd, " directory");
        if (attr & SMB_ATTR_ARCHIVE)
                smblogprint(cmd, " archive");
}

static SmbFile *
openfile(SmbSession *s, SmbTree *t, char *path, ushort mode, ushort attr, ushort ofun, ulong createoptions, uvlong createsize,
        ushort *fidp, Dir **dp, ushort *actionp)
{
        int p9mode;
        int share;
        Dir *d = nil;
        int fd = -1;
        ushort action;
        SmbFile *f = nil;
        SmbSharedFile *sf = nil;
        char *fullpath = nil;
        int diropen = 0;

//smblogprint(-1, "%s A %r", path);
        p9mode = (mode >> SMB_OPEN_MODE_ACCESS_SHIFT) & SMB_OPEN_MODE_ACCESS_MASK;      
        share = (mode >> SMB_OPEN_MODE_SHARE_SHIFT) & SMB_OPEN_MODE_SHARE_MASK; 
        if (share == SMB_OPEN_MODE_SHARE_COMPATIBILITY) {
        badshare:
//smblogprint(-1, "%s SMB_OPEN_MODE_SHARE_COMPATIBILITY", path);
                smbseterror(s, ERRDOS, ERRbadshare);
                goto done;
        }
        smbstringprint(&fullpath, "%s%s", t->serv->path, path);
        d = dirstat(fullpath);
        if (d) {
                /* file exists */
                int ofunexist;
                if (d->mode & DMDIR) {
                        if (createoptions & SMB_CO_FILE) {
                                smbseterror(s, ERRDOS, ERRnoaccess);
                                goto done;
                        }
                }
                else if (createoptions & SMB_CO_DIRECTORY) {
                        smbseterror(s, ERRDOS, ERRnoaccess);
                        goto done;
                }
                        
                sf = smbsharedfileget(d, p9mode, &share);
                if (sf == nil)
                        goto badshare;
                action = 1;
                ofunexist = (ofun >> SMB_OFUN_EXIST_SHIFT) & SMB_OFUN_EXIST_MASK;
                if (ofunexist == SMB_OFUN_EXIST_FAIL) {
                        smbseterror(s, ERRDOS, ERRfilexists);
                        goto done;
                }
                else if (ofunexist == SMB_OFUN_EXIST_TRUNCATE) {
                        if ((d->mode & DMDIR) || (p9mode != OWRITE && p9mode != ORDWR)) {
                                smbseterror(s, ERRDOS, ERRbadaccess);
                                goto done;
                        }
                        p9mode |= OTRUNC;
                        action = 3;
                }
                else if (ofunexist != SMB_OFUN_EXIST_OPEN) {
                        smbseterror(s, ERRDOS, ERRbadaccess);
                        goto done;
                }
                if (d->mode & DMDIR)
                        diropen = 1;
                else
                        fd = open(fullpath, p9mode);
        }
        else {
                /* file does not exist */
                ulong p9attr;
                action = 3;
                if ((ofun & SMB_OFUN_NOEXIST_CREATE) == 0) {
                        smbseterror(s, ERRDOS, ERRbadfile);
                        goto done;
                }
                if (createsize != 0) {
                        smbseterror(s, ERRDOS, ERRunsup);
                        goto done;
                }
//smblogprint(-1, "creating: attr 0x%.4ux co 0x%.8lux\n", attr, createoptions);
                if (createoptions & SMB_CO_FILE) {
                        attr &= SMB_ATTR_DIRECTORY;
                        if (attr == 0)
                                attr = SMB_ATTR_NORMAL;
                }
                else if (createoptions & SMB_CO_DIRECTORY) {
                        attr &= ~SMB_ATTR_NORMAL;
                        attr |= SMB_ATTR_DIRECTORY;
                        p9mode = OREAD;
                }
//smblogprint(-1, "creating: before conversion attr 0x%.4ux\n", attr);
                p9attr = smbdosattr2plan9mode(attr);
//smblogprint(-1, "creating: after conversion p9attr 0%.uo\n", p9attr);
                fd = create(fullpath, p9mode, p9attr);
                if (fd >= 0) {
                        d = dirfstat(fd);
                        sf = smbsharedfileget(d, p9mode, &share);
                        if (sf == nil) {
                                close(fd);
                                remove(path);
                                goto badshare;
                        }
                }
        }
//smblogprint(-1, "%s D %r", fullpath);
        if (!diropen && fd < 0) {
                smbseterror(s, ERRSRV, ERRaccess);
                goto done;
        }
        f = smbemalloc(sizeof(SmbFile));
        if (diropen) {
                f->ioallowed = 0;
                f->fd = -1;
        }
        else {
                f->ioallowed = 1;
                f->fd = fd;
        }
        f->name = smbestrdup(path);
        f->sf = sf;
        sf = nil;
        f->share = share;
        f->p9mode = p9mode;
        f->t = t;
        if (s->fidmap == nil)
                s->fidmap = smbidmapnew();
        *fidp = smbidmapadd(s->fidmap, f);
//smblogprint(h->command, "REPLY:\n t->id=0x%ux fid=%d path=%s\n", t->id, *fidp, path);
        smblogprintif(smbglobals.log.fids, "openfile: 0x%.4ux/0x%.4ux %s\n", t->id, *fidp, path);
        if (actionp)
                *actionp = action;
        if (dp) {
                *dp = d;
                d = nil;
        }
done:
        if (sf)
                smbsharedfileput(nil, sf, share);
        free(d);
        free(fullpath);
        return f;
}

SmbProcessResult
smbcomopenandx(SmbSession *s, SmbHeader *h, uchar *pdata, SmbBuffer *b)
{
        uchar andxcommand;
        ushort andxoffset, flags, mode, sattr, attr;
        ulong createtime;       
        ushort ofun;
        ulong createsize, timeout;
        char *path = nil;
        ulong andxoffsetfixupoffset;
        SmbProcessResult pr;
        ushort action;
        Dir *d = nil;
        SmbFile *f;
        SmbTree *t;
        ushort fid;

        if (!smbcheckwordcount("comopenandx", h, 15))
                return SmbProcessResultFormat;

        andxcommand = *pdata++;
        pdata++;
        andxoffset = smbnhgets(pdata); pdata += 2;
        flags = smbnhgets(pdata); pdata += 2;
        mode = smbnhgets(pdata); pdata += 2;
        sattr = smbnhgets(pdata); pdata += 2;
        attr = smbnhgets(pdata); pdata += 2;
        createtime = smbnhgetl(pdata); pdata += 4;
        ofun = smbnhgets(pdata); pdata += 2;
        createsize = smbnhgetl(pdata); pdata += 4;
        timeout = smbnhgetl(pdata); pdata += 4;
        pdata += 4;
        USED(pdata);
        if (!smbbuffergetstring(b, h, SMB_STRING_PATH, &path)) {
                pr = SmbProcessResultFormat;
                goto done;
        }

        smbloglock();
        smblogprint(h->command, "flags 0x%.4ux", flags);
        if (flags & SMB_OPEN_FLAGS_ADDITIONAL)
                smblogprint(h->command, " additional");
        if (flags & SMB_OPEN_FLAGS_OPLOCK)
                smblogprint(h->command, " oplock");
        if (flags & SMB_OPEN_FLAGS_OPBATCH)
                smblogprint(h->command, " opbatch");
        smblogprint(h->command, "\n");
        smblogprint(h->command, "mode 0x%.4ux", mode);
        switch ((mode >> SMB_OPEN_MODE_ACCESS_SHIFT) & SMB_OPEN_MODE_ACCESS_MASK) {
        case OREAD:
                smblogprint(h->command, " OREAD");
                break;
        case OWRITE:
                smblogprint(h->command, " OWRITE");
                break;
        case ORDWR:
                smblogprint(h->command, " ORDWR");
                break;
        case OEXEC:
                smblogprint(h->command, " OEXEC");
                break;
        }
        switch ((mode >> SMB_OPEN_MODE_SHARE_SHIFT) & SMB_OPEN_MODE_SHARE_MASK) {
        case SMB_OPEN_MODE_SHARE_COMPATIBILITY:
                smblogprint(h->command, " compatinility");
                break;
        case SMB_OPEN_MODE_SHARE_EXCLUSIVE:
                smblogprint(h->command, " exclusive");
                break;
        case SMB_OPEN_MODE_SHARE_DENY_WRITE:
                smblogprint(h->command, " deny write");
                break;
        case SMB_OPEN_MODE_SHARE_DENY_READOREXEC:
                smblogprint(h->command, " deny readorxec");
                break;
        case SMB_OPEN_MODE_SHARE_DENY_NONE:
                smblogprint(h->command, " deny none");
                break;
        }
        if (mode & SMB_OPEN_MODE_WRITE_THROUGH)
                smblogprint(h->command, " write through");
        smblogprint(h->command, "\n");
        smblogprint(h->command, "sattr 0x%.4ux", sattr);
        smblogprintattr(h->command, sattr);
        smblogprint(h->command, "\n");
        smblogprint(h->command, "attr 0x%.4ux", attr);
        smblogprintattr(h->command, attr);
        smblogprint(h->command, "\n");
        smblogprint(h->command, "createtime 0x%.8lux\n", createtime);
        smblogprint(h->command, "ofun 0x%.4ux", ofun);
        if (ofun & SMB_OFUN_NOEXIST_CREATE)
                smblogprint(h->command, " noexistscreate");
        else
                smblogprint(h->command, " noexistfail");
        switch ((ofun >> SMB_OFUN_EXIST_SHIFT) & SMB_OFUN_EXIST_MASK) {
        case SMB_OFUN_EXIST_FAIL:
                smblogprint(h->command, " existfail");
                break;
        case SMB_OFUN_EXIST_OPEN:
                smblogprint(h->command, " existopen");
                break;
        case SMB_OFUN_EXIST_TRUNCATE:
                smblogprint(h->command, " existtruncate");
                break;
        }
        smblogprint(h->command, "\n");
        smblogprint(h->command, "createsize 0x%.8lux\n", createsize);
        smblogprint(h->command, "timeout 0x%.8lux\n", timeout);
        smblogprint(h->command, "path %s\n", path);
        smblogunlock();

        t = smbidmapfind(s->tidmap, h->tid);
        if (t == nil) {
                smbseterror(s, ERRSRV, ERRinvtid);
                goto errordone;
        }

        f = openfile(s, t, path, mode, attr, ofun, 0, createsize, &fid, &d, &action);
        if (f == nil) {
                pr = SmbProcessResultError;
                goto done;
        }
        h->wordcount = 15;
        if (!smbbufferputandxheader(s->response, h, &s->peerinfo, andxcommand, &andxoffsetfixupoffset)
                || !smbbufferputs(s->response, fid)
                || !smbbufferputs(s->response, smbplan9mode2dosattr(d->mode))
                || !smbbufferputl(s->response, smbplan9time2utime(d->mtime, s->tzoff))
                || !smbbufferputl(s->response, smbplan9length2size32(d->length))
                || !smbbufferputs(s->response, smbplan9mode2dosattr(d->mode)) // probbaly bogus
                || !smbbufferputs(s->response, 0)       // all files are files
                || !smbbufferputs(s->response, 0)       // pipe state
                || !smbbufferputs(s->response, action)
                || !smbbufferputl(s->response, 0)       // fileID
                || !smbbufferputs(s->response, 0)
                || !smbbufferputs(s->response, 0)) {    // bytecount 0
                smbfileclose(s, f);
                pr = SmbProcessResultMisc;
                goto done;
        }
        if (andxcommand != SMB_COM_NO_ANDX_COMMAND)
                pr = smbchaincommand(s, h, andxoffsetfixupoffset, andxcommand, andxoffset, b);
        else
                pr = SmbProcessResultReply;
        goto done;      
errordone:
        pr = SmbProcessResultError;
done:
        free(path);
        free(d);
        return pr;
}

SmbProcessResult
smbcomopen(SmbSession *s, SmbHeader *h, uchar *pdata, SmbBuffer *b)
{
        uchar fmt;
        char *path;
        ushort mode, attr;
        SmbTree *t;
        ushort fid;
        Dir *d = nil;
        SmbFile *f;
        SmbProcessResult pr;

        if (!smbcheckwordcount("comopen", h, 2))
                return SmbProcessResultFormat;
        mode = smbnhgets(pdata);
        attr = smbnhgets(pdata + 2);
        if (!smbbuffergetb(b, &fmt)
                || fmt != 4
                || !smbbuffergetstring(b, h, SMB_STRING_PATH, &path)) {
                pr = SmbProcessResultFormat;
                goto done;
        }
        t = smbidmapfind(s->tidmap, h->tid);
        if (t == nil) {
                smbseterror(s, ERRSRV, ERRinvtid);
        error:
                pr = SmbProcessResultError;
                goto done;
        }
        f = openfile(s, t, path, mode, attr,
                SMB_OFUN_EXIST_OPEN << SMB_OFUN_EXIST_SHIFT,
                0, 0, &fid, &d, nil);
        if (f == nil)
                goto error;
        h->wordcount = 7;
        if (!smbbufferputheader(s->response, h, &s->peerinfo)
                || !smbbufferputs(s->response, fid)
                || !smbbufferputs(s->response, smbplan9mode2dosattr(d->mode))
                || !smbbufferputl(s->response, smbplan9time2utime(d->mtime, s->tzoff))
                || !smbbufferputl(s->response, smbplan9length2size32(d->length))
                || !smbbufferputs(s->response, 2)               // lies - this should be the actual access allowed
                || !smbbufferputs(s->response, 0))
                pr = SmbProcessResultMisc;
        else
                pr = SmbProcessResultReply;
done:
        free(path);
        free(d);
        return pr;
}


/*
   smb_com      SMBcreate       smb_com      SMBcreate
   smb_wct      3               smb_wct      1
   smb_vwv[0]   attribute       smb_vwv[0]   file handle
   smb_vwv[1]   time low        smb_bcc      0
   smb_vwv[2]   time high
   smb_bcc      min = 2
   smb_buf[]    ASCII -- 04
                file pathname
*/

SmbProcessResult
smbcomcreate(SmbSession *s, SmbHeader *h, uchar *pdata, SmbBuffer *b)
{
        int ofun, attr, mode;
        long createtime;
        char *path;
        uchar fmt;
        SmbFile *f;
        SmbTree *t;
        ushort fid;
        SmbProcessResult pr;

        path = nil;
        if (!smbcheckwordcount("comcreate", h, 3))
                return SmbProcessResultFormat;

        smblogprint(h->command, "tid=%d\n", h->tid);
        attr = smbnhgets(pdata); pdata += 2;
        createtime = smbnhgetl(pdata);
        if (!smbbuffergetb(b, &fmt) || fmt != 0x04 || 
            !smbbuffergetstring(b, h, SMB_STRING_PATH, &path)){
                pr = SmbProcessResultError;
                goto done;
        }

        smbloglock();
        smblogprint(h->command, "path %s\n", path);
        smblogprint(h->command, "attr 0x%.4ux", attr);
        smblogprintattr(h->command, attr);
        smblogprint(h->command, "\n");
        smblogprint(h->command, "createtime 0x%.8lux\n", createtime);
        smblogunlock();

        t = smbidmapfind(s->tidmap, h->tid);
        if (t == nil) {
                pr = SmbProcessResultError;
                goto done;
        }

        mode = (ORDWR<<SMB_OPEN_MODE_ACCESS_SHIFT) | // SFS: FIXME: should be OWRITE?
                (SMB_OPEN_MODE_SHARE_EXCLUSIVE<<SMB_OPEN_MODE_SHARE_SHIFT);
        ofun = SMB_OFUN_NOEXIST_CREATE|(SMB_OFUN_EXIST_FAIL<<SMB_OFUN_EXIST_SHIFT);
        f = openfile(s, t, path, mode, attr, ofun, SMB_CO_FILE, 0, &fid, nil, nil);
        if (f == nil) {
                pr = SmbProcessResultError;
                goto done;
        }

        h->wordcount = 1;               // SFS: FIXME: unsure of this constant, maybe should be 3
        if (!smbbufferputheader(s->response, h, &s->peerinfo)
                || !smbbufferputs(s->response, fid)
                || !smbbufferputs(s->response, 0)){     // bytecount 0
                pr = SmbProcessResultMisc;
                goto done;
        }
        pr = SmbProcessResultReply;
        goto done;

done:
        free(path);
        return pr;
}


typedef struct SmbSblut {
        char *s;
        ulong mask;
} SmbSblut;

static SmbSblut dasblut[] = {
        { "SMB_DA_SPECIFIC_READ_DATA", SMB_DA_SPECIFIC_READ_DATA },
        { "SMB_DA_SPECIFIC_WRITE_DATA", SMB_DA_SPECIFIC_WRITE_DATA },
        { "SMB_DA_SPECIFIC_APPEND_DATA", SMB_DA_SPECIFIC_APPEND_DATA },
        { "SMB_DA_SPECIFIC_READ_EA", SMB_DA_SPECIFIC_READ_EA },
        { "SMB_DA_SPECIFIC_WRITE_EA", SMB_DA_SPECIFIC_WRITE_EA },
        { "SMB_DA_SPECIFIC_EXECUTE", SMB_DA_SPECIFIC_EXECUTE },
        { "SMB_DA_SPECIFIC_DELETE_CHILD", SMB_DA_SPECIFIC_DELETE_CHILD },
        { "SMB_DA_SPECIFIC_READ_ATTRIBUTES", SMB_DA_SPECIFIC_READ_ATTRIBUTES },
        { "SMB_DA_SPECIFIC_WRITE_ATTRIBUTES", SMB_DA_SPECIFIC_WRITE_ATTRIBUTES },
        { "SMB_DA_STANDARD_DELETE_ACCESS", SMB_DA_STANDARD_DELETE_ACCESS },
        { "SMB_DA_STANDARD_READ_CONTROL_ACCESS", SMB_DA_STANDARD_READ_CONTROL_ACCESS },
        { "SMB_DA_STANDARD_WRITE_DAC_ACCESS", SMB_DA_STANDARD_WRITE_DAC_ACCESS },
        { "SMB_DA_STANDARD_WRITE_OWNER_ACCESS", SMB_DA_STANDARD_WRITE_OWNER_ACCESS },
        { "SMB_DA_STANDARD_SYNCHRONIZE_ACCESS", SMB_DA_STANDARD_SYNCHRONIZE_ACCESS },
        { "SMB_DA_GENERIC_ALL_ACCESS", SMB_DA_GENERIC_ALL_ACCESS },
        { "SMB_DA_GENERIC_EXECUTE_ACCESS", SMB_DA_GENERIC_EXECUTE_ACCESS },
        { "SMB_DA_GENERIC_WRITE_ACCESS", SMB_DA_GENERIC_WRITE_ACCESS },
        { "SMB_DA_GENERIC_READ_ACCESS", SMB_DA_GENERIC_READ_ACCESS },
        { 0 }
};

static SmbSblut efasblut[] = {
        { "SMB_ATTR_READ_ONLY", SMB_ATTR_READ_ONLY },
        { "SMB_ATTR_HIDDEN", SMB_ATTR_HIDDEN },
        { "SMB_ATTR_SYSTEM", SMB_ATTR_SYSTEM },
        { "SMB_ATTR_DIRECTORY", SMB_ATTR_DIRECTORY },
        { "SMB_ATTR_ARCHIVE", SMB_ATTR_ARCHIVE },
        { "SMB_ATTR_NORMAL", SMB_ATTR_NORMAL },
        { "SMB_ATTR_COMPRESSED", SMB_ATTR_COMPRESSED },
        { "SMB_ATTR_TEMPORARY", SMB_ATTR_TEMPORARY },
        { "SMB_ATTR_WRITETHROUGH", SMB_ATTR_WRITETHROUGH },
        { "SMB_ATTR_NO_BUFFERING", SMB_ATTR_NO_BUFFERING },
        { "SMB_ATTR_RANDOM_ACCESS", SMB_ATTR_RANDOM_ACCESS },
        { 0 }
};

static SmbSblut sasblut[] = {
        { "SMB_SA_SHARE_READ", SMB_SA_SHARE_READ },
        { "SMB_SA_SHARE_WRITE", SMB_SA_SHARE_WRITE },
        { "SMB_SA_SHARE_DELETE", SMB_SA_SHARE_DELETE },
        { "SMB_SA_NO_SHARE", SMB_SA_NO_SHARE },
        { 0 }
};

static SmbSblut cosblut[] = {
        { "SMB_CO_DIRECTORY", SMB_CO_DIRECTORY },
        { "SMB_CO_WRITETHROUGH", SMB_CO_WRITETHROUGH },
        { "SMB_CO_SEQUENTIAL_ONLY", SMB_CO_SEQUENTIAL_ONLY },
        { "SMB_CO_FILE", SMB_CO_FILE },
        { "SMB_CO_NO_EA_KNOWLEDGE", SMB_CO_NO_EA_KNOWLEDGE },
        { "SMB_CO_EIGHT_DOT_THREE_ONLY", SMB_CO_EIGHT_DOT_THREE_ONLY },
        { "SMB_CO_RANDOM_ACCESS", SMB_CO_RANDOM_ACCESS },
        { "SMB_CO_DELETE_ON_CLOSE", SMB_CO_DELETE_ON_CLOSE },
        { 0 }
};

static SmbSlut cdslut[] = {
        { "SMB_CD_SUPERCEDE", SMB_CD_SUPERCEDE },
        { "SMB_CD_OPEN", SMB_CD_OPEN },
        { "SMB_CD_CREATE", SMB_CD_CREATE },
        { "SMB_CD_OPEN_IF", SMB_CD_OPEN_IF },
        { "SMB_CD_OVERWRITE", SMB_CD_OVERWRITE },
        { "SMB_CD_OVERWRITE_IF", SMB_CD_OVERWRITE_IF },
        { 0 }
};

static void
smbsblutlogprint(uchar cmd, SmbSblut *sblut, ulong mask)
{
        while (sblut->s) {
                if (mask && (sblut->mask & mask) || (mask == 0 && sblut->mask == 0))
                        smblogprint(cmd, " %s", sblut->s);
                sblut++;
        }
}

SmbProcessResult
smbcomntcreateandx(SmbSession *s, SmbHeader *h, uchar *pdata, SmbBuffer *b)
{
        uchar andxcommand;
        ushort andxoffset;
        char *path = nil;
        SmbProcessResult pr;
        ulong namelength;
        ulong flags;
        ulong rootdirectoryfid, desiredaccess;
        uvlong allocationsize;
        ulong extfileattributes, shareaccess, createdisposition, createoptions, impersonationlevel;
        uchar securityflags;
        int p9mode;
        int sharemode;
        ushort mode;
        SmbTree *t;
        ushort ofun;
        SmbFile *f;
        ushort fid;
        Dir *d = nil;
        ushort action;
        uvlong mtime;
        ulong andxoffsetfixup;

        if (!smbcheckwordcount("comntcreateandx", h, 24))
                return SmbProcessResultFormat;

        andxcommand = *pdata++;
        pdata++;
        andxoffset = smbnhgets(pdata); pdata += 2;
        pdata++;
        namelength = smbnhgets(pdata); pdata += 2;
        flags = smbnhgetl(pdata); pdata += 4;
        rootdirectoryfid = smbnhgetl(pdata); pdata += 4;
        desiredaccess = smbnhgetl(pdata); pdata += 4;
        allocationsize = smbnhgetv(pdata); pdata += 8;
        extfileattributes = smbnhgetl(pdata); pdata += 4;
        shareaccess = smbnhgetl(pdata); pdata += 4;
        createdisposition = smbnhgetl(pdata); pdata += 4;
        createoptions = smbnhgetl(pdata); pdata += 4;
        impersonationlevel = smbnhgetl(pdata); pdata += 4;
        securityflags = *pdata++;
        USED(pdata);

        if (!smbbuffergetstring(b, h, SMB_STRING_PATH, &path)) {
                pr = SmbProcessResultFormat;
                goto done;
        }

        smblogprint(h->command, "namelength %d\n", namelength);
        smblogprint(h->command, "flags 0x%.8lux\n", flags);
        smblogprint(h->command, "rootdirectoryfid %lud\n", rootdirectoryfid);
        smblogprint(h->command, "desiredaccess 0x%.8lux", desiredaccess);
        smbsblutlogprint(h->command, dasblut, desiredaccess);
        smblogprint(h->command, "\n");
        smblogprint(h->command, "allocationsize %llud\n", allocationsize);
        smblogprint(h->command, "extfileattributes 0x%.8lux", extfileattributes);
        smbsblutlogprint(h->command, efasblut, extfileattributes);
        smblogprint(h->command, "\n");
        smblogprint(h->command, "shareaccess 0x%.8lux", shareaccess);
        smbsblutlogprint(h->command, sasblut, shareaccess);
        smblogprint(h->command, "\n");
        smblogprint(h->command, "createdisposition 0x%.8lux %s\n",
                createdisposition, smbrevslut(cdslut, createdisposition));
        smblogprint(h->command, "createoptions 0x%.8lux", createoptions);
        smbsblutlogprint(h->command, cosblut, createoptions);
        smblogprint(h->command, "\n");
        smblogprint(h->command, "impersonationlevel 0x%.8lux\n", impersonationlevel);
        smblogprint(h->command, "securityflags 0x%.2ux\n", securityflags);
        smblogprint(h->command, "path %s\n", path);

        if (rootdirectoryfid != 0) {
                smblogprint(-1, "smbcomntcreateandx: fid relative not implemented\n");
                goto unimp;
        }

        if (desiredaccess & SMB_DA_GENERIC_MASK)
                switch (desiredaccess & SMB_DA_GENERIC_MASK){
                case SMB_DA_GENERIC_READ_ACCESS:
                        p9mode = OREAD;
                        break;
                case SMB_DA_GENERIC_WRITE_ACCESS:
                        p9mode = OWRITE;
                        break;
                case SMB_DA_GENERIC_ALL_ACCESS:
                        p9mode = ORDWR;
                        break;
                case SMB_DA_GENERIC_EXECUTE_ACCESS:
                        p9mode = OEXEC;
                        break;
                default:
                        p9mode = OREAD;
                        break;
                }
        else
        if (desiredaccess & SMB_DA_SPECIFIC_READ_DATA)
                if (desiredaccess & (SMB_DA_SPECIFIC_WRITE_DATA | SMB_DA_SPECIFIC_APPEND_DATA))
                        p9mode = ORDWR;
                else
                        p9mode = OREAD;
        else if (desiredaccess & (SMB_DA_SPECIFIC_WRITE_DATA | SMB_DA_SPECIFIC_APPEND_DATA))
                p9mode = ORDWR;
        else
                p9mode = OREAD;

        if (shareaccess == SMB_SA_NO_SHARE)
                sharemode = SMB_OPEN_MODE_SHARE_EXCLUSIVE;
        else if (shareaccess & (SMB_SA_SHARE_READ | SMB_SA_SHARE_WRITE) ==
                (SMB_SA_SHARE_READ | SMB_SA_SHARE_WRITE))
                sharemode = SMB_OPEN_MODE_SHARE_DENY_NONE;
        else if (shareaccess & SMB_SA_SHARE_READ)
                sharemode = SMB_OPEN_MODE_SHARE_DENY_WRITE;
        else if (shareaccess & SMB_SA_SHARE_WRITE)
                sharemode = SMB_OPEN_MODE_SHARE_DENY_READOREXEC;
        else
                sharemode = SMB_OPEN_MODE_SHARE_DENY_NONE;

        mode = (sharemode << SMB_OPEN_MODE_SHARE_SHIFT) | (p9mode << SMB_OPEN_MODE_ACCESS_SHIFT);

        switch (createdisposition) {
        default:
                smblogprint(-1, "smbcomntcreateandx: createdisposition 0x%.8lux not implemented\n", createdisposition);
                goto unimp;
        case SMB_CD_OPEN:
                ofun = SMB_OFUN_EXIST_OPEN;
                break;
        case SMB_CD_CREATE:
                ofun = SMB_OFUN_EXIST_FAIL | SMB_OFUN_NOEXIST_CREATE;
                break;
        case SMB_CD_OPEN_IF:
                ofun = SMB_OFUN_EXIST_OPEN | SMB_OFUN_NOEXIST_CREATE;
                break;
        case SMB_CD_OVERWRITE:
                ofun = SMB_OFUN_EXIST_TRUNCATE;
                break;
        case SMB_CD_OVERWRITE_IF:
                ofun = SMB_OFUN_EXIST_TRUNCATE | SMB_OFUN_NOEXIST_CREATE;
                break;
        }

        t = smbidmapfind(s->tidmap, h->tid);
        if (t == nil) {
                smbseterror(s, ERRSRV, ERRinvtid);
                pr = SmbProcessResultError;
                goto done;
        }

        f = openfile(s, t, path, mode, extfileattributes, ofun, createoptions, allocationsize, &fid, &d, &action);

        if (f == nil) {
                pr = SmbProcessResultError;
                goto done;
        }

        h->wordcount = 42;
        mtime =  smbplan9time2time(d->mtime);
        if (!smbbufferputandxheader(s->response, h, &s->peerinfo, andxcommand, &andxoffsetfixup)
                || !smbbufferputb(s->response, 0)               // oplocks? pah
                || !smbbufferputs(s->response, fid)
                || !smbbufferputl(s->response, action)
                || !smbbufferputv(s->response, mtime)
                || !smbbufferputv(s->response, smbplan9time2time(d->atime))
                || !smbbufferputv(s->response, mtime)
                || !smbbufferputv(s->response, mtime)
                || !smbbufferputl(s->response, smbplan9mode2dosattr(d->mode))
                || !smbbufferputv(s->response, smbl2roundupvlong(d->length, smbglobals.l2allocationsize))
                || !smbbufferputv(s->response, d->length)
                || !smbbufferputbytes(s->response, nil, 4)
                || !smbbufferputb(s->response, (d->qid.type & QTDIR) != 0)
                || !smbbufferputbytes(s->response, nil, 8)
                || !smbbufferputs(s->response, 0)) {
                pr = SmbProcessResultMisc;
                goto done;
        }

        if (andxcommand != SMB_COM_NO_ANDX_COMMAND)
                pr = smbchaincommand(s, h, andxoffsetfixup, andxcommand, andxoffset, b);
        else
                pr = SmbProcessResultReply;

        goto done;

unimp:
        pr = SmbProcessResultUnimp;

done:
        free(path);
        free(d);

        return pr;
}