2 EFI_FILE_PROTOCOL.Open() member function for the Virtio Filesystem driver.
4 Copyright (C) 2020, Red Hat, Inc.
6 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include <Library/BaseLib.h> // AsciiStrCmp()
10 #include <Library/MemoryAllocationLib.h> // AllocatePool()
12 #include "VirtioFsDxe.h"
15 Open the root directory, possibly for writing.
17 @param[in,out] VirtioFs The Virtio Filesystem device whose root directory
20 @param[out] NewHandle The new EFI_FILE_PROTOCOL instance through which
21 the root directory can be accessed.
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.
31 @retval EFI_SUCCESS The root directory has been opened successfully.
33 @retval EFI_ACCESS_DENIED OpenForWriting is TRUE, but the root directory is
36 @return Error codes propagated from underlying functions.
41 IN OUT VIRTIO_FS
*VirtioFs
,
42 OUT EFI_FILE_PROTOCOL
**NewHandle
,
43 IN BOOLEAN OpenForWriting
47 VIRTIO_FS_FILE
*NewVirtioFsFile
;
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
57 VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr
;
58 EFI_FILE_INFO FileInfo
;
60 Status
= VirtioFsFuseGetAttr (
62 VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID
,
65 if (EFI_ERROR (Status
)) {
69 Status
= VirtioFsFuseAttrToEfiFileInfo (&FuseAttr
, &FileInfo
);
70 if (EFI_ERROR (Status
)) {
74 if ((FileInfo
.Attribute
& EFI_FILE_READ_ONLY
) != 0) {
75 return EFI_ACCESS_DENIED
;
79 Status
= VirtioFsOpenVolume (&VirtioFs
->SimpleFs
, NewHandle
);
80 if (EFI_ERROR (Status
)) {
84 NewVirtioFsFile
= VIRTIO_FS_FILE_FROM_SIMPLE_FILE (*NewHandle
);
85 NewVirtioFsFile
->IsOpenForWriting
= OpenForWriting
;
90 Open an existent regular file or non-root directory.
92 @param[in,out] VirtioFs The Virtio Filesystem device on which the
93 regular file or directory should be opened.
95 @param[in] DirNodeId The inode number of the immediate parent
96 directory of the regular file or directory to
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.
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
111 @param[out] NodeId The inode number of the regular file or
112 directory, returned by the Virtio Filesystem
115 @param[out] FuseHandle The open handle to the regular file or
116 directory, returned by the Virtio Filesystem
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.
123 @retval EFI_SUCCESS The regular file or directory has been looked up
124 and opened successfully.
126 @retval EFI_ACCESS_DENIED OpenForWriting is TRUE, but the regular file or
127 directory is marked read-only.
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.)
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.
139 OpenExistentFileOrDirectory (
140 IN OUT VIRTIO_FS
*VirtioFs
,
143 IN BOOLEAN OpenForWriting
,
145 OUT UINT64
*FuseHandle
,
146 OUT BOOLEAN
*NodeIsDirectory
150 UINT64 ResolvedNodeId
;
151 VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr
;
152 EFI_FILE_INFO FileInfo
;
154 UINT64 NewFuseHandle
;
156 Status
= VirtioFsFuseLookup (
163 if (EFI_ERROR (Status
)) {
167 Status
= VirtioFsFuseAttrToEfiFileInfo (&FuseAttr
, &FileInfo
);
168 if (EFI_ERROR (Status
)) {
169 goto ForgetResolvedNodeId
;
172 if (OpenForWriting
&& ((FileInfo
.Attribute
& EFI_FILE_READ_ONLY
) != 0)) {
173 Status
= EFI_ACCESS_DENIED
;
174 goto ForgetResolvedNodeId
;
177 IsDirectory
= (BOOLEAN
)((FileInfo
.Attribute
& EFI_FILE_DIRECTORY
) != 0);
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.
185 Status
= VirtioFsFuseOpenDir (VirtioFs
, ResolvedNodeId
, &NewFuseHandle
);
187 Status
= VirtioFsFuseOpen (
195 if (EFI_ERROR (Status
)) {
196 goto ForgetResolvedNodeId
;
199 *NodeId
= ResolvedNodeId
;
200 *FuseHandle
= NewFuseHandle
;
201 *NodeIsDirectory
= IsDirectory
;
204 ForgetResolvedNodeId
:
205 VirtioFsFuseForget (VirtioFs
, ResolvedNodeId
);
206 return (Status
== EFI_NOT_FOUND
) ? EFI_DEVICE_ERROR
: Status
;
212 @param[in,out] VirtioFs The Virtio Filesystem device on which the directory
215 @param[in] DirNodeId The inode number of the immediate parent directory
216 of the directory to create.
218 @param[in] Name The single-component filename of the directory to
219 create, under the immediate parent directory
220 identified by DirNodeId.
222 @param[out] NodeId The inode number of the directory created, returned
223 by the Virtio Filesystem device.
225 @param[out] FuseHandle The open handle to the directory created, returned
226 by the Virtio Filesystem device.
228 @retval EFI_SUCCESS The directory has been created successfully.
230 @return Errors propagated from underlying functions.
235 IN OUT VIRTIO_FS
*VirtioFs
,
239 OUT UINT64
*FuseHandle
243 UINT64 NewChildDirNodeId
;
244 UINT64 NewFuseHandle
;
246 Status
= VirtioFsFuseMkDir (VirtioFs
, DirNodeId
, Name
, &NewChildDirNodeId
);
247 if (EFI_ERROR (Status
)) {
251 Status
= VirtioFsFuseOpenDir (VirtioFs
, NewChildDirNodeId
, &NewFuseHandle
);
252 if (EFI_ERROR (Status
)) {
253 goto RemoveNewChildDir
;
256 *NodeId
= NewChildDirNodeId
;
257 *FuseHandle
= NewFuseHandle
;
261 VirtioFsFuseRemoveFileOrDir (VirtioFs
, DirNodeId
, Name
, TRUE
/* IsDir */);
262 VirtioFsFuseForget (VirtioFs
, NewChildDirNodeId
);
267 Create a regular file.
269 @param[in,out] VirtioFs The Virtio Filesystem device on which the regular
270 file should be created.
272 @param[in] DirNodeId The inode number of the immediate parent directory
273 of the regular file to create.
275 @param[in] Name The single-component filename of the regular file to
276 create, under the immediate parent directory
277 identified by DirNodeId.
279 @param[out] NodeId The inode number of the regular file created,
280 returned by the Virtio Filesystem device.
282 @param[out] FuseHandle The open handle to the regular file created,
283 returned by the Virtio Filesystem device.
285 @retval EFI_SUCCESS The regular file has been created successfully.
287 @return Errors propagated from underlying functions.
292 IN OUT VIRTIO_FS
*VirtioFs
,
296 OUT UINT64
*FuseHandle
299 return VirtioFsFuseOpenOrCreate (
310 VirtioFsSimpleFileOpen (
311 IN EFI_FILE_PROTOCOL
*This
,
312 OUT EFI_FILE_PROTOCOL
**NewHandle
,
318 VIRTIO_FS_FILE
*VirtioFsFile
;
320 BOOLEAN OpenForWriting
;
321 BOOLEAN PermitCreation
;
322 BOOLEAN CreateDirectoryIfCreating
;
323 VIRTIO_FS_FILE
*NewVirtioFsFile
;
325 CHAR8
*NewCanonicalPath
;
328 CHAR8
*LastComponent
;
330 UINT64 NewFuseHandle
;
331 BOOLEAN NewNodeIsDirectory
;
333 VirtioFsFile
= VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This
);
334 VirtioFs
= VirtioFsFile
->OwnerFs
;
337 // Validate OpenMode.
340 case EFI_FILE_MODE_READ
:
341 OpenForWriting
= FALSE
;
342 PermitCreation
= FALSE
;
344 case EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
:
345 OpenForWriting
= TRUE
;
346 PermitCreation
= FALSE
;
348 case EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
| EFI_FILE_MODE_CREATE
:
349 OpenForWriting
= TRUE
;
350 PermitCreation
= TRUE
;
353 return EFI_INVALID_PARAMETER
;
357 // Set CreateDirectoryIfCreating to suppress incorrect compiler/analyzer
360 CreateDirectoryIfCreating
= FALSE
;
363 // Validate the Attributes requested for the case when the file ends up being
364 // created, provided creation is permitted.
366 if (PermitCreation
) {
367 if ((Attributes
& ~EFI_FILE_VALID_ATTR
) != 0) {
369 // Unknown attribute requested.
371 return EFI_INVALID_PARAMETER
;
374 ASSERT (OpenForWriting
);
375 if ((Attributes
& EFI_FILE_READ_ONLY
) != 0) {
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"),
383 VirtioFsFile
->CanonicalPathname
,
388 return EFI_INVALID_PARAMETER
;
391 CreateDirectoryIfCreating
= (BOOLEAN
)((Attributes
&
392 EFI_FILE_DIRECTORY
) != 0);
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).
400 if (!VirtioFsFile
->IsDirectory
) {
403 ("%a: Label=\"%s\" CanonicalPathname=\"%a\" FileName=\"%s\": "
404 "nonsensical request to open a file or directory relative to a regular "
408 VirtioFsFile
->CanonicalPathname
,
411 return EFI_INVALID_PARAMETER
;
415 // Allocate the new VIRTIO_FS_FILE object.
417 NewVirtioFsFile
= AllocatePool (sizeof *NewVirtioFsFile
);
418 if (NewVirtioFsFile
== NULL
) {
419 return EFI_OUT_OF_RESOURCES
;
423 // Create the canonical pathname at which the desired file is expected to
426 Status
= VirtioFsAppendPath (
427 VirtioFsFile
->CanonicalPathname
,
432 if (EFI_ERROR (Status
)) {
433 goto FreeNewVirtioFsFile
;
437 Status
= EFI_ACCESS_DENIED
;
438 goto FreeNewCanonicalPath
;
442 // If the desired file is the root directory, just open the volume one more
443 // time, without looking up anything.
445 if (AsciiStrCmp (NewCanonicalPath
, "/") == 0) {
446 FreePool (NewCanonicalPath
);
447 FreePool (NewVirtioFsFile
);
448 return OpenRootDirectory (VirtioFs
, NewHandle
, OpenForWriting
);
452 // Split the new canonical pathname into most specific parent directory
453 // (given by DirNodeId) and last pathname component (i.e., immediate child
454 // within that parent directory).
456 Status
= VirtioFsLookupMostSpecificParentDir (
462 if (EFI_ERROR (Status
)) {
463 goto FreeNewCanonicalPath
;
467 // Set NewNodeIsDirectory to suppress incorrect compiler/analyzer warnings.
469 NewNodeIsDirectory
= FALSE
;
472 // Try to open LastComponent directly under DirNodeId, as an existent regular
473 // file or directory.
475 Status
= OpenExistentFileOrDirectory (
485 // If LastComponent could not be found under DirNodeId, but the request
486 // allows us to create a new entry, attempt creating the requested regular
487 // file or directory.
489 if ((Status
== EFI_NOT_FOUND
) && PermitCreation
) {
490 ASSERT (OpenForWriting
);
491 if (CreateDirectoryIfCreating
) {
492 Status
= CreateDirectory (
500 Status
= CreateRegularFile (
509 NewNodeIsDirectory
= CreateDirectoryIfCreating
;
513 // Regardless of the branch taken, we're done with DirNodeId.
515 if (DirNodeId
!= VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID
) {
516 VirtioFsFuseForget (VirtioFs
, DirNodeId
);
519 if (EFI_ERROR (Status
)) {
520 goto FreeNewCanonicalPath
;
524 // Populate the new VIRTIO_FS_FILE object.
526 NewVirtioFsFile
->Signature
= VIRTIO_FS_FILE_SIG
;
527 NewVirtioFsFile
->SimpleFile
.Revision
= EFI_FILE_PROTOCOL_REVISION
;
528 NewVirtioFsFile
->SimpleFile
.Open
= VirtioFsSimpleFileOpen
;
529 NewVirtioFsFile
->SimpleFile
.Close
= VirtioFsSimpleFileClose
;
530 NewVirtioFsFile
->SimpleFile
.Delete
= VirtioFsSimpleFileDelete
;
531 NewVirtioFsFile
->SimpleFile
.Read
= VirtioFsSimpleFileRead
;
532 NewVirtioFsFile
->SimpleFile
.Write
= VirtioFsSimpleFileWrite
;
533 NewVirtioFsFile
->SimpleFile
.GetPosition
= VirtioFsSimpleFileGetPosition
;
534 NewVirtioFsFile
->SimpleFile
.SetPosition
= VirtioFsSimpleFileSetPosition
;
535 NewVirtioFsFile
->SimpleFile
.GetInfo
= VirtioFsSimpleFileGetInfo
;
536 NewVirtioFsFile
->SimpleFile
.SetInfo
= VirtioFsSimpleFileSetInfo
;
537 NewVirtioFsFile
->SimpleFile
.Flush
= VirtioFsSimpleFileFlush
;
538 NewVirtioFsFile
->IsDirectory
= NewNodeIsDirectory
;
539 NewVirtioFsFile
->IsOpenForWriting
= OpenForWriting
;
540 NewVirtioFsFile
->OwnerFs
= VirtioFs
;
541 NewVirtioFsFile
->CanonicalPathname
= NewCanonicalPath
;
542 NewVirtioFsFile
->FilePosition
= 0;
543 NewVirtioFsFile
->NodeId
= NewNodeId
;
544 NewVirtioFsFile
->FuseHandle
= NewFuseHandle
;
545 NewVirtioFsFile
->FileInfoArray
= NULL
;
546 NewVirtioFsFile
->SingleFileInfoSize
= 0;
547 NewVirtioFsFile
->NumFileInfo
= 0;
548 NewVirtioFsFile
->NextFileInfo
= 0;
551 // One more file is now open for the filesystem.
553 InsertTailList (&VirtioFs
->OpenFiles
, &NewVirtioFsFile
->OpenFilesEntry
);
555 *NewHandle
= &NewVirtioFsFile
->SimpleFile
;
558 FreeNewCanonicalPath
:
559 FreePool (NewCanonicalPath
);
562 FreePool (NewVirtioFsFile
);