/* * 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. */ #include <u.h>#include <libc.h>#include <fcall.h>#include <thread.h>#include <libsec.h>#include <9p.h>#include "cifs.h" #define max(a,b) (((a) > (b))? (a): (b))#define min(a,b) (((a) < (b))? (a): (b)) typedef struct Aux Aux;struct Aux { Aux *next; Aux *prev; char *path; /* full path fo file */ Share *sp; /* this share's info */ long expire; /* expiration time of cache */ long off; /* file pos of start of cache */ long end; /* file pos of end of cache */ char *cache; int fh; /* file handle */ int sh; /* search handle */ long srch; /* find first's internal state */}; extern int chatty9p; int Checkcase = 1; /* enforce case significance on filenames */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 */Qid Root; /* root of remote system */Share Ipc; /* Share info of IPC$ share */Session *Sess; /* current session */int Active = IDLE_TIME; /* secs until next keepalive is sent */static int Keeppid; /* process ID of keepalive thread */Share Shares[MAX_SHARES]; /* table of connected shares */int Nshares = 0; /* number of Shares connected */Aux *Auxroot = nil; /* linked list of Aux structs */char *Host = nil; /* host we are connected to */ static char *Ipcname = "IPC$"; #define ptype(x) (((x) & 0xf))#define pindex(x) (((x) & 0xff0) >> 4) voidsetup(void){ int fd; char buf[32]; /* * This is revolting but I cannot see any other way to get * the pid of the server. We need this as Windows doesn't * drop the TCP connection when it closes a connection. * Thus we keepalive() to detect when/if we are thrown off. */ Attachpid = getpid(); snprint(buf, sizeof buf, "#p/%d/args", getpid()); if((fd = open(buf, OWRITE)) >= 0){ fprint(fd, "%s network", Host); close(fd); }} intfiletableinfo(Fmt *f){ Aux *ap; char *type; if((ap = Auxroot) != nil) do{ type = "walked"; if(ap->sh != -1) type = "opendir"; if(ap->fh != -1) type = "openfile"; fmtprint(f, "%-9s %s\n", type, ap->path); ap = ap->next; }while(ap != Auxroot); return 0;} Qidmkqid(char *s, int is_dir, long vers, int subtype, long path){ Qid q; union { /* align digest suitably */ uchar digest[SHA1dlen]; uvlong uvl; } u; sha1((uchar *)s, strlen(s), u.digest, nil); q.type = (is_dir)? QTDIR: 0; q.vers = vers; if(subtype){ q.path = *((uvlong *)u.digest) & ~0xfffL; q.path |= ((path & 0xff) << 4); q.path |= (subtype & 0xf); } else q.path = *((uvlong *)u.digest) & ~0xfL; return q;} /* * used only for root dir and shares */static voidV2D(Dir *d, Qid qid, char *name){ memset(d, 0, sizeof(Dir)); d->type = 'C'; d->dev = 1; d->name = estrdup9p(name); d->uid = estrdup9p("bill"); d->muid = estrdup9p("boyd"); d->gid = estrdup9p("trog"); d->mode = 0755 | DMDIR; d->atime = time(nil); d->mtime = d->atime; d->length = 0; d->qid = qid;} static voidI2D(Dir *d, Share *sp, char *path, FInfo *fi){ char *name; if((name = strrchr(fi->name, '\\')) != nil) name++; else name = fi->name; d->name = estrdup9p(name); d->type = 'C'; d->dev = sp->tid; d->uid = estrdup9p("bill"); d->gid = estrdup9p("trog"); d->muid = estrdup9p("boyd"); d->atime = fi->accessed; d->mtime = fi->written; if(fi->attribs & ATTR_READONLY) d->mode = 0444; else d->mode = 0666; d->length = fi->size; d->qid = mkqid(path, fi->attribs & ATTR_DIRECTORY, fi->changed, 0, 0); if(fi->attribs & ATTR_DIRECTORY){ d->length = 0; d->mode |= DMDIR|0111; }} static voidresponderrstr(Req *r){ char e[ERRMAX]; *e = 0; rerrstr(e, sizeof e); respond(r, e);} static char *newpath(char *path, char *name){ char *p, *q; assert((p = strrchr(path, '/')) != nil); if(strcmp(name, "..") == 0){ if(p == path) return estrdup9p("/"); q = emalloc9p((p-path)+1); strecpy(q, q+(p-path)+1, path); return q; } if(strcmp(path, "/") == 0) return smprint("/%s", name); return smprint("%s/%s", path, name);} static intdirgen(int slot, Dir *d, void *aux){ long off; FInfo *fi; int rc, got; Aux *a = aux; char *npath; int numinf = numinfo(); int slots = min(Sess->mtu, MTU) / sizeof(FInfo); if(strcmp(a->path, "/") == 0){ if(slot < numinf){ dirgeninfo(slot, d); return 0; } else slot -= numinf; if(slot >= Nshares) return -1; V2D(d, mkqid(Shares[slot].name, 1, 1, Pshare, slot), Shares[slot].name); return 0; } off = slot * sizeof(FInfo); if(off >= a->off && off < a->end && time(nil) < a->expire) goto from_cache; if(off == 0){ fi = (FInfo *)a->cache; npath = smprint("%s/*", mapfile(a->path)); a->sh = T2findfirst(Sess, a->sp, slots, npath, &got, &a->srch, (FInfo *)a->cache); free(npath); if(a->sh == -1) return -1; a->off = 0; a->end = got * sizeof(FInfo); if(got >= 2 && strcmp(fi[0].name, ".") == 0 && strcmp(fi[1].name, "..") == 0){ a->end = (got - 2) * sizeof(FInfo); memmove(a->cache, a->cache + sizeof(FInfo)*2, a->end - a->off); } } while(off >= a->end && a->sh != -1){ fi = (FInfo *)(a->cache + (a->end - a->off) - sizeof(FInfo)); a->off = a->end; npath = smprint("%s/%s", mapfile(a->path), fi->name); rc = T2findnext(Sess, a->sp, slots, npath, &got, &a->srch, (FInfo *)a->cache, a->sh); free(npath); if(rc == -1 || got == 0) break; a->end = a->off + got * sizeof(FInfo); } a->expire = time(nil) + CACHETIME; if(got < slots){ if(a->sh != -1) CIFSfindclose2(Sess, a->sp, a->sh); a->sh = -1; } if(off >= a->end) return -1; from_cache: fi = (FInfo *)(a->cache + (off - a->off)); npath = smprint("%s/%s", mapfile(a->path), fi->name); I2D(d, a->sp, npath, fi); if(Billtrog == 0) upd_names(Sess, a->sp, npath, d); free(npath); return 0;} static voidfsattach(Req *r){ Aux *a; static int first = 1; char *spec = r->ifcall.aname; if(first) setup(); if(spec && *spec){ respond(r, "invalid attach specifier"); return; } r->ofcall.qid = mkqid("/", 1, 1, Proot, 0); r->fid->qid = r->ofcall.qid; a = r->fid->aux = emalloc9p(sizeof(Aux)); memset(a, 0, sizeof(Aux)); a->path = estrdup9p("/"); a->sp = nil; a->fh = -1; a->sh = -1; if(Auxroot){ a->prev = Auxroot; a->next = Auxroot->next; Auxroot->next->prev = a; Auxroot->next = a; } else { Auxroot = a; a->next = a; a->prev = a; } respond(r, nil);} static char*fsclone(Fid *ofid, Fid *fid){ Aux *oa = ofid->aux; Aux *a = emalloc9p(sizeof(Aux)); fid->aux = a; memset(a, 0, sizeof(Aux)); a->sh = -1; a->fh = -1; a->sp = oa->sp; a->path = estrdup9p(oa->path); if(Auxroot){ a->prev = Auxroot; a->next = Auxroot->next; Auxroot->next->prev = a; Auxroot->next = a; } else { Auxroot = a; a->next = a; a->prev = a; } 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 intvalidfile(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) return strcmp(want, share) == 0; /* * OK, a DFS redirection points us from a directory XXX * to a share named YYY. There is no case checking we can * do so we allow either case - it's 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){ FInfo fi; int rc, n, i; Aux *a = fid->aux; static char e[ERRMAX]; char *p, *npath, *winpath; *e = 0; npath = newpath(a->path, name); if(strcmp(npath, "/") == 0){ /* root dir */ *qid = mkqid("/", 1, 1, Proot, 0); 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 { /* volume name */ for(i = 0; i < Nshares; i++){ n = strlen(Shares[i].name); 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){ free(npath); return "not found"; } a->sp = Shares+i; *qid = mkqid(npath, 1, 1, Pshare, i); } 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){ rerrstr(e, sizeof(e)); free(npath); return e; } 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(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){ if(redirect(Sess, a->sp, npath) != -1) goto again; } 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; return nil;} static voidfsstat(Req *r){ int rc; FInfo fi; Aux *a = r->fid->aux; if(ptype(r->fid->qid.path) == Proot) V2D(&r->d, r->fid->qid, ""); else if(ptype(r->fid->qid.path) == Pinfo) dirgeninfo(pindex(r->fid->qid.path), &r->d); else if(ptype(r->fid->qid.path) == Pshare) V2D(&r->d, r->fid->qid, a->path +1); else{ memset(&fi, 0, sizeof fi); if(Sess->caps & CAP_NT_SMBS) rc = T2queryall(Sess, a->sp, mapfile(a->path), &fi); else rc = T2querystandard(Sess, a->sp, mapfile(a->path), &fi);Lines 1–500 of 1292. Load more
/* * 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. */ #include <u.h> #include <libc.h> #include <fcall.h> #include <thread.h> #include <libsec.h> #include <9p.h> #include "cifs.h" #define max(a,b) (((a) > (b))? (a): (b)) #define min(a,b) (((a) < (b))? (a): (b)) typedef struct Aux Aux; struct Aux { Aux *next; Aux *prev; char *path; /* full path fo file */ Share *sp; /* this share's info */ long expire; /* expiration time of cache */ long off; /* file pos of start of cache */ long end; /* file pos of end of cache */ char *cache; int fh; /* file handle */ int sh; /* search handle */ long srch; /* find first's internal state */ }; extern int chatty9p; int Checkcase = 1; /* enforce case significance on filenames */ 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 */ Qid Root; /* root of remote system */ Share Ipc; /* Share info of IPC$ share */ Session *Sess; /* current session */ int Active = IDLE_TIME; /* secs until next keepalive is sent */ static int Keeppid; /* process ID of keepalive thread */ Share Shares[MAX_SHARES]; /* table of connected shares */ int Nshares = 0; /* number of Shares connected */ Aux *Auxroot = nil; /* linked list of Aux structs */ char *Host = nil; /* host we are connected to */ static char *Ipcname = "IPC$"; #define ptype(x) (((x) & 0xf)) #define pindex(x) (((x) & 0xff0) >> 4) void setup(void) { int fd; char buf[32]; /* * This is revolting but I cannot see any other way to get * the pid of the server. We need this as Windows doesn't * drop the TCP connection when it closes a connection. * Thus we keepalive() to detect when/if we are thrown off. */ Attachpid = getpid(); snprint(buf, sizeof buf, "#p/%d/args", getpid()); if((fd = open(buf, OWRITE)) >= 0){ fprint(fd, "%s network", Host); close(fd); } } int filetableinfo(Fmt *f) { Aux *ap; char *type; if((ap = Auxroot) != nil) do{ type = "walked"; if(ap->sh != -1) type = "opendir"; if(ap->fh != -1) type = "openfile"; fmtprint(f, "%-9s %s\n", type, ap->path); ap = ap->next; }while(ap != Auxroot); return 0; } Qid mkqid(char *s, int is_dir, long vers, int subtype, long path) { Qid q; union { /* align digest suitably */ uchar digest[SHA1dlen]; uvlong uvl; } u; sha1((uchar *)s, strlen(s), u.digest, nil); q.type = (is_dir)? QTDIR: 0; q.vers = vers; if(subtype){ q.path = *((uvlong *)u.digest) & ~0xfffL; q.path |= ((path & 0xff) << 4); q.path |= (subtype & 0xf); } else q.path = *((uvlong *)u.digest) & ~0xfL; return q; } /* * used only for root dir and shares */ static void V2D(Dir *d, Qid qid, char *name) { memset(d, 0, sizeof(Dir)); d->type = 'C'; d->dev = 1; d->name = estrdup9p(name); d->uid = estrdup9p("bill"); d->muid = estrdup9p("boyd"); d->gid = estrdup9p("trog"); d->mode = 0755 | DMDIR; d->atime = time(nil); d->mtime = d->atime; d->length = 0; d->qid = qid; } static void I2D(Dir *d, Share *sp, char *path, FInfo *fi) { char *name; if((name = strrchr(fi->name, '\\')) != nil) name++; else name = fi->name; d->name = estrdup9p(name); d->type = 'C'; d->dev = sp->tid; d->uid = estrdup9p("bill"); d->gid = estrdup9p("trog"); d->muid = estrdup9p("boyd"); d->atime = fi->accessed; d->mtime = fi->written; if(fi->attribs & ATTR_READONLY) d->mode = 0444; else d->mode = 0666; d->length = fi->size; d->qid = mkqid(path, fi->attribs & ATTR_DIRECTORY, fi->changed, 0, 0); if(fi->attribs & ATTR_DIRECTORY){ d->length = 0; d->mode |= DMDIR|0111; } } static void responderrstr(Req *r) { char e[ERRMAX]; *e = 0; rerrstr(e, sizeof e); respond(r, e); } static char * newpath(char *path, char *name) { char *p, *q; assert((p = strrchr(path, '/')) != nil); if(strcmp(name, "..") == 0){ if(p == path) return estrdup9p("/"); q = emalloc9p((p-path)+1); strecpy(q, q+(p-path)+1, path); return q; } if(strcmp(path, "/") == 0) return smprint("/%s", name); return smprint("%s/%s", path, name); } static int dirgen(int slot, Dir *d, void *aux) { long off; FInfo *fi; int rc, got; Aux *a = aux; char *npath; int numinf = numinfo(); int slots = min(Sess->mtu, MTU) / sizeof(FInfo); if(strcmp(a->path, "/") == 0){ if(slot < numinf){ dirgeninfo(slot, d); return 0; } else slot -= numinf; if(slot >= Nshares) return -1; V2D(d, mkqid(Shares[slot].name, 1, 1, Pshare, slot), Shares[slot].name); return 0; } off = slot * sizeof(FInfo); if(off >= a->off && off < a->end && time(nil) < a->expire) goto from_cache; if(off == 0){ fi = (FInfo *)a->cache; npath = smprint("%s/*", mapfile(a->path)); a->sh = T2findfirst(Sess, a->sp, slots, npath, &got, &a->srch, (FInfo *)a->cache); free(npath); if(a->sh == -1) return -1; a->off = 0; a->end = got * sizeof(FInfo); if(got >= 2 && strcmp(fi[0].name, ".") == 0 && strcmp(fi[1].name, "..") == 0){ a->end = (got - 2) * sizeof(FInfo); memmove(a->cache, a->cache + sizeof(FInfo)*2, a->end - a->off); } } while(off >= a->end && a->sh != -1){ fi = (FInfo *)(a->cache + (a->end - a->off) - sizeof(FInfo)); a->off = a->end; npath = smprint("%s/%s", mapfile(a->path), fi->name); rc = T2findnext(Sess, a->sp, slots, npath, &got, &a->srch, (FInfo *)a->cache, a->sh); free(npath); if(rc == -1 || got == 0) break; a->end = a->off + got * sizeof(FInfo); } a->expire = time(nil) + CACHETIME; if(got < slots){ if(a->sh != -1) CIFSfindclose2(Sess, a->sp, a->sh); a->sh = -1; } if(off >= a->end) return -1; from_cache: fi = (FInfo *)(a->cache + (off - a->off)); npath = smprint("%s/%s", mapfile(a->path), fi->name); I2D(d, a->sp, npath, fi); if(Billtrog == 0) upd_names(Sess, a->sp, npath, d); free(npath); return 0; } static void fsattach(Req *r) { Aux *a; static int first = 1; char *spec = r->ifcall.aname; if(first) setup(); if(spec && *spec){ respond(r, "invalid attach specifier"); return; } r->ofcall.qid = mkqid("/", 1, 1, Proot, 0); r->fid->qid = r->ofcall.qid; a = r->fid->aux = emalloc9p(sizeof(Aux)); memset(a, 0, sizeof(Aux)); a->path = estrdup9p("/"); a->sp = nil; a->fh = -1; a->sh = -1; if(Auxroot){ a->prev = Auxroot; a->next = Auxroot->next; Auxroot->next->prev = a; Auxroot->next = a; } else { Auxroot = a; a->next = a; a->prev = a; } respond(r, nil); } static char* fsclone(Fid *ofid, Fid *fid) { Aux *oa = ofid->aux; Aux *a = emalloc9p(sizeof(Aux)); fid->aux = a; memset(a, 0, sizeof(Aux)); a->sh = -1; a->fh = -1; a->sp = oa->sp; a->path = estrdup9p(oa->path); if(Auxroot){ a->prev = Auxroot; a->next = Auxroot->next; Auxroot->next->prev = a; Auxroot->next = a; } else { Auxroot = a; a->next = a; a->prev = a; } 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) return strcmp(want, share) == 0; /* * OK, a DFS redirection points us from a directory XXX * to a share named YYY. There is no case checking we can * do so we allow either case - it's 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) { FInfo fi; int rc, n, i; Aux *a = fid->aux; static char e[ERRMAX]; char *p, *npath, *winpath; *e = 0; npath = newpath(a->path, name); if(strcmp(npath, "/") == 0){ /* root dir */ *qid = mkqid("/", 1, 1, Proot, 0); 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 { /* volume name */ for(i = 0; i < Nshares; i++){ n = strlen(Shares[i].name); 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){ free(npath); return "not found"; } a->sp = Shares+i; *qid = mkqid(npath, 1, 1, Pshare, i); } 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){ rerrstr(e, sizeof(e)); free(npath); return e; } 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(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){ if(redirect(Sess, a->sp, npath) != -1) goto again; } 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; return nil; } static void fsstat(Req *r) { int rc; FInfo fi; Aux *a = r->fid->aux; if(ptype(r->fid->qid.path) == Proot) V2D(&r->d, r->fid->qid, ""); else if(ptype(r->fid->qid.path) == Pinfo) dirgeninfo(pindex(r->fid->qid.path), &r->d); else if(ptype(r->fid->qid.path) == Pshare) V2D(&r->d, r->fid->qid, a->path +1); else{ memset(&fi, 0, sizeof fi); if(Sess->caps & CAP_NT_SMBS) rc = T2queryall(Sess, a->sp, mapfile(a->path), &fi); else rc = T2querystandard(Sess, a->sp, mapfile(a->path), &fi);