9fans.net/go@v0.0.7/cmd/acme/fsys1.go (about)

     1  // #include <u.h>
     2  // #include <libc.h>
     3  // #include <draw.h>
     4  // #include <thread.h>
     5  // #include <cursor.h>
     6  // #include <mouse.h>
     7  // #include <keyboard.h>
     8  // #include <frame.h>
     9  // #include <fcall.h>
    10  // #include <plumb.h>
    11  // #include <libsec.h>
    12  // #include "dat.h"
    13  // #include "fns.h"
    14  
    15  package main
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"sort"
    21  	"strconv"
    22  	"strings"
    23  	"sync"
    24  	"time"
    25  
    26  	"9fans.net/go/cmd/acme/internal/ui"
    27  	"9fans.net/go/cmd/acme/internal/util"
    28  	"9fans.net/go/cmd/acme/internal/wind"
    29  	"9fans.net/go/cmd/internal/base"
    30  	"9fans.net/go/plan9"
    31  )
    32  
    33  func QID(w, q int) uint64  { return uint64(w<<8 | q) }
    34  func WIN(q plan9.Qid) int  { return int(q.Path>>8) & 0xFFFFFF }
    35  func FILE(q plan9.Qid) int { return int(q.Path & 0xFF) }
    36  
    37  var sfdR, sfdW *os.File
    38  
    39  const (
    40  	Nhash = 16
    41  	DEBUG = 0
    42  )
    43  
    44  var fids [Nhash]*Fid
    45  
    46  var fcall [plan9.Tmax]func(*Xfid, *Fid) *Xfid
    47  
    48  func initfcall() {
    49  	fcall[plan9.Tflush] = fsysflush
    50  	fcall[plan9.Tversion] = fsysversion
    51  	fcall[plan9.Tauth] = fsysauth
    52  	fcall[plan9.Tattach] = fsysattach
    53  	fcall[plan9.Twalk] = fsyswalk
    54  	fcall[plan9.Topen] = fsysopen
    55  	fcall[plan9.Tcreate] = fsyscreate
    56  	fcall[plan9.Tread] = fsysread
    57  	fcall[plan9.Twrite] = fsyswrite
    58  	fcall[plan9.Tclunk] = fsysclunk
    59  	fcall[plan9.Tremove] = fsysremove
    60  	fcall[plan9.Tstat] = fsysstat
    61  	fcall[plan9.Twstat] = fsyswstat
    62  }
    63  
    64  var Eperm string = "permission denied"
    65  var Eexist string = "file does not exist"
    66  var Enotdir string = "not a directory"
    67  
    68  var dirtab = [11]Dirtab{
    69  	Dirtab{".", plan9.QTDIR, Qdir, 0500 | plan9.DMDIR},
    70  	Dirtab{"acme", plan9.QTDIR, Qacme, 0500 | plan9.DMDIR},
    71  	Dirtab{"cons", plan9.QTFILE, Qcons, 0600},
    72  	Dirtab{"consctl", plan9.QTFILE, Qconsctl, 0000},
    73  	Dirtab{"draw", plan9.QTDIR, Qdraw, 0000 | plan9.DMDIR}, // to suppress graphics progs started in acme
    74  	Dirtab{"editout", plan9.QTFILE, Qeditout, 0200},
    75  	Dirtab{"index", plan9.QTFILE, Qindex, 0400},
    76  	Dirtab{"label", plan9.QTFILE, Qlabel, 0600},
    77  	Dirtab{"log", plan9.QTFILE, Qlog, 0400},
    78  	Dirtab{"new", plan9.QTDIR, Qnew, 0500 | plan9.DMDIR},
    79  }
    80  
    81  var dirtabw = [13]Dirtab{
    82  	Dirtab{".", plan9.QTDIR, Qdir, 0500 | plan9.DMDIR},
    83  	Dirtab{"addr", plan9.QTFILE, QWaddr, 0600},
    84  	Dirtab{"body", plan9.QTAPPEND, QWbody, 0600 | plan9.DMAPPEND},
    85  	Dirtab{"ctl", plan9.QTFILE, QWctl, 0600},
    86  	Dirtab{"data", plan9.QTFILE, QWdata, 0600},
    87  	Dirtab{"editout", plan9.QTFILE, QWeditout, 0200},
    88  	Dirtab{"errors", plan9.QTFILE, QWerrors, 0200},
    89  	Dirtab{"event", plan9.QTFILE, QWevent, 0600},
    90  	Dirtab{"rdsel", plan9.QTFILE, QWrdsel, 0400},
    91  	Dirtab{"wrsel", plan9.QTFILE, QWwrsel, 0200},
    92  	Dirtab{"tag", plan9.QTAPPEND, QWtag, 0600 | plan9.DMAPPEND},
    93  	Dirtab{"xdata", plan9.QTFILE, QWxdata, 0600},
    94  }
    95  
    96  type Mnt struct {
    97  	lk sync.Mutex
    98  	id int
    99  	md *base.Mntdir
   100  }
   101  
   102  var mnt Mnt
   103  
   104  var user string = "Wile E. Coyote"
   105  var closing bool
   106  var messagesize int = 8192 + plan9.IOHDRSZ // good start
   107  
   108  func fsysinit() {
   109  	initfcall()
   110  	r1, w1, err := os.Pipe()
   111  	if err != nil {
   112  		util.Fatal("can't create pipe")
   113  	}
   114  	r2, w2, err := os.Pipe()
   115  	if err != nil {
   116  		util.Fatal("can't create pipe")
   117  	}
   118  	sfdR, sfdW = r1, w2
   119  	if err := post9pservice(r2, w1, "acme", mtpt); err != nil {
   120  		util.Fatal("can't post service")
   121  	}
   122  	u := os.Getenv("USER")
   123  	if u != "" {
   124  		user = u
   125  	}
   126  	go fsysproc()
   127  }
   128  
   129  func fsysproc() {
   130  	var x *Xfid
   131  	for {
   132  		if x == nil {
   133  			cxfidalloc <- nil
   134  			x = <-cxfidalloc
   135  		}
   136  		fc, err := plan9.ReadFcall(sfdR)
   137  		if err != nil {
   138  			if closing {
   139  				break
   140  			}
   141  			util.Fatal("i/o error on server channel")
   142  		}
   143  		if DEBUG != 0 {
   144  			fmt.Fprintf(os.Stderr, "-> %v\n", fc)
   145  		}
   146  		x.fcall = fc
   147  		if int(x.fcall.Type) >= len(fcall) || fcall[x.fcall.Type] == nil {
   148  			var t plan9.Fcall
   149  			x = respond(x, &t, "bad fcall type")
   150  		} else {
   151  			var f *Fid
   152  			var t plan9.Fcall
   153  			switch x.fcall.Type {
   154  			case plan9.Tversion, plan9.Tauth, plan9.Tflush:
   155  				f = nil
   156  			case plan9.Tattach:
   157  				f = newfid(int(x.fcall.Fid))
   158  			default:
   159  				f = newfid(int(x.fcall.Fid))
   160  				if !f.busy {
   161  					x.f = f
   162  					x = respond(x, &t, "fid not in use")
   163  					continue
   164  				}
   165  			}
   166  			x.f = f
   167  			x = fcall[x.fcall.Type](x, f)
   168  		}
   169  	}
   170  }
   171  
   172  func fsysaddid(dir []rune, incl [][]rune) *base.Mntdir {
   173  	mnt.lk.Lock()
   174  	defer mnt.lk.Unlock()
   175  	mnt.id++
   176  	mnt.md = &base.Mntdir{
   177  		ID:   mnt.id,
   178  		Dir:  dir,
   179  		Ref:  1, // one for Command, one will be incremented in attach
   180  		Next: mnt.md,
   181  		Incl: incl,
   182  	}
   183  	return mnt.md
   184  }
   185  
   186  func fsysincid(m *base.Mntdir) {
   187  	mnt.lk.Lock()
   188  	defer mnt.lk.Unlock()
   189  	m.Ref++
   190  }
   191  
   192  func fsysdelid(idm *base.Mntdir) {
   193  	if idm == nil {
   194  		return
   195  	}
   196  	mnt.lk.Lock()
   197  	idm.Ref--
   198  	if idm.Ref > 0 {
   199  		mnt.lk.Unlock()
   200  		return
   201  	}
   202  	var prev *base.Mntdir
   203  	for m := mnt.md; m != nil; m = m.Next {
   204  		if m == idm {
   205  			if prev != nil {
   206  				prev.Next = m.Next
   207  			} else {
   208  				mnt.md = m.Next
   209  			}
   210  			mnt.lk.Unlock()
   211  			return
   212  		}
   213  		prev = m
   214  	}
   215  	mnt.lk.Unlock()
   216  	cerr <- []byte(fmt.Sprintf("fsysdelid: can't find id %d\n", idm.ID))
   217  }
   218  
   219  // fsysmount is called only in exec.runproc.
   220  //
   221  // In the original acme, this would mount the 9p
   222  // pipe onto /mnt/acme and bind -b /mnt/acme /dev
   223  // to make /dev/cons available to the process running
   224  // fsysmount (a subprocess of acme in a separate
   225  // fd group)
   226  func fsysmount(dir []rune, incl [][]rune) *base.Mntdir {
   227  	return fsysaddid(dir, incl)
   228  }
   229  
   230  func fsysclose() {
   231  	closing = true
   232  	/*
   233  		 * apparently this is not kosher on openbsd.
   234  		 * perhaps because fsysproc is reading from sfd right now,
   235  		 * the close hangs indefinitely.
   236  		close(sfd);
   237  	*/
   238  }
   239  
   240  func respond(x *Xfid, t *plan9.Fcall, err string) *Xfid {
   241  	if err != "" {
   242  		t.Type = plan9.Rerror
   243  		t.Ename = err
   244  	} else {
   245  		t.Type = x.fcall.Type + 1
   246  	}
   247  	t.Fid = x.fcall.Fid
   248  	t.Tag = x.fcall.Tag
   249  	if DEBUG != 0 {
   250  		fmt.Fprintf(os.Stderr, "<- %v\n", t)
   251  	}
   252  	if err := plan9.WriteFcall(sfdW, t); err != nil {
   253  		util.Fatal("write error in respond")
   254  	}
   255  	return x
   256  }
   257  
   258  func fsysversion(x *Xfid, f *Fid) *Xfid {
   259  	var t plan9.Fcall
   260  	if x.fcall.Msize < 256 {
   261  		return respond(x, &t, "version: message size too small")
   262  	}
   263  	messagesize = int(x.fcall.Msize)
   264  	t.Msize = uint32(messagesize)
   265  	if !strings.HasPrefix(x.fcall.Version, "9P2000") {
   266  		return respond(x, &t, "unrecognized 9P version")
   267  	}
   268  	t.Version = "9P2000"
   269  	return respond(x, &t, "")
   270  }
   271  
   272  func fsysauth(x *Xfid, f *Fid) *Xfid {
   273  	var t plan9.Fcall
   274  	return respond(x, &t, "acme: authentication not required")
   275  }
   276  
   277  func fsysflush(x *Xfid, f *Fid) *Xfid {
   278  	x.c <- xfidflush
   279  	return nil
   280  }
   281  
   282  func fsysattach(x *Xfid, f *Fid) *Xfid {
   283  	var t plan9.Fcall
   284  	if x.fcall.Uname != user {
   285  		return respond(x, &t, Eperm)
   286  	}
   287  	f.busy = true
   288  	f.open = false
   289  	f.qid.Path = Qdir
   290  	f.qid.Type = plan9.QTDIR
   291  	f.qid.Vers = 0
   292  	f.dir = dirtab[:]
   293  	f.rpart = f.rpart[:0]
   294  	f.w = nil
   295  	t.Qid = f.qid
   296  	f.mntdir = nil
   297  	id, _ := strconv.Atoi(x.fcall.Aname)
   298  	mnt.lk.Lock()
   299  	var m *base.Mntdir
   300  	for m = mnt.md; m != nil; m = m.Next {
   301  		if m.ID == id {
   302  			f.mntdir = m
   303  			m.Ref++
   304  			break
   305  		}
   306  	}
   307  	if m == nil && len(x.fcall.Aname) > 0 {
   308  		cerr <- []byte(fmt.Sprintf("unknown id '%s' in attach", x.fcall.Aname))
   309  	}
   310  	mnt.lk.Unlock()
   311  	return respond(x, &t, "")
   312  }
   313  
   314  func fsyswalk(x *Xfid, f *Fid) *Xfid {
   315  	var nf *Fid
   316  	var w *wind.Window
   317  	var t plan9.Fcall
   318  	if f.open {
   319  		return respond(x, &t, "walk of open file")
   320  	}
   321  	if x.fcall.Fid != x.fcall.Newfid {
   322  		nf = newfid(int(x.fcall.Newfid))
   323  		if nf.busy {
   324  			return respond(x, &t, "newfid already in use")
   325  		}
   326  		nf.busy = true
   327  		nf.open = false
   328  		nf.mntdir = f.mntdir
   329  		if f.mntdir != nil {
   330  			f.mntdir.Ref++
   331  		}
   332  		nf.dir = f.dir
   333  		nf.qid = f.qid
   334  		nf.w = f.w
   335  		nf.rpart = nf.rpart[:0] // not open, so must be zero
   336  		if nf.w != nil {
   337  			util.Incref(&nf.w.Ref)
   338  		}
   339  		f = nf // walk f
   340  	}
   341  
   342  	t.Wqid = nil
   343  	var err string
   344  	var dir []Dirtab
   345  	id := WIN(f.qid)
   346  	q := f.qid
   347  	if len(x.fcall.Wname) > 0 {
   348  		var i int
   349  		for i = 0; i < len(x.fcall.Wname); i++ {
   350  			if q.Type&plan9.QTDIR == 0 {
   351  				err = Enotdir
   352  				break
   353  			}
   354  			var typ uint8
   355  			var path_ int
   356  
   357  			if x.fcall.Wname[i] == ".." {
   358  				typ = plan9.QTDIR
   359  				path_ = Qdir
   360  				id = 0
   361  				if w != nil {
   362  					wind.Winclose(w)
   363  					w = nil
   364  				}
   365  				goto Accept
   366  			}
   367  
   368  			// is it a numeric name?
   369  			for j := 0; j < len(x.fcall.Wname[i]); j++ {
   370  				c := x.fcall.Wname[i][j]
   371  				if c < '0' || '9' < c {
   372  					goto Regular
   373  				}
   374  			}
   375  			// yes: it's a directory
   376  			if w != nil { // name has form 27/23; get out before losing w
   377  				break
   378  			}
   379  			id, _ = strconv.Atoi(x.fcall.Wname[i])
   380  			wind.TheRow.Lk.Lock()
   381  			w = ui.LookID(id)
   382  			if w == nil {
   383  				wind.TheRow.Lk.Unlock()
   384  				break
   385  			}
   386  			util.Incref(&w.Ref) // we'll drop reference at end if there's an error
   387  			path_ = Qdir
   388  			typ = plan9.QTDIR
   389  			wind.TheRow.Lk.Unlock()
   390  			dir = dirtabw[:]
   391  			goto Accept
   392  
   393  		Regular:
   394  			if x.fcall.Wname[i] == "new" {
   395  				if w != nil {
   396  					util.Fatal("w set in walk to new")
   397  				}
   398  				cnewwindow <- nil // signal newwindowthread
   399  				w = <-cnewwindow  // receive new window
   400  				util.Incref(&w.Ref)
   401  				typ = plan9.QTDIR
   402  				path_ = Qdir
   403  				id = w.ID
   404  				dir = dirtabw[:]
   405  				goto Accept
   406  			}
   407  			{
   408  				var d []Dirtab
   409  
   410  				if id == 0 {
   411  					d = dirtab[:]
   412  				} else {
   413  					d = dirtabw[:]
   414  				}
   415  				d = d[1:] // skip '.'
   416  				for ; len(d) > 0; d = d[1:] {
   417  					if x.fcall.Wname[i] == d[0].name {
   418  						path_ = int(d[0].qid) // TODO(rsc)
   419  						typ = d[0].typ
   420  						dir = d
   421  						goto Accept
   422  					}
   423  				}
   424  
   425  				break // file not found
   426  			}
   427  
   428  		Accept:
   429  			if i == plan9.MAXWELEM {
   430  				err = "name too long"
   431  				break
   432  			}
   433  			q.Type = typ
   434  			q.Vers = 0
   435  			q.Path = QID(id, path_)
   436  			t.Wqid = append(t.Wqid, q)
   437  			continue
   438  		}
   439  
   440  		if i == 0 && err == "" {
   441  			err = Eexist
   442  		}
   443  	}
   444  
   445  	if err != "" || len(t.Wqid) < len(x.fcall.Wname) {
   446  		if nf != nil {
   447  			nf.busy = false
   448  			fsysdelid(nf.mntdir)
   449  		}
   450  	} else if len(t.Wqid) == len(x.fcall.Wname) {
   451  		if w != nil {
   452  			f.w = w
   453  			w = nil // don't drop the reference
   454  		}
   455  		if dir != nil {
   456  			f.dir = dir
   457  		}
   458  		f.qid = q
   459  	}
   460  
   461  	if w != nil {
   462  		wind.Winclose(w)
   463  	}
   464  
   465  	return respond(x, &t, err)
   466  }
   467  
   468  func fsysopen(x *Xfid, f *Fid) *Xfid {
   469  	// can't truncate anything, so just disregard
   470  	x.fcall.Mode &^= plan9.OTRUNC | plan9.OCEXEC
   471  	// can't execute or remove anything
   472  	if x.fcall.Mode == plan9.OEXEC || x.fcall.Mode&plan9.ORCLOSE != 0 {
   473  		goto Deny
   474  	}
   475  	{
   476  		var m int
   477  		switch x.fcall.Mode {
   478  		default:
   479  			goto Deny
   480  		case plan9.OREAD:
   481  			m = 0400
   482  		case plan9.OWRITE:
   483  			m = 0200
   484  		case plan9.ORDWR:
   485  			m = 0600
   486  		}
   487  		if (f.dir[0].perm & ^(plan9.DMDIR|plan9.DMAPPEND))&m != m {
   488  			goto Deny
   489  		}
   490  
   491  		x.c <- xfidopen
   492  		return nil
   493  	}
   494  
   495  Deny:
   496  	var t plan9.Fcall
   497  	return respond(x, &t, Eperm)
   498  }
   499  
   500  func fsyscreate(x *Xfid, f *Fid) *Xfid {
   501  	var t plan9.Fcall
   502  	return respond(x, &t, Eperm)
   503  }
   504  
   505  func fsysread(x *Xfid, f *Fid) *Xfid {
   506  	if f.qid.Type&plan9.QTDIR != 0 {
   507  		var t plan9.Fcall
   508  		if FILE(f.qid) == Qacme { // empty dir
   509  			t.Data = nil
   510  			t.Count = 0
   511  			respond(x, &t, "")
   512  			return x
   513  		}
   514  		o := int64(x.fcall.Offset)
   515  		e := int64(x.fcall.Offset) + int64(x.fcall.Count)
   516  		clock := getclock()
   517  		b := make([]byte, messagesize)
   518  		id := WIN(f.qid)
   519  		n := 0
   520  		var d []Dirtab
   521  		if id > 0 {
   522  			d = dirtabw[:]
   523  		} else {
   524  			d = dirtab[:]
   525  		}
   526  		d = d[1:] // first entry is '.'
   527  		var w int
   528  		var i int
   529  		for i = 0; len(d) > 0 && int64(i) < e; i += w {
   530  			buf, err := dostat(WIN(x.f.qid), &d[0], clock)
   531  			if err != nil {
   532  				break
   533  			}
   534  			if n+len(buf) > int(x.fcall.Count) {
   535  				break
   536  			}
   537  			copy(b[n:], buf)
   538  			w = len(buf)
   539  			if w <= 2 {
   540  				break
   541  			}
   542  			if int64(i) >= o {
   543  				n += w
   544  			}
   545  			d = d[1:]
   546  		}
   547  		if id == 0 {
   548  			wind.TheRow.Lk.Lock()
   549  			nids := 0
   550  			var ids []int
   551  			var j int
   552  			var k int
   553  			for j = 0; j < len(wind.TheRow.Col); j++ {
   554  				c := wind.TheRow.Col[j]
   555  				for k = 0; k < len(c.W); k++ {
   556  					ids = append(ids, c.W[k].ID)
   557  				}
   558  			}
   559  			wind.TheRow.Lk.Unlock()
   560  			sort.Ints(ids)
   561  			j = 0
   562  			var dt Dirtab
   563  			var nn int
   564  			for ; j < nids && int64(i) < e; i += nn {
   565  				k = ids[j]
   566  				dt.name = fmt.Sprintf("%d", k)
   567  				dt.qid = int(QID(k, Qdir)) // TODO(rsc)
   568  				dt.typ = plan9.QTDIR
   569  				dt.perm = plan9.DMDIR | 0700
   570  				buf, err := dostat(k, &dt, clock)
   571  				if err != nil || len(b) > int(x.fcall.Count)-n {
   572  					break
   573  				}
   574  				copy(b[n:], buf)
   575  				nn = len(buf)
   576  				if int64(i) >= o {
   577  					n += nn
   578  				}
   579  				j++
   580  			}
   581  		}
   582  		t.Data = b[:n]
   583  		t.Count = uint32(n)
   584  		respond(x, &t, "")
   585  		return x
   586  	}
   587  	x.c <- xfidread
   588  	return nil
   589  }
   590  
   591  func fsyswrite(x *Xfid, f *Fid) *Xfid {
   592  	x.c <- xfidwrite
   593  	return nil
   594  }
   595  
   596  func fsysclunk(x *Xfid, f *Fid) *Xfid {
   597  	fsysdelid(f.mntdir)
   598  	x.c <- xfidclose
   599  	return nil
   600  }
   601  
   602  func fsysremove(x *Xfid, f *Fid) *Xfid {
   603  	var t plan9.Fcall
   604  	return respond(x, &t, Eperm)
   605  }
   606  
   607  func fsysstat(x *Xfid, f *Fid) *Xfid {
   608  	var t plan9.Fcall
   609  	var err error
   610  	t.Stat, err = dostat(WIN(x.f.qid), &f.dir[0], getclock())
   611  	if err != nil {
   612  		return respond(x, &t, err.Error())
   613  	}
   614  	if len(t.Stat) > messagesize-plan9.IOHDRSZ {
   615  		return respond(x, &t, "stat too long")
   616  	}
   617  	return respond(x, &t, "")
   618  }
   619  
   620  func fsyswstat(x *Xfid, f *Fid) *Xfid {
   621  	var t plan9.Fcall
   622  	return respond(x, &t, Eperm)
   623  }
   624  
   625  func newfid(fid int) *Fid {
   626  	var ff *Fid
   627  	fh := &fids[fid&(Nhash-1)]
   628  	var f *Fid
   629  	for f = *fh; f != nil; f = f.next {
   630  		if f.fid == fid {
   631  			return f
   632  		} else if ff == nil && !f.busy {
   633  			ff = f
   634  		}
   635  	}
   636  	if ff != nil {
   637  		ff.fid = fid
   638  		return ff
   639  	}
   640  	f = new(Fid)
   641  	f.fid = fid
   642  	f.next = *fh
   643  	*fh = f
   644  	return f
   645  }
   646  
   647  func getclock() int {
   648  	return int(time.Now().Unix())
   649  }
   650  
   651  func dostat(id int, dir *Dirtab, clock int) ([]byte, error) {
   652  	var d plan9.Dir
   653  	d.Qid.Path = QID(id, dir.qid)
   654  	d.Qid.Vers = 0
   655  	d.Qid.Type = dir.typ
   656  	d.Mode = plan9.Perm(dir.perm)
   657  	d.Length = 0 // would be nice to do better
   658  	d.Name = dir.name
   659  	d.Uid = user
   660  	d.Gid = user
   661  	d.Muid = user
   662  	d.Atime = uint32(clock)
   663  	d.Mtime = uint32(clock)
   664  	return d.Bytes()
   665  }