lib9p

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

qid_unix.go (2937B)


      1 //go:build unix
      2 
      3 package diskfs
      4 
      5 import (
      6 	"fmt"
      7 	"io/fs"
      8 	"os"
      9 	"path/filepath"
     10 	"syscall"
     11 	"time"
     12 
     13 	"git.mtkn.jp/lib9p"
     14 )
     15 
     16 // fileID is the identifier of a unix file.
     17 // It is used to assosiate the file with Qid.
     18 type fileID struct {
     19 	device uint64
     20 	inode  uint64
     21 }
     22 
     23 // qidReq is used to update the Qid.Vers by checking the file's
     24 // modified time.
     25 type qidRec struct {
     26 	qid   *lib9p.Qid
     27 	mtime time.Time
     28 }
     29 
     30 // QidPool is the list of Qids in the file system.
     31 type QidPool struct {
     32 	m       map[fileID]*qidRec
     33 	nextQid uint64
     34 }
     35 
     36 // id derives the fileID from the file.
     37 func (f *File) id() (fileID, error) {
     38 	ospath := filepath.Join(f.fs.rootPath, f.path)
     39 	fi, err := os.Stat(ospath)
     40 	if err != nil {
     41 		return fileID{}, err
     42 	}
     43 	sys := fi.Sys().(*syscall.Stat_t)
     44 	id := fileID{
     45 		device: uint64(sys.Dev),
     46 		inode:  sys.Ino,
     47 	}
     48 	return id, nil
     49 }
     50 
     51 // idFromInfo derives the fileID from the fs.FileInfo.
     52 func idFromInfo(path string, info fs.FileInfo) fileID {
     53 	stat := info.Sys().(*syscall.Stat_t)
     54 	return fileID{device: uint64(stat.Dev), inode: stat.Ino}
     55 }
     56 
     57 // newQidPool allocates a QidPool.
     58 func newQidPool() *QidPool {
     59 	return &QidPool{
     60 		m: make(map[fileID]*qidRec),
     61 	}
     62 }
     63 
     64 // lookup looks up the file in the QidPool.
     65 // If found, it returns the corresponding Qid and true.
     66 // If not found, it returns nil and false.
     67 func (pool *QidPool) lookup(f *File) (lib9p.Qid, bool) {
     68 	id, err := f.id()
     69 	if err != nil {
     70 		return lib9p.Qid{}, false
     71 	}
     72 	ospath := filepath.Join(f.fs.rootPath, f.path)
     73 	fsfi, err := os.Stat(ospath)
     74 	if err != nil {
     75 		return lib9p.Qid{}, false
     76 	}
     77 	return pool.lookupID(id, fsfi.ModTime())
     78 }
     79 
     80 // lookupID looksup the file in the QidPool by fileID.
     81 // It also checks if the file is modified after the last access by
     82 // comparing the qidReq.mtime and mtime, and update Qid.Vers if needed.
     83 func (pool *QidPool) lookupID(id fileID, mtime time.Time) (lib9p.Qid, bool) {
     84 	qrec, ok := pool.m[id]
     85 	if !ok {
     86 		return lib9p.Qid{}, false
     87 	}
     88 	if qrec.mtime.Before(mtime) {
     89 		qrec.qid.Vers++
     90 		qrec.mtime = mtime
     91 	}
     92 	return *qrec.qid, ok
     93 }
     94 
     95 // add adds the file's Qid to the QidPool and returns the newly added Qid.
     96 func (pool *QidPool) add(f *File) (lib9p.Qid, error) {
     97 	id, err := f.id()
     98 	if err != nil {
     99 		return lib9p.Qid{}, fmt.Errorf("get id: %v", err)
    100 	}
    101 	ospath := filepath.Join(f.fs.rootPath, f.path)
    102 	fi, err := os.Stat(ospath)
    103 	if err != nil {
    104 		return lib9p.Qid{}, fmt.Errorf("stat %v: %v", f, err)
    105 	}
    106 	return pool.addID(id, fi)
    107 }
    108 
    109 // addID does the same as add, but by fileID.
    110 func (pool *QidPool) addID(id fileID, info fs.FileInfo) (lib9p.Qid, error) {
    111 	qtype := lib9p.FSModeToQidType(info.Mode())
    112 	qid := &lib9p.Qid{
    113 		Path: pool.nextQid,
    114 		Type: qtype,
    115 	}
    116 	pool.m[id] = &qidRec{qid: qid, mtime: info.ModTime()}
    117 	pool.nextQid++
    118 	return *qid, nil
    119 }
    120 
    121 // delete deletes Qid associated with f from the QidPool.
    122 func (pool *QidPool) delete(f *File) {
    123 	id, err := f.id()
    124 	if err != nil {
    125 		return
    126 	}
    127 	delete(pool.m, id)
    128 }