VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/OvmfPkg/VirtioFsDxe/SimpleFsOpen.c@ 105670

Last change on this file since 105670 was 105670, checked in by vboxsync, 4 months ago

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

  • Property svn:eol-style set to native
File size: 17.9 KB
Line 
1/** @file
2 EFI_FILE_PROTOCOL.Open() member function for the Virtio Filesystem driver.
3
4 Copyright (C) 2020, Red Hat, Inc.
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7**/
8
9#include <Library/BaseLib.h> // AsciiStrCmp()
10#include <Library/MemoryAllocationLib.h> // AllocatePool()
11
12#include "VirtioFsDxe.h"
13
14/**
15 Open the root directory, possibly for writing.
16
17 @param[in,out] VirtioFs The Virtio Filesystem device whose root directory
18 should be opened.
19
20 @param[out] NewHandle The new EFI_FILE_PROTOCOL instance through which
21 the root directory can be accessed.
22
23 @param[in] OpenForWriting TRUE if the root directory should be opened for
24 read-write access. FALSE if the root directory
25 should be opened for read-only access. Opening the
26 root directory for read-write access is useful for
27 calling EFI_FILE_PROTOCOL.Flush() or
28 EFI_FILE_PROTOCOL.SetInfo() later, for syncing or
29 touching the root directory, respectively.
30
31 @retval EFI_SUCCESS The root directory has been opened successfully.
32
33 @retval EFI_ACCESS_DENIED OpenForWriting is TRUE, but the root directory is
34 marked as read-only.
35
36 @return Error codes propagated from underlying functions.
37**/
38STATIC
39EFI_STATUS
40OpenRootDirectory (
41 IN OUT VIRTIO_FS *VirtioFs,
42 OUT EFI_FILE_PROTOCOL **NewHandle,
43 IN BOOLEAN OpenForWriting
44 )
45{
46 EFI_STATUS Status;
47 VIRTIO_FS_FILE *NewVirtioFsFile;
48
49 //
50 // VirtioFsOpenVolume() opens the root directory for read-only access. If the
51 // current request is to open the root directory for read-write access, so
52 // that EFI_FILE_PROTOCOL.Flush() or EFI_FILE_PROTOCOL.SetInfo()+timestamps
53 // can be used on the root directory later, then we have to check for write
54 // permission first.
55 //
56 if (OpenForWriting) {
57 VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
58 EFI_FILE_INFO FileInfo;
59
60 Status = VirtioFsFuseGetAttr (
61 VirtioFs,
62 VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID,
63 &FuseAttr
64 );
65 if (EFI_ERROR (Status)) {
66 return Status;
67 }
68
69 Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, &FileInfo);
70 if (EFI_ERROR (Status)) {
71 return Status;
72 }
73
74 if ((FileInfo.Attribute & EFI_FILE_READ_ONLY) != 0) {
75 return EFI_ACCESS_DENIED;
76 }
77 }
78
79 Status = VirtioFsOpenVolume (&VirtioFs->SimpleFs, NewHandle);
80 if (EFI_ERROR (Status)) {
81 return Status;
82 }
83
84 NewVirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (*NewHandle);
85 NewVirtioFsFile->IsOpenForWriting = OpenForWriting;
86 return EFI_SUCCESS;
87}
88
89/**
90 Open an existent regular file or non-root directory.
91
92 @param[in,out] VirtioFs The Virtio Filesystem device on which the
93 regular file or directory should be opened.
94
95 @param[in] DirNodeId The inode number of the immediate parent
96 directory of the regular file or directory to
97 open.
98
99 @param[in] Name The single-component filename of the regular
100 file or directory to open, under the immediate
101 parent directory identified by DirNodeId.
102
103 @param[in] OpenForWriting TRUE if the regular file or directory should be
104 opened for read-write access. FALSE if the
105 regular file or directory should be opened for
106 read-only access. Opening a directory for
107 read-write access is useful for deleting,
108 renaming, syncing or touching the directory
109 later.
110
111 @param[out] NodeId The inode number of the regular file or
112 directory, returned by the Virtio Filesystem
113 device.
114
115 @param[out] FuseHandle The open handle to the regular file or
116 directory, returned by the Virtio Filesystem
117 device.
118
119 @param[out] NodeIsDirectory Set to TRUE on output if Name was found to refer
120 to a directory. Set to FALSE if Name was found
121 to refer to a regular file.
122
123 @retval EFI_SUCCESS The regular file or directory has been looked up
124 and opened successfully.
125
126 @retval EFI_ACCESS_DENIED OpenForWriting is TRUE, but the regular file or
127 directory is marked read-only.
128
129 @retval EFI_NOT_FOUND A directory entry called Name was not found in the
130 directory identified by DirNodeId. (EFI_NOT_FOUND
131 is not returned for any other condition.)
132
133 @return Errors propagated from underlying functions. If
134 the error code to propagate were EFI_NOT_FOUND, it
135 is remapped to EFI_DEVICE_ERROR.
136**/
137STATIC
138EFI_STATUS
139OpenExistentFileOrDirectory (
140 IN OUT VIRTIO_FS *VirtioFs,
141 IN UINT64 DirNodeId,
142 IN CHAR8 *Name,
143 IN BOOLEAN OpenForWriting,
144 OUT UINT64 *NodeId,
145 OUT UINT64 *FuseHandle,
146 OUT BOOLEAN *NodeIsDirectory
147 )
148{
149 EFI_STATUS Status;
150 UINT64 ResolvedNodeId;
151 VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
152 EFI_FILE_INFO FileInfo;
153 BOOLEAN IsDirectory;
154 UINT64 NewFuseHandle;
155
156 Status = VirtioFsFuseLookup (
157 VirtioFs,
158 DirNodeId,
159 Name,
160 &ResolvedNodeId,
161 &FuseAttr
162 );
163 if (EFI_ERROR (Status)) {
164 return Status;
165 }
166
167 Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, &FileInfo);
168 if (EFI_ERROR (Status)) {
169 goto ForgetResolvedNodeId;
170 }
171
172 if (OpenForWriting && ((FileInfo.Attribute & EFI_FILE_READ_ONLY) != 0)) {
173 Status = EFI_ACCESS_DENIED;
174 goto ForgetResolvedNodeId;
175 }
176
177 IsDirectory = (BOOLEAN)((FileInfo.Attribute & EFI_FILE_DIRECTORY) != 0);
178 if (IsDirectory) {
179 //
180 // If OpenForWriting is TRUE here, that's not passed to
181 // VirtioFsFuseOpenDir(); it does not affect the FUSE_OPENDIR request we
182 // send. OpenForWriting=TRUE will only permit attempts to delete, rename,
183 // flush (sync), and touch the directory.
184 //
185 Status = VirtioFsFuseOpenDir (VirtioFs, ResolvedNodeId, &NewFuseHandle);
186 } else {
187 Status = VirtioFsFuseOpen (
188 VirtioFs,
189 ResolvedNodeId,
190 OpenForWriting,
191 &NewFuseHandle
192 );
193 }
194
195 if (EFI_ERROR (Status)) {
196 goto ForgetResolvedNodeId;
197 }
198
199 *NodeId = ResolvedNodeId;
200 *FuseHandle = NewFuseHandle;
201 *NodeIsDirectory = IsDirectory;
202 return EFI_SUCCESS;
203
204ForgetResolvedNodeId:
205 VirtioFsFuseForget (VirtioFs, ResolvedNodeId);
206 return (Status == EFI_NOT_FOUND) ? EFI_DEVICE_ERROR : Status;
207}
208
209/**
210 Create a directory.
211
212 @param[in,out] VirtioFs The Virtio Filesystem device on which the directory
213 should be created.
214
215 @param[in] DirNodeId The inode number of the immediate parent directory
216 of the directory to create.
217
218 @param[in] Name The single-component filename of the directory to
219 create, under the immediate parent directory
220 identified by DirNodeId.
221
222 @param[out] NodeId The inode number of the directory created, returned
223 by the Virtio Filesystem device.
224
225 @param[out] FuseHandle The open handle to the directory created, returned
226 by the Virtio Filesystem device.
227
228 @retval EFI_SUCCESS The directory has been created successfully.
229
230 @return Errors propagated from underlying functions.
231**/
232STATIC
233EFI_STATUS
234CreateDirectory (
235 IN OUT VIRTIO_FS *VirtioFs,
236 IN UINT64 DirNodeId,
237 IN CHAR8 *Name,
238 OUT UINT64 *NodeId,
239 OUT UINT64 *FuseHandle
240 )
241{
242 EFI_STATUS Status;
243 UINT64 NewChildDirNodeId;
244 UINT64 NewFuseHandle;
245
246 Status = VirtioFsFuseMkDir (VirtioFs, DirNodeId, Name, &NewChildDirNodeId);
247 if (EFI_ERROR (Status)) {
248 return Status;
249 }
250
251 Status = VirtioFsFuseOpenDir (VirtioFs, NewChildDirNodeId, &NewFuseHandle);
252 if (EFI_ERROR (Status)) {
253 goto RemoveNewChildDir;
254 }
255
256 *NodeId = NewChildDirNodeId;
257 *FuseHandle = NewFuseHandle;
258 return EFI_SUCCESS;
259
260RemoveNewChildDir:
261 VirtioFsFuseRemoveFileOrDir (VirtioFs, DirNodeId, Name, TRUE /* IsDir */);
262 VirtioFsFuseForget (VirtioFs, NewChildDirNodeId);
263 return Status;
264}
265
266/**
267 Create a regular file.
268
269 @param[in,out] VirtioFs The Virtio Filesystem device on which the regular
270 file should be created.
271
272 @param[in] DirNodeId The inode number of the immediate parent directory
273 of the regular file to create.
274
275 @param[in] Name The single-component filename of the regular file to
276 create, under the immediate parent directory
277 identified by DirNodeId.
278
279 @param[out] NodeId The inode number of the regular file created,
280 returned by the Virtio Filesystem device.
281
282 @param[out] FuseHandle The open handle to the regular file created,
283 returned by the Virtio Filesystem device.
284
285 @retval EFI_SUCCESS The regular file has been created successfully.
286
287 @return Errors propagated from underlying functions.
288**/
289STATIC
290EFI_STATUS
291CreateRegularFile (
292 IN OUT VIRTIO_FS *VirtioFs,
293 IN UINT64 DirNodeId,
294 IN CHAR8 *Name,
295 OUT UINT64 *NodeId,
296 OUT UINT64 *FuseHandle
297 )
298{
299 return VirtioFsFuseOpenOrCreate (
300 VirtioFs,
301 DirNodeId,
302 Name,
303 NodeId,
304 FuseHandle
305 );
306}
307
308EFI_STATUS
309EFIAPI
310VirtioFsSimpleFileOpen (
311 IN EFI_FILE_PROTOCOL *This,
312 OUT EFI_FILE_PROTOCOL **NewHandle,
313 IN CHAR16 *FileName,
314 IN UINT64 OpenMode,
315 IN UINT64 Attributes
316 )
317{
318 VIRTIO_FS_FILE *VirtioFsFile;
319 VIRTIO_FS *VirtioFs;
320 BOOLEAN OpenForWriting;
321 BOOLEAN PermitCreation;
322 BOOLEAN CreateDirectoryIfCreating;
323 VIRTIO_FS_FILE *NewVirtioFsFile;
324 EFI_STATUS Status;
325 CHAR8 *NewCanonicalPath;
326 BOOLEAN RootEscape;
327 UINT64 DirNodeId;
328 CHAR8 *LastComponent;
329 UINT64 NewNodeId;
330 UINT64 NewFuseHandle;
331 BOOLEAN NewNodeIsDirectory;
332
333 VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
334 VirtioFs = VirtioFsFile->OwnerFs;
335
336 //
337 // Validate OpenMode.
338 //
339 switch (OpenMode) {
340 case EFI_FILE_MODE_READ:
341 OpenForWriting = FALSE;
342 PermitCreation = FALSE;
343 break;
344 case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE:
345 OpenForWriting = TRUE;
346 PermitCreation = FALSE;
347 break;
348 case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE:
349 OpenForWriting = TRUE;
350 PermitCreation = TRUE;
351 break;
352 default:
353 return EFI_INVALID_PARAMETER;
354 }
355
356 //
357 // Set CreateDirectoryIfCreating to suppress incorrect compiler/analyzer
358 // warnings.
359 //
360 CreateDirectoryIfCreating = FALSE;
361
362 //
363 // Validate the Attributes requested for the case when the file ends up being
364 // created, provided creation is permitted.
365 //
366 if (PermitCreation) {
367 if ((Attributes & ~EFI_FILE_VALID_ATTR) != 0) {
368 //
369 // Unknown attribute requested.
370 //
371 return EFI_INVALID_PARAMETER;
372 }
373
374 ASSERT (OpenForWriting);
375 if ((Attributes & EFI_FILE_READ_ONLY) != 0) {
376 DEBUG ((
377 DEBUG_ERROR,
378 ("%a: Label=\"%s\" CanonicalPathname=\"%a\" FileName=\"%s\" "
379 "OpenMode=0x%Lx Attributes=0x%Lx: nonsensical request to possibly "
380 "create a file marked read-only, for read-write access\n"),
381 __func__,
382 VirtioFs->Label,
383 VirtioFsFile->CanonicalPathname,
384 FileName,
385 OpenMode,
386 Attributes
387 ));
388 return EFI_INVALID_PARAMETER;
389 }
390
391 CreateDirectoryIfCreating = (BOOLEAN)((Attributes &
392 EFI_FILE_DIRECTORY) != 0);
393 }
394
395 //
396 // Referring to a file relative to a regular file makes no sense (or at least
397 // it cannot be implemented consistently with how a file is referred to
398 // relative to a directory). See USWG Mantis ticket #2367.
399 //
400 if (!VirtioFsFile->IsDirectory) {
401 BOOLEAN BugCompat;
402
403 //
404 // Tolerate this bug in the caller if FileName is absolute. If FileName is
405 // absolute, then VirtioFsAppendPath() below will disregard
406 // VirtioFsFile->CanonicalPathname.
407 //
408 BugCompat = (FileName[0] == L'\\');
409
410 DEBUG ((
411 BugCompat ? DEBUG_WARN : DEBUG_ERROR,
412 ("%a: Label=\"%s\" CanonicalPathname=\"%a\" FileName=\"%s\": "
413 "nonsensical request to open a file or directory relative to a regular "
414 "file\n"),
415 __func__,
416 VirtioFs->Label,
417 VirtioFsFile->CanonicalPathname,
418 FileName
419 ));
420 if (!BugCompat) {
421 return EFI_INVALID_PARAMETER;
422 }
423 }
424
425 //
426 // Allocate the new VIRTIO_FS_FILE object.
427 //
428 NewVirtioFsFile = AllocatePool (sizeof *NewVirtioFsFile);
429 if (NewVirtioFsFile == NULL) {
430 return EFI_OUT_OF_RESOURCES;
431 }
432
433 //
434 // Create the canonical pathname at which the desired file is expected to
435 // exist.
436 //
437 Status = VirtioFsAppendPath (
438 VirtioFsFile->CanonicalPathname,
439 FileName,
440 &NewCanonicalPath,
441 &RootEscape
442 );
443 if (EFI_ERROR (Status)) {
444 goto FreeNewVirtioFsFile;
445 }
446
447 if (RootEscape) {
448 Status = EFI_ACCESS_DENIED;
449 goto FreeNewCanonicalPath;
450 }
451
452 //
453 // If the desired file is the root directory, just open the volume one more
454 // time, without looking up anything.
455 //
456 if (AsciiStrCmp (NewCanonicalPath, "/") == 0) {
457 FreePool (NewCanonicalPath);
458 FreePool (NewVirtioFsFile);
459 return OpenRootDirectory (VirtioFs, NewHandle, OpenForWriting);
460 }
461
462 //
463 // Split the new canonical pathname into most specific parent directory
464 // (given by DirNodeId) and last pathname component (i.e., immediate child
465 // within that parent directory).
466 //
467 Status = VirtioFsLookupMostSpecificParentDir (
468 VirtioFs,
469 NewCanonicalPath,
470 &DirNodeId,
471 &LastComponent
472 );
473 if (EFI_ERROR (Status)) {
474 goto FreeNewCanonicalPath;
475 }
476
477 //
478 // Set NewNodeIsDirectory to suppress incorrect compiler/analyzer warnings.
479 //
480 NewNodeIsDirectory = FALSE;
481
482 //
483 // Try to open LastComponent directly under DirNodeId, as an existent regular
484 // file or directory.
485 //
486 Status = OpenExistentFileOrDirectory (
487 VirtioFs,
488 DirNodeId,
489 LastComponent,
490 OpenForWriting,
491 &NewNodeId,
492 &NewFuseHandle,
493 &NewNodeIsDirectory
494 );
495 //
496 // If LastComponent could not be found under DirNodeId, but the request
497 // allows us to create a new entry, attempt creating the requested regular
498 // file or directory.
499 //
500 if ((Status == EFI_NOT_FOUND) && PermitCreation) {
501 ASSERT (OpenForWriting);
502 if (CreateDirectoryIfCreating) {
503 Status = CreateDirectory (
504 VirtioFs,
505 DirNodeId,
506 LastComponent,
507 &NewNodeId,
508 &NewFuseHandle
509 );
510 } else {
511 Status = CreateRegularFile (
512 VirtioFs,
513 DirNodeId,
514 LastComponent,
515 &NewNodeId,
516 &NewFuseHandle
517 );
518 }
519
520 NewNodeIsDirectory = CreateDirectoryIfCreating;
521 }
522
523 //
524 // Regardless of the branch taken, we're done with DirNodeId.
525 //
526 if (DirNodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
527 VirtioFsFuseForget (VirtioFs, DirNodeId);
528 }
529
530 if (EFI_ERROR (Status)) {
531 goto FreeNewCanonicalPath;
532 }
533
534 //
535 // Populate the new VIRTIO_FS_FILE object.
536 //
537 NewVirtioFsFile->Signature = VIRTIO_FS_FILE_SIG;
538 NewVirtioFsFile->SimpleFile.Revision = EFI_FILE_PROTOCOL_REVISION;
539 NewVirtioFsFile->SimpleFile.Open = VirtioFsSimpleFileOpen;
540 NewVirtioFsFile->SimpleFile.Close = VirtioFsSimpleFileClose;
541 NewVirtioFsFile->SimpleFile.Delete = VirtioFsSimpleFileDelete;
542 NewVirtioFsFile->SimpleFile.Read = VirtioFsSimpleFileRead;
543 NewVirtioFsFile->SimpleFile.Write = VirtioFsSimpleFileWrite;
544 NewVirtioFsFile->SimpleFile.GetPosition = VirtioFsSimpleFileGetPosition;
545 NewVirtioFsFile->SimpleFile.SetPosition = VirtioFsSimpleFileSetPosition;
546 NewVirtioFsFile->SimpleFile.GetInfo = VirtioFsSimpleFileGetInfo;
547 NewVirtioFsFile->SimpleFile.SetInfo = VirtioFsSimpleFileSetInfo;
548 NewVirtioFsFile->SimpleFile.Flush = VirtioFsSimpleFileFlush;
549 NewVirtioFsFile->IsDirectory = NewNodeIsDirectory;
550 NewVirtioFsFile->IsOpenForWriting = OpenForWriting;
551 NewVirtioFsFile->OwnerFs = VirtioFs;
552 NewVirtioFsFile->CanonicalPathname = NewCanonicalPath;
553 NewVirtioFsFile->FilePosition = 0;
554 NewVirtioFsFile->NodeId = NewNodeId;
555 NewVirtioFsFile->FuseHandle = NewFuseHandle;
556 NewVirtioFsFile->FileInfoArray = NULL;
557 NewVirtioFsFile->SingleFileInfoSize = 0;
558 NewVirtioFsFile->NumFileInfo = 0;
559 NewVirtioFsFile->NextFileInfo = 0;
560
561 //
562 // One more file is now open for the filesystem.
563 //
564 InsertTailList (&VirtioFs->OpenFiles, &NewVirtioFsFile->OpenFilesEntry);
565
566 *NewHandle = &NewVirtioFsFile->SimpleFile;
567 return EFI_SUCCESS;
568
569FreeNewCanonicalPath:
570 FreePool (NewCanonicalPath);
571
572FreeNewVirtioFsFile:
573 FreePool (NewVirtioFsFile);
574
575 return Status;
576}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette