github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libfs/fs.go (about)

     1  // Copyright 2017 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libfs
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"crypto/rand"
    11  	"encoding/base64"
    12  	"fmt"
    13  	"net/http"
    14  	"os"
    15  	"path"
    16  	"strings"
    17  	"sync"
    18  	"time"
    19  
    20  	"github.com/keybase/client/go/kbfs/data"
    21  	"github.com/keybase/client/go/kbfs/idutil"
    22  	"github.com/keybase/client/go/kbfs/libkbfs"
    23  	"github.com/keybase/client/go/kbfs/tlfhandle"
    24  	"github.com/keybase/client/go/logger"
    25  	"github.com/keybase/client/go/protocol/keybase1"
    26  	"github.com/pkg/errors"
    27  	billy "gopkg.in/src-d/go-billy.v4"
    28  )
    29  
    30  // FSEventType is FS event type.
    31  type FSEventType int
    32  
    33  const (
    34  	_ FSEventType = iota
    35  	// FSEventLock indicates Lock method has been called.
    36  	FSEventLock
    37  	// FSEventUnlock indicates Unlock method has been called.
    38  	FSEventUnlock
    39  )
    40  
    41  // FSEvent is the type for events sent into the events channel passed into
    42  // NewFS.
    43  type FSEvent struct {
    44  	EventType FSEventType
    45  	File      *File
    46  	Done      <-chan struct{}
    47  }
    48  
    49  type fsInner struct {
    50  	config   libkbfs.Config
    51  	root     libkbfs.Node
    52  	rootInfo data.EntryInfo
    53  	h        *tlfhandle.Handle
    54  	subdir   string
    55  	uniqID   string
    56  	log      logger.Logger
    57  	deferLog logger.Logger
    58  	priority keybase1.MDPriority
    59  	empty    bool
    60  	// lockNamespace is the prefix used by any *File generated by this *FS when
    61  	// they need to generate a lockID. By default, we use a canonical unix path
    62  	// of the root of this FS as lock namespace. But one can call
    63  	// SetLockNamespace to set it explicitly, which can be any bytes. When
    64  	// Chroot is called, a slash ('/') followed by the changed subpath are
    65  	// appended to the existing lockNamespace to form the new one. Note that
    66  	// this is a naive append without and path clean.
    67  	lockNamespace []byte
    68  
    69  	eventsLock sync.RWMutex // nolint
    70  	events     map[chan<- FSEvent]bool
    71  }
    72  
    73  // FS is a wrapper around a KBFS subdirectory that implements the
    74  // billy.Filesystem interface.  It uses forward-slash separated paths.
    75  // It may return errors wrapped with the `github.com/pkg/errors`
    76  // package.
    77  type FS struct {
    78  	// Yes, storing ctx in a struct is a mortal sin, but the
    79  	// billy.Filesystem interface doesn't give us a way to accept ctxs
    80  	// any other way.
    81  	ctx context.Context
    82  	*fsInner
    83  }
    84  
    85  var _ billy.Filesystem = (*FS)(nil)
    86  
    87  const (
    88  	maxSymlinkLevels = 40 // same as Linux
    89  )
    90  
    91  func followSymlink(parentPath, link string) (newPath string, err error) {
    92  	if path.IsAbs(link) {
    93  		return "", errors.Errorf("Can't follow absolute link: %s", link)
    94  	}
    95  
    96  	newPath = path.Clean(path.Join(parentPath, link))
    97  	if strings.HasPrefix(newPath, "..") {
    98  		return "", errors.Errorf(
    99  			"Cannot follow symlink out of chroot: %s", newPath)
   100  	}
   101  
   102  	return newPath, nil
   103  }
   104  
   105  func pathForLogging(
   106  	ctx context.Context, config libkbfs.Config, root libkbfs.Node,
   107  	filename string) string {
   108  	if root == nil || root.Obfuscator() == nil {
   109  		return filename
   110  	}
   111  
   112  	if filename == "." {
   113  		return ""
   114  	}
   115  
   116  	parts := strings.Split(filename, "/")
   117  	if len(parts) == 0 {
   118  		return ""
   119  	}
   120  	n := root
   121  	ret := ""
   122  	for i := 0; i < len(parts)-1; i++ {
   123  		p := parts[i]
   124  		childName := n.ChildName(p)
   125  		ret = path.Join(ret, childName.String())
   126  		nextNode, _, err := config.KBFSOps().Lookup(ctx, n, childName)
   127  		// If filename is a path that includes a symlink, we can get a nil node
   128  		// here. So just move on if nextNode == nil. In the very rare case of
   129  		// an entry that gets a duplicated obfuscated name within a directory,
   130  		// this could give the wrong answer. But it's not worth it at this time
   131  		// to follow the symlnk for logging.
   132  		if err != nil || nextNode == nil {
   133  			// Just keep using the parent node to obfuscate.
   134  			continue
   135  		}
   136  		n = nextNode
   137  	}
   138  	ret = path.Join(ret, n.ChildName(parts[len(parts)-1]).String())
   139  	return ret
   140  }
   141  
   142  // ErrNotADirectory is returned when a non-final path element exists but is not
   143  // a directory.
   144  type ErrNotADirectory struct {
   145  	Name string
   146  }
   147  
   148  func (e ErrNotADirectory) Error() string {
   149  	return fmt.Sprintf("%s is not a directory", e.Name)
   150  }
   151  
   152  type accessType int
   153  
   154  const (
   155  	readwrite accessType = iota
   156  	// readwriteNoCreate creates a read-write file system, but doesn't
   157  	// create the TLF if it doesn't already exists.  Instead, it
   158  	// creates an empty file system.
   159  	readwriteNoCreate
   160  	readonly
   161  )
   162  
   163  func newFS(ctx context.Context, config libkbfs.Config,
   164  	tlfHandle *tlfhandle.Handle, branch data.BranchName, subdir string,
   165  	uniqID string, priority keybase1.MDPriority, unwrap bool,
   166  	atype accessType) (*FS, error) {
   167  	rootNodeGetter := config.KBFSOps().GetOrCreateRootNode
   168  	if branch != data.MasterBranch || atype != readwrite {
   169  		rootNodeGetter = config.KBFSOps().GetRootNode
   170  	}
   171  
   172  	rootNode, ei, err := rootNodeGetter(ctx, tlfHandle, branch)
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  	log := config.MakeLogger("")
   177  	if rootNode == nil {
   178  		if len(subdir) > 0 {
   179  			return nil, errors.New("Subdir doesn't exist in empty TLF")
   180  		}
   181  		log.CDebugf(ctx,
   182  			"Returning empty FS for empty TLF=%s", tlfHandle.GetCanonicalName())
   183  		return &FS{
   184  			ctx: ctx,
   185  			fsInner: &fsInner{
   186  				config:   config,
   187  				empty:    true,
   188  				h:        tlfHandle,
   189  				uniqID:   uniqID,
   190  				log:      log,
   191  				deferLog: log.CloneWithAddedDepth(1),
   192  			},
   193  		}, nil
   194  	}
   195  
   196  	if unwrap {
   197  		rootNode = rootNode.Unwrap()
   198  	}
   199  	if subdir != "" {
   200  		subdir = path.Clean(subdir)
   201  	}
   202  
   203  	if atype == readonly {
   204  		rootNode = libkbfs.ReadonlyNode{Node: rootNode}
   205  	}
   206  	n := rootNode
   207  
   208  	// Look up the subdir's root.
   209  	var parts []string
   210  	if len(subdir) > 0 {
   211  		parts = strings.Split(subdir, "/")
   212  	}
   213  	// Loop while we follow symlinks.
   214  outer:
   215  	for {
   216  		for i, p := range parts {
   217  			n, ei, err = config.KBFSOps().Lookup(ctx, n, n.ChildName(p))
   218  			if err != nil {
   219  				return nil, err
   220  			}
   221  			switch ei.Type {
   222  			case data.Dir:
   223  				continue
   224  			case data.Sym:
   225  				parentParts := parts[:i]
   226  				newPath, err := followSymlink(
   227  					path.Join(parentParts...), ei.SymPath)
   228  				if err != nil {
   229  					return nil, err
   230  				}
   231  				newParts := strings.Split(newPath, "/")
   232  				newParts = append(newParts, parts[i+1:]...)
   233  				// Fix subdir so we'll get the correct default lock namespace.
   234  				oldSubdir := subdir
   235  				subdir = path.Join(newParts...)
   236  				config.MakeLogger("").CDebugf(ctx, "Expanding symlink: %s->%s",
   237  					pathForLogging(ctx, config, rootNode, oldSubdir),
   238  					pathForLogging(ctx, config, rootNode, subdir))
   239  				parts = newParts
   240  				n = rootNode
   241  				continue outer
   242  			default:
   243  				return nil, ErrNotADirectory{Name: path.Join(parts[:i+1]...)}
   244  			}
   245  		}
   246  		// Successfully looked up all directories.
   247  		break
   248  	}
   249  
   250  	log.CDebugf(ctx, "Made new FS for TLF=%s, subdir=%s",
   251  		tlfHandle.GetCanonicalName(),
   252  		pathForLogging(ctx, config, rootNode, subdir))
   253  
   254  	// Use the canonical unix path for default locking namespace, as this needs
   255  	// to be the same across all platforms.
   256  	unixFullPath := path.Join("/keybase", tlfHandle.Type().String(), subdir)
   257  
   258  	return &FS{
   259  		ctx: ctx,
   260  		fsInner: &fsInner{
   261  			config:        config,
   262  			root:          n,
   263  			rootInfo:      ei,
   264  			h:             tlfHandle,
   265  			subdir:        subdir,
   266  			uniqID:        uniqID,
   267  			log:           log,
   268  			deferLog:      log.CloneWithAddedDepth(1),
   269  			lockNamespace: []byte(unixFullPath),
   270  			priority:      priority,
   271  			events:        make(map[chan<- FSEvent]bool),
   272  		},
   273  	}, nil
   274  }
   275  
   276  // NewUnwrappedFS returns a new FS instance, chroot'd to the given TLF
   277  // and subdir within that TLF, but all the nodes are unwrapped.
   278  // `subdir` must exist, and point to a directory, before this function
   279  // is called.  `uniqID` needs to uniquely identify this instance among
   280  // all users of this TLF globally; for example, a device ID combined
   281  // with a local tempfile name is recommended.
   282  func NewUnwrappedFS(ctx context.Context, config libkbfs.Config,
   283  	tlfHandle *tlfhandle.Handle, branch data.BranchName, subdir string,
   284  	uniqID string, priority keybase1.MDPriority) (*FS, error) {
   285  	return newFS(
   286  		ctx, config, tlfHandle, branch, subdir, uniqID, priority, true,
   287  		readwrite)
   288  }
   289  
   290  // NewReadonlyFS returns a new FS instance, chroot'd to the given TLF
   291  // and subdir within that TLF, but all the nodes are read-only.
   292  // `subdir` must exist, and point to a directory, before this function
   293  // is called.  `uniqID` needs to uniquely identify this instance among
   294  // all users of this TLF globally; for example, a device ID combined
   295  // with a local tempfile name is recommended.
   296  //
   297  // Note that this should only be used for subdirectories that will
   298  // never be accessed in read-write mode by this process, because the
   299  // nodes created via this FS might stay read-only in the libkbfs
   300  // NodeCache for a while.
   301  func NewReadonlyFS(ctx context.Context, config libkbfs.Config,
   302  	tlfHandle *tlfhandle.Handle, branch data.BranchName, subdir string,
   303  	uniqID string, priority keybase1.MDPriority) (*FS, error) {
   304  	return newFS(
   305  		ctx, config, tlfHandle, branch, subdir, uniqID, priority, false,
   306  		readonly)
   307  }
   308  
   309  // NewFSIfExists returns a new FS instance, chroot'd to the given TLF
   310  // and subdir within that TLF, but only if the TLF already exists.
   311  // `subdir` must exist, and point to a directory, before this function
   312  // is called.  `uniqID` needs to uniquely identify this instance among
   313  // all users of this TLF globally; for example, a device ID combined
   314  // with a local tempfile name is recommended.
   315  //
   316  // If the TLF hasn't been initialized yet, this will return an FS that
   317  // is always empty.  `IsEmpty()` will tell if you this is happening.
   318  // If there's a need to re-check the TLF, a new FS must be
   319  // constructed.
   320  func NewFSIfExists(ctx context.Context, config libkbfs.Config,
   321  	tlfHandle *tlfhandle.Handle, branch data.BranchName, subdir string,
   322  	uniqID string, priority keybase1.MDPriority) (*FS, error) {
   323  	return newFS(
   324  		ctx, config, tlfHandle, branch, subdir, uniqID, priority, false,
   325  		readwriteNoCreate)
   326  }
   327  
   328  // NewFS returns a new FS instance, chroot'd to the given TLF and
   329  // subdir within that TLF.  `subdir` must exist, and point to a
   330  // directory, before this function is called.  `uniqID` needs to
   331  // uniquely identify this instance among all users of this TLF
   332  // globally; for example, a device ID combined with a local tempfile
   333  // name is recommended.
   334  func NewFS(ctx context.Context, config libkbfs.Config,
   335  	tlfHandle *tlfhandle.Handle, branch data.BranchName, subdir string,
   336  	uniqID string, priority keybase1.MDPriority) (*FS, error) {
   337  	return newFS(
   338  		ctx, config, tlfHandle, branch, subdir, uniqID, priority, false,
   339  		readwrite)
   340  }
   341  
   342  // PathForLogging returns the obfuscated path for the given filename.
   343  func (fs *FS) PathForLogging(filename string) string {
   344  	return pathForLogging(fs.ctx, fs.config, fs.root, filename)
   345  }
   346  
   347  // lookupOrCreateEntryNoFollow looks up the entry for a file in a
   348  // given parent node.  If the entry is a symlink, it will return a nil
   349  // Node and a nil error.  If the entry doesn't exist and O_CREATE is
   350  // set in `flag`, it will create the entry as a file.
   351  func (fs *FS) lookupOrCreateEntryNoFollow(
   352  	dir libkbfs.Node, filename data.PathPartString, flag int,
   353  	perm os.FileMode) (libkbfs.Node, data.EntryInfo, error) {
   354  	n, ei, err := fs.config.KBFSOps().Lookup(fs.ctx, dir, filename)
   355  	switch errors.Cause(err).(type) {
   356  	case idutil.NoSuchNameError:
   357  		// The file doesn't exist yet; create if requested
   358  		if flag&os.O_CREATE == 0 {
   359  			return nil, data.EntryInfo{}, err
   360  		}
   361  		fs.log.CDebugf(
   362  			fs.ctx, "Creating %s since it doesn't exist yet", filename)
   363  		excl := libkbfs.NoExcl
   364  		if flag&os.O_EXCL != 0 {
   365  			excl = libkbfs.WithExcl
   366  		}
   367  		isExec := (perm & 0100) != 0
   368  		n, ei, err = fs.config.KBFSOps().CreateFile(
   369  			fs.ctx, dir, filename, isExec, excl)
   370  		switch errors.Cause(err).(type) {
   371  		case data.NameExistsError:
   372  			// Someone made it already; recurse to try the lookup again.
   373  			fs.log.CDebugf(
   374  				fs.ctx, "Attempting lookup again after failed create")
   375  			return fs.lookupOrCreateEntryNoFollow(dir, filename, flag, perm)
   376  		case nil:
   377  			return n, ei, nil
   378  		default:
   379  			return nil, data.EntryInfo{}, err
   380  		}
   381  	case nil:
   382  		// If we were supposed to have exclusively-created this file,
   383  		// we must fail.
   384  		if flag&os.O_CREATE != 0 && flag&os.O_EXCL != 0 {
   385  			return nil, data.EntryInfo{}, errors.Wrap(os.ErrExist,
   386  				"Exclusive create failed because the file exists")
   387  		}
   388  
   389  		if ei.Type == data.Sym {
   390  			// The caller must retry if desired.
   391  			return nil, ei, nil
   392  		}
   393  
   394  		return n, ei, nil
   395  	default:
   396  		return nil, data.EntryInfo{}, err
   397  	}
   398  }
   399  
   400  // lookupParentWithDepth looks up the parent node of the given
   401  // filename.  It follows symlinks in the path, but doesn't resolve the
   402  // final base name.  If `exitEarly` is true, it returns on the first
   403  // not-found error and `base` will contain the subpath of filename not
   404  // yet found.
   405  func (fs *FS) lookupParentWithDepth(
   406  	filename string, exitEarly bool, depth int) (
   407  	parent libkbfs.Node, parentDir, base string, err error) {
   408  	parts := strings.Split(filename, "/")
   409  	n := fs.root
   410  	// Iterate through each of the parent directories of the file, but
   411  	// not the file itself.
   412  	for i := 0; i < len(parts)-1; i++ {
   413  		p := parts[i]
   414  		nextNode, ei, err := fs.config.KBFSOps().Lookup(
   415  			fs.ctx, n, n.ChildName(p))
   416  		switch errors.Cause(err).(type) {
   417  		case idutil.NoSuchNameError:
   418  			if exitEarly {
   419  				parentDir = path.Join(parts[:i]...)
   420  				base = path.Join(parts[i:]...)
   421  				return n, parentDir, base, nil
   422  			}
   423  			return nil, "", "", err
   424  		case nil:
   425  			n = nextNode
   426  		default:
   427  			return nil, "", "", err
   428  		}
   429  
   430  		switch ei.Type {
   431  		case data.Sym:
   432  			if depth == maxSymlinkLevels {
   433  				return nil, "", "", errors.New("Too many levels of symlinks")
   434  			}
   435  			parentDir = path.Join(parts[:i]...)
   436  			newPath, err := followSymlink(parentDir, ei.SymPath)
   437  			if err != nil {
   438  				return nil, "", "", err
   439  			}
   440  			newPathPlusRemainder := append([]string{newPath}, parts[i+1:]...)
   441  			return fs.lookupParentWithDepth(
   442  				path.Join(newPathPlusRemainder...), exitEarly, depth+1)
   443  		case data.Dir:
   444  			continue
   445  		default:
   446  			return nil, "", "", ErrNotADirectory{Name: path.Join(parts[:i+1]...)}
   447  		}
   448  	}
   449  
   450  	parentDir = path.Join(parts[:len(parts)-1]...)
   451  	base = parts[len(parts)-1]
   452  	return n, parentDir, base, nil
   453  }
   454  
   455  func (fs *FS) lookupParent(filename string) (
   456  	parent libkbfs.Node, parentDir, base string, err error) {
   457  	return fs.lookupParentWithDepth(filename, false, 0)
   458  }
   459  
   460  // lookupOrCreateEntry looks up the entry for a filename, following
   461  // symlinks in the path (including if the final entry is a symlink).
   462  // If the entry doesn't exist an O_CREATE is set in `flag`, it will
   463  // create the entry as a file.
   464  func (fs *FS) lookupOrCreateEntry(
   465  	filename string, flag int, perm os.FileMode) (
   466  	n libkbfs.Node, ei data.EntryInfo, err error) {
   467  	// Shortcut the case where there's nothing to look up.
   468  	if filename == "" || filename == "/" || filename == "." {
   469  		return fs.root, fs.rootInfo, nil
   470  	}
   471  	filename = strings.TrimPrefix(filename, "/")
   472  
   473  	for i := 0; i < maxSymlinkLevels; i++ {
   474  		var parentDir, fName string
   475  		n, parentDir, fName, err = fs.lookupParent(filename)
   476  		if err != nil {
   477  			return nil, data.EntryInfo{}, err
   478  		}
   479  
   480  		n, ei, err := fs.lookupOrCreateEntryNoFollow(
   481  			n, n.ChildName(fName), flag, perm)
   482  		if err != nil {
   483  			return nil, data.EntryInfo{}, err
   484  		}
   485  
   486  		if ei.Type != data.Sym {
   487  			return n, ei, nil
   488  		}
   489  		fs.log.CDebugf(fs.ctx, "Following symlink=%s from dir=%s",
   490  			fs.PathForLogging(ei.SymPath), fs.PathForLogging(parentDir))
   491  		filename, err = followSymlink(parentDir, ei.SymPath)
   492  		if err != nil {
   493  			return nil, data.EntryInfo{}, err
   494  		}
   495  	}
   496  	return nil, data.EntryInfo{}, errors.New("Too many levels of symlinks")
   497  }
   498  
   499  func translateErr(err error) error {
   500  	switch errors.Cause(err).(type) {
   501  	case idutil.NoSuchNameError, ErrNotADirectory:
   502  		return os.ErrNotExist
   503  	case libkbfs.TlfAccessError, tlfhandle.ReadAccessError:
   504  		return os.ErrPermission
   505  	case libkbfs.NotDirError, libkbfs.NotFileError:
   506  		return os.ErrInvalid
   507  	case data.NameExistsError:
   508  		return os.ErrExist
   509  	default:
   510  		return err
   511  	}
   512  }
   513  
   514  func (fs *FS) mkdirAll(filename string, perm os.FileMode) (err error) {
   515  	defer func() {
   516  		err = translateErr(err)
   517  	}()
   518  
   519  	if filename == "/" || filename == "" || filename == "." {
   520  		return nil
   521  	}
   522  
   523  	n, _, leftover, err := fs.lookupParentWithDepth(filename, true, 0)
   524  	if err != nil {
   525  		return err
   526  	}
   527  
   528  	parts := strings.Split(leftover, "/")
   529  	// Make all necessary dirs.
   530  	for _, p := range parts {
   531  		child, _, err := fs.config.KBFSOps().CreateDir(
   532  			fs.ctx, n, n.ChildName(p))
   533  		switch errors.Cause(err).(type) {
   534  		case data.NameExistsError:
   535  			// The child directory already exists.
   536  		case tlfhandle.WriteAccessError, libkbfs.WriteToReadonlyNodeError:
   537  			// If the child already exists, this doesn't matter.
   538  			var lookupErr error
   539  			child, _, lookupErr = fs.config.KBFSOps().Lookup(
   540  				fs.ctx, n, n.ChildName(p))
   541  			if lookupErr != nil {
   542  				return err
   543  			}
   544  		case nil:
   545  		default:
   546  			return err
   547  		}
   548  		n = child
   549  	}
   550  
   551  	return nil
   552  }
   553  
   554  func (fs *FS) ensureParentDir(filename string) error {
   555  	err := fs.mkdirAll(path.Dir(filename), 0755)
   556  	if err != nil && !os.IsExist(err) {
   557  		switch errors.Cause(err).(type) {
   558  		case tlfhandle.WriteAccessError, libkbfs.WriteToReadonlyNodeError:
   559  			// We're not allowed to create any of the parent
   560  			// directories automatically, so give back a proper
   561  			// isNotExist error.
   562  			fs.log.CDebugf(fs.ctx, "ensureParentDir: "+
   563  				"can't mkdir all due to permission error %+v", err)
   564  			return os.ErrNotExist
   565  		default:
   566  			return err
   567  		}
   568  	}
   569  	return nil
   570  }
   571  
   572  type onFsEmpty bool
   573  
   574  const (
   575  	onFsEmptyErrNotExist     onFsEmpty = true
   576  	onFsEmptyErrNotSupported onFsEmpty = false
   577  )
   578  
   579  // chooseErrorIfEmpty checks if fs is empty, and returns an error if it is.
   580  // Based on onFsEmpty, it returns either os.ErrNotExist or a custom error. This
   581  // is useful for operations like Stat and allows caller to treat lookups in an
   582  // empty FS as not exist, as they should.
   583  func (fs *FS) chooseErrorIfEmpty(onFsEmpty onFsEmpty) error {
   584  	if fs.empty && onFsEmpty == onFsEmptyErrNotExist {
   585  		return os.ErrNotExist
   586  	} else if fs.empty {
   587  		return errors.New("Not supported for an empty TLF")
   588  	}
   589  	return nil
   590  }
   591  
   592  // OpenFile implements the billy.Filesystem interface for FS.
   593  func (fs *FS) OpenFile(filename string, flag int, perm os.FileMode) (
   594  	f billy.File, err error) {
   595  	fs.log.CDebugf(
   596  		fs.ctx, "OpenFile %s, flag=%d, perm=%o",
   597  		fs.PathForLogging(filename), flag, perm)
   598  	defer func() {
   599  		fs.deferLog.CDebugf(fs.ctx, "OpenFile done: %+v", err)
   600  		err = translateErr(err)
   601  	}()
   602  
   603  	if err := fs.chooseErrorIfEmpty(flag&os.O_CREATE == 0); err != nil {
   604  		return nil, err
   605  	}
   606  
   607  	err = fs.ensureParentDir(filename)
   608  	if err != nil {
   609  		return nil, err
   610  	}
   611  
   612  	n, ei, err := fs.lookupOrCreateEntry(filename, flag, perm)
   613  	if err != nil {
   614  		return nil, err
   615  	}
   616  
   617  	// Make sure this is a file.
   618  	if !ei.Type.IsFile() {
   619  		return nil, errors.Errorf("%s is not a file", filename)
   620  	}
   621  
   622  	if flag&os.O_TRUNC != 0 {
   623  		err := fs.config.KBFSOps().Truncate(fs.ctx, n, 0)
   624  		if err != nil {
   625  			return nil, err
   626  		}
   627  	}
   628  
   629  	offset := int64(0)
   630  	if flag&os.O_APPEND != 0 {
   631  		if ei.Size >= uint64(1<<63) {
   632  			return nil, errors.New("offset too large")
   633  		}
   634  		offset = int64(ei.Size)
   635  	}
   636  
   637  	return &File{
   638  		fs:       fs,
   639  		filename: filename,
   640  		node:     n,
   641  		readOnly: flag == os.O_RDONLY,
   642  		offset:   offset,
   643  	}, nil
   644  }
   645  
   646  // Create implements the billy.Filesystem interface for FS.
   647  func (fs *FS) Create(filename string) (billy.File, error) {
   648  	return fs.OpenFile(filename, os.O_CREATE, 0600)
   649  }
   650  
   651  // Open implements the billy.Filesystem interface for FS.
   652  func (fs *FS) Open(filename string) (billy.File, error) {
   653  	return fs.OpenFile(filename, os.O_RDONLY, 0600)
   654  }
   655  
   656  func (fs *FS) makeFileInfo(
   657  	ei data.EntryInfo, node libkbfs.Node, name string) os.FileInfo {
   658  	if IsFastModeEnabled(fs.ctx) {
   659  		return &FileInfoFast{
   660  			name: name,
   661  			ei:   ei,
   662  		}
   663  	}
   664  	return &FileInfo{
   665  		fs:   fs,
   666  		ei:   ei,
   667  		node: node,
   668  		name: name,
   669  	}
   670  }
   671  
   672  // Stat implements the billy.Filesystem interface for FS.
   673  func (fs *FS) Stat(filename string) (fi os.FileInfo, err error) {
   674  	fs.log.CDebugf(fs.ctx, "Stat %s", fs.PathForLogging(filename))
   675  	defer func() {
   676  		fs.deferLog.CDebugf(fs.ctx, "Stat done: %+v", err)
   677  		err = translateErr(err)
   678  	}()
   679  
   680  	if fs.empty && (filename == "" || filename == ".") {
   681  		// We can't just uncondionally use FileInfoFast here as that'd result
   682  		// in WritePerm unset for non-existent TLFs.
   683  		return fs.makeFileInfo(data.EntryInfo{
   684  			Type: data.Dir,
   685  		}, nil, filename), nil
   686  	} else if err := fs.chooseErrorIfEmpty(onFsEmptyErrNotExist); err != nil {
   687  		return nil, err
   688  	}
   689  
   690  	n, ei, err := fs.lookupOrCreateEntry(filename, os.O_RDONLY, 0)
   691  	if err != nil {
   692  		return nil, err
   693  	}
   694  
   695  	return fs.makeFileInfo(ei, n, n.GetBasename().Plaintext()), nil
   696  }
   697  
   698  // Rename implements the billy.Filesystem interface for FS.
   699  func (fs *FS) Rename(oldpath, newpath string) (err error) {
   700  	fs.log.CDebugf(fs.ctx, "Rename %s -> %s",
   701  		fs.PathForLogging(oldpath), fs.PathForLogging(newpath))
   702  	defer func() {
   703  		fs.deferLog.CDebugf(fs.ctx, "Rename done: %+v", err)
   704  		err = translateErr(err)
   705  	}()
   706  
   707  	if err := fs.chooseErrorIfEmpty(onFsEmptyErrNotSupported); err != nil {
   708  		return err
   709  	}
   710  
   711  	err = fs.mkdirAll(path.Dir(newpath), 0755)
   712  	if err != nil && !os.IsExist(err) {
   713  		return err
   714  	}
   715  
   716  	oldParent, _, oldBase, err := fs.lookupParent(oldpath)
   717  	if err != nil {
   718  		return err
   719  	}
   720  
   721  	newParent, _, newBase, err := fs.lookupParent(newpath)
   722  	if err != nil {
   723  		return err
   724  	}
   725  
   726  	return fs.config.KBFSOps().Rename(
   727  		fs.ctx, oldParent, oldParent.ChildName(oldBase), newParent,
   728  		newParent.ChildName(newBase))
   729  }
   730  
   731  // Remove implements the billy.Filesystem interface for FS.
   732  func (fs *FS) Remove(filename string) (err error) {
   733  	fs.log.CDebugf(fs.ctx, "Remove %s", fs.PathForLogging(filename))
   734  	defer func() {
   735  		fs.deferLog.CDebugf(fs.ctx, "Remove done: %+v", err)
   736  		err = translateErr(err)
   737  	}()
   738  
   739  	if err := fs.chooseErrorIfEmpty(onFsEmptyErrNotSupported); err != nil {
   740  		return err
   741  	}
   742  
   743  	parent, _, base, err := fs.lookupParent(filename)
   744  	if err != nil {
   745  		return err
   746  	}
   747  
   748  	basePart := parent.ChildName(base)
   749  	_, ei, err := fs.config.KBFSOps().Lookup(fs.ctx, parent, basePart)
   750  	if err != nil {
   751  		return err
   752  	}
   753  
   754  	if ei.Type == data.Dir {
   755  		return fs.config.KBFSOps().RemoveDir(fs.ctx, parent, basePart)
   756  	}
   757  	return fs.config.KBFSOps().RemoveEntry(fs.ctx, parent, basePart)
   758  }
   759  
   760  // Join implements the billy.Filesystem interface for FS.
   761  func (fs *FS) Join(elem ...string) string {
   762  	return path.Clean(path.Join(elem...))
   763  }
   764  
   765  // TempFile implements the billy.Filesystem interface for FS.
   766  func (fs *FS) TempFile(dir, prefix string) (billy.File, error) {
   767  	if err := fs.chooseErrorIfEmpty(onFsEmptyErrNotSupported); err != nil {
   768  		return nil, err
   769  	}
   770  
   771  	// We'd have to turn off journaling to support TempFile perfectly,
   772  	// but the given uniq ID and a random number should be good
   773  	// enough.  Especially since most users will end up renaming the
   774  	// temp file before journal flushing even happens.
   775  	b := make([]byte, 8)
   776  	_, err := rand.Read(b)
   777  	if err != nil {
   778  		return nil, err
   779  	}
   780  	suffix := fs.uniqID + "-" + base64.URLEncoding.EncodeToString(b)
   781  	return fs.OpenFile(path.Join(dir, prefix+suffix),
   782  		os.O_CREATE|os.O_EXCL, 0600)
   783  }
   784  
   785  func (fs *FS) readDir(n libkbfs.Node) (fis []os.FileInfo, err error) {
   786  	children, err := fs.config.KBFSOps().GetDirChildren(fs.ctx, n)
   787  	if err != nil {
   788  		return nil, err
   789  	}
   790  
   791  	fis = make([]os.FileInfo, 0, len(children))
   792  	for name, ei := range children {
   793  		var child libkbfs.Node
   794  		if !IsFastModeEnabled(fs.ctx) { // node is not used in FileInfoFast
   795  			child, _, err = fs.config.KBFSOps().Lookup(fs.ctx, n, name)
   796  			if err != nil {
   797  				return nil, err
   798  			}
   799  		}
   800  
   801  		fis = append(fis, fs.makeFileInfo(ei, child, name.Plaintext()))
   802  	}
   803  	return fis, nil
   804  }
   805  
   806  // ReadDir implements the billy.Filesystem interface for FS.
   807  func (fs *FS) ReadDir(p string) (fis []os.FileInfo, err error) {
   808  	fs.log.CDebugf(fs.ctx, "ReadDir %s", fs.PathForLogging(p))
   809  	defer func() {
   810  		fs.deferLog.CDebugf(fs.ctx, "ReadDir done: %+v", err)
   811  		err = translateErr(err)
   812  	}()
   813  
   814  	if fs.empty && (p == "" || p == "." || p == "/") {
   815  		return nil, nil
   816  	} else if err := fs.chooseErrorIfEmpty(onFsEmptyErrNotExist); err != nil {
   817  		return nil, err
   818  	}
   819  
   820  	n, _, err := fs.lookupOrCreateEntry(p, os.O_RDONLY, 0)
   821  	if err != nil {
   822  		return nil, err
   823  	}
   824  	return fs.readDir(n)
   825  }
   826  
   827  // MkdirAll implements the billy.Filesystem interface for FS.
   828  func (fs *FS) MkdirAll(filename string, perm os.FileMode) (err error) {
   829  	fs.log.CDebugf(fs.ctx, "MkdirAll %s", fs.PathForLogging(filename))
   830  	defer func() {
   831  		fs.deferLog.CDebugf(fs.ctx, "MkdirAll done: %+v", err)
   832  	}()
   833  
   834  	if err := fs.chooseErrorIfEmpty(onFsEmptyErrNotSupported); err != nil {
   835  		return err
   836  	}
   837  
   838  	return fs.mkdirAll(filename, perm)
   839  }
   840  
   841  // Lstat implements the billy.Filesystem interface for FS.
   842  func (fs *FS) Lstat(filename string) (fi os.FileInfo, err error) {
   843  	fs.log.CDebugf(fs.ctx, "Lstat %s", fs.PathForLogging(filename))
   844  	defer func() {
   845  		fs.deferLog.CDebugf(fs.ctx, "Lstat done: %+v", err)
   846  		err = translateErr(err)
   847  	}()
   848  
   849  	if fs.empty && (filename == "" || filename == ".") {
   850  		// We can't just uncondionally use FileInfoFast here as that'd result
   851  		// in WritePerm unset for non-existent TLFs.
   852  		return fs.makeFileInfo(data.EntryInfo{
   853  			Type: data.Dir,
   854  		}, nil, filename), nil
   855  	} else if err := fs.chooseErrorIfEmpty(onFsEmptyErrNotExist); err != nil {
   856  		return nil, err
   857  	}
   858  
   859  	n, _, base, err := fs.lookupParent(filename)
   860  	if err != nil {
   861  		return nil, err
   862  	}
   863  
   864  	if base == "" {
   865  		ei, err := fs.config.KBFSOps().Stat(fs.ctx, n)
   866  		if err != nil {
   867  			return nil, err
   868  		}
   869  		return fs.makeFileInfo(ei, n, ""), nil
   870  	}
   871  
   872  	n, ei, err := fs.config.KBFSOps().Lookup(fs.ctx, n, n.ChildName(base))
   873  	if err != nil {
   874  		return nil, err
   875  	}
   876  
   877  	return fs.makeFileInfo(ei, n, base), nil
   878  }
   879  
   880  // Symlink implements the billy.Filesystem interface for FS.
   881  func (fs *FS) Symlink(target, link string) (err error) {
   882  	fs.log.CDebugf(fs.ctx, "Symlink target=%s link=%s",
   883  		fs.PathForLogging(target), fs.root.ChildName(link))
   884  	defer func() {
   885  		fs.deferLog.CDebugf(fs.ctx, "Symlink done: %+v", err)
   886  		err = translateErr(err)
   887  	}()
   888  
   889  	if err := fs.chooseErrorIfEmpty(onFsEmptyErrNotSupported); err != nil {
   890  		return err
   891  	}
   892  
   893  	err = fs.ensureParentDir(link)
   894  	if err != nil {
   895  		return err
   896  	}
   897  
   898  	n, _, base, err := fs.lookupParent(link)
   899  	if err != nil {
   900  		return err
   901  	}
   902  
   903  	_, err = fs.config.KBFSOps().CreateLink(
   904  		fs.ctx, n, n.ChildName(base), n.ChildName(target))
   905  	return err
   906  }
   907  
   908  // Readlink implements the billy.Filesystem interface for FS.
   909  func (fs *FS) Readlink(link string) (target string, err error) {
   910  	fs.log.CDebugf(fs.ctx, "Readlink %s", fs.PathForLogging(link))
   911  	defer func() {
   912  		fs.deferLog.CDebugf(fs.ctx, "Readlink done: %+v", err)
   913  		err = translateErr(err)
   914  	}()
   915  
   916  	if err := fs.chooseErrorIfEmpty(onFsEmptyErrNotExist); err != nil {
   917  		return "", err
   918  	}
   919  
   920  	n, _, base, err := fs.lookupParent(link)
   921  	if err != nil {
   922  		return "", err
   923  	}
   924  
   925  	_, ei, err := fs.config.KBFSOps().Lookup(fs.ctx, n, n.ChildName(base))
   926  	if err != nil {
   927  		return "", err
   928  	}
   929  
   930  	if ei.Type != data.Sym {
   931  		return "", errors.Errorf("%s is not a symlink", link)
   932  	}
   933  	return ei.SymPath, nil
   934  }
   935  
   936  // Chmod implements the billy.Filesystem interface for FS.
   937  func (fs *FS) Chmod(name string, mode os.FileMode) (err error) {
   938  	fs.log.CDebugf(fs.ctx, "Chmod %s %s", fs.PathForLogging(name), mode)
   939  	defer func() {
   940  		fs.deferLog.CDebugf(fs.ctx, "Chmod done: %+v", err)
   941  		err = translateErr(err)
   942  	}()
   943  
   944  	if err := fs.chooseErrorIfEmpty(onFsEmptyErrNotSupported); err != nil {
   945  		return err
   946  	}
   947  
   948  	n, _, err := fs.lookupOrCreateEntry(name, os.O_RDONLY, 0)
   949  	if err != nil {
   950  		return err
   951  	}
   952  
   953  	isExec := (mode & 0100) != 0
   954  	return fs.config.KBFSOps().SetEx(fs.ctx, n, isExec)
   955  }
   956  
   957  // Lchown implements the billy.Filesystem interface for FS.
   958  func (fs *FS) Lchown(name string, uid, gid int) error {
   959  	// KBFS doesn't support ownership changes.
   960  	fs.log.CDebugf(fs.ctx, "Ignoring Lchown %s %d %d",
   961  		fs.PathForLogging(name), uid, gid)
   962  	return nil
   963  }
   964  
   965  // Chown implements the billy.Filesystem interface for FS.
   966  func (fs *FS) Chown(name string, uid, gid int) error {
   967  	// KBFS doesn't support ownership changes.
   968  	fs.log.CDebugf(fs.ctx, "Ignoring Chown %s %d %d",
   969  		fs.PathForLogging(name), uid, gid)
   970  	return nil
   971  }
   972  
   973  // Chtimes implements the billy.Filesystem interface for FS.
   974  func (fs *FS) Chtimes(name string, atime time.Time, mtime time.Time) (
   975  	err error) {
   976  	fs.log.CDebugf(fs.ctx, "Chtimes %s mtime=%s; ignoring atime=%s",
   977  		fs.PathForLogging(name), mtime, atime)
   978  	defer func() {
   979  		fs.deferLog.CDebugf(fs.ctx, "Chtimes done: %+v", err)
   980  		err = translateErr(err)
   981  	}()
   982  
   983  	if err := fs.chooseErrorIfEmpty(onFsEmptyErrNotSupported); err != nil {
   984  		return err
   985  	}
   986  
   987  	n, _, err := fs.lookupOrCreateEntry(name, os.O_RDONLY, 0)
   988  	if err != nil {
   989  		return err
   990  	}
   991  
   992  	return fs.config.KBFSOps().SetMtime(fs.ctx, n, &mtime)
   993  }
   994  
   995  // ChrootAsLibFS returns a *FS whose root is p.
   996  func (fs *FS) ChrootAsLibFS(p string) (newFS *FS, err error) {
   997  	fs.log.CDebugf(fs.ctx, "Chroot %s", fs.PathForLogging(p))
   998  	defer func() {
   999  		fs.deferLog.CDebugf(fs.ctx, "Chroot done: %+v", err)
  1000  		err = translateErr(err)
  1001  	}()
  1002  
  1003  	if p == "" || p == "." {
  1004  		return fs, nil
  1005  	}
  1006  
  1007  	if err := fs.chooseErrorIfEmpty(onFsEmptyErrNotExist); err != nil {
  1008  		return nil, err
  1009  	}
  1010  
  1011  	// lookupOrCreateEntry doesn't handle "..", so we don't have to
  1012  	// worry about someone trying to break out of the jail since this
  1013  	// lookup will fail.
  1014  	n, ei, err := fs.lookupOrCreateEntry(p, os.O_RDONLY, 0)
  1015  	if err != nil {
  1016  		return nil, err
  1017  	}
  1018  
  1019  	return &FS{
  1020  		ctx: fs.ctx,
  1021  		fsInner: &fsInner{
  1022  			config:   fs.config,
  1023  			root:     n,
  1024  			rootInfo: ei,
  1025  			h:        fs.h,
  1026  			subdir:   path.Clean(path.Join(fs.subdir, p)),
  1027  			uniqID:   fs.uniqID,
  1028  			log:      fs.log,
  1029  			deferLog: fs.deferLog,
  1030  
  1031  			// Original lock namespace plus '/' plus the subdir.
  1032  			lockNamespace: bytes.Join(
  1033  				[][]byte{fs.lockNamespace, []byte(p)}, []byte{'/'}),
  1034  			priority: fs.priority,
  1035  			events:   make(map[chan<- FSEvent]bool),
  1036  		},
  1037  	}, nil
  1038  }
  1039  
  1040  // Chroot implements the billy.Filesystem interface for FS.
  1041  func (fs *FS) Chroot(p string) (newFS billy.Filesystem, err error) {
  1042  	return fs.ChrootAsLibFS(p)
  1043  }
  1044  
  1045  // Root implements the billy.Filesystem interface for FS.
  1046  func (fs *FS) Root() string {
  1047  	return path.Join(fs.h.GetCanonicalPath(), fs.subdir)
  1048  }
  1049  
  1050  // SyncAll syncs any outstanding buffered writes to the KBFS journal.
  1051  func (fs *FS) SyncAll() error {
  1052  	if err := fs.chooseErrorIfEmpty(onFsEmptyErrNotSupported); err != nil {
  1053  		return err
  1054  	}
  1055  	return fs.config.KBFSOps().SyncAll(fs.ctx, fs.root.GetFolderBranch())
  1056  }
  1057  
  1058  // Config returns the underlying Config object of this FS.
  1059  func (fs *FS) Config() libkbfs.Config {
  1060  	return fs.config
  1061  }
  1062  
  1063  // SetLockNamespace sets the namespace used in locking.
  1064  func (fs *FS) SetLockNamespace(lockNamespace []byte) {
  1065  	fs.lockNamespace = make([]byte, len(lockNamespace))
  1066  	copy(fs.lockNamespace, lockNamespace)
  1067  }
  1068  
  1069  // GetLockNamespace returns the namespace used in locking.
  1070  func (fs *FS) GetLockNamespace() (lockNamespace []byte) {
  1071  	return fs.lockNamespace
  1072  }
  1073  
  1074  // SubscribeToEvents causes *File objects constructed from this *FS to send
  1075  // events to the channel at beginning of Lock and Unlock. The send is done
  1076  // blockingly so caller needs to drain the channel properly or make it buffered
  1077  // with enough size.
  1078  func (fs *FS) SubscribeToEvents(ch chan<- FSEvent) {
  1079  	fs.eventsLock.Lock()
  1080  	defer fs.eventsLock.Unlock()
  1081  	fs.events[ch] = true
  1082  }
  1083  
  1084  // UnsubscribeToEvents stops *File objects constructed from this *FS from
  1085  // sending events to ch. It also closes ch.
  1086  func (fs *FS) UnsubscribeToEvents(ch chan<- FSEvent) {
  1087  	fs.eventsLock.Lock()
  1088  	defer fs.eventsLock.Unlock()
  1089  	delete(fs.events, ch)
  1090  	close(ch)
  1091  }
  1092  
  1093  func (fs *FS) sendEvents(e FSEvent) {
  1094  	fs.eventsLock.RLock()
  1095  	defer fs.eventsLock.RUnlock()
  1096  	for ch := range fs.events {
  1097  		ch <- e
  1098  	}
  1099  }
  1100  
  1101  // WithContext returns a *FS based on fs, with its ctx replaced with ctx.
  1102  func (fs *FS) WithContext(ctx context.Context) *FS {
  1103  	return &FS{
  1104  		ctx:     ctx,
  1105  		fsInner: fs.fsInner,
  1106  	}
  1107  }
  1108  
  1109  // ToHTTPFileSystem calls fs.WithCtx with ctx to create a *FS with the new ctx,
  1110  // and returns a wrapper around it that satisfies the http.FileSystem
  1111  // interface.
  1112  func (fs *FS) ToHTTPFileSystem(ctx context.Context) http.FileSystem {
  1113  	return httpFileSystem{fs: fs.WithContext(ctx)}
  1114  }
  1115  
  1116  // RootNode returns the Node of the root directory of this FS.
  1117  func (fs *FS) RootNode() libkbfs.Node {
  1118  	return fs.root
  1119  }
  1120  
  1121  // Handle returns the TLF handle corresponding to this FS.
  1122  func (fs *FS) Handle() *tlfhandle.Handle {
  1123  	return fs.h
  1124  }
  1125  
  1126  type folderHandleChangeObserver func()
  1127  
  1128  func (folderHandleChangeObserver) LocalChange(
  1129  	context.Context, libkbfs.Node, libkbfs.WriteRange) {
  1130  }
  1131  func (folderHandleChangeObserver) BatchChanges(
  1132  	context.Context, []libkbfs.NodeChange, []libkbfs.NodeID) {
  1133  }
  1134  func (o folderHandleChangeObserver) TlfHandleChange(
  1135  	context.Context, *tlfhandle.Handle) {
  1136  	o()
  1137  }
  1138  
  1139  // SubscribeToObsolete returns a channel that will be closed when this *FS
  1140  // reaches obsolescence, meaning if user of this object caches it for long term
  1141  // use, it should invalide this entry and create a new one using NewFS.
  1142  func (fs *FS) SubscribeToObsolete() (<-chan struct{}, error) {
  1143  	if err := fs.chooseErrorIfEmpty(onFsEmptyErrNotSupported); err != nil {
  1144  		return nil, err
  1145  	}
  1146  
  1147  	c := make(chan struct{})
  1148  	var once sync.Once
  1149  	onHandleChange := folderHandleChangeObserver(
  1150  		func() { once.Do(func() { close(c) }) })
  1151  	if err := fs.config.Notifier().RegisterForChanges(
  1152  		[]data.FolderBranch{fs.root.GetFolderBranch()},
  1153  		onHandleChange); err != nil {
  1154  		return nil, err
  1155  	}
  1156  	return c, nil
  1157  }
  1158  
  1159  // IsEmpty returns true if this is a faked-out empty TLF.
  1160  func (fs *FS) IsEmpty() bool {
  1161  	return fs.empty
  1162  }