bug in virtual filesystem which could cause the filesystem to disappear if you looked in the Shares virtual file tweeks to Auth code (mainly tidying) and trying to get auth to work to vista. turns out you need to apply a hotfix (regedit) http://support.microsoft.com/kb/957441 and cifs is happy with vista. major overhall of DFS code. works much better but still does not cross servers and doesn't give any helpful messages when it fails :-( case significance is now enforced by cifs, though you still cannot create Makefile and makefile as seperate entities. This is enabled by default but can be disabled by a command line option. also fixed a bug in UNICODE handling, I got away with this on windows but cinap's cifsd drew blood. execute requests are always denied on cifs files. Perhaps this too should be a command line option, but having plan9 executables on a windows server is a rarity IMHO. in CIFStreeconnect() removed ths strupr() call - trees are not neccessarly upper case, some servers are case significant on volume/share names. -Steve Notes: Fri Jul 29 17:18:20 EDT 2011 geoff just going to update to current source. Reference: /n/sources/patch/sorry/cifs-big Date: Wed Sep 29 16:38:46 CES 2010 Signed-off-by: steve@quintile.net Reviewed-by: geoff --- /sys/src/cmd/cifs/auth-testcase.c Wed Sep 29 16:36:50 2010 +++ /sys/src/cmd/cifs/auth-testcase.c Wed Sep 29 16:36:47 2010 @@ -137,30 +137,28 @@ static int ntv2_blob(uchar *blob, int len, char *windom) { - int n; - uvlong nttime; - Rune r; - char *d; + uvlong t; uchar *p; enum { /* name types */ Beof, /* end of name list */ - Bnetbios, /* Netbios machine name */ + Bhost, /* Netbios host name */ Bdomain, /* Windows Domain name (NT) */ - Bdnsfqdn, /* DNS Fully Qualified Domain Name */ - Bdnsname, /* DNS machine name (win2k) */ + Bdnshost, /* DNS host name */ + Bdnsdomain, /* DNS domain name */ }; p = blob; - *p++ = 1; /* response type */ - *p++ = 1; /* max response type understood by client */ + *p++ = 1; /* 8bit: response type */ + *p++ = 1; /* 8bit: max response type understood by client */ + *p++ = 0; /* 16bit: reserved */ *p++ = 0; - *p++ = 0; /* 2 bytes reserved */ + *p++ = 0; /* 32bit: unknown */ *p++ = 0; *p++ = 0; *p++ = 0; - *p++ = 0; /* 4 bytes unknown */ + #ifdef NTLMV2_TEST *p++ = 0xf0; @@ -172,18 +170,19 @@ *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; + t = time(nil); /* 64bit: time in NT format */ + t += 11644473600LL; + t *= 10000000LL; + *p++ = t; + *p++ = t >> 8; + *p++ = t >> 16; + *p++ = t >> 24; + *p++ = t >> 32; + *p++ = t >> 40; + *p++ = t >> 48; + *p++ = t >> 56; #endif + #ifdef NTLMV2_TEST *p++ = 0x05; *p++ = 0x83; @@ -195,38 +194,17 @@ *p++ = 0x6d; #else genrandom(p, 8); - p += 8; /* client nonce */ + p += 8; /* 64bit: 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; /* 32bit: unknown data */ *p++ = 0; - *p++ = Beof; /* name type */ - - *p++ = 0; - *p++ = 0; /* name length */ - - *p++ = 0x65; *p++ = 0; *p++ = 0; - *p++ = 0; /* unknown data */ + + p += putname(p, len - (p-blob), windom, Bdomain); + p += putname(p, len - (p-blob), "", Beof); + return p - blob; } --- /sys/src/cmd/cifs/auth.c Wed Sep 29 16:36:54 2010 +++ /sys/src/cmd/cifs/auth.c Wed Sep 29 16:36:52 2010 @@ -135,77 +135,80 @@ static int -ntv2_blob(uchar *blob, int len, char *windom) +putname(uchar *buf, int len, char *name, int type) { int n; - uvlong nttime; Rune r; char *d; + uchar *p = buf; + + *p++ = type; + *p++ = 0; /* 16bit: name type */ + + n = utflen(name) * 2; + *p++ = n; + *p++ = n >> 8; /* 16bit: name length */ + + d = name; + while(*d != 0 && p-buf < len-8){ + d += chartorune(&r, d); + r = toupperrune(r); + *p++ = r; + *p++ = r >> 8; + } /* var: actual name */ + + return p - buf; +} + +static int +ntv2_blob(uchar *blob, int len, char *windom) +{ + uvlong t; uchar *p; enum { /* name types */ Beof, /* end of name list */ - Bnetbios, /* Netbios machine name */ + Bhost, /* Netbios host name */ Bdomain, /* Windows Domain name (NT) */ - Bdnsfqdn, /* DNS Fully Qualified Domain Name */ - Bdnsname, /* DNS machine name (win2k) */ + Bdnshost, /* DNS host name */ + Bdnsdomain, /* DNS domain name */ }; p = blob; - *p++ = 1; /* response type */ - *p++ = 1; /* max response type understood by client */ + *p++ = 1; /* 8bit: response type */ + *p++ = 1; /* 8bit: max response type understood by client */ + *p++ = 0; /* 16bit: reserved */ *p++ = 0; - *p++ = 0; /* 2 bytes reserved */ + *p++ = 0; /* 32bit: unknown */ *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 */ + t = time(nil); + t += 11644473600LL; + t *= 10000000LL; - 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++ = t; /* 64bit: time in NT format */ + *p++ = t >> 8; + *p++ = t >> 16; + *p++ = t >> 24; + *p++ = t >> 32; + *p++ = t >> 40; + *p++ = t >> 48; + *p++ = t >> 56; - *p++ = 0; - *p++ = Beof; /* name type */ + genrandom(p, 8); + p += 8; /* 64bit: client nonce */ + *p++ = 0; /* 32bit: unknown data */ *p++ = 0; - *p++ = 0; /* name length */ - - *p++ = 0x65; *p++ = 0; *p++ = 0; - *p++ = 0; /* unknown data */ + + p += putname(p, len - (p-blob), windom, Bdomain); + p += putname(p, len - (p-blob), "", Beof); + return p - blob; } @@ -293,7 +296,7 @@ memcpy(ap->mackey[0]+MD5dlen, ap->resp[0], MACkeylen-MD5dlen); /* NTLM v2 */ - n = ntv2_blob(blob, sizeof(blob), windom); + 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; --- /sys/src/cmd/cifs/cifs.c Wed Sep 29 16:36:59 2010 +++ /sys/src/cmd/cifs/cifs.c Wed Sep 29 16:36:56 2010 @@ -39,7 +39,7 @@ 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->flags2 = FL2_KNOWS_LONG_NAMES | FL2_HAS_LONG_NAMES | FL2_PAGEING_IO | FL2_UNICODE; s->macidx = -1; return s; @@ -137,7 +137,7 @@ int cifsrpc(Pkt *p) { - int flags2, got, err; + int got, err; uint tid, uid, seq; uchar *pos; char m[nelem(magic)]; @@ -160,7 +160,7 @@ gmem(p, m, nelem(magic)); if(memcmp(m, magic, nelem(magic)) != 0){ - werrstr("cifsrpc: bad magic number in packet %20ux%02ux%02ux%02ux", + werrstr("cifsrpc: bad magic number in packet 0x%02ux%02ux%02ux%02ux", m[0], m[1], m[2], m[3]); return -1; } @@ -168,7 +168,7 @@ g8(p); /* cmd */ err = gl32(p); /* errcode */ g8(p); /* flags */ - flags2 = gl16(p); /* flags2 */ + p->flags2 = gl16(p); /* flags2 */ gl16(p); /* PID MS bits */ seq = gl32(p); /* reserved */ gl32(p); /* MAC (if in use) */ @@ -205,7 +205,7 @@ if(p->s->uid == NO_UID) p->s->uid = uid; - if(flags2 & FL2_NT_ERRCODES){ + if(p->flags2 & FL2_NT_ERRCODES){ /* is it a real error rather than info/warning/chatter? */ if((err & 0xF0000000) == 0xC0000000){ werrstr("%s", nterrstr(err)); @@ -247,7 +247,7 @@ pbytes(p); for(i = 0; i < nelem(dialects); i++){ p8(p, STR_DIALECT); - pstr(p, dialects[i]); + pascii(p, dialects[i]); } if(cifsrpc(p) == -1){ @@ -277,7 +277,7 @@ 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->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 */ @@ -368,7 +368,7 @@ gl16(p); gl16(p); /* no security blob here - we don't understand extended security anyway */ - gstr(p, os, sizeof(os)); + gstr(p, os, sizeof os); s->remos = estrdup9p(os); free(p); @@ -386,9 +386,9 @@ resp = Sess->auth->resp[0]; len = Sess->auth->len[0]; if((s->secmode & SECMODE_USER) != SECMODE_USER){ - memset(zeros, 0, sizeof(zeros)); + memset(zeros, 0, sizeof zeros); resp = zeros; - len = sizeof(zeros); + len = sizeof zeros; } p = cifshdr(s, nil, SMB_COM_TREE_CONNECT_ANDX); @@ -408,11 +408,11 @@ } 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) */ + pascii(p, "?????"); /* service type any (so we can do RAP calls) */ if(cifsrpc(p) == -1){ free(p); --- /sys/src/cmd/cifs/cifs.h Wed Sep 29 16:37:04 2010 +++ /sys/src/cmd/cifs/cifs.h Wed Sep 29 16:37:01 2010 @@ -350,10 +350,10 @@ int uid; /* user (authentication) ID */ int seq; /* sequence number */ int seqrun; /* sequence numbering active */ - int caps; /* server capabilities */ + int caps; /* server's capabilities */ int support; /* support bits */ - int flags; /* SMB flags */ - int flags2; /* SMB flags 2 */ + int flags; /* SMB flags that we will send in the next packet */ + int flags2; /* SMB flags 2 that we will send in the next packet */ int nocache; /* disable write behind caching in server */ int pid; /* process ID */ int mid; /* multiplex ID */ @@ -378,6 +378,7 @@ int tid; /* tree ID received from server */ int seq; /* sequence number expected in reply */ + int flags2; /* flags2 received with this packet */ uchar *seqbase; /* cifs: pos of sequence number in packet */ uchar *wordbase; /* cifs: base of words section of data */ --- /sys/src/cmd/cifs/dfs.c Wed Sep 29 16:37:08 2010 +++ /sys/src/cmd/cifs/dfs.c Wed Sep 29 16:37:06 2010 @@ -5,7 +5,7 @@ * * 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 + * 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 @@ -15,34 +15,33 @@ * * 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 + * 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. + * systems may report more meaningful values. The expiry time my is + * similarly zero so I guess at 5 mins. * - * If the redirection points to a "hidden" share (i.e., its name + * 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 + * as us which replies to a ping (I.E. is up). This could short + * circuit the tests as the a server on the same sumnet 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 (?!). + * 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". + * 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. + * 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 #include @@ -53,18 +52,24 @@ #include <9p.h> #include "cifs.h" -#define SINT_MAX 0x7fffffff +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 *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; @@ -100,27 +105,30 @@ } static Dfscache * -lookup(char *opath, int *exact) +lookup(char *path, int *match) { - char *path; int len, n, m; Dfscache *cp, *best; - *exact = 0; + if(match) + *match = Nomatch; len = 0; best = nil; - path = opath; - m = strlen(opath); + m = strlen(path); for(cp = Cache; cp; cp = cp->next){ n = strlen(cp->src); - if(n < len || cistrncmp(path, cp->src, n) != 0 || - path[n] != 0 && path[n] != '/') + 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){ - *exact = 1; + if(match) + *match = Exactmatch; break; } } @@ -130,21 +138,23 @@ char * mapfile(char *opath) { - int exact; - char *p, *path; 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)); + if((cp = lookup(path, nil)) != 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) + if(Debug && strstr(Debug, "dfs")) print("mapfile src=%q => dst=%q\n", opath, p); return p; } @@ -152,47 +162,50 @@ int mapshare(char *path, Share **osp) { - int i, exact; - char *try, *tail[] = { "", "$" }; - Dfscache *cp; + int i; Share *sp; + Dfscache *cp; + char *s, *try; + char *tail[] = { "", "$" }; - if((cp = lookup(path, &exact)) == nil) + if((cp = lookup(path, nil)) == nil) return 0; - for(sp = Shares; sp < Shares+Nshares; sp++) - if(cistrcmp(cp->share, trimshare(sp->name)) == 0){ - if(Debug && strstr(Debug, "dfs") != nil) - print("mapshare, already connected, src=%q => dst=%q\n", - path, sp->name); - *osp = sp; - 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")) + 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. + * 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; + 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); + if(Debug && strstr(Debug, "dfs")) + print("mapshare connected, src=%q dst=%q\n", path, cp->share); return 0; } free(try); } - if(Debug && strstr(Debug, "dfs") != nil) + if(Debug && strstr(Debug, "dfs")) print("mapshare failed src=%s\n", path); + werrstr("not found"); return -1; } @@ -200,7 +213,7 @@ * 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 + * 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. */ @@ -212,12 +225,13 @@ char *p, *a[4]; enum { Hostname = 1, - Shre = 2, - Path = 3, - Rtt_tol = 10, + Sharename = 2, + Pathname = 3, + + Rtt_tol = 10 }; - if(Debug && strstr(Debug, "dfs") != nil) + if(Debug && strstr(Debug, "dfs")) print(" remap %s\n", re->addr); for(p = re->addr; *p; p++) @@ -225,29 +239,29 @@ *p = '/'; if(cp->prox < re->prox){ - if(Debug && strstr(Debug, "dfs") != nil) + if(Debug && strstr(Debug, "dfs")) 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) + if(Debug && strstr(Debug, "dfs")) print(" remap nfields=%d\n", n); return -1; } if((rtt = ping(a[Hostname], Dfstout)) == -1){ - if(Debug && strstr(Debug, "dfs") != nil) + if(Debug && strstr(Debug, "dfs")) 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); + if(Debug && strstr(Debug, "dfs")) + 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[Path] = ""; + a[Pathname] = ""; if(re->ttl == 0) re->ttl = 60*5; @@ -258,10 +272,10 @@ cp->prox = re->prox; cp->expiry = time(nil)+re->ttl; cp->host = estrdup9p(a[Hostname]); - cp->share = estrdup9p(trimshare(a[Shre])); - cp->path = estrdup9p(a[Path]); - if(Debug && strstr(Debug, "dfs") != nil) - print(" remap ping OK prox=%d host=%s share=%s path=%s\n", + cp->share = estrdup9p(trimshare(a[Sharename])); + cp->path = estrdup9p(a[Pathname]); + if(Debug && strstr(Debug, "dfs")) + print(" remap ping OK prox=%d host=%s share=%s path=%s\n", cp->prox, cp->host, cp->share, cp->path); return 0; } @@ -275,8 +289,7 @@ if(level > 8) return -1; - if((n = T2getdfsreferral(s, &Ipc, path, &gflags, &used, retab, - nelem(retab))) == -1) + if((n = T2getdfsreferral(s, &Ipc, path, &gflags, &used, retab, nelem(retab))) == -1) return -1; if(! (gflags & DFS_HEADER_ROOT)) @@ -284,23 +297,25 @@ 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", + if(Debug && strstr(Debug, "dfs")) + 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) /* ???? */ + } + 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", + + if(Debug && strstr(Debug, "dfs")) + print("referal level=%d path=%q found=%d used=%d\n", level, path, found, used); if(!found) return -1; @@ -315,49 +330,60 @@ int redirect(Session *s, Share *sp, char *path) { - int exact; + int match; char *unc; Dfscache *cp; - if(Debug && strstr(Debug, "dfs") != nil) + if(Debug && strstr(Debug, "dfs")) print("redirect name=%q path=%q\n", sp->name, path); - cp = lookup(path, &exact); - if(cp && exact){ - if(cp->expiry >= time(nil)){ /* cache hit */ - if(Debug && strstr(Debug, "dfs") != nil) + 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")) 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 */ + } + 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); + + 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); + if(Debug && strstr(Debug, "dfs")) + print("redirect refresh failed unc=%q\n", unc); free(unc); return -1; } free(unc); - if(Debug && strstr(Debug, "dfs") != nil) + if(Debug && strstr(Debug, "dfs")) 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); + 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) @@ -369,7 +395,7 @@ cp->prox = SINT_MAX; if(redir1(s, unc, cp, 1) == -1){ - if(Debug && strstr(Debug, "dfs") != nil) + if(Debug && strstr(Debug, "dfs")) print("redirect new failed unc=%q\n", unc); free(unc); free(cp); @@ -380,8 +406,9 @@ cp->src = estrdup9p(path); cp->next = Cache; Cache = cp; - if(Debug && strstr(Debug, "dfs") != nil) + if(Debug && strstr(Debug, "dfs")) print("redirect cache=miss src=%q => share=%q path=%q\n", cp->src, cp->share, cp->path); return 0; } + --- /sys/src/cmd/cifs/fs.c Wed Sep 29 16:37:12 2010 +++ /sys/src/cmd/cifs/fs.c Wed Sep 29 16:37:10 2010 @@ -8,6 +8,7 @@ static char *period(long sec); + int shareinfo(Fmt *f) { @@ -30,20 +31,22 @@ sp = &Shares[j]; break; } - sp->tid = Ipc.tid; + 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; + 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 %s", type, si2.comment); + fmtprint(f, "%-8s %5d/%-5d %s", type, + si2.activeusrs, si2.maxusrs, si2.comment); free(si2.name); free(si2.comment); free(si2.path); @@ -64,12 +67,15 @@ fi = nil; if((got = RAPFileenum2(Sess, &Ipc, "", "", &fi)) == -1){ - fmtprint(f, "RAPfileenum: %r (Only Administrator has permission)\n"); + fmtprint(f, "RAPfileenum: %r\n"); return 0; } for(i = 0; i < got; i++){ - fmtprint(f, "0x%02x %-4d %-24q %q ", fi[i].perms, + 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); @@ -83,10 +89,11 @@ { int i; typedef struct { - int val; - char *name; + int val; + char *name; } Tab; - static Tab captab[] = { + + Tab captab[] = { { 1, "raw-mode" }, { 2, "mpx-mode" }, { 4, "unicode" }, @@ -106,7 +113,8 @@ { 0x40000000, "compressed" }, { 0x80000000, "extended-security" }, }; - static Tab sectab[] = { + + Tab sectab[] = { { 1, "user-auth" }, { 2, "challange-response" }, { 4, "signing-available" }, @@ -116,7 +124,7 @@ 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": ""); + Sess->slip, Sess->mtu, (Sess->isguest)? "as guest": ""); fmtprint(f, "caps: "); for(i = 0; i < nelem(captab); i++) @@ -169,10 +177,11 @@ * 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 + * 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) @@ -180,14 +189,14 @@ Refer *re, retab[128]; int n, used, flags; - n = T2getdfsreferral(Sess, &Ipc, path, &flags, &used, retab, nelem(retab)); - if(n == -1) + if((n = T2getdfsreferral(Sess, &Ipc, path, &flags, &used, retab, nelem(retab))) == -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); } @@ -219,7 +228,8 @@ fmtprint(f, "%-24q ", nl[i].name); if(RAPuserinfo(Sess, &Ipc, nl[i].name, &ui) != -1){ - fmtprint(f, "%-48q %q", ui.fullname, ui.comment); + fmtprint(f, "%-48q %q", + ui.fullname, ui.comment); free(ui.user); free(ui.comment); free(ui.fullname); @@ -266,32 +276,32 @@ { int more, got, i, j; Serverinfo *si; - static char *types[] = { + char *types[] = { [0] "workstation", [1] "server", - [2] "SQL server", + [2] "SQL", [3] "DC", - [4] "backup DC", - [5] "time source", - [6] "Apple server", - [7] "Novell server", - [8] "domain member", + [4] "BDC", + [5] "time", + [6] "Apple", + [7] "NetWare", + [8] nil, // domain member [9] "printer server", [10] "dial-up server", [11] "Unix", [12] "NT", [13] "WFW", - [14] "MFPN (?)", + [14] "MFPN", // DEC pathworks ? [15] "NT server", - [16] "potential browser", - [17] "backup browser", + [16] nil, // potential master browser + [17] "BMB", [18] "LMB", [19] "DMB", [20] "OSF Unix", [21] "VMS", [22] "Win95", [23] "DFS", - [24] "NT cluster", + [24] "Cluster", [25] "Terminal server", [26] "[26]", [27] "[27]", @@ -299,14 +309,12 @@ }; si = nil; - if((got = RAPServerenum2(Sess, &Ipc, Sess->auth->windom, type, &more, - &si)) == -1){ + 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){ + if((got = RAPServerenum3(Sess, &Ipc, Sess->auth->windom, type, got-1, si)) == -1){ fmtprint(f, "RAPServerenum3: %r\n"); return 0; } @@ -345,7 +353,7 @@ int days, hrs, min; static char when[32]; - days = sec / (60L * 60L * 24L); + days = sec / (60L * 60L * 24L); sec -= days * (60L * 60L * 24L); hrs = sec / (60L * 60L); sec -= hrs * (60L * 60L); @@ -357,3 +365,5 @@ snprint(when, sizeof(when), "%d:%d:%ld ", hrs, min, sec); return when; } + + --- /sys/src/cmd/cifs/info.c Wed Sep 29 16:37:16 2010 +++ /sys/src/cmd/cifs/info.c Wed Sep 29 16:37:14 2010 @@ -8,22 +8,22 @@ struct { - char *name; - int (*func)(Fmt *f); - char *buf; - int len; + 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 }, + { "Users" , userinfo }, + { "Groups" , groupinfo }, + { "Shares" , shareinfo }, + { "Connection" , conninfo }, + { "Sessions" , sessioninfo }, + { "Dfsroot" , dfsrootinfo }, + { "Dfscache" , dfscacheinfo }, + { "Domains" , domaininfo }, + { "Openfiles" , openfileinfo }, + { "Workstations" , workstationinfo }, + { "Filetable" , filetableinfo }, }; int @@ -63,6 +63,7 @@ d->qid.vers = 1; d->qid.path = slot; d->qid.type = 0; + return 0; } @@ -76,10 +77,12 @@ if(Infdir[path].buf != nil) return 0; fmtstrinit(&f); - if((*Infdir[path].func)(&f) == -1l) + if((*Infdir[path].func)(&f) == -1) + return -1; + if((Infdir[path].buf = fmtstrflush(&f)) == nil) + return -1; + if((Infdir[path].len = strlen(Infdir[path].buf)) <= 0) return -1; - Infdir[path].buf = fmtstrflush(&f); - Infdir[path].len = strlen(Infdir[path].buf); return 0; } @@ -104,3 +107,4 @@ free(Infdir[path].buf); Infdir[path].buf = nil; } + --- /sys/src/cmd/cifs/main.c Wed Sep 29 16:37:21 2010 +++ /sys/src/cmd/cifs/main.c Wed Sep 29 16:37:18 2010 @@ -25,8 +25,9 @@ }; extern int chatty9p; +extern int Checkcase = 1; -int Dfstout = 100; /* timeout (in ms) for ping of dfs servers (assume they are local) */ +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 */ @@ -112,7 +113,7 @@ memset(d, 0, sizeof(Dir)); d->type = 'C'; d->dev = 1; - d->name = strlwr(estrdup9p(name)); + d->name = estrdup9p(name); d->uid = estrdup9p("bill"); d->muid = estrdup9p("boyd"); d->gid = estrdup9p("trog"); @@ -329,64 +330,138 @@ 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){ + if(strcmp(want, share) == 0) + return 1; + else + return 0; + } + /* + * OK, a DFS redirection points us from a directory XXX + * to a share named YYY. There is no casechecking we can + * do so we allow either case - its 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) { int rc, n, i; - char *npath; + char *p, *npath, *winpath; Aux *a = fid->aux; FInfo fi; static char e[ERRMAX]; *e = 0; npath = newpath(a->path, name); - if(strcmp(npath, "/") == 0) + if(strcmp(npath, "/") == 0){ /* root dir */ *qid = mkqid("/", 1, 1, Proot, 0); - else if(strrchr(npath, '/') == npath){ - if((n = walkinfo(name)) != -1) + 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 { + } + else { /* share name */ for(i = 0; i < Nshares; i++){ n = strlen(Shares[i].name); - if(cistrncmp(npath+1, Shares[i].name, n) != 0 || - npath[n+1] != 0 && npath[n+1] != '/') + 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){ - a->sp = Shares+i; - *qid = mkqid(npath, 1, 1, Pshare, i); - } else { + if(i >= Nshares){ free(npath); return "not found"; } + a->sp = Shares+i; + *qid = mkqid(npath, 1, 1, Pshare, i); } - } else { + 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){ - free(npath); - return "not found"; - } + if(mapshare(npath, &a->sp) == -1){ + rerrstr(e, sizeof(e)); + free(npath); + return e; + } - memset(&fi, 0, sizeof fi); + 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(Sess->caps & CAP_NT_SMBS) - rc = T2queryall(Sess, a->sp, mapfile(npath), &fi); - else - rc = T2querystandard(Sess, a->sp, mapfile(npath), &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 && - redirect(Sess, a->sp, npath) != -1) + 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(rc == -1){ - rerrstr(e, sizeof(e)); - free(npath); - return e; - } - *qid = mkqid(npath, fi.attribs & ATTR_DIRECTORY, fi.changed, 0, 0); } + 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; @@ -463,8 +538,15 @@ access = 2; break; case OEXEC: - access = 3; - break; + werrstr("will not exec via cifs"); + return -1; + /* Files called ls or cat are possible on a Windows share, + * however they are unlikely to be plan9 exeutables, + * so we fail attempts to exec(2) them. + * + * access = 3; + * break; + */ default: werrstr("%d bad open mode", mode & OMASK); return -1; @@ -540,8 +622,15 @@ access = GENERIC_ALL; break; case OEXEC: - access = GENERIC_EXECUTE; - break; + werrstr("will not exec via cifs"); + return -1; + /* Files called ls or cat are possible on a Windows share, + * however they are unlikely to be plan9 exeutables. + * so we fail attempts to exec(2) them. + * + * access = GENERIC_EXECUTE; + * break; + */ default: werrstr("%d bad open mode", mode & OMASK); return -1; @@ -1078,6 +1167,9 @@ case 'd': Debug = EARGF(usage()); break; + case 'i': + Checkcase = 0; + break; case 'k': keyp = EARGF(usage()); break; @@ -1140,7 +1232,7 @@ Sess->cname = strlwr(estrdup9p(cname)); if(CIFStreeconnect(Sess, cname, "IPC$", &Ipc) == -1) - fprint(2, "IPC$, %r - can't connect\n"); + fprint(2, "%s, %r - can't connect\n", "IPC$"); Nshares = 0; if(argc == 1){ @@ -1153,7 +1245,6 @@ 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; @@ -1176,7 +1267,7 @@ ", %r\n", argv0, Host, argv[i]); continue; } - Shares[Nshares].name = strlwr(estrdup9p(argv[i])); + Shares[Nshares].name = estrdup9p(argv[i]); Nshares++; } --- /sys/src/cmd/cifs/mkfile Wed Sep 29 16:37:25 2010 +++ /sys/src/cmd/cifs/mkfile Wed Sep 29 16:37:23 2010 @@ -12,9 +12,3 @@ s->caps & CAP_UNICODE){ + if(p->s->flags2 & FL2_UNICODE){ if(((p->pos - p->buf) % 2) != 0) /* pad to even offset */ p8(p, 0); while(*str){ @@ -57,7 +57,7 @@ if(!str) return s; - if(p->s->caps & CAP_UNICODE){ + if(p->s->flags2 & FL2_UNICODE){ if(((p->pos - p->buf) % 2) != 0) p8(p, 0); /* pad to even offset */ while(*str){ @@ -238,7 +238,7 @@ if(!n || !str) return; - if(p->s->caps & CAP_UNICODE){ + if(p->flags2 & FL2_UNICODE){ i = 0; while(*p->pos && n && p->pos < p->eop){ r = gl16(p); --- /sys/src/cmd/cifs/remsmb.h Wed Sep 29 16:37:39 2010 +++ /sys/src/cmd/cifs/remsmb.h Wed Sep 29 16:37:37 2010 @@ -48,11 +48,11 @@ #ifndef _REMDEF_ #define _REMDEF_ -/* - * ==================================================================== - * SMB XACT message descriptors. - * ==================================================================== - */ +//==================================================================== +// +/* SMB XACT message descriptors. */ +// +//==================================================================== #define REMSmb_share_info_0 "B13" #define REMSmb_share_info_1 "B13BWz" @@ -402,11 +402,11 @@ #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). - */ +// +/* 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" @@ -458,9 +458,9 @@ #define REMSmb_NetAccountConfirmUpd_P "b12g12D" #define REMSmb_update_info_0 "K" -/* - * SamrOemChangePasswordUser2 api support - */ -#define REMSmb_SamOEMChgPasswordUser2 "B516B16" /* data that is passed */ +// +/* SamrOemChangePasswordUser2 api support */ +// +#define REMSmb_SamOEMChgPasswordUser2 "B516B16" /* data that is passed */ #endif /* ndef _REMDEF_ */ --- /sys/src/cmd/cifs/trans.c Wed Sep 29 16:37:44 2010 +++ /sys/src/cmd/cifs/trans.c Wed Sep 29 16:37:41 2010 @@ -279,7 +279,7 @@ } q = *sip + ngot; - while(nret-- != 0){ + while(nret-- > 0){ gconv(p, conv, tmp, sizeof tmp); q->wrkstn = estrdup9p(tmp); gconv(p, conv, tmp, sizeof tmp); @@ -338,7 +338,7 @@ memset(*nlp, 0, sizeof(Namelist) * navail); q = *nlp + ngot; - while(ngot < navail && nret--){ + while(ngot < navail && nret-- > 0){ gmem(p, tmp, 21); tmp[21] = 0; q->name = estrdup9p(tmp); @@ -394,7 +394,7 @@ memset(*nlp, 0, sizeof(Namelist) * navail); q = *nlp + ngot; - while(ngot < navail && nret--){ + while(ngot < navail && nret-- > 0){ gmem(p, tmp, 21); tmp[21] = 0; q->name = estrdup9p(tmp); @@ -448,7 +448,7 @@ memset(*nlp, 0, sizeof(Namelist) * navail); q = *nlp + ngot; - while(ngot < navail && nret--){ + while(ngot < navail && nret-- > 0){ gmem(p, tmp, 21); tmp[21] = 0; q->name = estrdup9p(tmp); @@ -507,7 +507,7 @@ memset(*nlp, 0, sizeof(Namelist) * navail); } q = *nlp + ngot; - while(ngot < navail && nret--){ + while(ngot < navail && nret-- > 0){ gmem(p, tmp, 21); tmp[21] = 0; q->name = estrdup9p(tmp); --- /sys/src/cmd/cifs/README.Vista Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/cifs/README.Vista Wed Sep 29 16:37:45 2010 @@ -0,0 +1,11 @@ +Connection fails to Windows Vista or Windows 2008 server using NTLMv2 auth + +This is due to a bug in Windows, either use an alternative +authentication strategy, or apply the Microsoft HotFix, which +is just a matter of creating a new registry entry, see: + + http://support.microsoft.com/kb/957441 + +-Steve + +Tue Sep 21 17:05:48 BST 2010 --- /sys/man/4/cifs Wed Sep 29 16:37:49 2010 +++ /sys/man/4/cifs Wed Sep 29 16:37:47 2010 @@ -20,6 +20,9 @@ ] [ .B -m .I mntpnt +] [ +.B -t +.I dfs-timeout ] .I host [ @@ -32,8 +35,7 @@ (shares or trees in MS terminology) published by such servers. .PP The root of the mounted directory contains one subdirectory per share, -always named in lower case, and a few virtual files of mixed case which -give additional server, session, share, and user information. +and a few virtual files give additional information. The arguments are: .TF "-a\fI auth-method" .PD @@ -71,6 +73,14 @@ .B -D 9P request debug. .TP +.B -i +By default +.I cifs(4) +attempts to enforce case significance file and directory names, though objects +which differ only in their case still cannot co-exist in the same directory. The +.B -i +option disables this behaveiour. +.TP .BI -k " keyparam" lists extra parameters which will be passed to .IR factotum (4) @@ -112,6 +122,11 @@ post the service as .BI /srv/ srvname. .TP +.BI -t " dfs-timeout" +sets the timeout in for DFS redirections - it defaults to 100ms. +This is a reasonable minimum, it should have a value just greater than +the RTT to the most distant server being accessed. +.TP .I host The address of the remote server to connect to. .TP @@ -126,7 +141,7 @@ .TP .B Shares Contains a list of the currently attached shares, -with fields giving the share name, disk free space / capacity, the share type, +with fields giving the share name, the share type, disk free space / capacity, and a descriptive comment from the server. .TP .B Connection @@ -161,28 +176,48 @@ giving the features of that OS. .TP .B Dfsroot -Top level DFS routing giving the DFS link type, time to live of the data, -proximity of the server, the Netbios or DNS name and -a physical path or a machine that this maps to. -.IP -DNS paths are usually assigned dynamicially as a form of load balancing. +Lists the top level DFS domains and the servers that +provision them. +.TP +.B Dfscache +Contents of the DFS referal cache, giving the path prefix, +the expiry time (or -1 for never), the measured RTT to the server +in milliseconds, the server proximity (0 is local), the server name, +and the share name on that server. +.SH COMPATIBILITY +.I Cifs +has been tested against +.IR aquarela (8), +Windows 95, NT4.0sp6, +Windows server 2003, Windows server 2003, WinXP pro, +Samba 2.0 (Pluto VideoSpace), and Samba 3.0. +.LP +Windows Vista require a hotfix (registry change) +to support NTLMv2 without GSSAPI, see http://support.microsoft.com/kb/957441. +Alternatively the +.B -a +option can be used to force +.I cifs +to use one of the less secure authentication mechnisms. +.LP +Windows 7 has dropped support for RAP, which is used to generate +the synthetic files offered by +.IR cifs . +RAP is also used to enumerate the shares offered by the remote host so +remote share names must always be specified on the command line. +.LP +The NetApp Filer was supported by earlier releases, however recent +attempts to mount one have failed. Should a server be available it is +likely that this could be easily fixed. +.PP .SH SOURCE .B /sys/src/cmd/cifs .SH SEE ALSO .IR factotum (4), .IR aquarela (8) .SH BUGS -NetApp Filer compatibility has not yet been tested; there may not be any. +DFS support is unfinished, it will not follow referals that span servers. .PP -DFS support is unfinished. -.PP -Kerberos authentication is unfinished. +Kerberos authentication is not supported. .PP NetBios name resolution is not supported, though it is now rarely used. -.PP -.I Cifs -has only been tested against -.IR aquarela (8), -Windows 95, NT4.0sp6, -Windows server 2003, WinXP pro, Samba 3.0, and Samba 2.0 (Pluto VideoSpace). -No support is attempted for servers predating NT 4.0.