gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/sentry/syscalls/linux/sys_getdents.go (about) 1 // Copyright 2020 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 "fmt" 19 20 "gvisor.dev/gvisor/pkg/errors/linuxerr" 21 "gvisor.dev/gvisor/pkg/hostarch" 22 "gvisor.dev/gvisor/pkg/sentry/arch" 23 "gvisor.dev/gvisor/pkg/sentry/kernel" 24 "gvisor.dev/gvisor/pkg/sentry/vfs" 25 "gvisor.dev/gvisor/pkg/sync" 26 "gvisor.dev/gvisor/pkg/usermem" 27 ) 28 29 // Getdents implements Linux syscall getdents(2). 30 func Getdents(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 31 return getdents(t, args, false /* isGetdents64 */) 32 } 33 34 // Getdents64 implements Linux syscall getdents64(2). 35 func Getdents64(t *kernel.Task, sysno uintptr, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { 36 return getdents(t, args, true /* isGetdents64 */) 37 } 38 39 // DirentStructBytesWithoutName is enough to fit (struct linux_dirent) and 40 // (struct linux_dirent64) without accounting for the name parameter. 41 const DirentStructBytesWithoutName = 8 + 8 + 2 + 1 + 1 42 43 func getdents(t *kernel.Task, args arch.SyscallArguments, isGetdents64 bool) (uintptr, *kernel.SyscallControl, error) { 44 fd := args[0].Int() 45 addr := args[1].Pointer() 46 size := args[2].Int() 47 48 if size < DirentStructBytesWithoutName { 49 return 0, nil, linuxerr.EINVAL 50 } 51 52 file := t.GetFile(fd) 53 if file == nil { 54 return 0, nil, linuxerr.EBADF 55 } 56 defer file.DecRef(t) 57 58 // We want to be sure of the allowed buffer size before calling IterDirents, 59 // because this function depends on IterDirents saving state of which dirent 60 // was the last one that was successfully operated on. 61 allowedSize, err := t.MemoryManager().EnsurePMAsExist(t, addr, int64(size), usermem.IOOpts{ 62 AddressSpaceActive: true, 63 }) 64 if allowedSize == 0 { 65 return 0, nil, err 66 } 67 68 cb := getGetdentsCallback(t, int(allowedSize), int(size), isGetdents64) 69 err = file.IterDirents(t, cb) 70 n, _ := t.CopyOutBytes(addr, cb.buf[:cb.copied]) 71 72 putGetdentsCallback(cb) 73 74 // Only report an error in case we didn't copy anything. 75 // If we did manage to give _something_ to the caller then the correct 76 // behaviour is to return success. 77 if n == 0 { 78 return 0, nil, err 79 } 80 81 return uintptr(n), nil, nil 82 } 83 84 type getdentsCallback struct { 85 t *kernel.Task 86 buf []byte 87 copied int 88 userReportedSize int 89 isGetdents64 bool 90 } 91 92 var getdentsCallbackPool = sync.Pool{ 93 New: func() any { 94 return &getdentsCallback{} 95 }, 96 } 97 98 func getGetdentsCallback(t *kernel.Task, size int, userReportedSize int, isGetdents64 bool) *getdentsCallback { 99 cb := getdentsCallbackPool.Get().(*getdentsCallback) 100 buf := cb.buf 101 if cap(buf) < size { 102 buf = make([]byte, size) 103 } else { 104 buf = buf[:size] 105 } 106 107 *cb = getdentsCallback{ 108 t: t, 109 buf: buf, 110 copied: 0, 111 userReportedSize: userReportedSize, 112 isGetdents64: isGetdents64, 113 } 114 return cb 115 } 116 117 func putGetdentsCallback(cb *getdentsCallback) { 118 cb.t = nil 119 cb.buf = cb.buf[:0] 120 getdentsCallbackPool.Put(cb) 121 } 122 123 // Handle implements vfs.IterDirentsCallback.Handle. 124 func (cb *getdentsCallback) Handle(dirent vfs.Dirent) error { 125 remaining := len(cb.buf) - cb.copied 126 if cb.isGetdents64 { 127 // struct linux_dirent64 { 128 // ino64_t d_ino; /* 64-bit inode number */ 129 // off64_t d_off; /* 64-bit offset to next structure */ 130 // unsigned short d_reclen; /* Size of this dirent */ 131 // unsigned char d_type; /* File type */ 132 // char d_name[]; /* Filename (null-terminated) */ 133 // }; 134 size := DirentStructBytesWithoutName + len(dirent.Name) 135 size = (size + 7) &^ 7 // round up to multiple of 8 136 if size > remaining { 137 // This is only needed to imitate Linux, since it writes out to the user 138 // as it's iterating over dirs. We don't do that because we can't take 139 // the mm.mappingMu while holding the filesystem mutex. 140 if cb.copied == 0 && cb.userReportedSize >= size { 141 return linuxerr.EFAULT 142 } 143 return linuxerr.EINVAL 144 } 145 buf := cb.buf[cb.copied : cb.copied+size] 146 hostarch.ByteOrder.PutUint64(buf[0:8], dirent.Ino) 147 hostarch.ByteOrder.PutUint64(buf[8:16], uint64(dirent.NextOff)) 148 hostarch.ByteOrder.PutUint16(buf[16:18], uint16(size)) 149 buf[18] = dirent.Type 150 copy(buf[19:], dirent.Name) 151 // Zero out all remaining bytes in buf, including the NUL terminator 152 // after dirent.Name. 153 bufTail := buf[19+len(dirent.Name):] 154 clear(bufTail) 155 cb.copied += size 156 } else { 157 // struct linux_dirent { 158 // unsigned long d_ino; /* Inode number */ 159 // unsigned long d_off; /* Offset to next linux_dirent */ 160 // unsigned short d_reclen; /* Length of this linux_dirent */ 161 // char d_name[]; /* Filename (null-terminated) */ 162 // /* length is actually (d_reclen - 2 - 163 // offsetof(struct linux_dirent, d_name)) */ 164 // /* 165 // char pad; // Zero padding byte 166 // char d_type; // File type (only since Linux 167 // // 2.6.4); offset is (d_reclen - 1) 168 // */ 169 // }; 170 if cb.t.Arch().Width() != 8 { 171 panic(fmt.Sprintf("unsupported sizeof(unsigned long): %d", cb.t.Arch().Width())) 172 } 173 size := DirentStructBytesWithoutName + len(dirent.Name) 174 size = (size + 7) &^ 7 // round up to multiple of sizeof(long) 175 if size > remaining { 176 if cb.copied == 0 && cb.userReportedSize >= size { 177 return linuxerr.EFAULT 178 } 179 return linuxerr.EINVAL 180 } 181 buf := cb.buf[cb.copied : cb.copied+size] 182 hostarch.ByteOrder.PutUint64(buf[0:8], dirent.Ino) 183 hostarch.ByteOrder.PutUint64(buf[8:16], uint64(dirent.NextOff)) 184 hostarch.ByteOrder.PutUint16(buf[16:18], uint16(size)) 185 copy(buf[18:], dirent.Name) 186 // Zero out all remaining bytes in buf, including the NUL terminator 187 // after dirent.Name and the zero padding byte between the name and 188 // dirent type. 189 bufTail := buf[18+len(dirent.Name) : size-1] 190 clear(bufTail) 191 buf[size-1] = dirent.Type 192 cb.copied += size 193 } 194 195 return nil 196 }