github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/dentry.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 fs
    16  
    17  import (
    18  	"sort"
    19  
    20  	"github.com/SagerNet/gvisor/pkg/sentry/device"
    21  )
    22  
    23  // DentAttr is the metadata of a directory entry. It is a subset of StableAttr.
    24  //
    25  // +stateify savable
    26  type DentAttr struct {
    27  	// Type is the InodeType of an Inode.
    28  	Type InodeType
    29  
    30  	// InodeID uniquely identifies an Inode on a device.
    31  	InodeID uint64
    32  }
    33  
    34  // GenericDentAttr returns a generic DentAttr where:
    35  //
    36  // Type == nt
    37  // InodeID == the inode id of a new inode on device.
    38  func GenericDentAttr(nt InodeType, device *device.Device) DentAttr {
    39  	return DentAttr{
    40  		Type:    nt,
    41  		InodeID: device.NextIno(),
    42  	}
    43  }
    44  
    45  // DentrySerializer serializes a directory entry.
    46  type DentrySerializer interface {
    47  	// CopyOut serializes a directory entry based on its name and attributes.
    48  	CopyOut(name string, attributes DentAttr) error
    49  
    50  	// Written returns the number of bytes written.
    51  	Written() int
    52  }
    53  
    54  // CollectEntriesSerializer copies DentAttrs to Entries. The order in
    55  // which entries are encountered is preserved in Order.
    56  type CollectEntriesSerializer struct {
    57  	Entries map[string]DentAttr
    58  	Order   []string
    59  }
    60  
    61  // CopyOut implements DentrySerializer.CopyOut.
    62  func (c *CollectEntriesSerializer) CopyOut(name string, attr DentAttr) error {
    63  	if c.Entries == nil {
    64  		c.Entries = make(map[string]DentAttr)
    65  	}
    66  	c.Entries[name] = attr
    67  	c.Order = append(c.Order, name)
    68  	return nil
    69  }
    70  
    71  // Written implements DentrySerializer.Written.
    72  func (c *CollectEntriesSerializer) Written() int {
    73  	return len(c.Entries)
    74  }
    75  
    76  // DirCtx is used in FileOperations.IterateDir to emit directory entries. It is
    77  // not thread-safe.
    78  type DirCtx struct {
    79  	// Serializer is used to serialize the node attributes.
    80  	Serializer DentrySerializer
    81  
    82  	// attrs are DentAttrs
    83  	attrs map[string]DentAttr
    84  
    85  	// DirCursor is the directory cursor.
    86  	DirCursor *string
    87  }
    88  
    89  // DirEmit is called for each directory entry.
    90  func (c *DirCtx) DirEmit(name string, attr DentAttr) error {
    91  	if c.Serializer != nil {
    92  		if err := c.Serializer.CopyOut(name, attr); err != nil {
    93  			return err
    94  		}
    95  	}
    96  	if c.attrs == nil {
    97  		c.attrs = make(map[string]DentAttr)
    98  	}
    99  	c.attrs[name] = attr
   100  	return nil
   101  }
   102  
   103  // DentAttrs returns a map of DentAttrs corresponding to the emitted directory
   104  // entries.
   105  func (c *DirCtx) DentAttrs() map[string]DentAttr {
   106  	if c.attrs == nil {
   107  		c.attrs = make(map[string]DentAttr)
   108  	}
   109  	return c.attrs
   110  }
   111  
   112  // GenericReaddir serializes DentAttrs based on a SortedDentryMap that must
   113  // contain _all_ up-to-date DentAttrs under a directory. If ctx.DirCursor is
   114  // not nil, it is updated to the name of the last DentAttr that was
   115  // successfully serialized.
   116  //
   117  // Returns the number of entries serialized.
   118  func GenericReaddir(ctx *DirCtx, s *SortedDentryMap) (int, error) {
   119  	// Retrieve the next directory entries.
   120  	var names []string
   121  	var entries map[string]DentAttr
   122  	if ctx.DirCursor != nil {
   123  		names, entries = s.GetNext(*ctx.DirCursor)
   124  	} else {
   125  		names, entries = s.GetAll()
   126  	}
   127  
   128  	// Try to serialize each entry.
   129  	var serialized int
   130  	for _, name := range names {
   131  		// Skip "" per POSIX. Skip "." and ".." which will be added by Dirent.Readdir.
   132  		if name == "" || name == "." || name == ".." {
   133  			continue
   134  		}
   135  
   136  		// Emit the directory entry.
   137  		if err := ctx.DirEmit(name, entries[name]); err != nil {
   138  			// Return potentially a partial serialized count.
   139  			return serialized, err
   140  		}
   141  
   142  		// We successfully serialized this entry.
   143  		serialized++
   144  
   145  		// Update the cursor with the name of the entry last serialized.
   146  		if ctx.DirCursor != nil {
   147  			*ctx.DirCursor = name
   148  		}
   149  	}
   150  
   151  	// Everything was serialized.
   152  	return serialized, nil
   153  }
   154  
   155  // SortedDentryMap is a sorted map of names and fs.DentAttr entries.
   156  //
   157  // +stateify savable
   158  type SortedDentryMap struct {
   159  	// names is always kept in sorted-order.
   160  	names []string
   161  
   162  	// entries maps names to fs.DentAttrs.
   163  	entries map[string]DentAttr
   164  }
   165  
   166  // NewSortedDentryMap maintains entries in name sorted order.
   167  func NewSortedDentryMap(entries map[string]DentAttr) *SortedDentryMap {
   168  	s := &SortedDentryMap{
   169  		names:   make([]string, 0, len(entries)),
   170  		entries: entries,
   171  	}
   172  	// Don't allow s.entries to be nil, because nil maps arn't Saveable.
   173  	if s.entries == nil {
   174  		s.entries = make(map[string]DentAttr)
   175  	}
   176  
   177  	// Collect names from entries and sort them.
   178  	for name := range s.entries {
   179  		s.names = append(s.names, name)
   180  	}
   181  	sort.Strings(s.names)
   182  	return s
   183  }
   184  
   185  // GetAll returns all names and entries in s. Callers should not modify the
   186  // returned values.
   187  func (s *SortedDentryMap) GetAll() ([]string, map[string]DentAttr) {
   188  	return s.names, s.entries
   189  }
   190  
   191  // GetNext returns names after cursor in s and all entries.
   192  func (s *SortedDentryMap) GetNext(cursor string) ([]string, map[string]DentAttr) {
   193  	i := sort.SearchStrings(s.names, cursor)
   194  	if i == len(s.names) {
   195  		return nil, s.entries
   196  	}
   197  
   198  	// Return everything strictly after the cursor.
   199  	if s.names[i] == cursor {
   200  		i++
   201  	}
   202  	return s.names[i:], s.entries
   203  }
   204  
   205  // Add adds an entry with the given name to the map, preserving sort order.  If
   206  // name already exists in the map, its entry will be overwritten.
   207  func (s *SortedDentryMap) Add(name string, entry DentAttr) {
   208  	if _, ok := s.entries[name]; !ok {
   209  		// Map does not yet contain an entry with this name.  We must
   210  		// insert it in s.names at the appropriate spot.
   211  		i := sort.SearchStrings(s.names, name)
   212  		s.names = append(s.names, "")
   213  		copy(s.names[i+1:], s.names[i:])
   214  		s.names[i] = name
   215  	}
   216  	s.entries[name] = entry
   217  }
   218  
   219  // Remove removes an entry with the given name from the map, preserving sort order.
   220  func (s *SortedDentryMap) Remove(name string) {
   221  	if _, ok := s.entries[name]; !ok {
   222  		return
   223  	}
   224  	i := sort.SearchStrings(s.names, name)
   225  	copy(s.names[i:], s.names[i+1:])
   226  	s.names = s.names[:len(s.names)-1]
   227  	delete(s.entries, name)
   228  }
   229  
   230  // Contains reports whether the map contains an entry with the given name.
   231  func (s *SortedDentryMap) Contains(name string) bool {
   232  	_, ok := s.entries[name]
   233  	return ok
   234  }