TianoCore EDK2 master
Loading...
Searching...
No Matches
SimpleFsRead.c
Go to the documentation of this file.
1
9#include <Library/BaseMemoryLib.h> // CopyMem()
10#include <Library/MemoryAllocationLib.h> // AllocatePool()
11
12#include "VirtioFsDxe.h"
13
41 IN UINTN SingleFileInfoSize,
43 )
44{
45 EFI_STATUS Status;
46
47 //
48 // Convert the name, set the actual size.
49 //
50 FileInfo->Size = SingleFileInfoSize;
52 if (EFI_ERROR (Status)) {
53 return Status;
54 }
55
56 //
57 // Populate the scalar fields.
58 //
59 Status = VirtioFsFuseAttrToEfiFileInfo (&Dirent->AttrResp, FileInfo);
60 return Status;
61}
62
69 IN OUT VIRTIO_FS_FILE *VirtioFsFile
70 )
71{
72 VIRTIO_FS *VirtioFs;
73 EFI_STATUS Status;
75 UINT32 DirentBufSize;
76 UINT8 *DirentBuf;
77 UINTN SingleFileInfoSize;
78 UINT8 *FileInfoArray;
79 UINT64 DirStreamCookie;
80 UINT64 CacheEndsAtCookie;
81 UINTN NumFileInfo;
82
83 //
84 // Allocate a DirentBuf that can receive at least
85 // VIRTIO_FS_FILE_MAX_FILE_INFO directory entries, based on the maximum
86 // filename length supported by the filesystem. Note that the multiplication
87 // is safe from overflow due to the VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE()
88 // check.
89 //
90 VirtioFs = VirtioFsFile->OwnerFs;
91 Status = VirtioFsFuseStatFs (VirtioFs, VirtioFsFile->NodeId, &FilesysAttr);
92 if (EFI_ERROR (Status)) {
93 return Status;
94 }
95
96 DirentBufSize = (UINT32)VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE (
97 FilesysAttr.Namelen
98 );
99 if (DirentBufSize == 0) {
100 return EFI_UNSUPPORTED;
101 }
102
103 DirentBufSize *= VIRTIO_FS_FILE_MAX_FILE_INFO;
104 DirentBuf = AllocatePool (DirentBufSize);
105 if (DirentBuf == NULL) {
106 return EFI_OUT_OF_RESOURCES;
107 }
108
109 //
110 // Allocate the EFI_FILE_INFO cache. A single EFI_FILE_INFO element is sized
111 // accordingly to the maximum filename length supported by the filesystem.
112 //
113 // Note that the calculation below cannot overflow, due to the filename limit
114 // imposed by the VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE() check above. The
115 // calculation takes the L'\0' character that we'll need to append into
116 // account.
117 //
118 SingleFileInfoSize = (OFFSET_OF (EFI_FILE_INFO, FileName) +
119 ((UINTN)FilesysAttr.Namelen + 1) * sizeof (CHAR16));
120 FileInfoArray = AllocatePool (
121 VIRTIO_FS_FILE_MAX_FILE_INFO * SingleFileInfoSize
122 );
123 if (FileInfoArray == NULL) {
124 Status = EFI_OUT_OF_RESOURCES;
125 goto FreeDirentBuf;
126 }
127
128 //
129 // Pick up reading the directory stream where the previous cache ended.
130 //
131 DirStreamCookie = VirtioFsFile->FilePosition;
132 CacheEndsAtCookie = VirtioFsFile->FilePosition;
133 NumFileInfo = 0;
134 do {
135 UINT32 Remaining;
136 UINT32 Consumed;
137
138 //
139 // Fetch a chunk of the directory stream. The chunk may hold more entries
140 // than what we can fit in the cache. The chunk may also not entirely fill
141 // the cache, especially after filtering out entries that cannot be
142 // supported under UEFI (sockets, FIFOs, filenames with backslashes, etc).
143 //
144 Remaining = DirentBufSize;
146 VirtioFs,
147 VirtioFsFile->NodeId,
148 VirtioFsFile->FuseHandle,
149 TRUE, // IsDir
150 DirStreamCookie, // Offset
151 &Remaining, // Size
152 DirentBuf // Data
153 );
154 if (EFI_ERROR (Status)) {
155 goto FreeFileInfoArray;
156 }
157
158 if (Remaining == 0) {
159 //
160 // The directory stream ends.
161 //
162 break;
163 }
164
165 //
166 // Iterate over all records in DirentBuf. Primarily, forget them all.
167 // Secondarily, if a record proves transformable to EFI_FILE_INFO, add it
168 // to the EFI_FILE_INFO cache (unless the cache is full).
169 //
170 Consumed = 0;
171 while (Remaining >= sizeof (VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE)) {
173 UINT32 DirentSize;
174
175 Dirent = (VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE *)(DirentBuf + Consumed);
176 DirentSize = (UINT32)VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE (
177 Dirent->Namelen
178 );
179 if (DirentSize == 0) {
180 //
181 // This means one of two things: (a) Dirent->Namelen is zero, or (b)
182 // (b) Dirent->Namelen is unsupportably large. (a) is just invalid for
183 // the Virtio Filesystem device to send, while (b) shouldn't happen
184 // because "FilesysAttr.Namelen" -- the maximum filename length
185 // supported by the filesystem -- proved acceptable above.
186 //
187 Status = EFI_PROTOCOL_ERROR;
188 goto FreeFileInfoArray;
189 }
190
191 if (DirentSize > Remaining) {
192 //
193 // Dirent->Namelen suggests that the filename byte array (plus any
194 // padding) are truncated. This should never happen; the Virtio
195 // Filesystem device is supposed to send complete entries only.
196 //
197 Status = EFI_PROTOCOL_ERROR;
198 goto FreeFileInfoArray;
199 }
200
201 if (Dirent->Namelen > FilesysAttr.Namelen) {
202 //
203 // This is possible without tripping the truncation check above, due to
204 // how entries are padded. The condition means that Dirent->Namelen is
205 // reportedly larger than the filesystem limit, without spilling into
206 // the next alignment bucket. Should never happen.
207 //
208 Status = EFI_PROTOCOL_ERROR;
209 goto FreeFileInfoArray;
210 }
211
212 //
213 // If we haven't filled the EFI_FILE_INFO cache yet, attempt transforming
214 // Dirent to EFI_FILE_INFO.
215 //
216 if (NumFileInfo < VIRTIO_FS_FILE_MAX_FILE_INFO) {
218
219 FileInfo = (EFI_FILE_INFO *)(FileInfoArray +
220 (NumFileInfo * SingleFileInfoSize));
221 Status = PopulateFileInfo (Dirent, SingleFileInfoSize, FileInfo);
222 if (!EFI_ERROR (Status)) {
223 //
224 // Dirent has been transformed and cached successfully.
225 //
226 NumFileInfo++;
227 //
228 // The next time we refill the cache, restart reading the directory
229 // stream right after the entry that we've just transformed and
230 // cached.
231 //
232 CacheEndsAtCookie = Dirent->CookieForNextEntry;
233 }
234
235 //
236 // If Dirent wasn't transformable to an EFI_FILE_INFO, we'll just skip
237 // it.
238 //
239 }
240
241 //
242 // Make the Virtio Filesystem device forget the NodeId in this directory
243 // entry, as we'll need it no more. (The "." and ".." entries need no
244 // FUSE_FORGET requests, when returned by FUSE_READDIRPLUS -- and so the
245 // Virtio Filesystem device reports their NodeId fields as zero.)
246 //
247 if (Dirent->NodeResp.NodeId != 0) {
248 VirtioFsFuseForget (VirtioFs, Dirent->NodeResp.NodeId);
249 }
250
251 //
252 // Advance to the next entry in DirentBuf.
253 //
254 DirStreamCookie = Dirent->CookieForNextEntry;
255 Consumed += DirentSize;
256 Remaining -= DirentSize;
257 }
258
259 if (Remaining > 0) {
260 //
261 // This suggests that a VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE header was
262 // truncated. This should never happen; the Virtio Filesystem device is
263 // supposed to send complete entries only.
264 //
265 Status = EFI_PROTOCOL_ERROR;
266 goto FreeFileInfoArray;
267 }
268
269 //
270 // Fetch another DirentBuf from the directory stream, unless we've filled
271 // the EFI_FILE_INFO cache.
272 //
273 } while (NumFileInfo < VIRTIO_FS_FILE_MAX_FILE_INFO);
274
275 //
276 // Commit the results. (Note that the result may be an empty cache.)
277 //
278 if (VirtioFsFile->FileInfoArray != NULL) {
279 FreePool (VirtioFsFile->FileInfoArray);
280 }
281
282 VirtioFsFile->FileInfoArray = FileInfoArray;
283 VirtioFsFile->SingleFileInfoSize = SingleFileInfoSize;
284 VirtioFsFile->NumFileInfo = NumFileInfo;
285 VirtioFsFile->NextFileInfo = 0;
286 VirtioFsFile->FilePosition = CacheEndsAtCookie;
287
288 FreePool (DirentBuf);
289 return EFI_SUCCESS;
290
291FreeFileInfoArray:
292 FreePool (FileInfoArray);
293
294FreeDirentBuf:
295 FreePool (DirentBuf);
296
297 return Status;
298}
299
303STATIC
306 IN OUT VIRTIO_FS_FILE *VirtioFsFile,
307 IN OUT UINTN *BufferSize,
308 OUT VOID *Buffer
309 )
310{
312 UINTN CallerAllocated;
313
314 //
315 // Refill the cache if needed. If the refill doesn't produce any new
316 // EFI_FILE_INFO, report End of Directory, by setting (*BufferSize) to 0.
317 //
318 if (VirtioFsFile->NextFileInfo == VirtioFsFile->NumFileInfo) {
319 EFI_STATUS Status;
320
321 Status = RefillFileInfoCache (VirtioFsFile);
322 if (EFI_ERROR (Status)) {
323 return (Status == EFI_BUFFER_TOO_SMALL) ? EFI_DEVICE_ERROR : Status;
324 }
325
326 if (VirtioFsFile->NumFileInfo == 0) {
327 *BufferSize = 0;
328 return EFI_SUCCESS;
329 }
330 }
331
332 FileInfo = (EFI_FILE_INFO *)(VirtioFsFile->FileInfoArray +
333 (VirtioFsFile->NextFileInfo *
334 VirtioFsFile->SingleFileInfoSize));
335
336 //
337 // Check if the caller is ready to accept FileInfo. If not, we'll just
338 // present the required size for now.
339 //
340 // (The (UINTN) cast below is safe because FileInfo->Size has been reduced
341 // from VirtioFsFile->SingleFileInfoSize, in
342 //
343 // RefillFileInfoCache()
344 // PopulateFileInfo()
345 // VirtioFsFuseDirentPlusToEfiFileInfo()
346 //
347 // and VirtioFsFile->SingleFileInfoSize was computed from
348 // FilesysAttr.Namelen, which had been accepted by
349 // VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE().)
350 //
351 CallerAllocated = *BufferSize;
352 *BufferSize = (UINTN)FileInfo->Size;
353 if (CallerAllocated < *BufferSize) {
354 return EFI_BUFFER_TOO_SMALL;
355 }
356
357 //
358 // Output FileInfo, and remove it from the cache.
359 //
360 CopyMem (Buffer, FileInfo, *BufferSize);
361 VirtioFsFile->NextFileInfo++;
362 return EFI_SUCCESS;
363}
364
368STATIC
371 IN OUT VIRTIO_FS_FILE *VirtioFsFile,
372 IN OUT UINTN *BufferSize,
373 OUT VOID *Buffer
374 )
375{
376 VIRTIO_FS *VirtioFs;
377 EFI_STATUS Status;
379 UINTN Transferred;
380 UINTN Left;
381
382 VirtioFs = VirtioFsFile->OwnerFs;
383 //
384 // The UEFI spec forbids reads that start beyond the end of the file.
385 //
386 Status = VirtioFsFuseGetAttr (VirtioFs, VirtioFsFile->NodeId, &FuseAttr);
387 if (EFI_ERROR (Status) || (VirtioFsFile->FilePosition > FuseAttr.Size)) {
388 return EFI_DEVICE_ERROR;
389 }
390
391 Status = EFI_SUCCESS;
392 Transferred = 0;
393 Left = *BufferSize;
394 while (Left > 0) {
395 UINT32 ReadSize;
396
397 //
398 // FUSE_READ cannot express a >=4GB buffer size.
399 //
400 ReadSize = (UINT32)MIN ((UINTN)MAX_UINT32, Left);
402 VirtioFs,
403 VirtioFsFile->NodeId,
404 VirtioFsFile->FuseHandle,
405 FALSE, // IsDir
406 VirtioFsFile->FilePosition + Transferred,
407 &ReadSize,
408 (UINT8 *)Buffer + Transferred
409 );
410 if (EFI_ERROR (Status) || (ReadSize == 0)) {
411 break;
412 }
413
414 Transferred += ReadSize;
415 Left -= ReadSize;
416 }
417
418 *BufferSize = Transferred;
419 VirtioFsFile->FilePosition += Transferred;
420 //
421 // If we managed to read some data, return success. If zero bytes were
422 // transferred due to zero-sized buffer on input or due to EOF on first read,
423 // return SUCCESS. Otherwise, return the error due to which zero bytes were
424 // transferred.
425 //
426 return (Transferred > 0) ? EFI_SUCCESS : Status;
427}
428
430EFIAPI
431VirtioFsSimpleFileRead (
432 IN EFI_FILE_PROTOCOL *This,
433 IN OUT UINTN *BufferSize,
434 OUT VOID *Buffer
435 )
436{
437 VIRTIO_FS_FILE *VirtioFsFile;
438 EFI_STATUS Status;
439
440 VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
441
442 if (VirtioFsFile->IsDirectory) {
443 Status = ReadFileInfoCache (VirtioFsFile, BufferSize, Buffer);
444 } else {
445 Status = ReadRegularFile (VirtioFsFile, BufferSize, Buffer);
446 }
447
448 return Status;
449}
UINT64 UINTN
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
VOID EFIAPI FreePool(IN VOID *Buffer)
EFI_STATUS VirtioFsFuseForget(IN OUT VIRTIO_FS *VirtioFs, IN UINT64 NodeId)
Definition: FuseForget.c:36
EFI_STATUS VirtioFsFuseGetAttr(IN OUT VIRTIO_FS *VirtioFs, IN UINT64 NodeId, OUT VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr)
Definition: FuseGetAttr.c:39
EFI_STATUS VirtioFsFuseReadFileOrDir(IN OUT VIRTIO_FS *VirtioFs, IN UINT64 NodeId, IN UINT64 FuseHandle, IN BOOLEAN IsDir, IN UINT64 Offset, IN OUT UINT32 *Size, OUT VOID *Data)
Definition: FuseRead.c:89
EFI_STATUS VirtioFsFuseStatFs(IN OUT VIRTIO_FS *VirtioFs, IN UINT64 NodeId, OUT VIRTIO_FS_FUSE_STATFS_RESPONSE *FilesysAttr)
Definition: FuseStatFs.c:38
#define NULL
Definition: Base.h:319
#define STATIC
Definition: Base.h:264
#define MIN(a, b)
Definition: Base.h:1007
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OFFSET_OF(TYPE, Field)
Definition: Base.h:758
#define OUT
Definition: Base.h:284
VOID *EFIAPI AllocatePool(IN UINTN AllocationSize)
EFI_FILE_INFO * FileInfo(IN EFI_FILE_HANDLE FHand)
STATIC EFI_STATUS PopulateFileInfo(IN VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE *Dirent, IN UINTN SingleFileInfoSize, OUT EFI_FILE_INFO *FileInfo)
Definition: SimpleFsRead.c:39
STATIC EFI_STATUS RefillFileInfoCache(IN OUT VIRTIO_FS_FILE *VirtioFsFile)
Definition: SimpleFsRead.c:68
STATIC EFI_STATUS ReadFileInfoCache(IN OUT VIRTIO_FS_FILE *VirtioFsFile, IN OUT UINTN *BufferSize, OUT VOID *Buffer)
Definition: SimpleFsRead.c:305
STATIC EFI_STATUS ReadRegularFile(IN OUT VIRTIO_FS_FILE *VirtioFsFile, IN OUT UINTN *BufferSize, OUT VOID *Buffer)
Definition: SimpleFsRead.c:370
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
#define VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE(Namelen)
Definition: VirtioFs.h:147
EFI_STATUS VirtioFsFuseDirentPlusToEfiFileInfo(IN VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE *FuseDirent, IN OUT EFI_FILE_INFO *FileInfo)
Definition: Helpers.c:2219
EFI_STATUS VirtioFsFuseAttrToEfiFileInfo(IN VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr, OUT EFI_FILE_INFO *FileInfo)
Definition: Helpers.c:2090
UINT64 Size
Definition: FileInfo.h:23