func (u *VuFs) Open(req *srv.Req) { fid := req.Fid.Aux.(*Fid) tc := req.Tc // Ensure open permission. st, err := os.Stat(fid.path) if err != nil { req.RespondError(srv.Enoent) return } f, err := dir2Dir(fid.path, st, req.Conn.Srv.Upool) if err != nil { req.RespondError(toError(err)) return } if !CheckPerm(f, req.Fid.User, mode2Perm(tc.Mode)) { req.RespondError(srv.Eperm) return } var e error fid.file, e = os.OpenFile(fid.path, omode2uflags(tc.Mode), 0) if e != nil { req.RespondError(toError(e)) return } req.RespondRopen(dir2Qid(st), 0) }
func (*VuFs) Create(req *srv.Req) { fid := req.Fid.Aux.(*Fid) tc := req.Tc st, err := os.Stat(fid.path) if err != nil { req.RespondError(toError(err)) return } path := fid.path + "/" + tc.Name var e error = nil var file *os.File = nil switch { case tc.Perm&p.DMDIR != 0: e = os.Mkdir(path, os.FileMode(tc.Perm&0777)) if e == nil { file, e = os.OpenFile(path, omode2uflags(tc.Mode), 0) } case tc.Perm&p.DMSYMLINK != 0: case tc.Perm&p.DMLINK != 0: case tc.Perm&p.DMNAMEDPIPE != 0: case tc.Perm&p.DMDEVICE != 0: case tc.Perm&p.DMSOCKET != 0: case tc.Perm&p.DMSETUID != 0: case tc.Perm&p.DMSETGID != 0: req.RespondError(srv.Ebaduse) return default: var mode uint32 = tc.Perm & 0777 file, e = os.OpenFile(path, omode2uflags(tc.Mode)|os.O_CREATE, os.FileMode(mode)) } if e != nil { req.RespondError(toError(e)) return } fid.path = path fid.file = file st, err = os.Stat(fid.path) if err != nil { req.RespondError(err) return } req.RespondRcreate(dir2Qid(st), 0) }
func (*Ufs) Stat(req *srv.Req) { fid := req.Fid.Aux.(*Fid) if err := fid.stat(); err != nil { req.RespondError(err) return } st, err := dir2Dir(fid.path, fid.st, req.Conn.Dotu, req.Conn.Srv.Upool) if err != nil { req.RespondError(err) return } req.RespondRstat(st) }
func (*VuFs) Remove(req *srv.Req) { fid := req.Fid.Aux.(*Fid) _, err := os.Stat(fid.path) if err != nil { req.RespondError(toError(err)) return } e := os.Remove(fid.path) if e != nil { req.RespondError(toError(e)) return } req.RespondRremove() }
func (*VuFs) Stat(req *srv.Req) { fid := req.Fid.Aux.(*Fid) st, err := os.Stat(fid.path) if err != nil { req.RespondError(toError(err)) return } dir, err := dir2Dir(fid.path, st, req.Conn.Srv.Upool) if err != nil { req.RespondError(err) return } req.RespondRstat(dir) }
func (*VuFs) Write(req *srv.Req) { fid := req.Fid.Aux.(*Fid) tc := req.Tc _, err := os.Stat(fid.path) if err != nil { req.RespondError(toError(err)) return } n, e := fid.file.WriteAt(tc.Data, int64(tc.Offset)) if e != nil { req.RespondError(toError(e)) return } req.RespondRwrite(uint32(n)) }
func (*Ufs) Open(req *srv.Req) { fid := req.Fid.Aux.(*Fid) tc := req.Tc err := fid.stat() if err != nil { req.RespondError(err) return } var e error fid.file, e = os.OpenFile(fid.path, omode2uflags(tc.Mode), 0) if e != nil { req.RespondError(toError(e)) return } req.RespondRopen(dir2Qid(fid.st), 0) }
// Always attach to the VuFs root. func (u *VuFs) Attach(req *srv.Req) { if req.Tc.Aname != "/" && req.Tc.Aname != "" { req.RespondError(srv.Eperm) return } st, err := os.Stat(u.Root) if err != nil { req.RespondError(toError(err)) return } fid := new(Fid) fid.path = u.Root req.Fid.Aux = fid qid := dir2Qid(st) req.RespondRattach(qid) }
func (*Ufs) Walk(req *srv.Req) { fid := req.Fid.Aux.(*Fid) tc := req.Tc err := fid.stat() if err != nil { req.RespondError(err) return } if req.Newfid.Aux == nil { req.Newfid.Aux = new(Fid) } nfid := req.Newfid.Aux.(*Fid) wqids := make([]p.Qid, len(tc.Wname)) path := fid.path i := 0 for ; i < len(tc.Wname); i++ { p := path + "/" + tc.Wname[i] st, err := os.Lstat(p) if err != nil { if i == 0 { req.RespondError(Enoent) return } break } wqids[i] = *dir2Qid(st) path = p } nfid.path = path req.RespondRwalk(wqids[0:i]) }
func (u *Ufs) Attach(req *srv.Req) { if req.Afid != nil { req.RespondError(srv.Enoauth) return } tc := req.Tc fid := new(Fid) // You can think of the ufs.Root as a 'chroot' of a sort. // client attaches are not allowed to go outside the // directory represented by ufs.Root fid.path = path.Join(u.Root, tc.Aname) req.Fid.Aux = fid err := fid.stat() if err != nil { req.RespondError(err) return } qid := dir2Qid(fid.st) req.RespondRattach(qid) }
func (*VuFs) Clunk(req *srv.Req) { req.RespondRclunk() }
func (u *VuFs) Read(req *srv.Req) { fid := req.Fid.Aux.(*Fid) tc := req.Tc rc := req.Rc st, err := os.Stat(fid.path) if err != nil { req.RespondError(err) return } p.InitRread(rc, tc.Count) var count int var e error if st.IsDir() { // Simpler to treat non-zero offset as an error for directories. if tc.Offset != 0 { req.RespondError(srv.Ebadoffset) return } dirs, e := fid.file.Readdir(-1) if e != nil { req.RespondError(toError(e)) return } // Bytes/one packed dir = 49 + len(name) + len(uid) + len(gid) + len(muid) // Estimate 49 + 20 + 20 + 20 + 11 // From ../../lionkov/go9p/p/p9.go:421,427 dirents := make([]byte, 0, 120*len(dirs)) for i := 0; i < len(dirs); i++ { path := fid.path + "/" + dirs[i].Name() st, err := dir2Dir(path, dirs[i], req.Conn.Srv.Upool) if err != nil { req.RespondError(toError(err)) return } b := p.PackDir(st, false) dirents = append(dirents, b...) } if len(dirents) > int(tc.Count) { req.RespondError(srv.Etoolarge) return } copy(rc.Data, dirents) count = len(dirents) } else { count, e = fid.file.ReadAt(rc.Data, int64(tc.Offset)) if e != nil && e != io.EOF { req.RespondError(toError(e)) return } } p.SetRreadCount(rc, uint32(count)) req.Respond() }
func (u *Ufs) Wstat(req *srv.Req) { var changed bool fid := req.Fid.Aux.(*Fid) err := fid.stat() if err != nil { req.RespondError(err) return } dir := &req.Tc.Dir if dir.Mode != 0xFFFFFFFF { changed = true mode := dir.Mode & 0777 if req.Conn.Dotu { if dir.Mode&p.DMSETUID > 0 { mode |= syscall.S_ISUID } if dir.Mode&p.DMSETGID > 0 { mode |= syscall.S_ISGID } } e := os.Chmod(fid.path, os.FileMode(mode)) if e != nil { req.RespondError(toError(e)) return } } uid, gid := p.NOUID, p.NOUID if req.Conn.Dotu { uid = dir.Uidnum gid = dir.Gidnum } // Try to find local uid, gid by name. if (dir.Uid != "" || dir.Gid != "") && !req.Conn.Dotu { changed = true uid, err = lookup(dir.Uid, false) if err != nil { req.RespondError(err) return } // BUG(akumar): Lookup will never find gids // corresponding to group names, because // it only operates on user names. gid, err = lookup(dir.Gid, true) if err != nil { req.RespondError(err) return } } if uid != p.NOUID || gid != p.NOUID { changed = true e := os.Chown(fid.path, int(uid), int(gid)) if e != nil { req.RespondError(toError(e)) return } } if dir.Name != "" { changed = true // If we path.Join dir.Name to / before adding it to // the fid path, that ensures nobody gets to walk out of the // root of this server. newname := path.Join(path.Dir(fid.path), path.Join("/", dir.Name)) // absolute renaming. Ufs can do this, so let's support it. // We'll allow an absolute path in the Name and, if it is, // we will make it relative to root. This is a gigantic performance // improvement in systems that allow it. if filepath.IsAbs(dir.Name) { newname = path.Join(u.Root, dir.Name) } err := syscall.Rename(fid.path, newname) if err != nil { req.RespondError(toError(err)) return } fid.path = newname } if dir.Length != 0xFFFFFFFFFFFFFFFF { changed = true e := os.Truncate(fid.path, int64(dir.Length)) if e != nil { req.RespondError(toError(e)) return } } // If either mtime or atime need to be changed, then // we must change both. if dir.Mtime != ^uint32(0) || dir.Atime != ^uint32(0) { changed = true mt, at := time.Unix(int64(dir.Mtime), 0), time.Unix(int64(dir.Atime), 0) if cmt, cat := (dir.Mtime == ^uint32(0)), (dir.Atime == ^uint32(0)); cmt || cat { st, e := os.Stat(fid.path) if e != nil { req.RespondError(toError(e)) return } switch cmt { case true: mt = st.ModTime() default: at = atime(st.Sys().(*syscall.Stat_t)) } } e := os.Chtimes(fid.path, at, mt) if e != nil { req.RespondError(toError(e)) return } } if !changed && fid.file != nil { fid.file.Sync() } req.RespondRwstat() }
func (u *Ufs) Read(req *srv.Req) { dbg := u.Debuglevel&srv.DbgLogFcalls != 0 fid := req.Fid.Aux.(*Fid) tc := req.Tc rc := req.Rc err := fid.stat() if err != nil { req.RespondError(err) return } p.InitRread(rc, tc.Count) var count int var e error if fid.st.IsDir() { if tc.Offset == 0 { var e error // If we got here, it was open. Can't really seek // in most cases, just close and reopen it. fid.file.Close() if fid.file, e = os.OpenFile(fid.path, omode2uflags(req.Fid.Omode), 0); e != nil { req.RespondError(toError(e)) return } if fid.dirs, e = fid.file.Readdir(-1); e != nil { req.RespondError(toError(e)) return } if dbg { log.Printf("Read: read %d entries", len(fid.dirs)) } fid.dirents = nil fid.direntends = nil for i := 0; i < len(fid.dirs); i++ { path := fid.path + "/" + fid.dirs[i].Name() st, err := dir2Dir(path, fid.dirs[i], req.Conn.Dotu, req.Conn.Srv.Upool) if err != nil { if dbg { log.Printf("dbg: stat of %v: %v", path, err) } continue } if dbg { log.Printf("Stat: %v is %v", path, st) } b := p.PackDir(st, req.Conn.Dotu) fid.dirents = append(fid.dirents, b...) count += len(b) fid.direntends = append(fid.direntends, count) if dbg { log.Printf("fid.direntends is %v\n", fid.direntends) } } } switch { case tc.Offset > uint64(len(fid.dirents)): count = 0 case len(fid.dirents[tc.Offset:]) > int(tc.Count): count = int(tc.Count) default: count = len(fid.dirents[tc.Offset:]) } if dbg { log.Printf("readdir: count %v @ offset %v", count, tc.Offset) } nextend := sort.SearchInts(fid.direntends, int(tc.Offset)+count) if nextend < len(fid.direntends) { if fid.direntends[nextend] > int(tc.Offset)+count { if nextend > 0 { count = fid.direntends[nextend-1] - int(tc.Offset) } else { count = 0 } } } if dbg { log.Printf("readdir: count adjusted %v @ offset %v", count, tc.Offset) } if count == 0 && int(tc.Offset) < len(fid.dirents) && len(fid.dirents) > 0 { req.RespondError(&p.Error{"too small read size for dir entry", p.EINVAL}) return } copy(rc.Data, fid.dirents[tc.Offset:int(tc.Offset)+count]) } else { count, e = fid.file.ReadAt(rc.Data, int64(tc.Offset)) if e != nil && e != io.EOF { req.RespondError(toError(e)) return } } p.SetRreadCount(rc, uint32(count)) req.Respond() }
func (*Ufs) Create(req *srv.Req) { fid := req.Fid.Aux.(*Fid) tc := req.Tc err := fid.stat() if err != nil { req.RespondError(err) return } path := fid.path + "/" + tc.Name var e error = nil var file *os.File = nil switch { case tc.Perm&p.DMDIR != 0: e = os.Mkdir(path, os.FileMode(tc.Perm&0777)) case tc.Perm&p.DMSYMLINK != 0: e = os.Symlink(tc.Ext, path) case tc.Perm&p.DMLINK != 0: n, e := strconv.ParseUint(tc.Ext, 10, 0) if e != nil { break } ofid := req.Conn.FidGet(uint32(n)) if ofid == nil { req.RespondError(srv.Eunknownfid) return } e = os.Link(ofid.Aux.(*Fid).path, path) ofid.DecRef() case tc.Perm&p.DMNAMEDPIPE != 0: case tc.Perm&p.DMDEVICE != 0: req.RespondError(&p.Error{"not implemented", p.EIO}) return default: var mode uint32 = tc.Perm & 0777 if req.Conn.Dotu { if tc.Perm&p.DMSETUID > 0 { mode |= syscall.S_ISUID } if tc.Perm&p.DMSETGID > 0 { mode |= syscall.S_ISGID } } file, e = os.OpenFile(path, omode2uflags(tc.Mode)|os.O_CREATE, os.FileMode(mode)) } if file == nil && e == nil { file, e = os.OpenFile(path, omode2uflags(tc.Mode), 0) } if e != nil { req.RespondError(toError(e)) return } fid.path = path fid.file = file err = fid.stat() if err != nil { req.RespondError(err) return } req.RespondRcreate(dir2Qid(fid.st), 0) }
func (u *VuFs) Wstat(req *srv.Req) { fid := req.Fid.Aux.(*Fid) _, err := os.Stat(fid.path) if err != nil { req.RespondError(toError(err)) return } dir := &req.Tc.Dir if dir.Mode != 0xFFFFFFFF { mode := dir.Mode & 0777 e := os.Chmod(fid.path, os.FileMode(mode)) if e != nil { req.RespondError(toError(e)) return } } /* // BUG(mbucc) implement chown uid, gid := p.NOUID, p.NOUID uid, err = lookup(dir.Uid, false) if err != nil { req.RespondError(err) return } gid, err = lookup(dir.Gid, true) if err != nil { req.RespondError(err) return } if uid != p.NOUID || gid != p.NOUID { e := os.Chown(fid.path, int(uid), int(gid)) if e != nil { req.RespondError(toError(e)) return } } */ if dir.Name != "" { // If we path.Join dir.Name to / before adding it to // the fid path, that ensures nobody gets to walk out of the // root of this server. newname := path.Join(path.Dir(fid.path), path.Join("/", dir.Name)) // absolute renaming. VuFs can do this, so let's support it. // We'll allow an absolute path in the Name and, if it is, // we will make it relative to root. This is a gigantic performance // improvement in systems that allow it. if filepath.IsAbs(dir.Name) { newname = path.Join(fid.path, dir.Name) } err := syscall.Rename(fid.path, newname) if err != nil { req.RespondError(toError(err)) return } fid.path = newname } if dir.Length != 0xFFFFFFFFFFFFFFFF { e := os.Truncate(fid.path, int64(dir.Length)) if e != nil { req.RespondError(toError(e)) return } } // If either mtime or atime need to be changed, then // we must change both. if dir.Mtime != ^uint32(0) || dir.Atime != ^uint32(0) { mt, at := time.Unix(int64(dir.Mtime), 0), time.Unix(int64(dir.Atime), 0) if cmt, cat := (dir.Mtime == ^uint32(0)), (dir.Atime == ^uint32(0)); cmt || cat { st, e := os.Stat(fid.path) if e != nil { req.RespondError(toError(e)) return } switch cmt { case true: mt = st.ModTime() default: at = atime(st.Sys().(*syscall.Stat_t)) } } e := os.Chtimes(fid.path, at, mt) if e != nil { req.RespondError(toError(e)) return } } req.RespondRwstat() }
// BUG(mbucc) does not fully implement spec when fid = newfid. // From http://plan9.bell-labs.com/magic/man2html/5/walk: // If newfid is the same as fid, the above discussion applies, with the // obvious difference that if the walk changes the state of newfid, it // also changes the state of fid; and if newfid is unaffected, then fid // is also unaffected. // func (u *VuFs) Walk(req *srv.Req) { fid := req.Fid.Aux.(*Fid) tc := req.Tc _, err := os.Stat(fid.path) if err != nil { req.RespondError(toError(err)) return } if req.Newfid.Aux == nil { req.Newfid.Aux = new(Fid) } newfid := req.Newfid.Aux.(*Fid) wqids := make([]p.Qid, len(tc.Wname)) path := fid.path i := 0 // Ensure execute permission on the walk root. st, err := os.Stat(path) if err != nil { req.RespondError(srv.Enoent) return } f, err := dir2Dir(path, st, req.Conn.Srv.Upool) if err != nil { req.RespondError(toError(err)) return } if !CheckPerm(f, req.Fid.User, p.DMEXEC) { req.RespondError(srv.Eperm) return } for ; i < len(tc.Wname); i++ { var newpath string // Don't allow client to dotdot out of the file system root. if tc.Wname[i] == ".." { if path == u.Root { continue } else { newpath = path[:strings.LastIndex(path, "/")] if newpath == u.Root { continue } } } else { newpath = path + "/" + tc.Wname[i] } st, err := os.Stat(newpath) if err != nil { if i == 0 { req.RespondError(srv.Enoent) return } break } wqids[i] = *dir2Qid(st) if (wqids[i].Type & p.QTDIR) > 0 { f, err := dir2Dir(newpath, st, req.Conn.Srv.Upool) if err != nil { req.RespondError(toError(err)) return } if !CheckPerm(f, req.Fid.User, p.DMEXEC) { req.RespondError(srv.Eperm) return } } path = newpath } newfid.path = path req.RespondRwalk(wqids[0:i]) }