lib9p

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

fs.go (3700B)


      1 package client
      2 
      3 import (
      4 	"context"
      5 	"fmt"
      6 	"io"
      7 	"path"
      8 	"strings"
      9 
     10 	"git.mtkn.jp/lib9p"
     11 )
     12 
     13 // FS represents the file system the client imports.
     14 type FS struct {
     15 	c     *Client
     16 	root  *File
     17 	tPool *tagPool
     18 	fPool *filePool
     19 }
     20 
     21 // OpenFile opens the file named name in fsys with omode.
     22 // If the file does not exist, it create it with perm.
     23 // The flag bits which are not implemeted by the library are just ignored.
     24 func (fsys *FS) OpenFile(name string, flag int) (lib9p.File, error) {
     25 	var (
     26 		qid    lib9p.Qid
     27 		iounit uint32
     28 	)
     29 	f, err := fsys.walkFile(name)
     30 	if err != nil {
     31 		return nil, err
     32 	} else {
     33 		// File exists. Open it.
     34 		tag, err := fsys.tPool.add()
     35 		if err != nil {
     36 			return nil, err
     37 		}
     38 		qid, iounit, err = fsys.c.Open(tag, f.fid, lib9p.FlagToMode(flag))
     39 		fsys.tPool.delete(tag)
     40 		if err != nil {
     41 			f.Close()
     42 			return nil, fmt.Errorf("open: %v", err)
     43 		}
     44 	}
     45 	f.omode = lib9p.FlagToMode(flag)
     46 	f.qid = qid
     47 	f.iounit = iounit
     48 	return f, nil
     49 }
     50 
     51 // CleanPath cleans name.
     52 // It first call path.Clean(name) and then
     53 // delete leading ".." elements.
     54 func CleanPath(name string) string {
     55 	name = path.Clean(name)
     56 	for strings.HasPrefix(name, "../") {
     57 		name = name[3:]
     58 	}
     59 	if len(name) == 0 || name == "/" || name == ".." {
     60 		name = "."
     61 	} else if name[0] == '/' {
     62 		name = name[1:]
     63 	}
     64 	return name
     65 }
     66 
     67 // walkFile walks the file system to the file named name and
     68 // returns the corresponding file.
     69 // returned file is not open.
     70 func (fsys *FS) walkFile(name string) (*File, error) {
     71 	f, err := fsys.fPool.add()
     72 	if err != nil {
     73 		return nil, fmt.Errorf("add file: %v", err)
     74 	}
     75 	var wname []string
     76 	if name != "." {
     77 		wname = strings.Split(CleanPath(name), "/")
     78 	}
     79 	tag, err := fsys.tPool.add()
     80 	if err != nil {
     81 		return nil, err
     82 	}
     83 	wqid, err := fsys.c.Walk(tag, fsys.root.fid, f.fid, wname)
     84 	fsys.tPool.delete(tag)
     85 	if err != nil {
     86 		return nil, fmt.Errorf("walk: %v", err)
     87 	}
     88 	if len(wqid) < len(wname) {
     89 		fsys.fPool.delete(f.fid)
     90 		return nil, fmt.Errorf("not found")
     91 	}
     92 	var qid lib9p.Qid
     93 	if name == "." {
     94 		qid = lib9p.Qid{} // TODO: ?? why it is not fsys.c.root.qid?
     95 	} else if len(wqid) > 0 {
     96 		qid = wqid[len(wqid)-1]
     97 	} else {
     98 		return nil, fmt.Errorf("invalid wqid: %v", wqid)
     99 	}
    100 	f.name = path.Base(name)
    101 	f.path = name
    102 	f.qid = qid
    103 	f.fs = fsys
    104 	return f, nil
    105 }
    106 
    107 // Mount initiates a 9P session and returns the resulting file system.
    108 // The 9P session is established by writing to w and reading from r.
    109 // When fs is not needed anymore, ctx should be canceled to stop the
    110 // underlying client's goroutines.
    111 // If non-nil error is returned, underlying client is stopped by this function,
    112 // and there is no need to cancel ctx.
    113 func Mount(ctx context.Context, r io.Reader, w io.Writer, uname, aname string) (fs *FS, err error) {
    114 	var (
    115 		mSize   uint32 = 8192
    116 		version        = "9P2000"
    117 	)
    118 	ctx0, cancel0 := context.WithCancel(ctx)
    119 	cfs := &FS{
    120 		c:     NewClient(ctx0, mSize, uname, r, w),
    121 		tPool: newTagPool(),
    122 		fPool: newFilePool(),
    123 	}
    124 	defer func() {
    125 		if err != nil {
    126 			cancel0()
    127 		}
    128 	}()
    129 	rmSize, rver, err := cfs.c.Version(lib9p.NOTAG, mSize, version)
    130 	if err != nil {
    131 		return nil, fmt.Errorf("version: %v", err)
    132 	}
    133 	if rver != version {
    134 		return nil, fmt.Errorf("incompatible version %s", rver)
    135 	}
    136 	if rmSize < mSize {
    137 		cfs.c.setMSize(rmSize)
    138 	}
    139 	// TODO: auth
    140 	f, err := cfs.fPool.add()
    141 	if err != nil {
    142 		return nil, fmt.Errorf("add file: %v", err)
    143 	}
    144 	tag, err := cfs.tPool.add()
    145 	if err != nil {
    146 		return nil, err
    147 	}
    148 	qid, err := cfs.c.Attach(tag, f.fid, lib9p.NOFID, uname, aname)
    149 	cfs.tPool.delete(tag)
    150 	if err != nil {
    151 		return nil, fmt.Errorf("attach: %v", err)
    152 	}
    153 	f.name = "."
    154 	f.path = "."
    155 	f.qid = qid
    156 	f.fs = cfs
    157 	cfs.root = f
    158 	return cfs, nil
    159 }