lib9p

Go 9P library.
Log | Files | Refs | LICENSE

stat_windows.go (4592B)


      1 //go:build windows
      2 
      3 package diskfs
      4 
      5 import (
      6 	"fmt"
      7 	"io/fs"
      8 	"os"
      9 	"os/user"
     10 	"path"
     11 	"strconv"
     12 	"syscall"
     13 	"time"
     14 
     15 	"git.mtkn.jp/lib9p"
     16 )
     17 
     18 func fileTimeToUnixTime(t syscall.Filetime) uint32 {
     19 	return uint32(uint64(t.HighDateTime<<32|t.LowDateTime)/1e7 - 11644473600)
     20 }
     21 
     22 // fiStat generates lib9p.Stat from the FileInfo.
     23 // It requires QidPool and fileID to fill the Qid field of Stat.
     24 func fiStat(pool *QidPool, id fileID, info fs.FileInfo) (*lib9p.Stat, error) {
     25 	qid, ok := pool.lookupID(id, info.ModTime())
     26 	if !ok {
     27 		var err error
     28 		qid, err = pool.addID(id, info)
     29 		if err != nil {
     30 			return nil, fmt.Errorf("allocID: %v", err)
     31 		}
     32 	}
     33 	stat := info.Sys().(*syscall.Win32FileAttributeData)
     34 	/*
     35 		// TODO: get actual file uid
     36 		usr, err := user.LookupId(strconv.Itoa(syscall.Getuid()))
     37 		if err != nil {
     38 			return nil, fmt.Errorf("user: %v", err)
     39 		}
     40 		// TODO: get actual file gid
     41 		group, err := user.LookupGroupId(strconv.Itoa(syscall.Getgid()))
     42 		if err != nil {
     43 			return nil, fmt.Errorf("group: %v", err)
     44 		}
     45 	*/
     46 	return &lib9p.Stat{
     47 		Type:   0,
     48 		Dev:    0,
     49 		Qid:    qid,
     50 		Mode:   info.Mode(),
     51 		Atime:  fileTimeToUnixTime(stat.LastAccessTime),
     52 		Mtime:  fileTimeToUnixTime(stat.LastWriteTime),
     53 		Length: info.Size(),
     54 		Name:   info.Name(),
     55 		Uid:    "glenda", //usr.Username,
     56 		Gid:    "glenda", //group.Name,
     57 		Muid:   "",
     58 	}, nil
     59 }
     60 
     61 var gidCache map[fileID]string
     62 
     63 func init() {
     64 	gidCache = make(map[fileID]string)
     65 }
     66 
     67 // Stat is real implementation of File.Stat.
     68 func (f *File) stat() (*lib9p.FileInfo, error) {
     69 	ospath := path.Join(f.fs.rootPath, f.path)
     70 	fsfi, err := os.Stat(ospath)
     71 	if err != nil {
     72 		return nil, fmt.Errorf("stat: %v", err)
     73 	}
     74 	id, err := f.id()
     75 	if err != nil {
     76 		return nil, fmt.Errorf("id: %v", err)
     77 	}
     78 	stat, err := fiStat(f.fs.qidPool, id, fsfi)
     79 	if err != nil {
     80 		return nil, fmt.Errorf("fiStat: %v", err)
     81 	}
     82 	return &lib9p.FileInfo{*stat}, nil
     83 }
     84 
     85 // wstat is the real implementation of File.WStat.
     86 // TODO: when error occurs, file stat should be restored.
     87 func (f *File) wstat(s *lib9p.Stat) error {
     88 	var file *os.File
     89 	fi, err := f.Stat()
     90 	if err != nil {
     91 		return fmt.Errorf("stat: %v", err)
     92 	}
     93 	oldStat := fi.Sys().(*lib9p.Stat)
     94 	if s.Length != oldStat.Length {
     95 		if file == nil {
     96 			file, err = os.OpenFile(path.Join(f.fs.rootPath, f.path), os.O_RDWR, 0)
     97 			if err != nil {
     98 				return err
     99 			}
    100 			defer file.Close()
    101 		}
    102 		if err := file.Truncate(s.Length); err != nil {
    103 			return fmt.Errorf("truncate: %v", err)
    104 		}
    105 		ret, err := file.Seek(0, 0)
    106 		if err != nil {
    107 			return fmt.Errorf("seek 0: %d, %w", ret, err)
    108 		}
    109 	}
    110 	if s.Mode != oldStat.Mode {
    111 		if file == nil {
    112 			file, err = os.OpenFile(path.Join(f.fs.rootPath, f.path), os.O_RDWR, 0)
    113 			if err != nil {
    114 				return err
    115 			}
    116 			defer file.Close()
    117 		}
    118 		if err := file.Chmod(s.Mode); err != nil {
    119 			return fmt.Errorf("chmod: %v", err)
    120 		}
    121 	}
    122 	if s.Mtime != oldStat.Mtime {
    123 		err := os.Chtimes(f.path, time.Time{}, time.Unix(int64(s.Mtime), 0))
    124 		if err != nil {
    125 			return fmt.Errorf("chtimes: %v", err)
    126 		}
    127 	}
    128 	if s.Gid != oldStat.Gid {
    129 		group, err := user.LookupGroup(s.Gid)
    130 		if err != nil {
    131 			return fmt.Errorf("lookupgroup: %v", err)
    132 		}
    133 		usr, err := user.Lookup(oldStat.Uid)
    134 		if err != nil {
    135 			return fmt.Errorf("lookup user: %v", err)
    136 		}
    137 		u, err := strconv.Atoi(usr.Uid)
    138 		if err != nil {
    139 			return fmt.Errorf("convert uid: %v", err)
    140 		}
    141 		g, err := strconv.Atoi(group.Gid)
    142 		if err != nil {
    143 			return fmt.Errorf("convert gid: %v", err)
    144 		}
    145 		if file == nil {
    146 			file, err = os.OpenFile(path.Join(f.fs.rootPath, f.path), os.O_RDWR, 0)
    147 			if err != nil {
    148 				return err
    149 			}
    150 			defer file.Close()
    151 		}
    152 		if err := file.Chown(u, g); err != nil {
    153 			return fmt.Errorf("chown: %v", err)
    154 		}
    155 	}
    156 	if s.Name != oldStat.Name {
    157 		// TODO: check neither Names contains "/"
    158 		oldpath := path.Join(f.fs.rootPath, path.Dir(f.path), oldStat.Name)
    159 		newpath := path.Join(f.fs.rootPath, path.Dir(f.path), s.Name)
    160 		if err := os.Rename(oldpath, newpath); err != nil {
    161 			return fmt.Errorf("rename: %v", err)
    162 		}
    163 	}
    164 	return nil
    165 }
    166 
    167 func chown(ospath string, uid, gid string) error {
    168 	usr, err := user.Lookup(uid)
    169 	if err != nil {
    170 		return fmt.Errorf("lookup user: %v", err)
    171 	}
    172 	u, err := strconv.Atoi(usr.Uid)
    173 	if err != nil {
    174 		return fmt.Errorf("convert uid: %v", err)
    175 	}
    176 	group, err := user.LookupGroup(gid)
    177 	if err != nil {
    178 		return fmt.Errorf("lookupgroup: %v", err)
    179 	}
    180 	g, err := strconv.Atoi(group.Gid)
    181 	if err != nil {
    182 		return fmt.Errorf("convert gid: %v", err)
    183 	}
    184 	if err := os.Chown(ospath, u, g); err != nil {
    185 		return fmt.Errorf("set owner and group: %v", err)
    186 	}
    187 	return nil
    188 }