github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/syscalls/linux/sys_getdents.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 linux 16 17 import ( 18 "bytes" 19 "io" 20 21 "github.com/SagerNet/gvisor/pkg/abi/linux" 22 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 23 "github.com/SagerNet/gvisor/pkg/hostarch" 24 "github.com/SagerNet/gvisor/pkg/sentry/arch" 25 "github.com/SagerNet/gvisor/pkg/sentry/fs" 26 "github.com/SagerNet/gvisor/pkg/sentry/kernel" 27 "github.com/SagerNet/gvisor/pkg/syserror" 28 "github.com/SagerNet/gvisor/pkg/usermem" 29 ) 30 31 // LINT.IfChange 32 33 // Getdents implements linux syscall getdents(2) for 64bit systems. 34 func Getdents(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 35 fd := args[0].Int() 36 addr := args[1].Pointer() 37 size := int(args[2].Uint()) 38 39 minSize := int(smallestDirent(t.Arch())) 40 if size < minSize { 41 // size is smaller than smallest possible dirent. 42 return 0, nil, linuxerr.EINVAL 43 } 44 45 n, err := getdents(t, fd, addr, size, (*dirent).Serialize) 46 return n, nil, err 47 } 48 49 // Getdents64 implements linux syscall getdents64(2). 50 func Getdents64(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 51 fd := args[0].Int() 52 addr := args[1].Pointer() 53 size := int(args[2].Uint()) 54 55 minSize := int(smallestDirent64(t.Arch())) 56 if size < minSize { 57 // size is smaller than smallest possible dirent. 58 return 0, nil, linuxerr.EINVAL 59 } 60 61 n, err := getdents(t, fd, addr, size, (*dirent).Serialize64) 62 return n, nil, err 63 } 64 65 // getdents implements the core of getdents(2)/getdents64(2). 66 // f is the syscall implementation dirent serialization function. 67 func getdents(t *kernel.Task, fd int32, addr hostarch.Addr, size int, f func(*dirent, io.Writer) (int, error)) (uintptr, error) { 68 dir := t.GetFile(fd) 69 if dir == nil { 70 return 0, linuxerr.EBADF 71 } 72 defer dir.DecRef(t) 73 74 w := &usermem.IOReadWriter{ 75 Ctx: t, 76 IO: t.MemoryManager(), 77 Addr: addr, 78 Opts: usermem.IOOpts{ 79 AddressSpaceActive: true, 80 }, 81 } 82 83 ds := newDirentSerializer(f, w, t.Arch(), size) 84 rerr := dir.Readdir(t, ds) 85 86 switch err := handleIOError(t, ds.Written() > 0, rerr, syserror.ERESTARTSYS, "getdents", dir); err { 87 case nil: 88 dir.Dirent.InotifyEvent(linux.IN_ACCESS, 0) 89 return uintptr(ds.Written()), nil 90 case io.EOF: 91 return 0, nil 92 default: 93 return 0, err 94 } 95 } 96 97 // oldDirentHdr is a fixed sized header matching the fixed size fields found in 98 // the old linux dirent struct. 99 // 100 // +marshal 101 type oldDirentHdr struct { 102 Ino uint64 103 Off uint64 104 Reclen uint16 `marshal:"unaligned"` // Struct ends mid-word. 105 } 106 107 // direntHdr is a fixed sized header matching the fixed size fields found in the 108 // new linux dirent struct. 109 // 110 // +marshal 111 type direntHdr struct { 112 OldHdr oldDirentHdr 113 Typ uint8 `marshal:"unaligned"` // Struct ends mid-word. 114 } 115 116 // dirent contains the data pointed to by a new linux dirent struct. 117 type dirent struct { 118 Hdr direntHdr 119 Name []byte 120 } 121 122 // newDirent returns a dirent from an fs.InodeOperationsInfo. 123 func newDirent(width uint, name string, attr fs.DentAttr, offset uint64) *dirent { 124 d := &dirent{ 125 Hdr: direntHdr{ 126 OldHdr: oldDirentHdr{ 127 Ino: attr.InodeID, 128 Off: offset, 129 }, 130 Typ: fs.ToDirentType(attr.Type), 131 }, 132 Name: []byte(name), 133 } 134 d.Hdr.OldHdr.Reclen = d.padRec(int(width)) 135 return d 136 } 137 138 // smallestDirent returns the size of the smallest possible dirent using 139 // the old linux dirent format. 140 func smallestDirent(a arch.Context) uint { 141 d := dirent{} 142 return uint(d.Hdr.OldHdr.SizeBytes()) + a.Width() + 1 143 } 144 145 // smallestDirent64 returns the size of the smallest possible dirent using 146 // the new linux dirent format. 147 func smallestDirent64(a arch.Context) uint { 148 d := dirent{} 149 return uint(d.Hdr.SizeBytes()) + a.Width() 150 } 151 152 // padRec pads the name field until the rec length is a multiple of the width, 153 // which must be a power of 2. It returns the padded rec length. 154 func (d *dirent) padRec(width int) uint16 { 155 a := d.Hdr.SizeBytes() + len(d.Name) 156 r := (a + width) &^ (width - 1) 157 padding := r - a 158 d.Name = append(d.Name, make([]byte, padding)...) 159 return uint16(r) 160 } 161 162 // Serialize64 serializes a Dirent struct to a byte slice, keeping the new 163 // linux dirent format. Returns the number of bytes serialized or an error. 164 func (d *dirent) Serialize64(w io.Writer) (int, error) { 165 n1, err := d.Hdr.WriteTo(w) 166 if err != nil { 167 return 0, err 168 } 169 n2, err := w.Write(d.Name) 170 if err != nil { 171 return 0, err 172 } 173 return int(n1) + n2, nil 174 } 175 176 // Serialize serializes a Dirent struct to a byte slice, using the old linux 177 // dirent format. 178 // Returns the number of bytes serialized or an error. 179 func (d *dirent) Serialize(w io.Writer) (int, error) { 180 n1, err := d.Hdr.OldHdr.WriteTo(w) 181 if err != nil { 182 return 0, err 183 } 184 n2, err := w.Write(d.Name) 185 if err != nil { 186 return 0, err 187 } 188 n3, err := w.Write([]byte{d.Hdr.Typ}) 189 if err != nil { 190 return 0, err 191 } 192 return int(n1) + n2 + n3, nil 193 } 194 195 // direntSerializer implements fs.InodeOperationsInfoSerializer, serializing dirents to an 196 // io.Writer. 197 type direntSerializer struct { 198 serialize func(*dirent, io.Writer) (int, error) 199 w io.Writer 200 // width is the arch native value width. 201 width uint 202 // offset is the current dirent offset. 203 offset uint64 204 // written is the total bytes serialized. 205 written int 206 // size is the size of the buffer to serialize into. 207 size int 208 } 209 210 func newDirentSerializer(f func(d *dirent, w io.Writer) (int, error), w io.Writer, ac arch.Context, size int) *direntSerializer { 211 return &direntSerializer{ 212 serialize: f, 213 w: w, 214 width: ac.Width(), 215 size: size, 216 } 217 } 218 219 // CopyOut implements fs.InodeOperationsInfoSerializer.CopyOut. 220 // It serializes and writes the fs.DentAttr to the direntSerializer io.Writer. 221 func (ds *direntSerializer) CopyOut(name string, attr fs.DentAttr) error { 222 ds.offset++ 223 224 d := newDirent(ds.width, name, attr, ds.offset) 225 226 // Serialize dirent into a temp buffer. 227 var b bytes.Buffer 228 n, err := ds.serialize(d, &b) 229 if err != nil { 230 ds.offset-- 231 return err 232 } 233 234 // Check that we have enough room remaining to write the dirent. 235 if n > (ds.size - ds.written) { 236 ds.offset-- 237 return io.EOF 238 } 239 240 // Write out the temp buffer. 241 if _, err := b.WriteTo(ds.w); err != nil { 242 ds.offset-- 243 return err 244 } 245 246 ds.written += n 247 return nil 248 } 249 250 // Written returns the total number of bytes written. 251 func (ds *direntSerializer) Written() int { 252 return ds.written 253 } 254 255 // LINT.ThenChange(vfs2/getdents.go)