github.com/scaleoutsean/fusego@v0.0.0-20220224074057-4a6429e46bb8/samples/memfs/memfs.go (about)

     1  // Copyright 2015 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package memfs
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"io"
    21  	"os"
    22  	"syscall"
    23  	"time"
    24  
    25  	"github.com/scaleoutsean/fusego"
    26  	"github.com/scaleoutsean/fusego/fuseops"
    27  	"github.com/scaleoutsean/fusego/fuseutil"
    28  	"github.com/jacobsa/syncutil"
    29  	"golang.org/x/sys/unix"
    30  )
    31  
    32  type memFS struct {
    33  	fuseutil.NotImplementedFileSystem
    34  
    35  	// The UID and GID that every inode receives.
    36  	uid uint32
    37  	gid uint32
    38  
    39  	/////////////////////////
    40  	// Mutable state
    41  	/////////////////////////
    42  
    43  	mu syncutil.InvariantMutex
    44  
    45  	// The collection of live inodes, indexed by ID. IDs of free inodes that may
    46  	// be re-used have nil entries. No ID less than fuseops.RootInodeID is ever
    47  	// used.
    48  	//
    49  	// All inodes are protected by the file system mutex.
    50  	//
    51  	// INVARIANT: For each inode in, in.CheckInvariants() does not panic.
    52  	// INVARIANT: len(inodes) > fuseops.RootInodeID
    53  	// INVARIANT: For all i < fuseops.RootInodeID, inodes[i] == nil
    54  	// INVARIANT: inodes[fuseops.RootInodeID] != nil
    55  	// INVARIANT: inodes[fuseops.RootInodeID].isDir()
    56  	inodes []*inode // GUARDED_BY(mu)
    57  
    58  	// A list of inode IDs within inodes available for reuse, not including the
    59  	// reserved IDs less than fuseops.RootInodeID.
    60  	//
    61  	// INVARIANT: This is all and only indices i of 'inodes' such that i >
    62  	// fuseops.RootInodeID and inodes[i] == nil
    63  	freeInodes []fuseops.InodeID // GUARDED_BY(mu)
    64  }
    65  
    66  // Create a file system that stores data and metadata in memory.
    67  //
    68  // The supplied UID/GID pair will own the root inode. This file system does no
    69  // permissions checking, and should therefore be mounted with the
    70  // default_permissions option.
    71  func NewMemFS(
    72  	uid uint32,
    73  	gid uint32) fuse.Server {
    74  	// Set up the basic struct.
    75  	fs := &memFS{
    76  		inodes: make([]*inode, fuseops.RootInodeID+1),
    77  		uid:    uid,
    78  		gid:    gid,
    79  	}
    80  
    81  	// Set up the root inode.
    82  	rootAttrs := fuseops.InodeAttributes{
    83  		Mode: 0700 | os.ModeDir,
    84  		Uid:  uid,
    85  		Gid:  gid,
    86  	}
    87  
    88  	fs.inodes[fuseops.RootInodeID] = newInode(rootAttrs)
    89  
    90  	// Set up invariant checking.
    91  	fs.mu = syncutil.NewInvariantMutex(fs.checkInvariants)
    92  
    93  	return fuseutil.NewFileSystemServer(fs)
    94  }
    95  
    96  ////////////////////////////////////////////////////////////////////////
    97  // Helpers
    98  ////////////////////////////////////////////////////////////////////////
    99  
   100  func (fs *memFS) checkInvariants() {
   101  	// Check reserved inodes.
   102  	for i := 0; i < fuseops.RootInodeID; i++ {
   103  		if fs.inodes[i] != nil {
   104  			panic(fmt.Sprintf("Non-nil inode for ID: %v", i))
   105  		}
   106  	}
   107  
   108  	// Check the root inode.
   109  	if !fs.inodes[fuseops.RootInodeID].isDir() {
   110  		panic("Expected root to be a directory.")
   111  	}
   112  
   113  	// Build our own list of free IDs.
   114  	freeIDsEncountered := make(map[fuseops.InodeID]struct{})
   115  	for i := fuseops.RootInodeID + 1; i < len(fs.inodes); i++ {
   116  		inode := fs.inodes[i]
   117  		if inode == nil {
   118  			freeIDsEncountered[fuseops.InodeID(i)] = struct{}{}
   119  			continue
   120  		}
   121  	}
   122  
   123  	// Check fs.freeInodes.
   124  	if len(fs.freeInodes) != len(freeIDsEncountered) {
   125  		panic(
   126  			fmt.Sprintf(
   127  				"Length mismatch: %v vs. %v",
   128  				len(fs.freeInodes),
   129  				len(freeIDsEncountered)))
   130  	}
   131  
   132  	for _, id := range fs.freeInodes {
   133  		if _, ok := freeIDsEncountered[id]; !ok {
   134  			panic(fmt.Sprintf("Unexected free inode ID: %v", id))
   135  		}
   136  	}
   137  
   138  	// INVARIANT: For each inode in, in.CheckInvariants() does not panic.
   139  	for _, in := range fs.inodes {
   140  		in.CheckInvariants()
   141  	}
   142  }
   143  
   144  // Find the given inode. Panic if it doesn't exist.
   145  //
   146  // LOCKS_REQUIRED(fs.mu)
   147  func (fs *memFS) getInodeOrDie(id fuseops.InodeID) *inode {
   148  	inode := fs.inodes[id]
   149  	if inode == nil {
   150  		panic(fmt.Sprintf("Unknown inode: %v", id))
   151  	}
   152  
   153  	return inode
   154  }
   155  
   156  // Allocate a new inode, assigning it an ID that is not in use.
   157  //
   158  // LOCKS_REQUIRED(fs.mu)
   159  func (fs *memFS) allocateInode(
   160  	attrs fuseops.InodeAttributes) (id fuseops.InodeID, inode *inode) {
   161  	// Create the inode.
   162  	inode = newInode(attrs)
   163  
   164  	// Re-use a free ID if possible. Otherwise mint a new one.
   165  	numFree := len(fs.freeInodes)
   166  	if numFree != 0 {
   167  		id = fs.freeInodes[numFree-1]
   168  		fs.freeInodes = fs.freeInodes[:numFree-1]
   169  		fs.inodes[id] = inode
   170  	} else {
   171  		id = fuseops.InodeID(len(fs.inodes))
   172  		fs.inodes = append(fs.inodes, inode)
   173  	}
   174  
   175  	return id, inode
   176  }
   177  
   178  // LOCKS_REQUIRED(fs.mu)
   179  func (fs *memFS) deallocateInode(id fuseops.InodeID) {
   180  	fs.freeInodes = append(fs.freeInodes, id)
   181  	fs.inodes[id] = nil
   182  }
   183  
   184  ////////////////////////////////////////////////////////////////////////
   185  // FileSystem methods
   186  ////////////////////////////////////////////////////////////////////////
   187  
   188  func (fs *memFS) StatFS(
   189  	ctx context.Context,
   190  	op *fuseops.StatFSOp) error {
   191  	return nil
   192  }
   193  
   194  func (fs *memFS) LookUpInode(
   195  	ctx context.Context,
   196  	op *fuseops.LookUpInodeOp) error {
   197  	fs.mu.Lock()
   198  	defer fs.mu.Unlock()
   199  
   200  	// Grab the parent directory.
   201  	inode := fs.getInodeOrDie(op.Parent)
   202  
   203  	// Does the directory have an entry with the given name?
   204  	childID, _, ok := inode.LookUpChild(op.Name)
   205  	if !ok {
   206  		return fuse.ENOENT
   207  	}
   208  
   209  	// Grab the child.
   210  	child := fs.getInodeOrDie(childID)
   211  
   212  	// Fill in the response.
   213  	op.Entry.Child = childID
   214  	op.Entry.Attributes = child.attrs
   215  
   216  	// We don't spontaneously mutate, so the kernel can cache as long as it wants
   217  	// (since it also handles invalidation).
   218  	op.Entry.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour)
   219  	op.Entry.EntryExpiration = op.Entry.AttributesExpiration
   220  
   221  	return nil
   222  }
   223  
   224  func (fs *memFS) GetInodeAttributes(
   225  	ctx context.Context,
   226  	op *fuseops.GetInodeAttributesOp) error {
   227  	fs.mu.Lock()
   228  	defer fs.mu.Unlock()
   229  
   230  	// Grab the inode.
   231  	inode := fs.getInodeOrDie(op.Inode)
   232  
   233  	// Fill in the response.
   234  	op.Attributes = inode.attrs
   235  
   236  	// We don't spontaneously mutate, so the kernel can cache as long as it wants
   237  	// (since it also handles invalidation).
   238  	op.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour)
   239  
   240  	return nil
   241  }
   242  
   243  func (fs *memFS) SetInodeAttributes(
   244  	ctx context.Context,
   245  	op *fuseops.SetInodeAttributesOp) error {
   246  	fs.mu.Lock()
   247  	defer fs.mu.Unlock()
   248  
   249  	var err error
   250  	if op.Size != nil && op.Handle == nil && *op.Size != 0 {
   251  		// require that truncate to non-zero has to be ftruncate()
   252  		// but allow open(O_TRUNC)
   253  		err = syscall.EBADF
   254  	}
   255  
   256  	// Grab the inode.
   257  	inode := fs.getInodeOrDie(op.Inode)
   258  
   259  	// Handle the request.
   260  	inode.SetAttributes(op.Size, op.Mode, op.Mtime)
   261  
   262  	// Fill in the response.
   263  	op.Attributes = inode.attrs
   264  
   265  	// We don't spontaneously mutate, so the kernel can cache as long as it wants
   266  	// (since it also handles invalidation).
   267  	op.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour)
   268  
   269  	return err
   270  }
   271  
   272  func (fs *memFS) MkDir(
   273  	ctx context.Context,
   274  	op *fuseops.MkDirOp) error {
   275  	fs.mu.Lock()
   276  	defer fs.mu.Unlock()
   277  
   278  	// Grab the parent, which we will update shortly.
   279  	parent := fs.getInodeOrDie(op.Parent)
   280  
   281  	// Ensure that the name doesn't already exist, so we don't wind up with a
   282  	// duplicate.
   283  	_, _, exists := parent.LookUpChild(op.Name)
   284  	if exists {
   285  		return fuse.EEXIST
   286  	}
   287  
   288  	// Set up attributes from the child.
   289  	childAttrs := fuseops.InodeAttributes{
   290  		Nlink: 1,
   291  		Mode:  op.Mode,
   292  		Uid:   fs.uid,
   293  		Gid:   fs.gid,
   294  	}
   295  
   296  	// Allocate a child.
   297  	childID, child := fs.allocateInode(childAttrs)
   298  
   299  	// Add an entry in the parent.
   300  	parent.AddChild(childID, op.Name, fuseutil.DT_Directory)
   301  
   302  	// Fill in the response.
   303  	op.Entry.Child = childID
   304  	op.Entry.Attributes = child.attrs
   305  
   306  	// We don't spontaneously mutate, so the kernel can cache as long as it wants
   307  	// (since it also handles invalidation).
   308  	op.Entry.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour)
   309  	op.Entry.EntryExpiration = op.Entry.AttributesExpiration
   310  
   311  	return nil
   312  }
   313  
   314  func (fs *memFS) MkNode(
   315  	ctx context.Context,
   316  	op *fuseops.MkNodeOp) error {
   317  	fs.mu.Lock()
   318  	defer fs.mu.Unlock()
   319  
   320  	var err error
   321  	op.Entry, err = fs.createFile(op.Parent, op.Name, op.Mode)
   322  	return err
   323  }
   324  
   325  // LOCKS_REQUIRED(fs.mu)
   326  func (fs *memFS) createFile(
   327  	parentID fuseops.InodeID,
   328  	name string,
   329  	mode os.FileMode) (fuseops.ChildInodeEntry, error) {
   330  	// Grab the parent, which we will update shortly.
   331  	parent := fs.getInodeOrDie(parentID)
   332  
   333  	// Ensure that the name doesn't already exist, so we don't wind up with a
   334  	// duplicate.
   335  	_, _, exists := parent.LookUpChild(name)
   336  	if exists {
   337  		return fuseops.ChildInodeEntry{}, fuse.EEXIST
   338  	}
   339  
   340  	// Set up attributes for the child.
   341  	now := time.Now()
   342  	childAttrs := fuseops.InodeAttributes{
   343  		Nlink:  1,
   344  		Mode:   mode,
   345  		Atime:  now,
   346  		Mtime:  now,
   347  		Ctime:  now,
   348  		Crtime: now,
   349  		Uid:    fs.uid,
   350  		Gid:    fs.gid,
   351  	}
   352  
   353  	// Allocate a child.
   354  	childID, child := fs.allocateInode(childAttrs)
   355  
   356  	// Add an entry in the parent.
   357  	parent.AddChild(childID, name, fuseutil.DT_File)
   358  
   359  	// Fill in the response entry.
   360  	var entry fuseops.ChildInodeEntry
   361  	entry.Child = childID
   362  	entry.Attributes = child.attrs
   363  
   364  	// We don't spontaneously mutate, so the kernel can cache as long as it wants
   365  	// (since it also handles invalidation).
   366  	entry.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour)
   367  	entry.EntryExpiration = entry.AttributesExpiration
   368  
   369  	return entry, nil
   370  }
   371  
   372  func (fs *memFS) CreateFile(
   373  	ctx context.Context,
   374  	op *fuseops.CreateFileOp) (err error) {
   375  	if op.Metadata.Pid == 0 {
   376  		// CreateFileOp should have a valid pid in metadata.
   377  		return fuse.EINVAL
   378  	}
   379  	fs.mu.Lock()
   380  	defer fs.mu.Unlock()
   381  
   382  	var err error
   383  	op.Entry, err = fs.createFile(op.Parent, op.Name, op.Mode)
   384  	return err
   385  }
   386  
   387  func (fs *memFS) CreateSymlink(
   388  	ctx context.Context,
   389  	op *fuseops.CreateSymlinkOp) error {
   390  	fs.mu.Lock()
   391  	defer fs.mu.Unlock()
   392  
   393  	// Grab the parent, which we will update shortly.
   394  	parent := fs.getInodeOrDie(op.Parent)
   395  
   396  	// Ensure that the name doesn't already exist, so we don't wind up with a
   397  	// duplicate.
   398  	_, _, exists := parent.LookUpChild(op.Name)
   399  	if exists {
   400  		return fuse.EEXIST
   401  	}
   402  
   403  	// Set up attributes from the child.
   404  	now := time.Now()
   405  	childAttrs := fuseops.InodeAttributes{
   406  		Nlink:  1,
   407  		Mode:   0444 | os.ModeSymlink,
   408  		Atime:  now,
   409  		Mtime:  now,
   410  		Ctime:  now,
   411  		Crtime: now,
   412  		Uid:    fs.uid,
   413  		Gid:    fs.gid,
   414  	}
   415  
   416  	// Allocate a child.
   417  	childID, child := fs.allocateInode(childAttrs)
   418  
   419  	// Set up its target.
   420  	child.target = op.Target
   421  
   422  	// Add an entry in the parent.
   423  	parent.AddChild(childID, op.Name, fuseutil.DT_Link)
   424  
   425  	// Fill in the response entry.
   426  	op.Entry.Child = childID
   427  	op.Entry.Attributes = child.attrs
   428  
   429  	// We don't spontaneously mutate, so the kernel can cache as long as it wants
   430  	// (since it also handles invalidation).
   431  	op.Entry.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour)
   432  	op.Entry.EntryExpiration = op.Entry.AttributesExpiration
   433  
   434  	return nil
   435  }
   436  
   437  func (fs *memFS) CreateLink(
   438  	ctx context.Context,
   439  	op *fuseops.CreateLinkOp) error {
   440  	fs.mu.Lock()
   441  	defer fs.mu.Unlock()
   442  
   443  	// Grab the parent, which we will update shortly.
   444  	parent := fs.getInodeOrDie(op.Parent)
   445  
   446  	// Ensure that the name doesn't already exist, so we don't wind up with a
   447  	// duplicate.
   448  	_, _, exists := parent.LookUpChild(op.Name)
   449  	if exists {
   450  		return fuse.EEXIST
   451  	}
   452  
   453  	// Get the target inode to be linked
   454  	target := fs.getInodeOrDie(op.Target)
   455  
   456  	// Update the attributes
   457  	now := time.Now()
   458  	target.attrs.Nlink++
   459  	target.attrs.Ctime = now
   460  
   461  	// Add an entry in the parent.
   462  	parent.AddChild(op.Target, op.Name, fuseutil.DT_File)
   463  
   464  	// Return the response.
   465  	op.Entry.Child = op.Target
   466  	op.Entry.Attributes = target.attrs
   467  
   468  	// We don't spontaneously mutate, so the kernel can cache as long as it wants
   469  	// (since it also handles invalidation).
   470  	op.Entry.AttributesExpiration = time.Now().Add(365 * 24 * time.Hour)
   471  	op.Entry.EntryExpiration = op.Entry.AttributesExpiration
   472  
   473  	return nil
   474  }
   475  
   476  func (fs *memFS) Rename(
   477  	ctx context.Context,
   478  	op *fuseops.RenameOp) error {
   479  	fs.mu.Lock()
   480  	defer fs.mu.Unlock()
   481  
   482  	// Ask the old parent for the child's inode ID and type.
   483  	oldParent := fs.getInodeOrDie(op.OldParent)
   484  	childID, childType, ok := oldParent.LookUpChild(op.OldName)
   485  
   486  	if !ok {
   487  		return fuse.ENOENT
   488  	}
   489  
   490  	// If the new name exists already in the new parent, make sure it's not a
   491  	// non-empty directory, then delete it.
   492  	newParent := fs.getInodeOrDie(op.NewParent)
   493  	existingID, _, ok := newParent.LookUpChild(op.NewName)
   494  	if ok {
   495  		existing := fs.getInodeOrDie(existingID)
   496  
   497  		var buf [4096]byte
   498  		if existing.isDir() && existing.ReadDir(buf[:], 0) > 0 {
   499  			return fuse.ENOTEMPTY
   500  		}
   501  
   502  		newParent.RemoveChild(op.NewName)
   503  	}
   504  
   505  	// Link the new name.
   506  	newParent.AddChild(
   507  		childID,
   508  		op.NewName,
   509  		childType)
   510  
   511  	// Finally, remove the old name from the old parent.
   512  	oldParent.RemoveChild(op.OldName)
   513  
   514  	return nil
   515  }
   516  
   517  func (fs *memFS) RmDir(
   518  	ctx context.Context,
   519  	op *fuseops.RmDirOp) error {
   520  	fs.mu.Lock()
   521  	defer fs.mu.Unlock()
   522  
   523  	// Grab the parent, which we will update shortly.
   524  	parent := fs.getInodeOrDie(op.Parent)
   525  
   526  	// Find the child within the parent.
   527  	childID, _, ok := parent.LookUpChild(op.Name)
   528  	if !ok {
   529  		return fuse.ENOENT
   530  	}
   531  
   532  	// Grab the child.
   533  	child := fs.getInodeOrDie(childID)
   534  
   535  	// Make sure the child is empty.
   536  	if child.Len() != 0 {
   537  		return fuse.ENOTEMPTY
   538  	}
   539  
   540  	// Remove the entry within the parent.
   541  	parent.RemoveChild(op.Name)
   542  
   543  	// Mark the child as unlinked.
   544  	child.attrs.Nlink--
   545  
   546  	return nil
   547  }
   548  
   549  func (fs *memFS) Unlink(
   550  	ctx context.Context,
   551  	op *fuseops.UnlinkOp) error {
   552  	fs.mu.Lock()
   553  	defer fs.mu.Unlock()
   554  
   555  	// Grab the parent, which we will update shortly.
   556  	parent := fs.getInodeOrDie(op.Parent)
   557  
   558  	// Find the child within the parent.
   559  	childID, _, ok := parent.LookUpChild(op.Name)
   560  	if !ok {
   561  		return fuse.ENOENT
   562  	}
   563  
   564  	// Grab the child.
   565  	child := fs.getInodeOrDie(childID)
   566  
   567  	// Remove the entry within the parent.
   568  	parent.RemoveChild(op.Name)
   569  
   570  	// Mark the child as unlinked.
   571  	child.attrs.Nlink--
   572  
   573  	return nil
   574  }
   575  
   576  func (fs *memFS) OpenDir(
   577  	ctx context.Context,
   578  	op *fuseops.OpenDirOp) error {
   579  	fs.mu.Lock()
   580  	defer fs.mu.Unlock()
   581  
   582  	// We don't mutate spontaneosuly, so if the VFS layer has asked for an
   583  	// inode that doesn't exist, something screwed up earlier (a lookup, a
   584  	// cache invalidation, etc.).
   585  	inode := fs.getInodeOrDie(op.Inode)
   586  
   587  	if !inode.isDir() {
   588  		panic("Found non-dir.")
   589  	}
   590  
   591  	return nil
   592  }
   593  
   594  func (fs *memFS) ReadDir(
   595  	ctx context.Context,
   596  	op *fuseops.ReadDirOp) error {
   597  	fs.mu.Lock()
   598  	defer fs.mu.Unlock()
   599  
   600  	// Grab the directory.
   601  	inode := fs.getInodeOrDie(op.Inode)
   602  
   603  	// Serve the request.
   604  	op.BytesRead = inode.ReadDir(op.Dst, int(op.Offset))
   605  
   606  	return nil
   607  }
   608  
   609  func (fs *memFS) OpenFile(
   610  	ctx context.Context,
   611  	op *fuseops.OpenFileOp) (err error) {
   612  	if op.Metadata.Pid == 0 {
   613  		// OpenFileOp should have a valid pid in metadata.
   614  		return fuse.EINVAL
   615  	}
   616  
   617  	fs.mu.Lock()
   618  	defer fs.mu.Unlock()
   619  
   620  	// We don't mutate spontaneosuly, so if the VFS layer has asked for an
   621  	// inode that doesn't exist, something screwed up earlier (a lookup, a
   622  	// cache invalidation, etc.).
   623  	inode := fs.getInodeOrDie(op.Inode)
   624  
   625  	if !inode.isFile() {
   626  		panic("Found non-file.")
   627  	}
   628  
   629  	return nil
   630  }
   631  
   632  func (fs *memFS) ReadFile(
   633  	ctx context.Context,
   634  	op *fuseops.ReadFileOp) error {
   635  	fs.mu.Lock()
   636  	defer fs.mu.Unlock()
   637  
   638  	// Find the inode in question.
   639  	inode := fs.getInodeOrDie(op.Inode)
   640  
   641  	// Serve the request.
   642  	var err error
   643  	op.BytesRead, err = inode.ReadAt(op.Dst, op.Offset)
   644  
   645  	// Don't return EOF errors; we just indicate EOF to fuse using a short read.
   646  	if err == io.EOF {
   647  		return nil
   648  	}
   649  
   650  	return err
   651  }
   652  
   653  func (fs *memFS) WriteFile(
   654  	ctx context.Context,
   655  	op *fuseops.WriteFileOp) error {
   656  	fs.mu.Lock()
   657  	defer fs.mu.Unlock()
   658  
   659  	// Find the inode in question.
   660  	inode := fs.getInodeOrDie(op.Inode)
   661  
   662  	// Serve the request.
   663  	_, err := inode.WriteAt(op.Data, op.Offset)
   664  
   665  	return err
   666  }
   667  
   668  func (fs *memFS) FlushFile(
   669  	ctx context.Context,
   670  	op *fuseops.FlushFileOp) (err error) {
   671  	if op.Metadata.Pid == 0 {
   672  		// FlushFileOp should have a valid pid in metadata.
   673  		return fuse.EINVAL
   674  	}
   675  	return
   676  }
   677  
   678  func (fs *memFS) ReadSymlink(
   679  	ctx context.Context,
   680  	op *fuseops.ReadSymlinkOp) error {
   681  	fs.mu.Lock()
   682  	defer fs.mu.Unlock()
   683  
   684  	// Find the inode in question.
   685  	inode := fs.getInodeOrDie(op.Inode)
   686  
   687  	// Serve the request.
   688  	op.Target = inode.target
   689  
   690  	return nil
   691  }
   692  
   693  func (fs *memFS) GetXattr(ctx context.Context,
   694  	op *fuseops.GetXattrOp) error {
   695  	fs.mu.Lock()
   696  	defer fs.mu.Unlock()
   697  
   698  	inode := fs.getInodeOrDie(op.Inode)
   699  	if value, ok := inode.xattrs[op.Name]; ok {
   700  		op.BytesRead = len(value)
   701  		if len(op.Dst) >= len(value) {
   702  			copy(op.Dst, value)
   703  		} else if len(op.Dst) != 0 {
   704  			return syscall.ERANGE
   705  		}
   706  	} else {
   707  		return fuse.ENOATTR
   708  	}
   709  
   710  	return nil
   711  }
   712  
   713  func (fs *memFS) ListXattr(ctx context.Context,
   714  	op *fuseops.ListXattrOp) error {
   715  	fs.mu.Lock()
   716  	defer fs.mu.Unlock()
   717  
   718  	inode := fs.getInodeOrDie(op.Inode)
   719  
   720  	dst := op.Dst[:]
   721  	for key := range inode.xattrs {
   722  		keyLen := len(key) + 1
   723  
   724  		if len(dst) >= keyLen {
   725  			copy(dst, key)
   726  			dst = dst[keyLen:]
   727  		} else if len(op.Dst) != 0 {
   728  			return syscall.ERANGE
   729  		}
   730  		op.BytesRead += keyLen
   731  	}
   732  
   733  	return nil
   734  }
   735  
   736  func (fs *memFS) RemoveXattr(ctx context.Context,
   737  	op *fuseops.RemoveXattrOp) error {
   738  	fs.mu.Lock()
   739  	defer fs.mu.Unlock()
   740  	inode := fs.getInodeOrDie(op.Inode)
   741  
   742  	if _, ok := inode.xattrs[op.Name]; ok {
   743  		delete(inode.xattrs, op.Name)
   744  	} else {
   745  		return fuse.ENOATTR
   746  	}
   747  	return nil
   748  }
   749  
   750  func (fs *memFS) SetXattr(ctx context.Context,
   751  	op *fuseops.SetXattrOp) error {
   752  	fs.mu.Lock()
   753  	defer fs.mu.Unlock()
   754  	inode := fs.getInodeOrDie(op.Inode)
   755  
   756  	_, ok := inode.xattrs[op.Name]
   757  
   758  	switch op.Flags {
   759  	case unix.XATTR_CREATE:
   760  		if ok {
   761  			return fuse.EEXIST
   762  		}
   763  	case unix.XATTR_REPLACE:
   764  		if !ok {
   765  			return fuse.ENOATTR
   766  		}
   767  	}
   768  
   769  	value := make([]byte, len(op.Value))
   770  	copy(value, op.Value)
   771  	inode.xattrs[op.Name] = value
   772  	return nil
   773  }
   774  
   775  func (fs *memFS) Fallocate(ctx context.Context,
   776  	op *fuseops.FallocateOp) error {
   777  	fs.mu.Lock()
   778  	defer fs.mu.Unlock()
   779  	inode := fs.getInodeOrDie(op.Inode)
   780  	inode.Fallocate(op.Mode, op.Length, op.Length)
   781  	return nil
   782  }