fs.go (3795B)
1 // Package diskfs provides a glue layer between OS's file system and 2 // 9P file system. It can be used to export OS's file tree via 9P protocol. 3 package diskfs 4 5 import ( 6 "fmt" 7 "io/fs" 8 "os" 9 "os/user" 10 "path" 11 "path/filepath" 12 13 "git.mtkn.jp/lib9p" 14 ) 15 16 // FS is a file system to export OS's file system via 9P protocol. 17 type FS struct { 18 rootPath string // slash-separated. 19 qidPool *QidPool 20 } 21 22 // Open opens OS's file tree as *FS rooted at name. 23 // Name is either os.PathSeparator-separated or slash-separated. 24 func Open(name string) (*FS, error) { 25 f, err := os.Open(name) 26 if err != nil { 27 return nil, err 28 } 29 defer f.Close() 30 fi, err := f.Stat() 31 if err != nil { 32 return nil, err 33 } 34 if !fi.IsDir() { 35 return nil, fmt.Errorf("not a directory") 36 } 37 root := &File{ 38 path: ".", 39 } 40 fsys := &FS{ 41 rootPath: filepath.ToSlash(name), 42 qidPool: newQidPool(), 43 } 44 root.fs = fsys 45 return fsys, nil 46 } 47 48 // OpenFile opens the named file with specified omode by calling os.OpenFile. 49 // Name is slash separated. 50 func (fsys *FS) OpenFile(name string, flag int) (lib9p.File, error) { 51 if !fs.ValidPath(name) { 52 return nil, &fs.PathError{ 53 Op: "openfile", 54 Path: name, 55 Err: fs.ErrInvalid, 56 } 57 } 58 osf, err := os.OpenFile(path.Join(fsys.rootPath, name), flag, 0) 59 if err != nil { 60 return nil, &fs.PathError{ 61 Op: "openfile", 62 Path: name, 63 Err: err, 64 } 65 } 66 return &File{fs: fsys, path: name, file: osf}, nil 67 } 68 69 // Name is slash-separated. 70 func (fsys *FS) Create(name string, uid string, omode lib9p.OpenMode, perm lib9p.FileMode) (lib9p.File, error) { 71 usr, err := user.Current() 72 if err != nil { 73 return nil, &fs.PathError{ 74 Op: "create", 75 Path: name, 76 Err: err, 77 } 78 } 79 if usr.Username != uid { 80 return nil, &fs.PathError{ 81 Op: "create", 82 Path: name, 83 Err: fmt.Errorf("file creation by a user other than the server's " + 84 "uid is not implemented"), 85 } 86 } 87 if !fs.ValidPath(name) { 88 return nil, &fs.PathError{ 89 Op: "create", 90 Path: name, 91 Err: fs.ErrInvalid, 92 } 93 } 94 ospath := path.Join(fsys.rootPath, name) 95 var flag int 96 switch omode & 3 { 97 case lib9p.OREAD, lib9p.OEXEC: 98 flag = os.O_RDONLY 99 case lib9p.OWRITE: 100 flag = os.O_WRONLY 101 case lib9p.ORDWR: 102 flag = os.O_RDWR 103 } 104 if omode&lib9p.OTRUNC != 0 { 105 flag |= os.O_TRUNC 106 } 107 // ORCLOSE is handled by the library. 108 var osfile *os.File 109 if perm&os.ModeDir != 0 { 110 if err := os.Mkdir(ospath, perm); err != nil { 111 return nil, &fs.PathError{ 112 Op: "create", 113 Path: name, 114 Err: fmt.Errorf("mkdir: %v", err), 115 } 116 } 117 osfile, err = os.OpenFile(ospath, flag, 0) 118 if err != nil { 119 return nil, &fs.PathError{ 120 Op: "create", 121 Path: name, 122 Err: err, 123 } 124 } 125 } else { 126 flag |= os.O_CREATE 127 osfile, err = os.OpenFile(ospath, flag, perm) 128 if err != nil { 129 return nil, &fs.PathError{ 130 Op: "create", 131 Path: name, 132 Err: err, 133 } 134 } 135 } 136 return &File{ 137 fs: fsys, 138 path: name, 139 file: osfile, 140 }, nil 141 } 142 143 // Remove removes the file specified by name. 144 // It also removes the corresponding qid record from fsys. 145 func (fsys *FS) Remove(name string) error { 146 ospath := path.Join(fsys.rootPath, name) 147 fi, err := os.Stat(ospath) 148 if err != nil { 149 return fmt.Errorf("stat %s: %v\n", name, err) 150 } 151 id := idFromInfo(ospath, fi) 152 fsys.qidPool.deleteID(id) 153 return os.Remove(ospath) 154 } 155 156 // ShowQidPool returns the string representing the content of 157 // fsys.qidPool for debugging. 158 func (fsys *FS) ShowQidPool() string { 159 var s string 160 fsys.qidPool.Lock() 161 defer fsys.qidPool.Unlock() 162 for id, qr := range fsys.qidPool.m { 163 s += fmt.Sprintf("%v: %v\n", qr.qid, id) 164 } 165 return s 166 } 167 168 func (fsys *FS) IsGroupLeader(group, uid string) bool { 169 return isGroupLeader(group, uid) 170 } 171 172 func (fsys *FS) IsGroupMember(group, uid string) bool { 173 return isGroupMember(group, uid) 174 }