github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/p9/file.go (about)

     1  // Copyright 2018 The gVisor Authors.
     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 p9
    16  
    17  import (
    18  	"errors"
    19  
    20  	"golang.org/x/sys/unix"
    21  	"github.com/SagerNet/gvisor/pkg/fd"
    22  )
    23  
    24  // Attacher is provided by the server.
    25  type Attacher interface {
    26  	// Attach returns a new File.
    27  	//
    28  	// The client-side attach will be translate to a series of walks from
    29  	// the file returned by this Attach call.
    30  	Attach() (File, error)
    31  }
    32  
    33  // File is a set of operations corresponding to a single node.
    34  //
    35  // Note that on the server side, the server logic places constraints on
    36  // concurrent operations to make things easier. This may reduce the need for
    37  // complex, error-prone locking and logic in the backend. These are documented
    38  // for each method.
    39  //
    40  // There are three different types of guarantees provided:
    41  //
    42  // none: There is no concurrency guarantee. The method may be invoked
    43  // concurrently with any other method on any other file.
    44  //
    45  // read: The method is guaranteed to be exclusive of any write or global
    46  // operation that is mutating the state of the directory tree starting at this
    47  // node. For example, this means creating new files, symlinks, directories or
    48  // renaming a directory entry (or renaming in to this target), but the method
    49  // may be called concurrently with other read methods.
    50  //
    51  // write: The method is guaranteed to be exclusive of any read, write or global
    52  // operation that is mutating the state of the directory tree starting at this
    53  // node, as described in read above. There may however, be other write
    54  // operations executing concurrently on other components in the directory tree.
    55  //
    56  // global: The method is guaranteed to be exclusive of any read, write or
    57  // global operation.
    58  type File interface {
    59  	// Walk walks to the path components given in names.
    60  	//
    61  	// Walk returns QIDs in the same order that the names were passed in.
    62  	//
    63  	// An empty list of arguments should return a copy of the current file.
    64  	//
    65  	// On the server, Walk has a read concurrency guarantee.
    66  	Walk(names []string) ([]QID, File, error)
    67  
    68  	// WalkGetAttr walks to the next file and returns its maximal set of
    69  	// attributes.
    70  	//
    71  	// Server-side p9.Files may return unix.ENOSYS to indicate that Walk
    72  	// and GetAttr should be used separately to satisfy this request.
    73  	//
    74  	// On the server, WalkGetAttr has a read concurrency guarantee.
    75  	WalkGetAttr([]string) ([]QID, File, AttrMask, Attr, error)
    76  
    77  	// MultiGetAttr batches up multiple calls to GetAttr(). names is a list of
    78  	// path components similar to Walk(). If the first component name is empty,
    79  	// the current file is stat'd and included in the results. If the walk reaches
    80  	// a file that doesn't exist or not a directory, MultiGetAttr returns the
    81  	// partial result with no error.
    82  	//
    83  	// On the server, MultiGetAttr has a read concurrency guarantee.
    84  	MultiGetAttr(names []string) ([]FullStat, error)
    85  
    86  	// StatFS returns information about the file system associated with
    87  	// this file.
    88  	//
    89  	// On the server, StatFS has no concurrency guarantee.
    90  	StatFS() (FSStat, error)
    91  
    92  	// GetAttr returns attributes of this node.
    93  	//
    94  	// On the server, GetAttr has a read concurrency guarantee.
    95  	GetAttr(req AttrMask) (QID, AttrMask, Attr, error)
    96  
    97  	// SetAttr sets attributes on this node.
    98  	//
    99  	// On the server, SetAttr has a write concurrency guarantee.
   100  	SetAttr(valid SetAttrMask, attr SetAttr) error
   101  
   102  	// GetXattr returns extended attributes of this node.
   103  	//
   104  	// Size indicates the size of the buffer that has been allocated to hold the
   105  	// attribute value. If the value is larger than size, implementations may
   106  	// return ERANGE to indicate that the buffer is too small, but they are also
   107  	// free to ignore the hint entirely (i.e. the value returned may be larger
   108  	// than size). All size checking is done independently at the syscall layer.
   109  	//
   110  	// On the server, GetXattr has a read concurrency guarantee.
   111  	GetXattr(name string, size uint64) (string, error)
   112  
   113  	// SetXattr sets extended attributes on this node.
   114  	//
   115  	// On the server, SetXattr has a write concurrency guarantee.
   116  	SetXattr(name, value string, flags uint32) error
   117  
   118  	// ListXattr lists the names of the extended attributes on this node.
   119  	//
   120  	// Size indicates the size of the buffer that has been allocated to hold the
   121  	// attribute list. If the list would be larger than size, implementations may
   122  	// return ERANGE to indicate that the buffer is too small, but they are also
   123  	// free to ignore the hint entirely (i.e. the value returned may be larger
   124  	// than size). All size checking is done independently at the syscall layer.
   125  	//
   126  	// On the server, ListXattr has a read concurrency guarantee.
   127  	ListXattr(size uint64) (map[string]struct{}, error)
   128  
   129  	// RemoveXattr removes extended attributes on this node.
   130  	//
   131  	// On the server, RemoveXattr has a write concurrency guarantee.
   132  	RemoveXattr(name string) error
   133  
   134  	// Allocate allows the caller to directly manipulate the allocated disk space
   135  	// for the file. See fallocate(2) for more details.
   136  	Allocate(mode AllocateMode, offset, length uint64) error
   137  
   138  	// Close is called when all references are dropped on the server side,
   139  	// and Close should be called by the client to drop all references.
   140  	//
   141  	// For server-side implementations of Close, the error is ignored.
   142  	//
   143  	// Close must be called even when Open has not been called.
   144  	//
   145  	// On the server, Close has no concurrency guarantee.
   146  	Close() error
   147  
   148  	// SetAttrClose is the equivalent of calling SetAttr() followed by Close().
   149  	// This can be used to set file times before closing the file in a single
   150  	// operation.
   151  	//
   152  	// On the server, SetAttr has a write concurrency guarantee.
   153  	// On the server, Close has no concurrency guarantee.
   154  	SetAttrClose(valid SetAttrMask, attr SetAttr) error
   155  
   156  	// Open must be called prior to using Read, Write or Readdir. Once Open
   157  	// is called, some operations, such as Walk, will no longer work.
   158  	//
   159  	// On the client, Open should be called only once. The fd return is
   160  	// optional, and may be nil.
   161  	//
   162  	// On the server, Open has a read concurrency guarantee. If an *fd.FD
   163  	// is provided, ownership now belongs to the caller. Open is guaranteed
   164  	// to be called only once.
   165  	//
   166  	// N.B. The server must resolve any lazy paths when open is called.
   167  	// After this point, read and write may be called on files with no
   168  	// deletion check, so resolving in the data path is not viable.
   169  	Open(flags OpenFlags) (*fd.FD, QID, uint32, error)
   170  
   171  	// Read reads from this file. Open must be called first.
   172  	//
   173  	// This may return io.EOF in addition to unix.Errno values.
   174  	//
   175  	// On the server, ReadAt has a read concurrency guarantee. See Open for
   176  	// additional requirements regarding lazy path resolution.
   177  	ReadAt(p []byte, offset uint64) (int, error)
   178  
   179  	// Write writes to this file. Open must be called first.
   180  	//
   181  	// This may return io.EOF in addition to unix.Errno values.
   182  	//
   183  	// On the server, WriteAt has a read concurrency guarantee. See Open
   184  	// for additional requirements regarding lazy path resolution.
   185  	WriteAt(p []byte, offset uint64) (int, error)
   186  
   187  	// FSync syncs this node. Open must be called first.
   188  	//
   189  	// On the server, FSync has a read concurrency guarantee.
   190  	FSync() error
   191  
   192  	// Create creates a new regular file and opens it according to the
   193  	// flags given. This file is already Open.
   194  	//
   195  	// N.B. On the client, the returned file is a reference to the current
   196  	// file, which now represents the created file. This is not the case on
   197  	// the server. These semantics are very subtle and can easily lead to
   198  	// bugs, but are a consequence of the 9P create operation.
   199  	//
   200  	// See p9.File.Open for a description of *fd.FD.
   201  	//
   202  	// On the server, Create has a write concurrency guarantee.
   203  	Create(name string, flags OpenFlags, permissions FileMode, uid UID, gid GID) (*fd.FD, File, QID, uint32, error)
   204  
   205  	// Mkdir creates a subdirectory.
   206  	//
   207  	// On the server, Mkdir has a write concurrency guarantee.
   208  	Mkdir(name string, permissions FileMode, uid UID, gid GID) (QID, error)
   209  
   210  	// Symlink makes a new symbolic link.
   211  	//
   212  	// On the server, Symlink has a write concurrency guarantee.
   213  	Symlink(oldName string, newName string, uid UID, gid GID) (QID, error)
   214  
   215  	// Link makes a new hard link.
   216  	//
   217  	// On the server, Link has a write concurrency guarantee.
   218  	Link(target File, newName string) error
   219  
   220  	// Mknod makes a new device node.
   221  	//
   222  	// On the server, Mknod has a write concurrency guarantee.
   223  	Mknod(name string, mode FileMode, major uint32, minor uint32, uid UID, gid GID) (QID, error)
   224  
   225  	// Rename renames the file.
   226  	//
   227  	// Rename will never be called on the server, and RenameAt will always
   228  	// be used instead.
   229  	Rename(newDir File, newName string) error
   230  
   231  	// RenameAt renames a given file to a new name in a potentially new
   232  	// directory.
   233  	//
   234  	// oldName must be a name relative to this file, which must be a
   235  	// directory. newName is a name relative to newDir.
   236  	//
   237  	// On the server, RenameAt has a global concurrency guarantee.
   238  	RenameAt(oldName string, newDir File, newName string) error
   239  
   240  	// UnlinkAt the given named file.
   241  	//
   242  	// name must be a file relative to this directory.
   243  	//
   244  	// Flags are implementation-specific (e.g. O_DIRECTORY), but are
   245  	// generally Linux unlinkat(2) flags.
   246  	//
   247  	// On the server, UnlinkAt has a write concurrency guarantee.
   248  	UnlinkAt(name string, flags uint32) error
   249  
   250  	// Readdir reads directory entries.
   251  	//
   252  	// This may return io.EOF in addition to unix.Errno values.
   253  	//
   254  	// On the server, Readdir has a read concurrency guarantee.
   255  	Readdir(offset uint64, count uint32) ([]Dirent, error)
   256  
   257  	// Readlink reads the link target.
   258  	//
   259  	// On the server, Readlink has a read concurrency guarantee.
   260  	Readlink() (string, error)
   261  
   262  	// Flush is called prior to Close.
   263  	//
   264  	// Whereas Close drops all references to the file, Flush cleans up the
   265  	// file state. Behavior is implementation-specific.
   266  	//
   267  	// Flush is not related to flush(9p). Flush is an extension to 9P2000.L,
   268  	// see version.go.
   269  	//
   270  	// On the server, Flush has a read concurrency guarantee.
   271  	Flush() error
   272  
   273  	// Connect establishes a new host-socket backed connection with a
   274  	// socket. A File does not need to be opened before it can be connected
   275  	// and it can be connected to multiple times resulting in a unique
   276  	// *fd.FD each time. In addition, the lifetime of the *fd.FD is
   277  	// independent from the lifetime of the p9.File and must be managed by
   278  	// the caller.
   279  	//
   280  	// The returned FD must be non-blocking.
   281  	//
   282  	// Flags indicates the requested type of socket.
   283  	//
   284  	// On the server, Connect has a read concurrency guarantee.
   285  	Connect(flags ConnectFlags) (*fd.FD, error)
   286  
   287  	// Renamed is called when this node is renamed.
   288  	//
   289  	// This may not fail. The file will hold a reference to its parent
   290  	// within the p9 package, and is therefore safe to use for the lifetime
   291  	// of this File (until Close is called).
   292  	//
   293  	// This method should not be called by clients, who should use the
   294  	// relevant Rename methods. (Although the method will be a no-op.)
   295  	//
   296  	// On the server, Renamed has a global concurrency guarantee.
   297  	Renamed(newDir File, newName string)
   298  }
   299  
   300  // DefaultWalkGetAttr implements File.WalkGetAttr to return ENOSYS for server-side Files.
   301  type DefaultWalkGetAttr struct{}
   302  
   303  // WalkGetAttr implements File.WalkGetAttr.
   304  func (DefaultWalkGetAttr) WalkGetAttr([]string) ([]QID, File, AttrMask, Attr, error) {
   305  	return nil, nil, AttrMask{}, Attr{}, unix.ENOSYS
   306  }
   307  
   308  // DisallowClientCalls panics if a client-only function is called.
   309  type DisallowClientCalls struct{}
   310  
   311  // SetAttrClose implements File.SetAttrClose.
   312  func (DisallowClientCalls) SetAttrClose(SetAttrMask, SetAttr) error {
   313  	panic("SetAttrClose should not be called on the server")
   314  }
   315  
   316  // DisallowServerCalls panics if a server-only function is called.
   317  type DisallowServerCalls struct{}
   318  
   319  // Renamed implements File.Renamed.
   320  func (*DisallowServerCalls) Renamed(File, string) {
   321  	panic("Renamed should not be called on the client")
   322  }
   323  
   324  // DefaultMultiGetAttr implements File.MultiGetAttr() on top of File.
   325  func DefaultMultiGetAttr(start File, names []string) ([]FullStat, error) {
   326  	stats := make([]FullStat, 0, len(names))
   327  	parent := start
   328  	mask := AttrMaskAll()
   329  	for i, name := range names {
   330  		if len(name) == 0 && i == 0 {
   331  			qid, valid, attr, err := parent.GetAttr(mask)
   332  			if err != nil {
   333  				return nil, err
   334  			}
   335  			stats = append(stats, FullStat{
   336  				QID:   qid,
   337  				Valid: valid,
   338  				Attr:  attr,
   339  			})
   340  			continue
   341  		}
   342  		qids, child, valid, attr, err := parent.WalkGetAttr([]string{name})
   343  		if parent != start {
   344  			_ = parent.Close()
   345  		}
   346  		if err != nil {
   347  			if errors.Is(err, unix.ENOENT) {
   348  				return stats, nil
   349  			}
   350  			return nil, err
   351  		}
   352  		stats = append(stats, FullStat{
   353  			QID:   qids[0],
   354  			Valid: valid,
   355  			Attr:  attr,
   356  		})
   357  		if attr.Mode.FileType() != ModeDirectory {
   358  			// Doesn't need to continue if entry is not a dir. Including symlinks
   359  			// that cannot be followed.
   360  			_ = child.Close()
   361  			break
   362  		}
   363  		parent = child
   364  	}
   365  	if parent != start {
   366  		_ = parent.Close()
   367  	}
   368  	return stats, nil
   369  }