]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/VirtioFsDxe/SimpleFsOpen.c
OvmfPkg: Apply uncrustify changes
[mirror_edk2.git] / OvmfPkg / VirtioFsDxe / SimpleFsOpen.c
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 **/
38 STATIC
39 EFI_STATUS
40 OpenRootDirectory (
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 **/
137 STATIC
138 EFI_STATUS
139 OpenExistentFileOrDirectory (
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
204 ForgetResolvedNodeId:
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 **/
232 STATIC
233 EFI_STATUS
234 CreateDirectory (
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
260 RemoveNewChildDir:
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 **/
289 STATIC
290 EFI_STATUS
291 CreateRegularFile (
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
308 EFI_STATUS
309 EFIAPI
310 VirtioFsSimpleFileOpen (
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 __FUNCTION__,
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).
399 //
400 if (!VirtioFsFile->IsDirectory) {
401 DEBUG ((
402 DEBUG_ERROR,
403 ("%a: Label=\"%s\" CanonicalPathname=\"%a\" FileName=\"%s\": "
404 "nonsensical request to open a file or directory relative to a regular "
405 "file\n"),
406 __FUNCTION__,
407 VirtioFs->Label,
408 VirtioFsFile->CanonicalPathname,
409 FileName
410 ));
411 return EFI_INVALID_PARAMETER;
412 }
413
414 //
415 // Allocate the new VIRTIO_FS_FILE object.
416 //
417 NewVirtioFsFile = AllocatePool (sizeof *NewVirtioFsFile);
418 if (NewVirtioFsFile == NULL) {
419 return EFI_OUT_OF_RESOURCES;
420 }
421
422 //
423 // Create the canonical pathname at which the desired file is expected to
424 // exist.
425 //
426 Status = VirtioFsAppendPath (
427 VirtioFsFile->CanonicalPathname,
428 FileName,
429 &NewCanonicalPath,
430 &RootEscape
431 );
432 if (EFI_ERROR (Status)) {
433 goto FreeNewVirtioFsFile;
434 }
435
436 if (RootEscape) {
437 Status = EFI_ACCESS_DENIED;
438 goto FreeNewCanonicalPath;
439 }
440
441 //
442 // If the desired file is the root directory, just open the volume one more
443 // time, without looking up anything.
444 //
445 if (AsciiStrCmp (NewCanonicalPath, "/") == 0) {
446 FreePool (NewCanonicalPath);
447 FreePool (NewVirtioFsFile);
448 return OpenRootDirectory (VirtioFs, NewHandle, OpenForWriting);
449 }
450
451 //
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).
455 //
456 Status = VirtioFsLookupMostSpecificParentDir (
457 VirtioFs,
458 NewCanonicalPath,
459 &DirNodeId,
460 &LastComponent
461 );
462 if (EFI_ERROR (Status)) {
463 goto FreeNewCanonicalPath;
464 }
465
466 //
467 // Set NewNodeIsDirectory to suppress incorrect compiler/analyzer warnings.
468 //
469 NewNodeIsDirectory = FALSE;
470
471 //
472 // Try to open LastComponent directly under DirNodeId, as an existent regular
473 // file or directory.
474 //
475 Status = OpenExistentFileOrDirectory (
476 VirtioFs,
477 DirNodeId,
478 LastComponent,
479 OpenForWriting,
480 &NewNodeId,
481 &NewFuseHandle,
482 &NewNodeIsDirectory
483 );
484 //
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.
488 //
489 if ((Status == EFI_NOT_FOUND) && PermitCreation) {
490 ASSERT (OpenForWriting);
491 if (CreateDirectoryIfCreating) {
492 Status = CreateDirectory (
493 VirtioFs,
494 DirNodeId,
495 LastComponent,
496 &NewNodeId,
497 &NewFuseHandle
498 );
499 } else {
500 Status = CreateRegularFile (
501 VirtioFs,
502 DirNodeId,
503 LastComponent,
504 &NewNodeId,
505 &NewFuseHandle
506 );
507 }
508
509 NewNodeIsDirectory = CreateDirectoryIfCreating;
510 }
511
512 //
513 // Regardless of the branch taken, we're done with DirNodeId.
514 //
515 if (DirNodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
516 VirtioFsFuseForget (VirtioFs, DirNodeId);
517 }
518
519 if (EFI_ERROR (Status)) {
520 goto FreeNewCanonicalPath;
521 }
522
523 //
524 // Populate the new VIRTIO_FS_FILE object.
525 //
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;
549
550 //
551 // One more file is now open for the filesystem.
552 //
553 InsertTailList (&VirtioFs->OpenFiles, &NewVirtioFsFile->OpenFilesEntry);
554
555 *NewHandle = &NewVirtioFsFile->SimpleFile;
556 return EFI_SUCCESS;
557
558 FreeNewCanonicalPath:
559 FreePool (NewCanonicalPath);
560
561 FreeNewVirtioFsFile:
562 FreePool (NewVirtioFsFile);
563
564 return Status;
565 }