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 }