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 (VirtioFs
, VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID
,
62 if (EFI_ERROR (Status
)) {
65 Status
= VirtioFsFuseAttrToEfiFileInfo (&FuseAttr
, &FileInfo
);
66 if (EFI_ERROR (Status
)) {
69 if ((FileInfo
.Attribute
& EFI_FILE_READ_ONLY
) != 0) {
70 return EFI_ACCESS_DENIED
;
74 Status
= VirtioFsOpenVolume (&VirtioFs
->SimpleFs
, NewHandle
);
75 if (EFI_ERROR (Status
)) {
79 NewVirtioFsFile
= VIRTIO_FS_FILE_FROM_SIMPLE_FILE (*NewHandle
);
80 NewVirtioFsFile
->IsOpenForWriting
= OpenForWriting
;
85 Open an existent regular file or non-root directory.
87 @param[in,out] VirtioFs The Virtio Filesystem device on which the
88 regular file or directory should be opened.
90 @param[in] DirNodeId The inode number of the immediate parent
91 directory of the regular file or directory to
94 @param[in] Name The single-component filename of the regular
95 file or directory to open, under the immediate
96 parent directory identified by DirNodeId.
98 @param[in] OpenForWriting TRUE if the regular file or directory should be
99 opened for read-write access. FALSE if the
100 regular file or directory should be opened for
101 read-only access. Opening a directory for
102 read-write access is useful for deleting,
103 renaming, syncing or touching the directory
106 @param[out] NodeId The inode number of the regular file or
107 directory, returned by the Virtio Filesystem
110 @param[out] FuseHandle The open handle to the regular file or
111 directory, returned by the Virtio Filesystem
114 @param[out] NodeIsDirectory Set to TRUE on output if Name was found to refer
115 to a directory. Set to FALSE if Name was found
116 to refer to a regular file.
118 @retval EFI_SUCCESS The regular file or directory has been looked up
119 and opened successfully.
121 @retval EFI_ACCESS_DENIED OpenForWriting is TRUE, but the regular file or
122 directory is marked read-only.
124 @retval EFI_NOT_FOUND A directory entry called Name was not found in the
125 directory identified by DirNodeId. (EFI_NOT_FOUND
126 is not returned for any other condition.)
128 @return Errors propagated from underlying functions. If
129 the error code to propagate were EFI_NOT_FOUND, it
130 is remapped to EFI_DEVICE_ERROR.
134 OpenExistentFileOrDirectory (
135 IN OUT VIRTIO_FS
*VirtioFs
,
138 IN BOOLEAN OpenForWriting
,
140 OUT UINT64
*FuseHandle
,
141 OUT BOOLEAN
*NodeIsDirectory
145 UINT64 ResolvedNodeId
;
146 VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr
;
147 EFI_FILE_INFO FileInfo
;
149 UINT64 NewFuseHandle
;
151 Status
= VirtioFsFuseLookup (VirtioFs
, DirNodeId
, Name
, &ResolvedNodeId
,
153 if (EFI_ERROR (Status
)) {
156 Status
= VirtioFsFuseAttrToEfiFileInfo (&FuseAttr
, &FileInfo
);
157 if (EFI_ERROR (Status
)) {
158 goto ForgetResolvedNodeId
;
161 if (OpenForWriting
&& (FileInfo
.Attribute
& EFI_FILE_READ_ONLY
) != 0) {
162 Status
= EFI_ACCESS_DENIED
;
163 goto ForgetResolvedNodeId
;
166 IsDirectory
= (BOOLEAN
)((FileInfo
.Attribute
& EFI_FILE_DIRECTORY
) != 0);
169 // If OpenForWriting is TRUE here, that's not passed to
170 // VirtioFsFuseOpenDir(); it does not affect the FUSE_OPENDIR request we
171 // send. OpenForWriting=TRUE will only permit attempts to delete, rename,
172 // flush (sync), and touch the directory.
174 Status
= VirtioFsFuseOpenDir (VirtioFs
, ResolvedNodeId
, &NewFuseHandle
);
176 Status
= VirtioFsFuseOpen (VirtioFs
, ResolvedNodeId
, OpenForWriting
,
179 if (EFI_ERROR (Status
)) {
180 goto ForgetResolvedNodeId
;
183 *NodeId
= ResolvedNodeId
;
184 *FuseHandle
= NewFuseHandle
;
185 *NodeIsDirectory
= IsDirectory
;
188 ForgetResolvedNodeId
:
189 VirtioFsFuseForget (VirtioFs
, ResolvedNodeId
);
190 return (Status
== EFI_NOT_FOUND
) ? EFI_DEVICE_ERROR
: Status
;
196 @param[in,out] VirtioFs The Virtio Filesystem device on which the directory
199 @param[in] DirNodeId The inode number of the immediate parent directory
200 of the directory to create.
202 @param[in] Name The single-component filename of the directory to
203 create, under the immediate parent directory
204 identified by DirNodeId.
206 @param[out] NodeId The inode number of the directory created, returned
207 by the Virtio Filesystem device.
209 @param[out] FuseHandle The open handle to the directory created, returned
210 by the Virtio Filesystem device.
212 @retval EFI_SUCCESS The directory has been created successfully.
214 @return Errors propagated from underlying functions.
219 IN OUT VIRTIO_FS
*VirtioFs
,
223 OUT UINT64
*FuseHandle
227 UINT64 NewChildDirNodeId
;
228 UINT64 NewFuseHandle
;
230 Status
= VirtioFsFuseMkDir (VirtioFs
, DirNodeId
, Name
, &NewChildDirNodeId
);
231 if (EFI_ERROR (Status
)) {
235 Status
= VirtioFsFuseOpenDir (VirtioFs
, NewChildDirNodeId
, &NewFuseHandle
);
236 if (EFI_ERROR (Status
)) {
237 goto RemoveNewChildDir
;
240 *NodeId
= NewChildDirNodeId
;
241 *FuseHandle
= NewFuseHandle
;
245 VirtioFsFuseRemoveFileOrDir (VirtioFs
, DirNodeId
, Name
, TRUE
/* IsDir */);
246 VirtioFsFuseForget (VirtioFs
, NewChildDirNodeId
);
251 Create a regular file.
253 @param[in,out] VirtioFs The Virtio Filesystem device on which the regular
254 file should be created.
256 @param[in] DirNodeId The inode number of the immediate parent directory
257 of the regular file to create.
259 @param[in] Name The single-component filename of the regular file to
260 create, under the immediate parent directory
261 identified by DirNodeId.
263 @param[out] NodeId The inode number of the regular file created,
264 returned by the Virtio Filesystem device.
266 @param[out] FuseHandle The open handle to the regular file created,
267 returned by the Virtio Filesystem device.
269 @retval EFI_SUCCESS The regular file has been created successfully.
271 @return Errors propagated from underlying functions.
276 IN OUT VIRTIO_FS
*VirtioFs
,
280 OUT UINT64
*FuseHandle
283 return VirtioFsFuseOpenOrCreate (VirtioFs
, DirNodeId
, Name
, NodeId
,
289 VirtioFsSimpleFileOpen (
290 IN EFI_FILE_PROTOCOL
*This
,
291 OUT EFI_FILE_PROTOCOL
**NewHandle
,
297 VIRTIO_FS_FILE
*VirtioFsFile
;
299 BOOLEAN OpenForWriting
;
300 BOOLEAN PermitCreation
;
301 BOOLEAN CreateDirectoryIfCreating
;
302 VIRTIO_FS_FILE
*NewVirtioFsFile
;
304 CHAR8
*NewCanonicalPath
;
307 CHAR8
*LastComponent
;
309 UINT64 NewFuseHandle
;
310 BOOLEAN NewNodeIsDirectory
;
312 VirtioFsFile
= VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This
);
313 VirtioFs
= VirtioFsFile
->OwnerFs
;
316 // Validate OpenMode.
319 case EFI_FILE_MODE_READ
:
320 OpenForWriting
= FALSE
;
321 PermitCreation
= FALSE
;
323 case EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
:
324 OpenForWriting
= TRUE
;
325 PermitCreation
= FALSE
;
327 case EFI_FILE_MODE_READ
| EFI_FILE_MODE_WRITE
| EFI_FILE_MODE_CREATE
:
328 OpenForWriting
= TRUE
;
329 PermitCreation
= TRUE
;
332 return EFI_INVALID_PARAMETER
;
336 // Set CreateDirectoryIfCreating to suppress incorrect compiler/analyzer
339 CreateDirectoryIfCreating
= FALSE
;
342 // Validate the Attributes requested for the case when the file ends up being
343 // created, provided creation is permitted.
345 if (PermitCreation
) {
346 if ((Attributes
& ~EFI_FILE_VALID_ATTR
) != 0) {
348 // Unknown attribute requested.
350 return EFI_INVALID_PARAMETER
;
353 ASSERT (OpenForWriting
);
354 if ((Attributes
& EFI_FILE_READ_ONLY
) != 0) {
357 ("%a: Label=\"%s\" CanonicalPathname=\"%a\" FileName=\"%s\" "
358 "OpenMode=0x%Lx Attributes=0x%Lx: nonsensical request to possibly "
359 "create a file marked read-only, for read-write access\n"),
362 VirtioFsFile
->CanonicalPathname
,
367 return EFI_INVALID_PARAMETER
;
369 CreateDirectoryIfCreating
= (BOOLEAN
)((Attributes
&
370 EFI_FILE_DIRECTORY
) != 0);
374 // Referring to a file relative to a regular file makes no sense (or at least
375 // it cannot be implemented consistently with how a file is referred to
376 // relative to a directory).
378 if (!VirtioFsFile
->IsDirectory
) {
381 ("%a: Label=\"%s\" CanonicalPathname=\"%a\" FileName=\"%s\": "
382 "nonsensical request to open a file or directory relative to a regular "
386 VirtioFsFile
->CanonicalPathname
,
389 return EFI_INVALID_PARAMETER
;
393 // Allocate the new VIRTIO_FS_FILE object.
395 NewVirtioFsFile
= AllocatePool (sizeof *NewVirtioFsFile
);
396 if (NewVirtioFsFile
== NULL
) {
397 return EFI_OUT_OF_RESOURCES
;
401 // Create the canonical pathname at which the desired file is expected to
404 Status
= VirtioFsAppendPath (VirtioFsFile
->CanonicalPathname
, FileName
,
405 &NewCanonicalPath
, &RootEscape
);
406 if (EFI_ERROR (Status
)) {
407 goto FreeNewVirtioFsFile
;
410 Status
= EFI_ACCESS_DENIED
;
411 goto FreeNewCanonicalPath
;
415 // If the desired file is the root directory, just open the volume one more
416 // time, without looking up anything.
418 if (AsciiStrCmp (NewCanonicalPath
, "/") == 0) {
419 FreePool (NewCanonicalPath
);
420 FreePool (NewVirtioFsFile
);
421 return OpenRootDirectory (VirtioFs
, NewHandle
, OpenForWriting
);
425 // Split the new canonical pathname into most specific parent directory
426 // (given by DirNodeId) and last pathname component (i.e., immediate child
427 // within that parent directory).
429 Status
= VirtioFsLookupMostSpecificParentDir (VirtioFs
, NewCanonicalPath
,
430 &DirNodeId
, &LastComponent
);
431 if (EFI_ERROR (Status
)) {
432 goto FreeNewCanonicalPath
;
436 // Set NewNodeIsDirectory to suppress incorrect compiler/analyzer warnings.
438 NewNodeIsDirectory
= FALSE
;
441 // Try to open LastComponent directly under DirNodeId, as an existent regular
442 // file or directory.
444 Status
= OpenExistentFileOrDirectory (VirtioFs
, DirNodeId
, LastComponent
,
445 OpenForWriting
, &NewNodeId
, &NewFuseHandle
, &NewNodeIsDirectory
);
447 // If LastComponent could not be found under DirNodeId, but the request
448 // allows us to create a new entry, attempt creating the requested regular
449 // file or directory.
451 if (Status
== EFI_NOT_FOUND
&& PermitCreation
) {
452 ASSERT (OpenForWriting
);
453 if (CreateDirectoryIfCreating
) {
454 Status
= CreateDirectory (VirtioFs
, DirNodeId
, LastComponent
, &NewNodeId
,
457 Status
= CreateRegularFile (VirtioFs
, DirNodeId
, LastComponent
,
458 &NewNodeId
, &NewFuseHandle
);
460 NewNodeIsDirectory
= CreateDirectoryIfCreating
;
464 // Regardless of the branch taken, we're done with DirNodeId.
466 if (DirNodeId
!= VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID
) {
467 VirtioFsFuseForget (VirtioFs
, DirNodeId
);
470 if (EFI_ERROR (Status
)) {
471 goto FreeNewCanonicalPath
;
475 // Populate the new VIRTIO_FS_FILE object.
477 NewVirtioFsFile
->Signature
= VIRTIO_FS_FILE_SIG
;
478 NewVirtioFsFile
->SimpleFile
.Revision
= EFI_FILE_PROTOCOL_REVISION
;
479 NewVirtioFsFile
->SimpleFile
.Open
= VirtioFsSimpleFileOpen
;
480 NewVirtioFsFile
->SimpleFile
.Close
= VirtioFsSimpleFileClose
;
481 NewVirtioFsFile
->SimpleFile
.Delete
= VirtioFsSimpleFileDelete
;
482 NewVirtioFsFile
->SimpleFile
.Read
= VirtioFsSimpleFileRead
;
483 NewVirtioFsFile
->SimpleFile
.Write
= VirtioFsSimpleFileWrite
;
484 NewVirtioFsFile
->SimpleFile
.GetPosition
= VirtioFsSimpleFileGetPosition
;
485 NewVirtioFsFile
->SimpleFile
.SetPosition
= VirtioFsSimpleFileSetPosition
;
486 NewVirtioFsFile
->SimpleFile
.GetInfo
= VirtioFsSimpleFileGetInfo
;
487 NewVirtioFsFile
->SimpleFile
.SetInfo
= VirtioFsSimpleFileSetInfo
;
488 NewVirtioFsFile
->SimpleFile
.Flush
= VirtioFsSimpleFileFlush
;
489 NewVirtioFsFile
->IsDirectory
= NewNodeIsDirectory
;
490 NewVirtioFsFile
->IsOpenForWriting
= OpenForWriting
;
491 NewVirtioFsFile
->OwnerFs
= VirtioFs
;
492 NewVirtioFsFile
->CanonicalPathname
= NewCanonicalPath
;
493 NewVirtioFsFile
->FilePosition
= 0;
494 NewVirtioFsFile
->NodeId
= NewNodeId
;
495 NewVirtioFsFile
->FuseHandle
= NewFuseHandle
;
496 NewVirtioFsFile
->FileInfoArray
= NULL
;
497 NewVirtioFsFile
->SingleFileInfoSize
= 0;
498 NewVirtioFsFile
->NumFileInfo
= 0;
499 NewVirtioFsFile
->NextFileInfo
= 0;
502 // One more file is now open for the filesystem.
504 InsertTailList (&VirtioFs
->OpenFiles
, &NewVirtioFsFile
->OpenFilesEntry
);
506 *NewHandle
= &NewVirtioFsFile
->SimpleFile
;
509 FreeNewCanonicalPath
:
510 FreePool (NewCanonicalPath
);
513 FreePool (NewVirtioFsFile
);