예제 #1
0
파일: vufs.go 프로젝트: postfix/vufs
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)
}
예제 #2
0
파일: vufs.go 프로젝트: postfix/vufs
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)
}
예제 #3
0
파일: ufs.go 프로젝트: rjkroege/go9p
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)
}
예제 #4
0
파일: vufs.go 프로젝트: postfix/vufs
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()
}
예제 #5
0
파일: vufs.go 프로젝트: postfix/vufs
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)
}
예제 #6
0
파일: vufs.go 프로젝트: postfix/vufs
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))
}
예제 #7
0
파일: ufs.go 프로젝트: rjkroege/go9p
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)
}
예제 #8
0
파일: vufs.go 프로젝트: postfix/vufs
// 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)
}
예제 #9
0
파일: ufs.go 프로젝트: rjkroege/go9p
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])
}
예제 #10
0
파일: ufs.go 프로젝트: rjkroege/go9p
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)
}
예제 #11
0
파일: vufs.go 프로젝트: postfix/vufs
func (*VuFs) Clunk(req *srv.Req) { req.RespondRclunk() }
예제 #12
0
파일: vufs.go 프로젝트: postfix/vufs
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()
}
예제 #13
0
파일: ufs.go 프로젝트: rjkroege/go9p
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()
}
예제 #14
0
파일: ufs.go 프로젝트: rjkroege/go9p
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()
}
예제 #15
0
파일: ufs.go 프로젝트: rjkroege/go9p
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)
}
예제 #16
0
파일: vufs.go 프로젝트: postfix/vufs
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()
}
예제 #17
0
파일: vufs.go 프로젝트: postfix/vufs
// 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])
}