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