lib9p

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

qid_unix.go (3250B)


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