github.com/hanwen/go-fuse@v1.0.0/fuse/server.go (about)

     1  // Copyright 2016 the Go-FUSE Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package fuse
     6  
     7  import (
     8  	"fmt"
     9  	"log"
    10  	"math"
    11  	"os"
    12  	"path/filepath"
    13  	"runtime"
    14  	"strings"
    15  	"sync"
    16  	"syscall"
    17  	"time"
    18  )
    19  
    20  const (
    21  	// The kernel caps writes at 128k.
    22  	MAX_KERNEL_WRITE = 128 * 1024
    23  )
    24  
    25  // Server contains the logic for reading from the FUSE device and
    26  // translating it to RawFileSystem interface calls.
    27  type Server struct {
    28  	// Empty if unmounted.
    29  	mountPoint string
    30  	fileSystem RawFileSystem
    31  
    32  	// writeMu serializes close and notify writes
    33  	writeMu sync.Mutex
    34  
    35  	// I/O with kernel and daemon.
    36  	mountFd int
    37  
    38  	latencies LatencyMap
    39  
    40  	opts *MountOptions
    41  
    42  	// Pool for request structs.
    43  	reqPool sync.Pool
    44  
    45  	// Pool for raw requests data
    46  	readPool       sync.Pool
    47  	reqMu          sync.Mutex
    48  	reqReaders     int
    49  	kernelSettings InitIn
    50  
    51  	// in-flight notify-retrieve queries
    52  	retrieveMu   sync.Mutex
    53  	retrieveNext uint64
    54  	retrieveTab  map[uint64]*retrieveCacheRequest // notifyUnique -> retrieve request
    55  
    56  	singleReader bool
    57  	canSplice    bool
    58  	loops        sync.WaitGroup
    59  
    60  	ready chan error
    61  }
    62  
    63  // SetDebug is deprecated. Use MountOptions.Debug instead.
    64  func (ms *Server) SetDebug(dbg bool) {
    65  	// This will typically trigger the race detector.
    66  	ms.opts.Debug = dbg
    67  }
    68  
    69  // KernelSettings returns the Init message from the kernel, so
    70  // filesystems can adapt to availability of features of the kernel
    71  // driver. The message should not be altered.
    72  func (ms *Server) KernelSettings() *InitIn {
    73  	ms.reqMu.Lock()
    74  	s := ms.kernelSettings
    75  	ms.reqMu.Unlock()
    76  
    77  	return &s
    78  }
    79  
    80  const _MAX_NAME_LEN = 20
    81  
    82  // This type may be provided for recording latencies of each FUSE
    83  // operation.
    84  type LatencyMap interface {
    85  	Add(name string, dt time.Duration)
    86  }
    87  
    88  // RecordLatencies switches on collection of timing for each request
    89  // coming from the kernel.P assing a nil argument switches off the
    90  func (ms *Server) RecordLatencies(l LatencyMap) {
    91  	ms.latencies = l
    92  }
    93  
    94  // Unmount calls fusermount -u on the mount. This has the effect of
    95  // shutting down the filesystem. After the Server is unmounted, it
    96  // should be discarded.
    97  func (ms *Server) Unmount() (err error) {
    98  	if ms.mountPoint == "" {
    99  		return nil
   100  	}
   101  	delay := time.Duration(0)
   102  	for try := 0; try < 5; try++ {
   103  		err = unmount(ms.mountPoint)
   104  		if err == nil {
   105  			break
   106  		}
   107  
   108  		// Sleep for a bit. This is not pretty, but there is
   109  		// no way we can be certain that the kernel thinks all
   110  		// open files have already been closed.
   111  		delay = 2*delay + 5*time.Millisecond
   112  		time.Sleep(delay)
   113  	}
   114  	if err != nil {
   115  		return
   116  	}
   117  	// Wait for event loops to exit.
   118  	ms.loops.Wait()
   119  	ms.mountPoint = ""
   120  	return err
   121  }
   122  
   123  // NewServer creates a server and attaches it to the given directory.
   124  func NewServer(fs RawFileSystem, mountPoint string, opts *MountOptions) (*Server, error) {
   125  	if opts == nil {
   126  		opts = &MountOptions{
   127  			MaxBackground: _DEFAULT_BACKGROUND_TASKS,
   128  		}
   129  	}
   130  	o := *opts
   131  	if o.SingleThreaded {
   132  		fs = NewLockingRawFileSystem(fs)
   133  	}
   134  
   135  	if o.Buffers == nil {
   136  		o.Buffers = defaultBufferPool
   137  	}
   138  	if o.MaxWrite < 0 {
   139  		o.MaxWrite = 0
   140  	}
   141  	if o.MaxWrite == 0 {
   142  		o.MaxWrite = 1 << 16
   143  	}
   144  	if o.MaxWrite > MAX_KERNEL_WRITE {
   145  		o.MaxWrite = MAX_KERNEL_WRITE
   146  	}
   147  	if o.Name == "" {
   148  		name := fs.String()
   149  		l := len(name)
   150  		if l > _MAX_NAME_LEN {
   151  			l = _MAX_NAME_LEN
   152  		}
   153  		o.Name = strings.Replace(name[:l], ",", ";", -1)
   154  	}
   155  
   156  	for _, s := range o.optionsStrings() {
   157  		if strings.Contains(s, ",") {
   158  			return nil, fmt.Errorf("found ',' in option string %q", s)
   159  		}
   160  	}
   161  
   162  	ms := &Server{
   163  		fileSystem:  fs,
   164  		opts:        &o,
   165  		retrieveTab: make(map[uint64]*retrieveCacheRequest),
   166  		// OSX has races when multiple routines read from the
   167  		// FUSE device: on unmount, sometime some reads do not
   168  		// error-out, meaning that unmount will hang.
   169  		singleReader: runtime.GOOS == "darwin",
   170  		ready:        make(chan error, 1),
   171  	}
   172  	ms.reqPool.New = func() interface{} { return new(request) }
   173  	ms.readPool.New = func() interface{} { return make([]byte, o.MaxWrite+pageSize) }
   174  
   175  	mountPoint = filepath.Clean(mountPoint)
   176  	if !filepath.IsAbs(mountPoint) {
   177  		cwd, err := os.Getwd()
   178  		if err != nil {
   179  			return nil, err
   180  		}
   181  		mountPoint = filepath.Clean(filepath.Join(cwd, mountPoint))
   182  	}
   183  	fd, err := mount(mountPoint, &o, ms.ready)
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  
   188  	ms.mountPoint = mountPoint
   189  	ms.mountFd = fd
   190  
   191  	if code := ms.handleInit(); !code.Ok() {
   192  		syscall.Close(fd)
   193  		// TODO - unmount as well?
   194  		return nil, fmt.Errorf("init: %s", code)
   195  	}
   196  	return ms, nil
   197  }
   198  
   199  func (o *MountOptions) optionsStrings() []string {
   200  	var r []string
   201  	r = append(r, o.Options...)
   202  
   203  	if o.AllowOther {
   204  		r = append(r, "allow_other")
   205  	}
   206  
   207  	if o.FsName != "" {
   208  		r = append(r, "fsname="+o.FsName)
   209  	}
   210  	if o.Name != "" {
   211  		r = append(r, "subtype="+o.Name)
   212  	}
   213  
   214  	return r
   215  }
   216  
   217  // DebugData returns internal status information for debugging
   218  // purposes.
   219  func (ms *Server) DebugData() string {
   220  	var r int
   221  	ms.reqMu.Lock()
   222  	r = ms.reqReaders
   223  	ms.reqMu.Unlock()
   224  
   225  	return fmt.Sprintf("readers: %d", r)
   226  }
   227  
   228  // What is a good number?  Maybe the number of CPUs?
   229  const _MAX_READERS = 2
   230  
   231  // handleEINTR retries the given function until it doesn't return syscall.EINTR.
   232  // This is similar to the HANDLE_EINTR() macro from Chromium ( see
   233  // https://code.google.com/p/chromium/codesearch#chromium/src/base/posix/eintr_wrapper.h
   234  // ) and the TEMP_FAILURE_RETRY() from glibc (see
   235  // https://www.gnu.org/software/libc/manual/html_node/Interrupted-Primitives.html
   236  // ).
   237  //
   238  // Don't use handleEINTR() with syscall.Close(); see
   239  // https://code.google.com/p/chromium/issues/detail?id=269623 .
   240  func handleEINTR(fn func() error) (err error) {
   241  	for {
   242  		err = fn()
   243  		if err != syscall.EINTR {
   244  			break
   245  		}
   246  	}
   247  	return
   248  }
   249  
   250  // Returns a new request, or error. In case exitIdle is given, returns
   251  // nil, OK if we have too many readers already.
   252  func (ms *Server) readRequest(exitIdle bool) (req *request, code Status) {
   253  	ms.reqMu.Lock()
   254  	if ms.reqReaders > _MAX_READERS {
   255  		ms.reqMu.Unlock()
   256  		return nil, OK
   257  	}
   258  	req = ms.reqPool.Get().(*request)
   259  	dest := ms.readPool.Get().([]byte)
   260  	ms.reqReaders++
   261  	ms.reqMu.Unlock()
   262  
   263  	var n int
   264  	err := handleEINTR(func() error {
   265  		var err error
   266  		n, err = syscall.Read(ms.mountFd, dest)
   267  		return err
   268  	})
   269  	if err != nil {
   270  		code = ToStatus(err)
   271  		ms.reqPool.Put(req)
   272  		ms.reqMu.Lock()
   273  		ms.reqReaders--
   274  		ms.reqMu.Unlock()
   275  		return nil, code
   276  	}
   277  
   278  	if ms.latencies != nil {
   279  		req.startTime = time.Now()
   280  	}
   281  	gobbled := req.setInput(dest[:n])
   282  
   283  	ms.reqMu.Lock()
   284  	if !gobbled {
   285  		ms.readPool.Put(dest)
   286  		dest = nil
   287  	}
   288  	ms.reqReaders--
   289  	if !ms.singleReader && ms.reqReaders <= 0 {
   290  		ms.loops.Add(1)
   291  		go ms.loop(true)
   292  	}
   293  	ms.reqMu.Unlock()
   294  
   295  	return req, OK
   296  }
   297  
   298  // returnRequest returns a request to the pool of unused requests.
   299  func (ms *Server) returnRequest(req *request) {
   300  	ms.recordStats(req)
   301  
   302  	if req.bufferPoolOutputBuf != nil {
   303  		ms.opts.Buffers.FreeBuffer(req.bufferPoolOutputBuf)
   304  		req.bufferPoolOutputBuf = nil
   305  	}
   306  
   307  	req.clear()
   308  
   309  	if p := req.bufferPoolInputBuf; p != nil {
   310  		req.bufferPoolInputBuf = nil
   311  		ms.readPool.Put(p)
   312  	}
   313  	ms.reqPool.Put(req)
   314  }
   315  
   316  func (ms *Server) recordStats(req *request) {
   317  	if ms.latencies != nil {
   318  		dt := time.Now().Sub(req.startTime)
   319  		opname := operationName(req.inHeader.Opcode)
   320  		ms.latencies.Add(opname, dt)
   321  	}
   322  }
   323  
   324  // Serve initiates the FUSE loop. Normally, callers should run Serve()
   325  // and wait for it to exit, but tests will want to run this in a
   326  // goroutine.
   327  //
   328  // Each filesystem operation executes in a separate goroutine.
   329  func (ms *Server) Serve() {
   330  	ms.loops.Add(1)
   331  	ms.loop(false)
   332  	ms.loops.Wait()
   333  
   334  	ms.writeMu.Lock()
   335  	syscall.Close(ms.mountFd)
   336  	ms.writeMu.Unlock()
   337  
   338  	// shutdown in-flight cache retrieves.
   339  	//
   340  	// It is possible that umount comes in the middle - after retrieve
   341  	// request was sent to kernel, but corresponding kernel reply has not
   342  	// yet been read. We unblock all such readers and wake them up with ENODEV.
   343  	ms.retrieveMu.Lock()
   344  	rtab := ms.retrieveTab
   345  	// retrieve attempts might be erroneously tried even after close
   346  	// we have to keep retrieveTab !nil not to panic.
   347  	ms.retrieveTab = make(map[uint64]*retrieveCacheRequest)
   348  	ms.retrieveMu.Unlock()
   349  	for _, reading := range rtab {
   350  		reading.n = 0
   351  		reading.st = ENODEV
   352  		close(reading.ready)
   353  	}
   354  }
   355  
   356  func (ms *Server) handleInit() Status {
   357  	// The first request should be INIT; read it synchronously,
   358  	// and don't spawn new readers.
   359  	orig := ms.singleReader
   360  	ms.singleReader = true
   361  	req, errNo := ms.readRequest(false)
   362  	ms.singleReader = orig
   363  
   364  	if errNo != OK || req == nil {
   365  		return errNo
   366  	}
   367  	if code := ms.handleRequest(req); !code.Ok() {
   368  		return code
   369  	}
   370  
   371  	// INIT is handled. Init the file system, but don't accept
   372  	// incoming requests, so the file system can setup itself.
   373  	ms.fileSystem.Init(ms)
   374  	return OK
   375  }
   376  
   377  func (ms *Server) loop(exitIdle bool) {
   378  	defer ms.loops.Done()
   379  exit:
   380  	for {
   381  		req, errNo := ms.readRequest(exitIdle)
   382  		switch errNo {
   383  		case OK:
   384  			if req == nil {
   385  				break exit
   386  			}
   387  		case ENOENT:
   388  			continue
   389  		case ENODEV:
   390  			// unmount
   391  			if ms.opts.Debug {
   392  				log.Printf("received ENODEV (unmount request), thread exiting")
   393  			}
   394  			break exit
   395  		default: // some other error?
   396  			log.Printf("Failed to read from fuse conn: %v", errNo)
   397  			break exit
   398  		}
   399  
   400  		if ms.singleReader {
   401  			go ms.handleRequest(req)
   402  		} else {
   403  			ms.handleRequest(req)
   404  		}
   405  	}
   406  }
   407  
   408  func (ms *Server) handleRequest(req *request) Status {
   409  	req.parse()
   410  	if req.handler == nil {
   411  		req.status = ENOSYS
   412  	}
   413  
   414  	if req.status.Ok() && ms.opts.Debug {
   415  		log.Println(req.InputDebug())
   416  	}
   417  
   418  	if req.inHeader.NodeId == pollHackInode {
   419  		// We want to avoid switching off features through our
   420  		// poll hack, so don't use ENOSYS
   421  		req.status = EIO
   422  		if req.inHeader.Opcode == _OP_POLL {
   423  			req.status = ENOSYS
   424  		}
   425  	} else if req.inHeader.NodeId == FUSE_ROOT_ID && len(req.filenames) > 0 && req.filenames[0] == pollHackName {
   426  		doPollHackLookup(ms, req)
   427  	} else if req.status.Ok() && req.handler.Func == nil {
   428  		log.Printf("Unimplemented opcode %v", operationName(req.inHeader.Opcode))
   429  		req.status = ENOSYS
   430  	} else if req.status.Ok() {
   431  		req.handler.Func(ms, req)
   432  	}
   433  
   434  	errNo := ms.write(req)
   435  	if errNo != 0 {
   436  		log.Printf("writer: Write/Writev failed, err: %v. opcode: %v",
   437  			errNo, operationName(req.inHeader.Opcode))
   438  	}
   439  	ms.returnRequest(req)
   440  	return Status(errNo)
   441  }
   442  
   443  func (ms *Server) allocOut(req *request, size uint32) []byte {
   444  	if cap(req.bufferPoolOutputBuf) >= int(size) {
   445  		req.bufferPoolOutputBuf = req.bufferPoolOutputBuf[:size]
   446  		return req.bufferPoolOutputBuf
   447  	}
   448  	if req.bufferPoolOutputBuf != nil {
   449  		ms.opts.Buffers.FreeBuffer(req.bufferPoolOutputBuf)
   450  	}
   451  	req.bufferPoolOutputBuf = ms.opts.Buffers.AllocBuffer(size)
   452  	return req.bufferPoolOutputBuf
   453  }
   454  
   455  func (ms *Server) write(req *request) Status {
   456  	// Forget/NotifyReply do not wait for reply from filesystem server.
   457  	switch req.inHeader.Opcode {
   458  	case _OP_FORGET, _OP_BATCH_FORGET, _OP_NOTIFY_REPLY:
   459  		return OK
   460  	}
   461  
   462  	header := req.serializeHeader(req.flatDataSize())
   463  	if ms.opts.Debug {
   464  		log.Println(req.OutputDebug())
   465  	}
   466  
   467  	if header == nil {
   468  		return OK
   469  	}
   470  
   471  	s := ms.systemWrite(req, header)
   472  	return s
   473  }
   474  
   475  // InodeNotify invalidates the information associated with the inode
   476  // (ie. data cache, attributes, etc.)
   477  func (ms *Server) InodeNotify(node uint64, off int64, length int64) Status {
   478  	if !ms.kernelSettings.SupportsNotify(NOTIFY_INVAL_INODE) {
   479  		return ENOSYS
   480  	}
   481  
   482  	req := request{
   483  		inHeader: &InHeader{
   484  			Opcode: _OP_NOTIFY_INVAL_INODE,
   485  		},
   486  		handler: operationHandlers[_OP_NOTIFY_INVAL_INODE],
   487  		status:  NOTIFY_INVAL_INODE,
   488  	}
   489  
   490  	entry := (*NotifyInvalInodeOut)(req.outData())
   491  	entry.Ino = node
   492  	entry.Off = off
   493  	entry.Length = length
   494  
   495  	// Protect against concurrent close.
   496  	ms.writeMu.Lock()
   497  	result := ms.write(&req)
   498  	ms.writeMu.Unlock()
   499  
   500  	if ms.opts.Debug {
   501  		log.Println("Response: INODE_NOTIFY", result)
   502  	}
   503  	return result
   504  }
   505  
   506  // InodeNotifyStoreCache tells kernel to store data into inode's cache.
   507  //
   508  // This call is similar to InodeNotify, but instead of only invalidating a data
   509  // region, it gives updated data directly to the kernel.
   510  func (ms *Server) InodeNotifyStoreCache(node uint64, offset int64, data []byte) Status {
   511  	if !ms.kernelSettings.SupportsNotify(NOTIFY_STORE_CACHE) {
   512  		return ENOSYS
   513  	}
   514  
   515  	for len(data) > 0 {
   516  		size := len(data)
   517  		if size > math.MaxInt32 {
   518  			// NotifyStoreOut has only uint32 for size.
   519  			// we check for max(int32), not max(uint32), because on 32-bit
   520  			// platforms int has only 31-bit for positive range.
   521  			size = math.MaxInt32
   522  		}
   523  
   524  		st := ms.inodeNotifyStoreCache32(node, offset, data[:size])
   525  		if st != OK {
   526  			return st
   527  		}
   528  
   529  		data = data[size:]
   530  		offset += int64(size)
   531  	}
   532  
   533  	return OK
   534  }
   535  
   536  // inodeNotifyStoreCache32 is internal worker for InodeNotifyStoreCache which
   537  // handles data chunks not larger than 2GB.
   538  func (ms *Server) inodeNotifyStoreCache32(node uint64, offset int64, data []byte) Status {
   539  	req := request{
   540  		inHeader: &InHeader{
   541  			Opcode: _OP_NOTIFY_STORE_CACHE,
   542  		},
   543  		handler: operationHandlers[_OP_NOTIFY_STORE_CACHE],
   544  		status:  NOTIFY_STORE_CACHE,
   545  	}
   546  
   547  	store := (*NotifyStoreOut)(req.outData())
   548  	store.Nodeid = node
   549  	store.Offset = uint64(offset) // NOTE not int64, as it is e.g. in NotifyInvalInodeOut
   550  	store.Size = uint32(len(data))
   551  
   552  	req.flatData = data
   553  
   554  	// Protect against concurrent close.
   555  	ms.writeMu.Lock()
   556  	result := ms.write(&req)
   557  	ms.writeMu.Unlock()
   558  
   559  	if ms.opts.Debug {
   560  		log.Printf("Response: INODE_NOTIFY_STORE_CACHE: %v", result)
   561  	}
   562  	return result
   563  }
   564  
   565  // InodeRetrieveCache retrieves data from kernel's inode cache.
   566  //
   567  // InodeRetrieveCache asks kernel to return data from its cache for inode at
   568  // [offset:offset+len(dest)) and waits for corresponding reply. If kernel cache
   569  // has fewer consecutive data starting at offset, that fewer amount is returned.
   570  // In particular if inode data at offset is not cached (0, OK) is returned.
   571  //
   572  // The kernel returns ENOENT if it does not currently have entry for this inode
   573  // in its dentry cache.
   574  func (ms *Server) InodeRetrieveCache(node uint64, offset int64, dest []byte) (n int, st Status) {
   575  	// the kernel won't send us in one go more then what we negotiated as MaxWrite.
   576  	// retrieve the data in chunks.
   577  	// TODO spawn some number of readahead retrievers in parallel.
   578  	ntotal := 0
   579  	for {
   580  		chunkSize := len(dest)
   581  		if chunkSize > ms.opts.MaxWrite {
   582  			chunkSize = ms.opts.MaxWrite
   583  		}
   584  		n, st = ms.inodeRetrieveCache1(node, offset, dest[:chunkSize])
   585  		if st != OK || n == 0 {
   586  			break
   587  		}
   588  
   589  		ntotal += n
   590  		offset += int64(n)
   591  		dest = dest[n:]
   592  	}
   593  
   594  	// if we could retrieve at least something - it is ok.
   595  	// if ntotal=0 - st will be st returned from first inodeRetrieveCache1.
   596  	if ntotal > 0 {
   597  		st = OK
   598  	}
   599  	return ntotal, st
   600  }
   601  
   602  // inodeRetrieveCache1 is internal worker for InodeRetrieveCache which
   603  // actually talks to kernel and retrieves chunks not larger than ms.opts.MaxWrite.
   604  func (ms *Server) inodeRetrieveCache1(node uint64, offset int64, dest []byte) (n int, st Status) {
   605  	if !ms.kernelSettings.SupportsNotify(NOTIFY_RETRIEVE_CACHE) {
   606  		return 0, ENOSYS
   607  	}
   608  
   609  	req := request{
   610  		inHeader: &InHeader{
   611  			Opcode: _OP_NOTIFY_RETRIEVE_CACHE,
   612  		},
   613  		handler: operationHandlers[_OP_NOTIFY_RETRIEVE_CACHE],
   614  		status:  NOTIFY_RETRIEVE_CACHE,
   615  	}
   616  
   617  	// retrieve up to 2GB not to overflow uint32 size in NotifyRetrieveOut.
   618  	// see InodeNotifyStoreCache in similar place for why it is only 2GB, not 4GB.
   619  	//
   620  	// ( InodeRetrieveCache calls us with chunks not larger than
   621  	//   ms.opts.MaxWrite, but MaxWrite is int, so let's be extra cautious )
   622  	size := len(dest)
   623  	if size > math.MaxInt32 {
   624  		size = math.MaxInt32
   625  	}
   626  	dest = dest[:size]
   627  
   628  	q := (*NotifyRetrieveOut)(req.outData())
   629  	q.Nodeid = node
   630  	q.Offset = uint64(offset) // not int64, as it is e.g. in NotifyInvalInodeOut
   631  	q.Size = uint32(len(dest))
   632  
   633  	reading := &retrieveCacheRequest{
   634  		nodeid: q.Nodeid,
   635  		offset: q.Offset,
   636  		dest:   dest,
   637  		ready:  make(chan struct{}),
   638  	}
   639  
   640  	ms.retrieveMu.Lock()
   641  	q.NotifyUnique = ms.retrieveNext
   642  	ms.retrieveNext++
   643  	ms.retrieveTab[q.NotifyUnique] = reading
   644  	ms.retrieveMu.Unlock()
   645  
   646  	// Protect against concurrent close.
   647  	ms.writeMu.Lock()
   648  	result := ms.write(&req)
   649  	ms.writeMu.Unlock()
   650  
   651  	if ms.opts.Debug {
   652  		log.Printf("Response: NOTIFY_RETRIEVE_CACHE: %v", result)
   653  	}
   654  	if result != OK {
   655  		ms.retrieveMu.Lock()
   656  		r := ms.retrieveTab[q.NotifyUnique]
   657  		if r == reading {
   658  			delete(ms.retrieveTab, q.NotifyUnique)
   659  		} else if r == nil {
   660  			// ok - might be dequeued by umount
   661  		} else {
   662  			// although very unlikely, it is possible that kernel sends
   663  			// unexpected NotifyReply with our notifyUnique, then
   664  			// retrieveNext wraps, makes full cycle, and another
   665  			// retrieve request is made with the same notifyUnique.
   666  			log.Printf("W: INODE_RETRIEVE_CACHE: request with notifyUnique=%d mutated", q.NotifyUnique)
   667  		}
   668  		ms.retrieveMu.Unlock()
   669  		return 0, result
   670  	}
   671  
   672  	// NotifyRetrieveOut sent to the kernel successfully. Now the kernel
   673  	// have to return data in a separate write-style NotifyReply request.
   674  	// Wait for the result.
   675  	<-reading.ready
   676  	return reading.n, reading.st
   677  }
   678  
   679  // retrieveCacheRequest represents in-flight cache retrieve request.
   680  type retrieveCacheRequest struct {
   681  	nodeid uint64
   682  	offset uint64
   683  	dest   []byte
   684  
   685  	// reply status
   686  	n     int
   687  	st    Status
   688  	ready chan struct{}
   689  }
   690  
   691  // DeleteNotify notifies the kernel that an entry is removed from a
   692  // directory.  In many cases, this is equivalent to EntryNotify,
   693  // except when the directory is in use, eg. as working directory of
   694  // some process. You should not hold any FUSE filesystem locks, as that
   695  // can lead to deadlock.
   696  func (ms *Server) DeleteNotify(parent uint64, child uint64, name string) Status {
   697  	if ms.kernelSettings.Minor < 18 {
   698  		return ms.EntryNotify(parent, name)
   699  	}
   700  
   701  	req := request{
   702  		inHeader: &InHeader{
   703  			Opcode: _OP_NOTIFY_DELETE,
   704  		},
   705  		handler: operationHandlers[_OP_NOTIFY_DELETE],
   706  		status:  NOTIFY_DELETE,
   707  	}
   708  
   709  	entry := (*NotifyInvalDeleteOut)(req.outData())
   710  	entry.Parent = parent
   711  	entry.Child = child
   712  	entry.NameLen = uint32(len(name))
   713  
   714  	// Many versions of FUSE generate stacktraces if the
   715  	// terminating null byte is missing.
   716  	nameBytes := make([]byte, len(name)+1)
   717  	copy(nameBytes, name)
   718  	nameBytes[len(nameBytes)-1] = '\000'
   719  	req.flatData = nameBytes
   720  
   721  	// Protect against concurrent close.
   722  	ms.writeMu.Lock()
   723  	result := ms.write(&req)
   724  	ms.writeMu.Unlock()
   725  
   726  	if ms.opts.Debug {
   727  		log.Printf("Response: DELETE_NOTIFY: %v", result)
   728  	}
   729  	return result
   730  }
   731  
   732  // EntryNotify should be used if the existence status of an entry
   733  // within a directory changes. You should not hold any FUSE filesystem
   734  // locks, as that can lead to deadlock.
   735  func (ms *Server) EntryNotify(parent uint64, name string) Status {
   736  	if !ms.kernelSettings.SupportsNotify(NOTIFY_INVAL_ENTRY) {
   737  		return ENOSYS
   738  	}
   739  	req := request{
   740  		inHeader: &InHeader{
   741  			Opcode: _OP_NOTIFY_INVAL_ENTRY,
   742  		},
   743  		handler: operationHandlers[_OP_NOTIFY_INVAL_ENTRY],
   744  		status:  NOTIFY_INVAL_ENTRY,
   745  	}
   746  	entry := (*NotifyInvalEntryOut)(req.outData())
   747  	entry.Parent = parent
   748  	entry.NameLen = uint32(len(name))
   749  
   750  	// Many versions of FUSE generate stacktraces if the
   751  	// terminating null byte is missing.
   752  	nameBytes := make([]byte, len(name)+1)
   753  	copy(nameBytes, name)
   754  	nameBytes[len(nameBytes)-1] = '\000'
   755  	req.flatData = nameBytes
   756  
   757  	// Protect against concurrent close.
   758  	ms.writeMu.Lock()
   759  	result := ms.write(&req)
   760  	ms.writeMu.Unlock()
   761  
   762  	if ms.opts.Debug {
   763  		log.Printf("Response: ENTRY_NOTIFY: %v", result)
   764  	}
   765  	return result
   766  }
   767  
   768  // SupportsVersion returns true if the kernel supports the given
   769  // protocol version or newer.
   770  func (in *InitIn) SupportsVersion(maj, min uint32) bool {
   771  	return in.Major >= maj || (in.Major == maj && in.Minor >= min)
   772  }
   773  
   774  // SupportsNotify returns whether a certain notification type is
   775  // supported. Pass any of the NOTIFY_* types as argument.
   776  func (in *InitIn) SupportsNotify(notifyType int) bool {
   777  	switch notifyType {
   778  	case NOTIFY_INVAL_ENTRY:
   779  		return in.SupportsVersion(7, 12)
   780  	case NOTIFY_INVAL_INODE:
   781  		return in.SupportsVersion(7, 12)
   782  	case NOTIFY_STORE_CACHE, NOTIFY_RETRIEVE_CACHE:
   783  		return in.SupportsVersion(7, 15)
   784  	case NOTIFY_DELETE:
   785  		return in.SupportsVersion(7, 18)
   786  	}
   787  	return false
   788  }
   789  
   790  var defaultBufferPool BufferPool
   791  
   792  func init() {
   793  	defaultBufferPool = NewBufferPool()
   794  }
   795  
   796  // WaitMount waits for the first request to be served. Use this to
   797  // avoid racing between accessing the (empty or not yet mounted)
   798  // mountpoint, and the OS trying to setup the user-space mount.
   799  func (ms *Server) WaitMount() error {
   800  	err := <-ms.ready
   801  	if err != nil {
   802  		return err
   803  	}
   804  	return pollHack(ms.mountPoint)
   805  }