123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- /*
- * DNS referrals give two main fields: the path to connect to in
- * /Netbios-host-name/share-name/path... form and a network
- * address of how to find this path of the form domain.dom.
- *
- * The domain.dom is resolved in XP/Win2k etc using AD to do
- * a lookup (this is a consensus view, I don't think anyone
- * has proved it). I cannot do this as AD needs Kerberos and
- * LDAP which I don't have.
- *
- * Instead I just use the NetBios names passed in the paths
- * and assume that the servers are in the same DNS domain as me
- * and have their DNS hostname set the same as their netbios
- * called-name; thankfully this always seems to be the case (so far).
- *
- * I have not added support for starting another instance of
- * cifs to connect to other servers referenced in DFS links,
- * this is not a problem for me and I think it hides a load
- * of problems of its own wrt plan9's private namespaces.
- *
- * The proximity of my test server (AD enabled) is always 0 but some
- * systems may report more meaningful values. The expiry time is
- * similarly zero, so I guess at 5 mins.
- *
- * If the redirection points to a "hidden" share (i.e., its name
- * ends in a $) then the type of the redirection is 0 (unknown) even
- * though it is a CIFS share.
- *
- * It would be nice to add a check for which subnet a server is on
- * so our first choice is always the the server on the same subnet
- * as us which replies to a ping (i.e., is up). This could short-
- * circuit the tests as the a server on the same subnet will always
- * be the fastest to get to.
- *
- * If I set Flags2_DFS then I don't see DFS links, I just get
- * path not found (?!).
- *
- * If I do a QueryFileInfo of a DFS link point (IE when I'am doing a walk)
- * Then I just see a directory, its not until I try to walk another level
- * That I get "IO reparse tag not handled" error rather than
- * "Path not covered".
- *
- * If I check the extended attributes of the QueryFileInfo in walk() then I can
- * see this is a reparse point and so I can get the referral. The only
- * problem here is that samba and the like may not support this.
- */
- #include <u.h>
- #include <libc.h>
- #include <fcall.h>
- #include <thread.h>
- #include <libsec.h>
- #include <ctype.h>
- #include <9p.h>
- #include "cifs.h"
- enum {
- Nomatch, /* not found in cache */
- Exactmatch, /* perfect match found */
- Badmatch /* matched but wrong case */
- };
- #define SINT_MAX 0x7fffffff
- typedef struct Dfscache Dfscache;
- struct Dfscache {
- Dfscache*next; /* next entry */
- char *src;
- char *host;
- char *share;
- char *path;
- int32_t expiry; /* expiry time in sec */
- int32_t rtt; /* round trip time, nsec */
- int prox; /* proximity, lower = closer */
- };
- Dfscache *Cache;
- int
- dfscacheinfo(Fmt *f)
- {
- int32_t ex;
- Dfscache *cp;
- for(cp = Cache; cp; cp = cp->next){
- ex = cp->expiry - time(nil);
- if(ex < 0)
- ex = -1;
- fmtprint(f, "%-42s %6ld %8.1f %4d %-16s %-24s %s\n",
- cp->src, ex, (double)cp->rtt/1000.0L, cp->prox,
- cp->host, cp->share, cp->path);
- }
- return 0;
- }
- char *
- trimshare(char *s)
- {
- char *p;
- static char name[128];
- strncpy(name, s, sizeof(name));
- name[sizeof(name)-1] = 0;
- if((p = strrchr(name, '$')) != nil && p[1] == 0)
- *p = 0;
- return name;
- }
- static Dfscache *
- lookup(char *path, int *match)
- {
- int len, n, m;
- Dfscache *cp, *best;
- if(match)
- *match = Nomatch;
- len = 0;
- best = nil;
- m = strlen(path);
- for(cp = Cache; cp; cp = cp->next){
- n = strlen(cp->src);
- if(n < len)
- continue;
- if(strncmp(path, cp->src, n) != 0)
- continue;
- if(path[n] != 0 && path[n] != '/')
- continue;
- best = cp;
- len = n;
- if(n == m){
- if(match)
- *match = Exactmatch;
- break;
- }
- }
- return best;
- }
- char *
- mapfile(char *opath)
- {
- int exact;
- Dfscache *cp;
- char *p, *path;
- static char npath[MAX_DFS_PATH];
- path = opath;
- if((cp = lookup(path, &exact)) != nil){
- snprint(npath, sizeof npath, "/%s%s%s%s", cp->share,
- *cp->path? "/": "", cp->path, path + strlen(cp->src));
- path = npath;
- }
- if((p = strchr(path+1, '/')) == nil)
- p = "/";
- if(Debug && strstr(Debug, "dfs") != nil)
- print("mapfile src=%q => dst=%q\n", opath, p);
- return p;
- }
- int
- mapshare(char *path, Share **osp)
- {
- int i;
- Share *sp;
- Dfscache *cp;
- char *s, *try;
- char *tail[] = { "", "$" };
- if((cp = lookup(path, nil)) == nil)
- return 0;
- for(sp = Shares; sp < Shares+Nshares; sp++){
- s = trimshare(sp->name);
- if(cistrcmp(cp->share, s) != 0)
- continue;
- if(Checkcase && strcmp(cp->share, s) != 0)
- continue;
- if(Debug && strstr(Debug, "dfs") != nil)
- print("mapshare, already connected, src=%q => dst=%q\n", path, sp->name);
- *osp = sp;
- return 0;
- }
- /*
- * Try to autoconnect to share if it is not known. Note even if you
- * didn't specify any shares and let the system autoconnect you may
- * not already have the share you need as RAP (which we use) throws
- * away names > 12 chars long. If we where to use RPC then this block
- * of code would be less important, though it would still be useful
- * to catch Shares added since cifs(1) was started.
- */
- sp = Shares + Nshares;
- for(i = 0; i < 2; i++){
- try = smprint("%s%s", cp->share, tail[i]);
- if(CIFStreeconnect(Sess, Sess->cname, try, sp) == 0){
- sp->name = try;
- *osp = sp;
- Nshares++;
- if(Debug && strstr(Debug, "dfs") != nil)
- print("mapshare connected, src=%q dst=%q\n",
- path, cp->share);
- return 0;
- }
- free(try);
- }
- if(Debug && strstr(Debug, "dfs") != nil)
- print("mapshare failed src=%s\n", path);
- werrstr("not found");
- return -1;
- }
- /*
- * Rtt_tol is the fractional tollerance for RTT comparisons.
- * If a later (further down the list) host's RTT is less than
- * 1/Rtt_tol better than my current best then I don't bother
- * with it. This biases me towards entries at the top of the list
- * which Active Directory has already chosen for me and prevents
- * noise in RTTs from pushing me to more distant machines.
- */
- static int
- remap(Dfscache *cp, Refer *re)
- {
- int n;
- int32_t rtt;
- char *p, *a[4];
- enum {
- Hostname = 1,
- Sharename = 2,
- Pathname = 3,
- Rtt_tol = 10
- };
- if(Debug && strstr(Debug, "dfs") != nil)
- print(" remap %s\n", re->addr);
- for(p = re->addr; *p; p++)
- if(*p == '\\')
- *p = '/';
- if(cp->prox < re->prox){
- if(Debug && strstr(Debug, "dfs") != nil)
- print(" remap %d < %d\n", cp->prox, re->prox);
- return -1;
- }
- if((n = getfields(re->addr, a, sizeof(a), 0, "/")) < 3){
- if(Debug && strstr(Debug, "dfs") != nil)
- print(" remap nfields=%d\n", n);
- return -1;
- }
- if((rtt = ping(a[Hostname], Dfstout)) == -1){
- if(Debug && strstr(Debug, "dfs") != nil)
- print(" remap ping failed\n");
- return -1;
- }
- if(cp->rtt < rtt && (rtt/labs(rtt-cp->rtt)) < Rtt_tol){
- if(Debug && strstr(Debug, "dfs") != nil)
- print(" remap bad ping %ld < %ld && %ld < %d\n",
- cp->rtt, rtt, (rtt/labs(rtt-cp->rtt)), Rtt_tol);
- return -1;
- }
- if(n < 4)
- a[Pathname] = "";
- if(re->ttl == 0)
- re->ttl = 60*5;
- free(cp->host);
- free(cp->share);
- free(cp->path);
- cp->rtt = rtt;
- cp->prox = re->prox;
- cp->expiry = time(nil)+re->ttl;
- cp->host = estrdup9p(a[Hostname]);
- cp->share = estrdup9p(trimshare(a[Sharename]));
- cp->path = estrdup9p(a[Pathname]);
- if(Debug && strstr(Debug, "dfs") != nil)
- print(" remap ping OK prox=%d host=%s share=%s path=%s\n",
- cp->prox, cp->host, cp->share, cp->path);
- return 0;
- }
- static int
- redir1(Session *s, char *path, Dfscache *cp, int level)
- {
- Refer retab[16], *re;
- int n, gflags, used, found;
- if(level > 8)
- return -1;
- if((n = T2getdfsreferral(s, &Ipc, path, &gflags, &used, retab,
- nelem(retab))) == -1)
- return -1;
- if(! (gflags & DFS_HEADER_ROOT))
- used = SINT_MAX;
- found = 0;
- for(re = retab; re < retab+n; re++){
- if(Debug && strstr(Debug, "dfs") != nil)
- print("referal level=%d prox=%d path=%q addr=%q\n",
- level, re->prox, re->path, re->addr);
- if(gflags & DFS_HEADER_STORAGE){
- if(remap(cp, re) == 0)
- found = 1;
- } else{
- if(redir1(s, re->addr, cp, level+1) != -1) /* ???? */
- found = 1;
- }
- free(re->addr);
- free(re->path);
- }
- if(Debug && strstr(Debug, "dfs") != nil)
- print("referal level=%d path=%q found=%d used=%d\n",
- level, path, found, used);
- if(!found)
- return -1;
- return used;
- }
- /*
- * We can afford to ignore the used count returned by redir
- * because of the semantics of 9p - we always walk to directories
- * ome and we a time and we always walk before any other file operations
- */
- int
- redirect(Session *s, Share *sp, char *path)
- {
- int match;
- char *unc;
- Dfscache *cp;
- if(Debug && strstr(Debug, "dfs") != nil)
- print("redirect name=%q path=%q\n", sp->name, path);
- cp = lookup(path, &match);
- if(match == Badmatch)
- return -1;
- if(cp && match == Exactmatch){
- if(cp->expiry >= time(nil)){ /* cache hit */
- if(Debug && strstr(Debug, "dfs") != nil)
- print("redirect cache=hit src=%q => share=%q path=%q\n",
- cp->src, cp->share, cp->path);
- return 0;
- } else{ /* cache hit, but entry stale */
- cp->rtt = SINT_MAX;
- cp->prox = SINT_MAX;
- unc = smprint("//%s/%s/%s%s%s", s->auth->windom,
- cp->share, cp->path, *cp->path? "/": "",
- path + strlen(cp->src) + 1);
- if(unc == nil)
- sysfatal("no memory: %r");
- if(redir1(s, unc, cp, 1) == -1){
- if(Debug && strstr(Debug, "dfs") != nil)
- print("redirect refresh failed unc=%q\n",
- unc);
- free(unc);
- return -1;
- }
- free(unc);
- if(Debug && strstr(Debug, "dfs") != nil)
- print("redirect refresh cache=stale src=%q => share=%q path=%q\n",
- cp->src, cp->share, cp->path);
- return 0;
- }
- }
- /* in-exact match or complete miss */
- if(cp)
- unc = smprint("//%s/%s/%s%s%s", s->auth->windom, cp->share,
- cp->path, *cp->path? "/": "", path + strlen(cp->src) + 1);
- else
- unc = smprint("//%s%s", s->auth->windom, path);
- if(unc == nil)
- sysfatal("no memory: %r");
- cp = emalloc9p(sizeof(Dfscache));
- memset(cp, 0, sizeof(Dfscache));
- cp->rtt = SINT_MAX;
- cp->prox = SINT_MAX;
- if(redir1(s, unc, cp, 1) == -1){
- if(Debug && strstr(Debug, "dfs") != nil)
- print("redirect new failed unc=%q\n", unc);
- free(unc);
- free(cp);
- return -1;
- }
- free(unc);
- cp->src = estrdup9p(path);
- cp->next = Cache;
- Cache = cp;
- if(Debug && strstr(Debug, "dfs") != nil)
- print("redirect cache=miss src=%q => share=%q path=%q\n",
- cp->src, cp->share, cp->path);
- return 0;
- }
|