Subversion Repositories planix.SVN

Rev

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

#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;
}