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 }