1671dfc47SDavid du Colombier /*
2671dfc47SDavid du Colombier * DNS referrals give two main fields: the path to connect to in
3671dfc47SDavid du Colombier * /Netbios-host-name/share-name/path... form and a network
4671dfc47SDavid du Colombier * address of how to find this path of the form domain.dom.
5671dfc47SDavid du Colombier *
6671dfc47SDavid du Colombier * The domain.dom is resolved in XP/Win2k etc using AD to do
7671dfc47SDavid du Colombier * a lookup (this is a consensus view, I don't think anyone
8671dfc47SDavid du Colombier * has proved it). I cannot do this as AD needs Kerberos and
9671dfc47SDavid du Colombier * LDAP which I don't have.
10671dfc47SDavid du Colombier *
11671dfc47SDavid du Colombier * Instead I just use the NetBios names passed in the paths
12671dfc47SDavid du Colombier * and assume that the servers are in the same DNS domain as me
13671dfc47SDavid du Colombier * and have their DNS hostname set the same as their netbios
14671dfc47SDavid du Colombier * called-name; thankfully this always seems to be the case (so far).
15671dfc47SDavid du Colombier *
16671dfc47SDavid du Colombier * I have not added support for starting another instance of
17671dfc47SDavid du Colombier * cifs to connect to other servers referenced in DFS links,
18671dfc47SDavid du Colombier * this is not a problem for me and I think it hides a load
19671dfc47SDavid du Colombier * of problems of its own wrt plan9's private namespaces.
20671dfc47SDavid du Colombier *
21671dfc47SDavid du Colombier * The proximity of my test server (AD enabled) is always 0 but some
22671dfc47SDavid du Colombier * systems may report more meaningful values. The expiry time is
23671dfc47SDavid du Colombier * similarly zero, so I guess at 5 mins.
24671dfc47SDavid du Colombier *
25671dfc47SDavid du Colombier * If the redirection points to a "hidden" share (i.e., its name
26671dfc47SDavid du Colombier * ends in a $) then the type of the redirection is 0 (unknown) even
27671dfc47SDavid du Colombier * though it is a CIFS share.
28671dfc47SDavid du Colombier *
29671dfc47SDavid du Colombier * It would be nice to add a check for which subnet a server is on
30671dfc47SDavid du Colombier * so our first choice is always the the server on the same subnet
31671dfc47SDavid du Colombier * as us which replies to a ping (i.e., is up). This could short-
32671dfc47SDavid du Colombier * circuit the tests as the a server on the same subnet will always
33671dfc47SDavid du Colombier * be the fastest to get to.
34671dfc47SDavid du Colombier *
35671dfc47SDavid du Colombier * If I set Flags2_DFS then I don't see DFS links, I just get
36671dfc47SDavid du Colombier * path not found (?!).
37671dfc47SDavid du Colombier *
38671dfc47SDavid du Colombier * If I do a QueryFileInfo of a DFS link point (IE when I'am doing a walk)
39671dfc47SDavid du Colombier * Then I just see a directory, its not until I try to walk another level
40671dfc47SDavid du Colombier * That I get "IO reparse tag not handled" error rather than
41671dfc47SDavid du Colombier * "Path not covered".
42671dfc47SDavid du Colombier *
43671dfc47SDavid du Colombier * If I check the extended attributes of the QueryFileInfo in walk() then I can
44671dfc47SDavid du Colombier * see this is a reparse point and so I can get the referral. The only
45671dfc47SDavid du Colombier * problem here is that samba and the like may not support this.
46671dfc47SDavid du Colombier */
47671dfc47SDavid du Colombier #include <u.h>
48671dfc47SDavid du Colombier #include <libc.h>
49671dfc47SDavid du Colombier #include <fcall.h>
50671dfc47SDavid du Colombier #include <thread.h>
51671dfc47SDavid du Colombier #include <libsec.h>
52671dfc47SDavid du Colombier #include <ctype.h>
53671dfc47SDavid du Colombier #include <9p.h>
54671dfc47SDavid du Colombier #include "cifs.h"
55671dfc47SDavid du Colombier
56*912e5f54SDavid du Colombier enum {
57*912e5f54SDavid du Colombier Nomatch, /* not found in cache */
58*912e5f54SDavid du Colombier Exactmatch, /* perfect match found */
59*912e5f54SDavid du Colombier Badmatch /* matched but wrong case */
60*912e5f54SDavid du Colombier };
61*912e5f54SDavid du Colombier
62671dfc47SDavid du Colombier #define SINT_MAX 0x7fffffff
63671dfc47SDavid du Colombier
64671dfc47SDavid du Colombier typedef struct Dfscache Dfscache;
65671dfc47SDavid du Colombier struct Dfscache {
66671dfc47SDavid du Colombier Dfscache*next; /* next entry */
67671dfc47SDavid du Colombier char *src;
68671dfc47SDavid du Colombier char *host;
69671dfc47SDavid du Colombier char *share;
70671dfc47SDavid du Colombier char *path;
71671dfc47SDavid du Colombier long expiry; /* expiry time in sec */
72671dfc47SDavid du Colombier long rtt; /* round trip time, nsec */
73671dfc47SDavid du Colombier int prox; /* proximity, lower = closer */
74671dfc47SDavid du Colombier };
75671dfc47SDavid du Colombier
76671dfc47SDavid du Colombier Dfscache *Cache;
77671dfc47SDavid du Colombier
78671dfc47SDavid du Colombier int
dfscacheinfo(Fmt * f)79671dfc47SDavid du Colombier dfscacheinfo(Fmt *f)
80671dfc47SDavid du Colombier {
81671dfc47SDavid du Colombier long ex;
82671dfc47SDavid du Colombier Dfscache *cp;
83671dfc47SDavid du Colombier
84671dfc47SDavid du Colombier for(cp = Cache; cp; cp = cp->next){
85671dfc47SDavid du Colombier ex = cp->expiry - time(nil);
86671dfc47SDavid du Colombier if(ex < 0)
87671dfc47SDavid du Colombier ex = -1;
88671dfc47SDavid du Colombier fmtprint(f, "%-42s %6ld %8.1f %4d %-16s %-24s %s\n",
89671dfc47SDavid du Colombier cp->src, ex, (double)cp->rtt/1000.0L, cp->prox,
90671dfc47SDavid du Colombier cp->host, cp->share, cp->path);
91671dfc47SDavid du Colombier }
92671dfc47SDavid du Colombier return 0;
93671dfc47SDavid du Colombier }
94671dfc47SDavid du Colombier
95976d3a68SDavid du Colombier char *
trimshare(char * s)96671dfc47SDavid du Colombier trimshare(char *s)
97671dfc47SDavid du Colombier {
98671dfc47SDavid du Colombier char *p;
99671dfc47SDavid du Colombier static char name[128];
100671dfc47SDavid du Colombier
101671dfc47SDavid du Colombier strncpy(name, s, sizeof(name));
102671dfc47SDavid du Colombier name[sizeof(name)-1] = 0;
103671dfc47SDavid du Colombier if((p = strrchr(name, '$')) != nil && p[1] == 0)
104671dfc47SDavid du Colombier *p = 0;
105671dfc47SDavid du Colombier return name;
106671dfc47SDavid du Colombier }
107671dfc47SDavid du Colombier
108671dfc47SDavid du Colombier static Dfscache *
lookup(char * path,int * match)109*912e5f54SDavid du Colombier lookup(char *path, int *match)
110671dfc47SDavid du Colombier {
111671dfc47SDavid du Colombier int len, n, m;
112671dfc47SDavid du Colombier Dfscache *cp, *best;
113671dfc47SDavid du Colombier
114*912e5f54SDavid du Colombier if(match)
115*912e5f54SDavid du Colombier *match = Nomatch;
116671dfc47SDavid du Colombier
117671dfc47SDavid du Colombier len = 0;
118671dfc47SDavid du Colombier best = nil;
119*912e5f54SDavid du Colombier m = strlen(path);
120671dfc47SDavid du Colombier for(cp = Cache; cp; cp = cp->next){
121671dfc47SDavid du Colombier n = strlen(cp->src);
122*912e5f54SDavid du Colombier if(n < len)
123*912e5f54SDavid du Colombier continue;
124*912e5f54SDavid du Colombier if(strncmp(path, cp->src, n) != 0)
125*912e5f54SDavid du Colombier continue;
126*912e5f54SDavid du Colombier if(path[n] != 0 && path[n] != '/')
127671dfc47SDavid du Colombier continue;
128671dfc47SDavid du Colombier best = cp;
129671dfc47SDavid du Colombier len = n;
130671dfc47SDavid du Colombier if(n == m){
131*912e5f54SDavid du Colombier if(match)
132*912e5f54SDavid du Colombier *match = Exactmatch;
133671dfc47SDavid du Colombier break;
134671dfc47SDavid du Colombier }
135671dfc47SDavid du Colombier }
136671dfc47SDavid du Colombier return best;
137671dfc47SDavid du Colombier }
138671dfc47SDavid du Colombier
139671dfc47SDavid du Colombier char *
mapfile(char * opath)140671dfc47SDavid du Colombier mapfile(char *opath)
141671dfc47SDavid du Colombier {
142671dfc47SDavid du Colombier int exact;
143671dfc47SDavid du Colombier Dfscache *cp;
144*912e5f54SDavid du Colombier char *p, *path;
145671dfc47SDavid du Colombier static char npath[MAX_DFS_PATH];
146671dfc47SDavid du Colombier
147671dfc47SDavid du Colombier path = opath;
148671dfc47SDavid du Colombier if((cp = lookup(path, &exact)) != nil){
149671dfc47SDavid du Colombier snprint(npath, sizeof npath, "/%s%s%s%s", cp->share,
150671dfc47SDavid du Colombier *cp->path? "/": "", cp->path, path + strlen(cp->src));
151671dfc47SDavid du Colombier path = npath;
152671dfc47SDavid du Colombier }
153671dfc47SDavid du Colombier
154671dfc47SDavid du Colombier if((p = strchr(path+1, '/')) == nil)
155671dfc47SDavid du Colombier p = "/";
156671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
157671dfc47SDavid du Colombier print("mapfile src=%q => dst=%q\n", opath, p);
158671dfc47SDavid du Colombier return p;
159671dfc47SDavid du Colombier }
160671dfc47SDavid du Colombier
161671dfc47SDavid du Colombier int
mapshare(char * path,Share ** osp)162671dfc47SDavid du Colombier mapshare(char *path, Share **osp)
163671dfc47SDavid du Colombier {
164*912e5f54SDavid du Colombier int i;
165671dfc47SDavid du Colombier Share *sp;
166*912e5f54SDavid du Colombier Dfscache *cp;
167*912e5f54SDavid du Colombier char *s, *try;
168*912e5f54SDavid du Colombier char *tail[] = { "", "$" };
169671dfc47SDavid du Colombier
170*912e5f54SDavid du Colombier if((cp = lookup(path, nil)) == nil)
171671dfc47SDavid du Colombier return 0;
172671dfc47SDavid du Colombier
173*912e5f54SDavid du Colombier for(sp = Shares; sp < Shares+Nshares; sp++){
174*912e5f54SDavid du Colombier s = trimshare(sp->name);
175*912e5f54SDavid du Colombier if(cistrcmp(cp->share, s) != 0)
176*912e5f54SDavid du Colombier continue;
177*912e5f54SDavid du Colombier if(Checkcase && strcmp(cp->share, s) != 0)
178*912e5f54SDavid du Colombier continue;
179671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
180*912e5f54SDavid du Colombier print("mapshare, already connected, src=%q => dst=%q\n", path, sp->name);
181671dfc47SDavid du Colombier *osp = sp;
182671dfc47SDavid du Colombier return 0;
183671dfc47SDavid du Colombier }
184671dfc47SDavid du Colombier /*
185671dfc47SDavid du Colombier * Try to autoconnect to share if it is not known. Note even if you
186671dfc47SDavid du Colombier * didn't specify any shares and let the system autoconnect you may
187671dfc47SDavid du Colombier * not already have the share you need as RAP (which we use) throws
188671dfc47SDavid du Colombier * away names > 12 chars long. If we where to use RPC then this block
189671dfc47SDavid du Colombier * of code would be less important, though it would still be useful
190671dfc47SDavid du Colombier * to catch Shares added since cifs(1) was started.
191671dfc47SDavid du Colombier */
192671dfc47SDavid du Colombier sp = Shares + Nshares;
193671dfc47SDavid du Colombier for(i = 0; i < 2; i++){
194671dfc47SDavid du Colombier try = smprint("%s%s", cp->share, tail[i]);
195671dfc47SDavid du Colombier if(CIFStreeconnect(Sess, Sess->cname, try, sp) == 0){
196671dfc47SDavid du Colombier sp->name = try;
197671dfc47SDavid du Colombier *osp = sp;
198671dfc47SDavid du Colombier Nshares++;
199671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
200671dfc47SDavid du Colombier print("mapshare connected, src=%q dst=%q\n",
201671dfc47SDavid du Colombier path, cp->share);
202671dfc47SDavid du Colombier return 0;
203671dfc47SDavid du Colombier }
204671dfc47SDavid du Colombier free(try);
205671dfc47SDavid du Colombier }
206671dfc47SDavid du Colombier
207671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
208671dfc47SDavid du Colombier print("mapshare failed src=%s\n", path);
209*912e5f54SDavid du Colombier werrstr("not found");
210671dfc47SDavid du Colombier return -1;
211671dfc47SDavid du Colombier }
212671dfc47SDavid du Colombier
213671dfc47SDavid du Colombier /*
214671dfc47SDavid du Colombier * Rtt_tol is the fractional tollerance for RTT comparisons.
215671dfc47SDavid du Colombier * If a later (further down the list) host's RTT is less than
216671dfc47SDavid du Colombier * 1/Rtt_tol better than my current best then I don't bother
217671dfc47SDavid du Colombier * with it. This biases me towards entries at the top of the list
218671dfc47SDavid du Colombier * which Active Directory has already chosen for me and prevents
219671dfc47SDavid du Colombier * noise in RTTs from pushing me to more distant machines.
220671dfc47SDavid du Colombier */
221671dfc47SDavid du Colombier static int
remap(Dfscache * cp,Refer * re)222671dfc47SDavid du Colombier remap(Dfscache *cp, Refer *re)
223671dfc47SDavid du Colombier {
224671dfc47SDavid du Colombier int n;
225671dfc47SDavid du Colombier long rtt;
226671dfc47SDavid du Colombier char *p, *a[4];
227671dfc47SDavid du Colombier enum {
228976d3a68SDavid du Colombier Hostname = 1,
229*912e5f54SDavid du Colombier Sharename = 2,
230*912e5f54SDavid du Colombier Pathname = 3,
231*912e5f54SDavid du Colombier
232*912e5f54SDavid du Colombier Rtt_tol = 10
233671dfc47SDavid du Colombier };
234671dfc47SDavid du Colombier
235671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
236671dfc47SDavid du Colombier print(" remap %s\n", re->addr);
237671dfc47SDavid du Colombier
238671dfc47SDavid du Colombier for(p = re->addr; *p; p++)
239671dfc47SDavid du Colombier if(*p == '\\')
240671dfc47SDavid du Colombier *p = '/';
241671dfc47SDavid du Colombier
242671dfc47SDavid du Colombier if(cp->prox < re->prox){
243671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
244671dfc47SDavid du Colombier print(" remap %d < %d\n", cp->prox, re->prox);
245671dfc47SDavid du Colombier return -1;
246671dfc47SDavid du Colombier }
247671dfc47SDavid du Colombier if((n = getfields(re->addr, a, sizeof(a), 0, "/")) < 3){
248671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
249671dfc47SDavid du Colombier print(" remap nfields=%d\n", n);
250671dfc47SDavid du Colombier return -1;
251671dfc47SDavid du Colombier }
252976d3a68SDavid du Colombier if((rtt = ping(a[Hostname], Dfstout)) == -1){
253671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
254671dfc47SDavid du Colombier print(" remap ping failed\n");
255671dfc47SDavid du Colombier return -1;
256671dfc47SDavid du Colombier }
257671dfc47SDavid du Colombier if(cp->rtt < rtt && (rtt/labs(rtt-cp->rtt)) < Rtt_tol){
258671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
259671dfc47SDavid du Colombier print(" remap bad ping %ld < %ld && %ld < %d\n",
260671dfc47SDavid du Colombier cp->rtt, rtt, (rtt/labs(rtt-cp->rtt)), Rtt_tol);
261671dfc47SDavid du Colombier return -1;
262671dfc47SDavid du Colombier }
263671dfc47SDavid du Colombier
264671dfc47SDavid du Colombier if(n < 4)
265*912e5f54SDavid du Colombier a[Pathname] = "";
266671dfc47SDavid du Colombier if(re->ttl == 0)
267671dfc47SDavid du Colombier re->ttl = 60*5;
268671dfc47SDavid du Colombier
269671dfc47SDavid du Colombier free(cp->host);
270671dfc47SDavid du Colombier free(cp->share);
271671dfc47SDavid du Colombier free(cp->path);
272671dfc47SDavid du Colombier cp->rtt = rtt;
273671dfc47SDavid du Colombier cp->prox = re->prox;
274671dfc47SDavid du Colombier cp->expiry = time(nil)+re->ttl;
275976d3a68SDavid du Colombier cp->host = estrdup9p(a[Hostname]);
276*912e5f54SDavid du Colombier cp->share = estrdup9p(trimshare(a[Sharename]));
277*912e5f54SDavid du Colombier cp->path = estrdup9p(a[Pathname]);
278671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
279671dfc47SDavid du Colombier print(" remap ping OK prox=%d host=%s share=%s path=%s\n",
280671dfc47SDavid du Colombier cp->prox, cp->host, cp->share, cp->path);
281671dfc47SDavid du Colombier return 0;
282671dfc47SDavid du Colombier }
283671dfc47SDavid du Colombier
284671dfc47SDavid du Colombier static int
redir1(Session * s,char * path,Dfscache * cp,int level)285671dfc47SDavid du Colombier redir1(Session *s, char *path, Dfscache *cp, int level)
286671dfc47SDavid du Colombier {
287671dfc47SDavid du Colombier Refer retab[16], *re;
288671dfc47SDavid du Colombier int n, gflags, used, found;
289671dfc47SDavid du Colombier
290671dfc47SDavid du Colombier if(level > 8)
291671dfc47SDavid du Colombier return -1;
292671dfc47SDavid du Colombier
293671dfc47SDavid du Colombier if((n = T2getdfsreferral(s, &Ipc, path, &gflags, &used, retab,
294671dfc47SDavid du Colombier nelem(retab))) == -1)
295671dfc47SDavid du Colombier return -1;
296671dfc47SDavid du Colombier
297671dfc47SDavid du Colombier if(! (gflags & DFS_HEADER_ROOT))
298671dfc47SDavid du Colombier used = SINT_MAX;
299671dfc47SDavid du Colombier
300671dfc47SDavid du Colombier found = 0;
301671dfc47SDavid du Colombier for(re = retab; re < retab+n; re++){
302671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
303671dfc47SDavid du Colombier print("referal level=%d prox=%d path=%q addr=%q\n",
304671dfc47SDavid du Colombier level, re->prox, re->path, re->addr);
305671dfc47SDavid du Colombier
306671dfc47SDavid du Colombier if(gflags & DFS_HEADER_STORAGE){
307671dfc47SDavid du Colombier if(remap(cp, re) == 0)
308671dfc47SDavid du Colombier found = 1;
309671dfc47SDavid du Colombier } else{
310671dfc47SDavid du Colombier if(redir1(s, re->addr, cp, level+1) != -1) /* ???? */
311671dfc47SDavid du Colombier found = 1;
312671dfc47SDavid du Colombier }
313671dfc47SDavid du Colombier free(re->addr);
314671dfc47SDavid du Colombier free(re->path);
315671dfc47SDavid du Colombier }
316671dfc47SDavid du Colombier
317671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
318671dfc47SDavid du Colombier print("referal level=%d path=%q found=%d used=%d\n",
319671dfc47SDavid du Colombier level, path, found, used);
320671dfc47SDavid du Colombier if(!found)
321671dfc47SDavid du Colombier return -1;
322671dfc47SDavid du Colombier return used;
323671dfc47SDavid du Colombier }
324671dfc47SDavid du Colombier
325671dfc47SDavid du Colombier /*
326671dfc47SDavid du Colombier * We can afford to ignore the used count returned by redir
327671dfc47SDavid du Colombier * because of the semantics of 9p - we always walk to directories
328671dfc47SDavid du Colombier * ome and we a time and we always walk before any other file operations
329671dfc47SDavid du Colombier */
330671dfc47SDavid du Colombier int
redirect(Session * s,Share * sp,char * path)331671dfc47SDavid du Colombier redirect(Session *s, Share *sp, char *path)
332671dfc47SDavid du Colombier {
333*912e5f54SDavid du Colombier int match;
334671dfc47SDavid du Colombier char *unc;
335671dfc47SDavid du Colombier Dfscache *cp;
336671dfc47SDavid du Colombier
337671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
338671dfc47SDavid du Colombier print("redirect name=%q path=%q\n", sp->name, path);
339671dfc47SDavid du Colombier
340*912e5f54SDavid du Colombier cp = lookup(path, &match);
341*912e5f54SDavid du Colombier if(match == Badmatch)
342*912e5f54SDavid du Colombier return -1;
343*912e5f54SDavid du Colombier
344*912e5f54SDavid du Colombier if(cp && match == Exactmatch){
345671dfc47SDavid du Colombier if(cp->expiry >= time(nil)){ /* cache hit */
346671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
347671dfc47SDavid du Colombier print("redirect cache=hit src=%q => share=%q path=%q\n",
348671dfc47SDavid du Colombier cp->src, cp->share, cp->path);
349671dfc47SDavid du Colombier return 0;
350671dfc47SDavid du Colombier
351671dfc47SDavid du Colombier } else{ /* cache hit, but entry stale */
352671dfc47SDavid du Colombier cp->rtt = SINT_MAX;
353671dfc47SDavid du Colombier cp->prox = SINT_MAX;
354671dfc47SDavid du Colombier
355671dfc47SDavid du Colombier unc = smprint("//%s/%s/%s%s%s", s->auth->windom,
356671dfc47SDavid du Colombier cp->share, cp->path, *cp->path? "/": "",
357671dfc47SDavid du Colombier path + strlen(cp->src) + 1);
358671dfc47SDavid du Colombier if(unc == nil)
359671dfc47SDavid du Colombier sysfatal("no memory: %r");
360671dfc47SDavid du Colombier if(redir1(s, unc, cp, 1) == -1){
361671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
362671dfc47SDavid du Colombier print("redirect refresh failed unc=%q\n",
363671dfc47SDavid du Colombier unc);
364671dfc47SDavid du Colombier free(unc);
365671dfc47SDavid du Colombier return -1;
366671dfc47SDavid du Colombier }
367671dfc47SDavid du Colombier free(unc);
368671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
369671dfc47SDavid du Colombier print("redirect refresh cache=stale src=%q => share=%q path=%q\n",
370671dfc47SDavid du Colombier cp->src, cp->share, cp->path);
371671dfc47SDavid du Colombier return 0;
372671dfc47SDavid du Colombier }
373671dfc47SDavid du Colombier }
374671dfc47SDavid du Colombier
375*912e5f54SDavid du Colombier
376671dfc47SDavid du Colombier /* in-exact match or complete miss */
377671dfc47SDavid du Colombier if(cp)
378671dfc47SDavid du Colombier unc = smprint("//%s/%s/%s%s%s", s->auth->windom, cp->share,
379671dfc47SDavid du Colombier cp->path, *cp->path? "/": "", path + strlen(cp->src) + 1);
380671dfc47SDavid du Colombier else
381671dfc47SDavid du Colombier unc = smprint("//%s%s", s->auth->windom, path);
382671dfc47SDavid du Colombier if(unc == nil)
383671dfc47SDavid du Colombier sysfatal("no memory: %r");
384671dfc47SDavid du Colombier
385671dfc47SDavid du Colombier cp = emalloc9p(sizeof(Dfscache));
386671dfc47SDavid du Colombier memset(cp, 0, sizeof(Dfscache));
387671dfc47SDavid du Colombier cp->rtt = SINT_MAX;
388671dfc47SDavid du Colombier cp->prox = SINT_MAX;
389671dfc47SDavid du Colombier
390671dfc47SDavid du Colombier if(redir1(s, unc, cp, 1) == -1){
391671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
392671dfc47SDavid du Colombier print("redirect new failed unc=%q\n", unc);
393671dfc47SDavid du Colombier free(unc);
394671dfc47SDavid du Colombier free(cp);
395671dfc47SDavid du Colombier return -1;
396671dfc47SDavid du Colombier }
397671dfc47SDavid du Colombier free(unc);
398671dfc47SDavid du Colombier
399671dfc47SDavid du Colombier cp->src = estrdup9p(path);
400671dfc47SDavid du Colombier cp->next = Cache;
401671dfc47SDavid du Colombier Cache = cp;
402671dfc47SDavid du Colombier if(Debug && strstr(Debug, "dfs") != nil)
403671dfc47SDavid du Colombier print("redirect cache=miss src=%q => share=%q path=%q\n",
404671dfc47SDavid du Colombier cp->src, cp->share, cp->path);
405671dfc47SDavid du Colombier return 0;
406671dfc47SDavid du Colombier }
407*912e5f54SDavid du Colombier
408