9 #define max(a,b) (((a) > (b))? (a): (b))
10 #define min(a,b) (((a) < (b))? (a): (b))
12 typedef struct Aux Aux;
16 char *path; /* full path fo file */
17 Share *sp; /* this share's info */
18 long expire; /* expiration time of cache */
19 long off; /* file pos of start of cache */
20 long end; /* file pos of end of cache */
21 long mtime; /* last modification time - windows updates it only on close */
23 int fh; /* file handle */
24 int sh; /* search handle */
25 long srch; /* find first's internal state */
30 int Checkcase = 1; /* enforce case significance on filenames */
31 int Dfstout = 100; /* timeout (in ms) for ping of dfs servers (assume they are local) */
32 int Billtrog = 1; /* enable file owner/group resolution */
33 int Attachpid; /* pid of proc that attaches (ugh !) */
34 char *Debug = nil; /* messages */
35 Qid Root; /* root of remote system */
36 Share Ipc; /* Share info of IPC$ share */
37 Session *Sess; /* current session */
38 int Active = IDLE_TIME; /* secs until next keepalive is sent */
39 static int Keeppid; /* process ID of keepalive thread */
40 Share Shares[MAX_SHARES]; /* table of connected shares */
41 int Nshares = 0; /* number of Shares connected */
42 Aux *Openfiles = nil; /* linked list of Aux structs */
43 char *Host = nil; /* host we are connected to */
45 static char *Ipcname = "IPC$";
47 #define ptype(x) (((x) & 0xf))
48 #define pindex(x) (((x) & 0xff0) >> 4)
57 * This is revolting but I cannot see any other way to get
58 * the pid of the server. We need this as Windows doesn't
59 * drop the TCP connection when it closes a connection.
60 * Thus we keepalive() to detect when/if we are thrown off.
64 snprint(buf, sizeof buf, "#p/%d/args", getpid());
65 if((fd = open(buf, OWRITE)) >= 0){
66 fprint(fd, "%s network", Host);
77 if((ap = Openfiles) != nil)
84 fmtprint(f, "%-9s %s\n", type, ap->path);
86 }while(ap != Openfiles);
91 mkqid(char *s, int is_dir, long vers, int subtype, long path)
94 union { /* align digest suitably */
95 uchar digest[SHA1dlen];
99 sha1((uchar *)s, strlen(s), u.digest, nil);
100 q.type = (is_dir)? QTDIR: 0;
103 q.path = *((uvlong *)u.digest) & ~0xfffL;
104 q.path |= ((path & 0xff) << 4);
105 q.path |= (subtype & 0xf);
108 q.path = *((uvlong *)u.digest) & ~0xfL;
113 * used only for root dir and shares
116 V2D(Dir *d, Qid qid, char *name)
118 memset(d, 0, sizeof(Dir));
121 d->name = estrdup9p(name);
122 d->uid = estrdup9p("bill");
123 d->muid = estrdup9p("boyd");
124 d->gid = estrdup9p("trog");
125 d->mode = 0755 | DMDIR;
126 d->atime = time(nil);
133 I2D(Dir *d, Share *sp, char *path, long mtime, FInfo *fi)
137 if((name = strrchr(fi->name, '\\')) != nil)
141 d->name = estrdup9p(name);
144 d->uid = estrdup9p("bill");
145 d->gid = estrdup9p("trog");
146 d->muid = estrdup9p("boyd");
147 d->atime = fi->accessed;
148 if(mtime > fi->written)
151 d->mtime = fi->written;
153 if(fi->attribs & ATTR_READONLY)
158 d->length = fi->size;
159 d->qid = mkqid(path, fi->attribs & ATTR_DIRECTORY, fi->changed, 0, 0);
161 if(fi->attribs & ATTR_DIRECTORY){
163 d->mode |= DMDIR|0111;
168 responderrstr(Req *r)
173 rerrstr(e, sizeof e);
178 newpath(char *path, char *name)
182 assert((p = strrchr(path, '/')) != nil);
184 if(strcmp(name, "..") == 0){
186 return estrdup9p("/");
187 q = emalloc9p((p-path)+1);
188 strecpy(q, q+(p-path)+1, path);
191 if(strcmp(path, "/") == 0)
192 return smprint("/%s", name);
193 return smprint("%s/%s", path, name);
197 * get the last write time if the file is open -
198 * windows only updates mtime when the file is closed
199 * which is not good enough for acme.
202 realmtime(char *path)
206 if((a = Openfiles) == nil)
210 if(a->fh != -1 && cistrcmp(path, a->path) == 0)
213 }while(a != Openfiles);
217 /* remove "." and ".." from the cache */
219 rmdots(Aux *a, int got)
225 fi = (FInfo *)a->cache;
226 for(i = 0; i < got; i++){
227 if(strcmp(fi->name, ".") == 0 || strcmp(fi->name, "..") == 0){
228 memmove(fi, fi+1, got * sizeof(FInfo));
239 dirgen(int slot, Dir *d, void *aux)
246 int numinf = numinfo();
249 slots = 32; /* number of dir entries to fetch at one time */
251 if(strcmp(a->path, "/") == 0){
260 V2D(d, mkqid(Shares[slot].name, 1, 1, Pshare, slot),
265 off = slot * sizeof(FInfo);
266 if(off >= a->off && off < a->end && time(nil) < a->expire)
270 npath = smprint("%s/*", mapfile(a->path));
271 a->sh = T2findfirst(Sess, a->sp, slots, npath, &got, &a->srch,
277 got = rmdots(a, got);
279 a->end = got * sizeof(FInfo);
283 while(off >= a->end && a->sh != -1){
284 fi = (FInfo *)(a->cache + (a->end - a->off) - sizeof(FInfo));
286 npath = smprint("%s/%s", mapfile(a->path), fi->name);
287 rc = T2findnext(Sess, a->sp, slots, npath,
288 &got, &a->srch, (FInfo *)a->cache, a->sh);
290 if(rc == -1 || got == 0)
292 got = rmdots(a, got);
293 a->end = a->off + got * sizeof(FInfo);
295 a->expire = time(nil) + CACHETIME;
299 CIFSfindclose2(Sess, a->sp, a->sh);
308 fi = (FInfo *)(a->cache + (off - a->off));
309 npath = smprint("%s/%s", mapfile(a->path), fi->name);
310 I2D(d, a->sp, npath, realmtime(npath), fi);
312 upd_names(Sess, a->sp, npath, d);
321 static int first = 1;
322 char *spec = r->ifcall.aname;
328 respond(r, "invalid attach specifier");
332 r->ofcall.qid = mkqid("/", 1, 1, Proot, 0);
333 r->fid->qid = r->ofcall.qid;
335 a = r->fid->aux = emalloc9p(sizeof(Aux));
336 memset(a, 0, sizeof(Aux));
337 a->path = estrdup9p("/");
344 a->next = Openfiles->next;
345 Openfiles->next->prev = a;
356 fsclone(Fid *ofid, Fid *fid)
359 Aux *a = emalloc9p(sizeof(Aux));
363 memset(a, 0, sizeof(Aux));
367 a->path = estrdup9p(oa->path);
371 a->next = Openfiles->next;
372 Openfiles->next->prev = a;
383 * for some weird reason T2queryall() returns share names
384 * in lower case so we have to do an extra test against
385 * our share table to validate filename case.
387 * on top of this here (snell & Wilcox) most of our
388 * redirections point to a share of the same name,
389 * but some do not, thus the tail of the filename
390 * returned by T2queryall() is not the same as
391 * the name we wanted.
393 * We work around this by not validating the names
394 * or files which resolve to share names as they must
395 * be correct, having been enforced in the dfs layer.
398 validfile(char *found, char *want, char *winpath, Share *sp)
402 if(strcmp(want, "..") == 0)
404 if(strcmp(winpath, "/") == 0){
405 share = trimshare(sp->name);
406 if(cistrcmp(want, share) == 0)
407 return strcmp(want, share) == 0;
409 * OK, a DFS redirection points us from a directory XXX
410 * to a share named YYY. There is no case checking we can
411 * do so we allow either case - it's all we can do.
415 if(cistrcmp(found, want) != 0)
419 if(strcmp(found, want) == 0)
426 fswalk1(Fid *fid, char *name, Qid *qid)
431 static char e[ERRMAX];
432 char *p, *npath, *winpath;
435 npath = newpath(a->path, name);
436 if(strcmp(npath, "/") == 0){ /* root dir */
437 *qid = mkqid("/", 1, 1, Proot, 0);
444 if(strrchr(npath, '/') == npath){ /* top level dir */
445 if((n = walkinfo(name)) != -1){ /* info file */
446 *qid = mkqid(npath, 0, 1, Pinfo, n);
448 else { /* volume name */
449 for(i = 0; i < Nshares; i++){
450 n = strlen(Shares[i].name);
451 if(cistrncmp(npath+1, Shares[i].name, n) != 0)
453 if(Checkcase && strncmp(npath+1, Shares[i].name, n) != 0)
455 if(npath[n+1] != 0 && npath[n+1] != '/')
464 *qid = mkqid(npath, 1, 1, Pshare, i);
472 /* must be a vanilla file or directory */
474 if(mapshare(npath, &a->sp) == -1){
475 rerrstr(e, sizeof(e));
480 winpath = mapfile(npath);
481 memset(&fi, 0, sizeof fi);
482 if(Sess->caps & CAP_NT_SMBS)
483 rc = T2queryall(Sess, a->sp, winpath, &fi);
485 rc = T2querystandard(Sess, a->sp, winpath, &fi);
488 rerrstr(e, sizeof(e));
493 if((a->sp->options & SMB_SHARE_IS_IN_DFS) != 0 &&
494 (fi.attribs & ATTR_REPARSE) != 0){
495 if(redirect(Sess, a->sp, npath) != -1)
499 if((p = strrchr(fi.name, '/')) == nil && (p = strrchr(fi.name, '\\')) == nil)
504 if(! validfile(p, name, winpath, a->sp)){
509 *qid = mkqid(npath, fi.attribs & ATTR_DIRECTORY, fi.changed, 0, 0);
511 a->mtime = realmtime(npath);
524 Aux *a = r->fid->aux;
526 if(ptype(r->fid->qid.path) == Proot)
527 V2D(&r->d, r->fid->qid, "");
528 else if(ptype(r->fid->qid.path) == Pinfo)
529 dirgeninfo(pindex(r->fid->qid.path), &r->d);
530 else if(ptype(r->fid->qid.path) == Pshare)
531 V2D(&r->d, r->fid->qid, a->path +1);
533 memset(&fi, 0, sizeof fi);
534 if(Sess->caps & CAP_NT_SMBS)
535 rc = T2queryall(Sess, a->sp, mapfile(a->path), &fi);
537 rc = T2querystandard(Sess, a->sp, mapfile(a->path), &fi);
542 I2D(&r->d, a->sp, a->path, a->mtime, &fi);
544 upd_names(Sess, a->sp, mapfile(a->path), &r->d);
550 smbcreateopen(Aux *a, char *path, int mode, int perm, int is_create,
551 int is_dir, FInfo *fip)
553 int rc, action, attrs, access, result;
555 if(is_create && is_dir){
556 if(CIFScreatedirectory(Sess, a->sp, path) == -1)
561 if(mode & DMAPPEND) {
562 werrstr("filesystem does not support DMAPPEND");
568 else if(mode & OTRUNC)
576 attrs = ATTR_NORMAL|ATTR_READONLY;
578 switch (mode & OMASK){
592 werrstr("%d bad open mode", mode & OMASK);
597 if((mode & DMEXCL) == 0)
602 if((a->fh = CIFS_SMB_opencreate(Sess, a->sp, path, access, attrs,
603 action, &result)) == -1)
606 if(Sess->caps & CAP_NT_SMBS)
607 rc = T2queryall(Sess, a->sp, mapfile(a->path), fip);
609 rc = T2querystandard(Sess, a->sp, mapfile(a->path), fip);
611 fprint(2, "internal error: stat of newly open/created file failed\n");
615 if((mode & OEXCL) && (result & 0x8000) == 0){
616 werrstr("%d bad open mode", mode & OMASK);
622 /* Uncle Bill, you have a lot to answer for... */
624 ntcreateopen(Aux *a, char *path, int mode, int perm, int is_create,
625 int is_dir, FInfo *fip)
627 int options, result, attrs, flags, access, action, share;
630 werrstr("CIFSopen, DMAPPEND not supported");
637 else if(mode & OTRUNC)
638 action = FILE_CREATE;
640 action = FILE_OVERWRITE_IF;
643 action = FILE_OVERWRITE_IF;
645 action = FILE_OPEN_IF;
648 flags = 0; /* FIXME: really not sure */
651 share = FILE_NO_SHARE;
653 share = FILE_SHARE_ALL;
655 switch (mode & OMASK){
657 access = GENERIC_READ;
660 access = GENERIC_WRITE;
663 access = GENERIC_ALL;
666 access = GENERIC_EXECUTE;
669 werrstr("%d bad open mode", mode & OMASK);
675 action = FILE_CREATE;
676 options = FILE_DIRECTORY_FILE;
678 attrs = ATTR_DIRECTORY;
680 attrs = ATTR_DIRECTORY|ATTR_READONLY;
682 options = FILE_NON_DIRECTORY_FILE;
686 attrs = ATTR_NORMAL|ATTR_READONLY;
690 options |= FILE_DELETE_ON_CLOSE;
691 attrs |= ATTR_DELETE_ON_CLOSE;
694 if((a->fh = CIFS_NT_opencreate(Sess, a->sp, path, flags, options,
695 attrs, access, share, action, &result, fip)) == -1)
698 if((mode & OEXCL) && (result & 0x8000) == 0){
699 werrstr("%d bad open mode", mode & OMASK);
712 Aux *a = r->fid->aux;
715 a->cache = emalloc9p(max(Sess->mtu, MTU));
717 is_dir = (r->ifcall.perm & DMDIR) == DMDIR;
718 npath = smprint("%s/%s", a->path, r->ifcall.name);
720 if(Sess->caps & CAP_NT_SMBS)
721 rc = ntcreateopen(a, mapfile(npath), r->ifcall.mode,
722 r->ifcall.perm, 1, is_dir, &fi);
724 rc = smbcreateopen(a, mapfile(npath), r->ifcall.mode,
725 r->ifcall.perm, 1, is_dir, &fi);
732 r->fid->qid = mkqid(npath, fi.attribs & ATTR_DIRECTORY, fi.changed, 0, 0);
734 r->ofcall.qid = r->fid->qid;
746 Aux *a = r->fid->aux;
749 a->cache = emalloc9p(max(Sess->mtu, MTU));
751 if(ptype(r->fid->qid.path) == Pinfo){
752 if(makeinfo(pindex(r->fid->qid.path)) != -1)
755 respond(r, "cannot generate info");
759 if(r->fid->qid.type & QTDIR){
764 if(Sess->caps & CAP_NT_SMBS)
765 rc = ntcreateopen(a, mapfile(a->path), r->ifcall.mode, 0777,
768 rc = smbcreateopen(a, mapfile(a->path), r->ifcall.mode, 0777,
781 Aux *a = r->fid->aux;
782 vlong len = r->ifcall.count;
783 vlong off = r->ifcall.offset;
784 char *buf = r->ifcall.data;
787 n = Sess->mtu -OVERHEAD;
791 m = CIFSwrite(Sess, a->sp, a->fh, off + got, buf + got, n);
794 } while(got < len && m >= n);
796 r->ofcall.count = got;
797 a->mtime = time(nil);
809 Aux *a = r->fid->aux;
810 char *buf = r->ofcall.data;
811 vlong len = r->ifcall.count;
812 vlong off = r->ifcall.offset;
814 if(ptype(r->fid->qid.path) == Pinfo){
815 r->ofcall.count = readinfo(pindex(r->fid->qid.path), buf, len,
821 if(r->fid->qid.type & QTDIR){
822 dirread9p(r, dirgen, a);
828 n = Sess->mtu -OVERHEAD;
832 m = CIFSread(Sess, a->sp, a->fh, off + got, buf + got, n, len);
835 } while(got < len && m >= n);
837 r->ofcall.count = got;
849 if(ptype(f->qid.path) == Pinfo)
850 freeinfo(pindex(f->qid.path));
855 if(CIFSclose(Sess, a->sp, a->fh) == -1)
856 fprint(2, "%s: close failed fh=%d %r\n", argv0, a->fh);
858 if(CIFSfindclose2(Sess, a->sp, a->sh) == -1)
859 fprint(2, "%s: findclose failed sh=%d %r\n",
868 a->prev->next = a->next;
869 a->next->prev = a->prev;
870 if(a->next == a->prev)
877 rdonly(Session *s, Share *sp, char *path, int rdonly)
882 if(Sess->caps & CAP_NT_SMBS)
883 rc = T2queryall(s, sp, path, &fi);
885 rc = T2querystandard(s, sp, path, &fi);
889 if((rdonly && !(fi.attribs & ATTR_READONLY)) ||
890 (!rdonly && (fi.attribs & ATTR_READONLY))){
891 fi.attribs &= ~ATTR_READONLY;
892 fi.attribs |= rdonly? ATTR_READONLY: 0;
893 rc = CIFSsetinfo(s, sp, path, &fi);
903 Aux *ap, *a = r->fid->aux;
906 if(ptype(r->fid->qid.path) == Proot ||
907 ptype(r->fid->qid.path) == Pshare){
908 respond(r, "illegal operation");
912 /* close all instences of this file/dir */
913 if((ap = Openfiles) != nil)
915 if(strcmp(ap->path, a->path) == 0){
917 CIFSfindclose2(Sess, ap->sp, ap->sh);
920 CIFSclose(Sess, ap->sp, ap->fh);
924 }while(ap != Openfiles);
927 if(r->fid->qid.type & QTDIR)
928 rc = CIFSdeletedirectory(Sess, a->sp, mapfile(a->path));
930 rc = CIFSdeletefile(Sess, a->sp, mapfile(a->path));
932 rerrstr(e, sizeof(e));
933 if(rc == -1 && try++ == 0 && strcmp(e, "permission denied") == 0 &&
934 rdonly(Sess, a->sp, mapfile(a->path), 0) == 0)
947 char *p, *from, *npath;
948 Aux *a = r->fid->aux;
950 if(ptype(r->fid->qid.path) == Proot ||
951 ptype(r->fid->qid.path) == Pshare){
952 respond(r, "illegal operation");
956 if((r->d.uid && r->d.uid[0]) || (r->d.gid && r->d.gid[0])){
957 respond(r, "cannot change ownership");
964 if(Sess->caps & CAP_NT_SMBS)
965 rc = T2queryall(Sess, a->sp, mapfile(a->path), &fi);
967 rc = T2querystandard(Sess, a->sp, mapfile(a->path), &fi);
969 werrstr("(query) - %r");
975 * always clear the readonly attribute if set,
976 * before trying to set any other fields.
977 * wstat() fails if the file/dir is readonly
978 * and this function is so full of races - who cares about one more?
980 rdonly(Sess, a->sp, mapfile(a->path), 0);
983 * rename - one piece of joy, renaming open files
984 * is legal (sharing permitting).
986 if(r->d.name && r->d.name[0]){
987 if((p = strrchr(a->path, '/')) == nil){
988 respond(r, "illegal path");
991 npath = emalloc9p((p-a->path)+strlen(r->d.name)+2);
992 strecpy(npath, npath+(p- a->path)+2, a->path);
993 strcat(npath, r->d.name);
995 from = estrdup9p(mapfile(a->path));
996 if(CIFSrename(Sess, a->sp, from, mapfile(npath)) == -1){
997 werrstr("(rename) - %r");
1009 * set the files length, do this before setting
1010 * the file times as open() will alter them
1013 fi.size = r->d.length;
1015 if(Sess->caps & CAP_NT_SMBS){
1016 if((fh = CIFS_NT_opencreate(Sess, a->sp, mapfile(a->path),
1017 0, FILE_NON_DIRECTORY_FILE,
1018 ATTR_NORMAL, GENERIC_WRITE, FILE_SHARE_ALL,
1019 FILE_OPEN_IF, &result, &tmpfi)) == -1){
1020 werrstr("(set length, open) - %r");
1024 rc = T2setfilelength(Sess, a->sp, fh, &fi);
1025 CIFSclose(Sess, a->sp, fh);
1027 werrstr("(set length), set) - %r");
1032 if((fh = CIFS_SMB_opencreate(Sess, a->sp, mapfile(a->path),
1033 1, ATTR_NORMAL, 1, &result)) == -1){
1034 werrstr("(set length, open) failed - %r");
1038 rc = CIFSwrite(Sess, a->sp, fh, fi.size, 0, 0);
1039 CIFSclose(Sess, a->sp, fh);
1041 werrstr("(set length, write) - %r");
1049 * This doesn't appear to set length or
1050 * attributes, no idea why, so I do those seperately
1052 if(~r->d.mtime || ~r->d.atime){
1054 fi.written = r->d.mtime;
1056 fi.accessed = r->d.atime;
1057 if(T2setpathinfo(Sess, a->sp, mapfile(a->path), &fi) == -1){
1058 werrstr("(set path info) - %r");
1065 * always update the readonly flag as
1066 * we may have cleared it above.
1069 if(r->d.mode & 0222)
1070 fi.attribs &= ~ATTR_READONLY;
1072 fi.attribs |= ATTR_READONLY;
1074 if(rdonly(Sess, a->sp, mapfile(a->path), fi.attribs & ATTR_READONLY) == -1){
1075 werrstr("(set info) - %r");
1081 * Win95 has a broken write-behind cache for metadata
1082 * on open files (writes go to the cache, reads bypass
1083 * the cache), so we must flush the file.
1085 if(r->fid->omode != -1 && CIFSflush(Sess, a->sp, a->fh) == -1){
1086 werrstr("(flush) %r");
1099 for(i = 0; i < Nshares; i++)
1100 CIFStreedisconnect(Sess, Shares+i);
1102 postnote(PNPROC, Keeppid, "die");
1106 .destroyfid = fsdestroyfid,
1123 fprint(2, "usage: %s [-d name] [-Dvb] [-a auth-method] [-s srvname] "
1124 "[-n called-name] [-k factotum-params] [-m mntpnt] "
1125 "host [share...]\n", argv0);
1130 * SMBecho looks like the function to use for keepalives,
1131 * sadly the echo packet does not seem to reload the
1132 * idle timer in Microsoft's servers. Instead we use
1133 * "get file system size" on each share until we get one that succeeds.
1140 int fd, i, slot, rc;
1142 snprint(buf, sizeof buf, "#p/%d/args", getpid());
1143 if((fd = open(buf, OWRITE)) >= 0){
1144 fprint(fd, "%s keepalive", Host);
1155 for(i = 0; i < Nshares; i++){
1156 if((rc = T2fssizeinfo(Sess, &Shares[slot], &tot, &fre)) == 0)
1158 if(++slot >= Nshares)
1162 postnote(PNPROC, Attachpid, "die");
1167 ding(void *u, char *msg)
1170 if(strstr(msg, "alarm") != nil)
1176 dmpkey(char *s, void *v, int n)
1179 unsigned char *p = (unsigned char *)v;
1182 for(i = 0; i < n; i++)
1183 print("%02ux ", *p++);
1188 main(int argc, char **argv)
1192 char windom[64], cname[64];
1193 char *p, *method, *sysname, *keyp, *mtpt, *svs;
1194 static char *sh[1024];
1200 strcpy(windom, "unknown");
1207 method = EARGF(autherr());
1216 Debug = EARGF(usage());
1222 keyp = EARGF(usage());
1228 mtpt = EARGF(usage());
1231 strncpy(cname, EARGF(usage()), sizeof(cname));
1232 cname[sizeof(cname) - 1] = 0;
1235 svs = EARGF(usage());
1238 Dfstout = atoi(EARGF(usage()));
1250 if(mtpt == nil && svs == nil){
1251 if((p = strchr(Host, '!')) != nil)
1252 mtpt = smprint("/n/%s", p+1);
1254 mtpt = smprint("/n/%s", Host);
1257 if((sysname = getenv("sysname")) == nil)
1258 sysname = "unknown";
1260 if(*cname && (Sess = cifsdial(Host, cname, sysname)) != nil)
1263 if(calledname(Host, cname) == 0 &&
1264 (Sess = cifsdial(Host, cname, sysname)) != nil)
1267 strcpy(cname, Host);
1268 if((p = strchr(cname, '!')) != nil)
1271 if((Sess = cifsdial(Host, cname, sysname)) != nil ||
1272 (Sess = cifsdial(Host, "*SMBSERVER", sysname)) != nil)
1275 sysfatal("%s - cannot dial, %r\n", Host);
1277 if(CIFSnegotiate(Sess, &svrtime, windom, sizeof windom, cname, sizeof cname) == -1)
1278 sysfatal("%s - cannot negioate common protocol, %r\n", Host);
1281 Sess->secmode &= ~SECMODE_SIGN_ENABLED;
1285 strcpy(windom, ".");
1287 Sess->auth = getauth(method, windom, keyp, Sess->secmode, Sess->chal,
1290 if(CIFSsession(Sess) < 0)
1291 sysfatal("session authentication failed, %r\n");
1293 Sess->slip = svrtime - time(nil);
1294 Sess->cname = estrdup9p(cname);
1296 if(CIFStreeconnect(Sess, cname, Ipcname, &Ipc) == -1)
1297 fprint(2, "%s, %r - can't connect\n", Ipcname);
1303 if((n = RAPshareenum(Sess, &Ipc, &sip)) < 1)
1304 sysfatal("can't enumerate shares: %r - specify share "
1305 "names on command line\n");
1307 for(i = 0; i < n; i++){
1308 #ifdef NO_HIDDEN_SHARES
1309 int l = strlen(sip[i].name);
1311 if(l > 1 && sip[i].name[l-1] == '$'){
1316 memcpy(Shares+Nshares, sip+i, sizeof(Share));
1317 if(CIFStreeconnect(Sess, Sess->cname,
1318 Shares[Nshares].name, Shares+Nshares) == -1){
1319 fprint(2, "%s: %s %q - can't connect to share"
1320 ", %r\n", argv0, Host, Shares[Nshares].name);
1321 free(Shares[Nshares].name);
1328 for(i = 1; i < argc; i++){
1329 if(CIFStreeconnect(Sess, Sess->cname, argv[i],
1330 Shares+Nshares) == -1){
1331 fprint(2, "%s: %s %q - can't connect to share"
1332 ", %r\n", argv0, Host, argv[i]);
1335 Shares[Nshares].name = strlwr(estrdup9p(argv[i]));
1340 fprint(2, "no available shares\n");
1342 if((Keeppid = rfork(RFPROC|RFMEM|RFNOTEG|RFFDG|RFNAMEG)) == 0){
1346 postmountsrv(&fs, svs, mtpt, MREPL|MCREATE);