lib9p

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

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 }