--- third_party/go9p/ufs_freebsd.go.orig 2018-08-08 17:01:39 UTC +++ third_party/go9p/ufs_freebsd.go @@ -0,0 +1,235 @@ +package go9p + +import ( + "fmt" + "os" + "os/user" + "path" + "strconv" + "strings" + "syscall" + "time" +) + +func atime(stat *syscall.Stat_t) time.Time { + return time.Unix(stat.Atimespec.Unix()) +} + +// IsBlock reports if the file is a block device +func isBlock(d os.FileInfo) bool { + stat := d.Sys().(*syscall.Stat_t) + return (stat.Mode & syscall.S_IFMT) == syscall.S_IFBLK + return true +} + +// IsChar reports if the file is a character device +func isChar(d os.FileInfo) bool { + stat := d.Sys().(*syscall.Stat_t) + return (stat.Mode & syscall.S_IFMT) == syscall.S_IFCHR + return true +} + +func dir2Qid(d os.FileInfo) *Qid { + var qid Qid + + qid.Path = uint64(d.Sys().(*syscall.Stat_t).Ino) + qid.Version = uint32(d.ModTime().UnixNano() / 1000000) + qid.Type = dir2QidType(d) + + return &qid +} + +func dir2Dir(path string, d os.FileInfo, dotu bool, upool Users) (*Dir, error) { + if r := recover(); r != nil { + fmt.Print("stat failed: ", r) + return nil, &os.PathError{"dir2Dir", path, nil} + } + sysif := d.Sys() + if sysif == nil { + return nil, &os.PathError{"dir2Dir: sysif is nil", path, nil} + } + sysMode := sysif.(*syscall.Stat_t) + + dir := new(ufsDir) + dir.Qid = *dir2Qid(d) + dir.Mode = dir2Npmode(d, dotu) + dir.Atime = uint32(0 /*atime(sysMode).Unix()*/) + dir.Mtime = uint32(d.ModTime().Unix()) + dir.Length = uint64(d.Size()) + dir.Name = path[strings.LastIndex(path, "/")+1:] + + if dotu { + dir.dotu(path, d, upool, sysMode) + return &dir.Dir, nil + } + + unixUid := int(sysMode.Uid) + unixGid := int(sysMode.Gid) + dir.Uid = strconv.Itoa(unixUid) + dir.Gid = strconv.Itoa(unixGid) + + // BUG(akumar): LookupId will never find names for + // groups, as it only operates on user ids. + u, err := user.LookupId(dir.Uid) + if err == nil { + dir.Uid = u.Username + } + g, err := user.LookupId(dir.Gid) + if err == nil { + dir.Gid = g.Username + } + + /* For Akaros, we use the Muid as the link value. */ + if *Akaros && (d.Mode()&os.ModeSymlink != 0) { + dir.Muid, err = os.Readlink(path) + if err == nil { + dir.Mode |= DMSYMLINK + } + } + return &dir.Dir, nil +} + +func (dir *ufsDir) dotu(path string, d os.FileInfo, upool Users, sysMode *syscall.Stat_t) { + u := upool.Uid2User(int(sysMode.Uid)) + g := upool.Gid2Group(int(sysMode.Gid)) + dir.Uid = u.Name() + if dir.Uid == "" { + dir.Uid = "none" + } + + dir.Gid = g.Name() + if dir.Gid == "" { + dir.Gid = "none" + } + dir.Muid = "none" + dir.Ext = "" + dir.Uidnum = uint32(u.Id()) + dir.Gidnum = uint32(g.Id()) + dir.Muidnum = NOUID + if d.Mode()&os.ModeSymlink != 0 { + var err error + dir.Ext, err = os.Readlink(path) + if err != nil { + dir.Ext = "" + } + } else if isBlock(d) { + dir.Ext = fmt.Sprintf("b %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF) + } else if isChar(d) { + dir.Ext = fmt.Sprintf("c %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF) + } +} + +func (u *Ufs) Wstat(req *SrvReq) { + fid := req.Fid.Aux.(*ufsFid) + err := fid.stat() + if err != nil { + req.RespondError(err) + return + } + + dir := &req.Tc.Dir + if dir.Mode != 0xFFFFFFFF { + mode := dir.Mode & 0777 + if req.Conn.Dotu { + if dir.Mode&DMSETUID > 0 { + mode |= syscall.S_ISUID + } + if dir.Mode&DMSETGID > 0 { + mode |= syscall.S_ISGID + } + } + e := os.Chmod(fid.path, os.FileMode(mode)) + if e != nil { + req.RespondError(toError(e)) + return + } + } + + uid, gid := NOUID, 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 { + 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 != NOUID || gid != NOUID { + e := os.Chown(fid.path, int(uid), int(gid)) + if e != nil { + req.RespondError(toError(e)) + return + } + } + + if dir.Name != "" { + fmt.Printf("Rename %s to %s\n", fid.path, dir.Name) + // if first char is / it is relative to root, else relative to + // cwd. + var destpath string + if dir.Name[0] == '/' { + destpath = path.Join(u.Root, dir.Name) + fmt.Printf("/ results in %s\n", destpath) + } else { + fiddir, _ := path.Split(fid.path) + destpath = path.Join(fiddir, dir.Name) + fmt.Printf("rel results in %s\n", destpath) + } + err := syscall.Rename(fid.path, destpath) + fmt.Printf("rename %s to %s gets %v\n", fid.path, destpath, err) + if err != nil { + req.RespondError(toError(err)) + return + } + fid.path = destpath + } + + 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 = time.Time(0)//atime(st.Sys().(*syscall.Stat_t)) + } + } + e := os.Chtimes(fid.path, at, mt) + if e != nil { + req.RespondError(toError(e)) + return + } + } + + req.RespondRwstat() +}