]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/VirtioFsDxe/SimpleFsSetInfo.c
OvmfPkg: Apply uncrustify changes
[mirror_edk2.git] / OvmfPkg / VirtioFsDxe / SimpleFsSetInfo.c
1 /** @file
2 EFI_FILE_PROTOCOL.SetInfo() 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 <Guid/FileSystemInfo.h> // gEfiFileSystemInfoGuid
10 #include <Guid/FileSystemVolumeLabelInfo.h> // gEfiFileSystemVolumeLabelInfo...
11 #include <Library/BaseLib.h> // StrCmp()
12 #include <Library/BaseMemoryLib.h> // CompareGuid()
13 #include <Library/MemoryAllocationLib.h> // FreePool()
14
15 #include "VirtioFsDxe.h"
16
17 /**
18 Validate a buffer that the EFI_FILE_PROTOCOL.SetInfo() caller passes in for a
19 particular InformationType GUID.
20
21 The structure to be validated is supposed to end with a variable-length,
22 NUL-terminated CHAR16 Name string.
23
24 @param[in] SizeByProtocolCaller The BufferSize parameter as provided by the
25 EFI_FILE_PROTOCOL.SetInfo() caller.
26
27 @param[in] MinimumStructSize The minimum structure size that is required
28 for the given InformationType GUID,
29 including a single CHAR16 element from the
30 trailing Name field.
31
32 @param[in] IsSizeByInfoPresent TRUE if and only if the expected structure
33 starts with a UINT64 Size field that reports
34 the actual structure size.
35
36 @param[in] Buffer The Buffer parameter as provided by the
37 EFI_FILE_PROTOCOL.SetInfo() caller.
38
39 @retval EFI_SUCCESS Validation successful, Buffer is well-formed.
40
41 @retval EFI_BAD_BUFFER_SIZE The EFI_FILE_PROTOCOL.SetInfo()
42 caller provided a BufferSize that is smaller
43 than the minimum structure size required for
44 the given InformationType GUID.
45
46 @retval EFI_INVALID_PARAMETER IsSizeByInfoPresent is TRUE, and the leading
47 UINT64 Size field does not match the
48 EFI_FILE_PROTOCOL.SetInfo() caller-provided
49 BufferSize.
50
51 @retval EFI_INVALID_PARAMETER The trailing Name field does not consist of a
52 whole multiple of CHAR16 elements.
53
54 @retval EFI_INVALID_PARAMETER The trailing Name field is not NUL-terminated.
55 **/
56 STATIC
57 EFI_STATUS
58 ValidateInfoStructure (
59 IN UINTN SizeByProtocolCaller,
60 IN UINTN MinimumStructSize,
61 IN BOOLEAN IsSizeByInfoPresent,
62 IN VOID *Buffer
63 )
64 {
65 UINTN NameFieldByteOffset;
66 UINTN NameFieldBytes;
67 UINTN NameFieldChar16s;
68 CHAR16 *NameField;
69
70 //
71 // Make sure the internal function asking for validation passes in sane
72 // values.
73 //
74 ASSERT (MinimumStructSize >= sizeof (CHAR16));
75 NameFieldByteOffset = MinimumStructSize - sizeof (CHAR16);
76
77 if (IsSizeByInfoPresent) {
78 ASSERT (MinimumStructSize >= sizeof (UINT64) + sizeof (CHAR16));
79 ASSERT (NameFieldByteOffset >= sizeof (UINT64));
80 }
81
82 //
83 // Check whether the protocol caller provided enough bytes for the minimum
84 // size of this info structure.
85 //
86 if (SizeByProtocolCaller < MinimumStructSize) {
87 return EFI_BAD_BUFFER_SIZE;
88 }
89
90 //
91 // If the info structure starts with a UINT64 Size field, check if that
92 // agrees with the protocol caller-provided size.
93 //
94 if (IsSizeByInfoPresent) {
95 UINT64 *SizeByInfo;
96
97 SizeByInfo = Buffer;
98 if (*SizeByInfo != SizeByProtocolCaller) {
99 return EFI_INVALID_PARAMETER;
100 }
101 }
102
103 //
104 // The CHAR16 Name field at the end of the structure must have an even number
105 // of bytes.
106 //
107 // The subtraction below cannot underflow, and yields at least
108 // sizeof(CHAR16).
109 //
110 ASSERT (SizeByProtocolCaller >= NameFieldByteOffset);
111 NameFieldBytes = SizeByProtocolCaller - NameFieldByteOffset;
112 ASSERT (NameFieldBytes >= sizeof (CHAR16));
113 if (NameFieldBytes % sizeof (CHAR16) != 0) {
114 return EFI_INVALID_PARAMETER;
115 }
116
117 //
118 // The CHAR16 Name field at the end of the structure must be NUL-terminated.
119 //
120 NameFieldChar16s = NameFieldBytes / sizeof (CHAR16);
121 ASSERT (NameFieldChar16s >= 1);
122
123 NameField = (CHAR16 *)((UINT8 *)Buffer + NameFieldByteOffset);
124 if (NameField[NameFieldChar16s - 1] != L'\0') {
125 return EFI_INVALID_PARAMETER;
126 }
127
128 return EFI_SUCCESS;
129 }
130
131 /**
132 Rename a VIRTIO_FS_FILE as requested in EFI_FILE_INFO.FileName.
133
134 @param[in,out] VirtioFsFile The VIRTIO_FS_FILE to rename.
135
136 @param[in] NewFileName The new file name requested by
137 EFI_FILE_PROTOCOL.SetInfo().
138
139 @retval EFI_SUCCESS The canonical format destination path that is
140 determined from the input value of
141 VirtioFsFile->CanonicalPathname and from
142 NewFileName is identical to the input value of
143 VirtioFsFile->CanonicalPathname. This means that
144 EFI_FILE_INFO does not constitute a rename
145 request. VirtioFsFile has not been changed.
146
147 @retval EFI_SUCCESS VirtioFsFile has been renamed.
148 VirtioFsFile->CanonicalPathname has assumed the
149 destination pathname in canonical format.
150
151 @retval EFI_ACCESS_DENIED VirtioFsFile refers to the root directory, and
152 NewFileName expresses an actual rename/move
153 request.
154
155 @retval EFI_ACCESS_DENIED VirtioFsFile is the (possibly indirect) parent
156 directory of at least one other VIRTIO_FS_FILE
157 that is open for the same Virtio Filesystem
158 (identified by VirtioFsFile->OwnerFs). Renaming
159 VirtioFsFile would invalidate the canonical
160 pathnames of those VIRTIO_FS_FILE instances;
161 therefore the request has been rejected.
162
163 @retval EFI_ACCESS_DENIED VirtioFsFile is not open for writing, but
164 NewFileName expresses an actual rename/move
165 request.
166
167 @retval EFI_NOT_FOUND At least one dot-dot component in NewFileName
168 attempted to escape the root directory.
169
170 @return Error codes propagated from underlying functions.
171 **/
172 STATIC
173 EFI_STATUS
174 Rename (
175 IN OUT VIRTIO_FS_FILE *VirtioFsFile,
176 IN CHAR16 *NewFileName
177 )
178 {
179 VIRTIO_FS *VirtioFs;
180 EFI_STATUS Status;
181 CHAR8 *Destination;
182 BOOLEAN RootEscape;
183 UINT64 OldParentDirNodeId;
184 CHAR8 *OldLastComponent;
185 UINT64 NewParentDirNodeId;
186 CHAR8 *NewLastComponent;
187
188 VirtioFs = VirtioFsFile->OwnerFs;
189
190 //
191 // The root directory cannot be renamed.
192 //
193 if (AsciiStrCmp (VirtioFsFile->CanonicalPathname, "/") == 0) {
194 if (StrCmp (NewFileName, L"") == 0) {
195 //
196 // Not a rename request anyway.
197 //
198 return EFI_SUCCESS;
199 }
200
201 return EFI_ACCESS_DENIED;
202 }
203
204 //
205 // Compose the canonical pathname for the destination.
206 //
207 Status = VirtioFsComposeRenameDestination (
208 VirtioFsFile->CanonicalPathname,
209 NewFileName,
210 &Destination,
211 &RootEscape
212 );
213 if (EFI_ERROR (Status)) {
214 return Status;
215 }
216
217 if (RootEscape) {
218 Status = EFI_NOT_FOUND;
219 goto FreeDestination;
220 }
221
222 //
223 // If the rename would leave VirtioFsFile->CanonicalPathname unchanged, then
224 // EFI_FILE_PROTOCOL.SetInfo() isn't asking for a rename actually.
225 //
226 if (AsciiStrCmp (VirtioFsFile->CanonicalPathname, Destination) == 0) {
227 Status = EFI_SUCCESS;
228 goto FreeDestination;
229 }
230
231 //
232 // Check if the rename would break the canonical pathnames of other
233 // VIRTIO_FS_FILE instances of the same VIRTIO_FS.
234 //
235 if (VirtioFsFile->IsDirectory) {
236 UINTN PathLen;
237 LIST_ENTRY *OpenFilesEntry;
238
239 PathLen = AsciiStrLen (VirtioFsFile->CanonicalPathname);
240 BASE_LIST_FOR_EACH (OpenFilesEntry, &VirtioFs->OpenFiles) {
241 VIRTIO_FS_FILE *OtherFile;
242
243 OtherFile = VIRTIO_FS_FILE_FROM_OPEN_FILES_ENTRY (OpenFilesEntry);
244 if ((OtherFile != VirtioFsFile) &&
245 (AsciiStrnCmp (
246 VirtioFsFile->CanonicalPathname,
247 OtherFile->CanonicalPathname,
248 PathLen
249 ) == 0) &&
250 ((OtherFile->CanonicalPathname[PathLen] == '\0') ||
251 (OtherFile->CanonicalPathname[PathLen] == '/')))
252 {
253 //
254 // OtherFile refers to the same directory as VirtioFsFile, or is a
255 // (possibly indirect) child of the directory referred to by
256 // VirtioFsFile.
257 //
258 Status = EFI_ACCESS_DENIED;
259 goto FreeDestination;
260 }
261 }
262 }
263
264 //
265 // From this point on, the file needs to be open for writing.
266 //
267 if (!VirtioFsFile->IsOpenForWriting) {
268 Status = EFI_ACCESS_DENIED;
269 goto FreeDestination;
270 }
271
272 //
273 // Split both source and destination canonical pathnames into (most specific
274 // parent directory, last component) pairs.
275 //
276 Status = VirtioFsLookupMostSpecificParentDir (
277 VirtioFs,
278 VirtioFsFile->CanonicalPathname,
279 &OldParentDirNodeId,
280 &OldLastComponent
281 );
282 if (EFI_ERROR (Status)) {
283 goto FreeDestination;
284 }
285
286 Status = VirtioFsLookupMostSpecificParentDir (
287 VirtioFs,
288 Destination,
289 &NewParentDirNodeId,
290 &NewLastComponent
291 );
292 if (EFI_ERROR (Status)) {
293 goto ForgetOldParentDirNodeId;
294 }
295
296 //
297 // Perform the rename. If the destination path exists, the rename will fail.
298 //
299 Status = VirtioFsFuseRename (
300 VirtioFs,
301 OldParentDirNodeId,
302 OldLastComponent,
303 NewParentDirNodeId,
304 NewLastComponent
305 );
306 if (EFI_ERROR (Status)) {
307 goto ForgetNewParentDirNodeId;
308 }
309
310 //
311 // Swap in the new canonical pathname.
312 //
313 FreePool (VirtioFsFile->CanonicalPathname);
314 VirtioFsFile->CanonicalPathname = Destination;
315 Destination = NULL;
316 Status = EFI_SUCCESS;
317
318 //
319 // Fall through.
320 //
321 ForgetNewParentDirNodeId:
322 if (NewParentDirNodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
323 VirtioFsFuseForget (VirtioFs, NewParentDirNodeId);
324 }
325
326 ForgetOldParentDirNodeId:
327 if (OldParentDirNodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
328 VirtioFsFuseForget (VirtioFs, OldParentDirNodeId);
329 }
330
331 FreeDestination:
332 if (Destination != NULL) {
333 FreePool (Destination);
334 }
335
336 return Status;
337 }
338
339 /**
340 Update the attributes of a VIRTIO_FS_FILE as requested in EFI_FILE_INFO.
341
342 @param[in,out] VirtioFsFile The VIRTIO_FS_FILE to update the attributes of.
343
344 @param[in] NewFileInfo The new attributes requested by
345 EFI_FILE_PROTOCOL.SetInfo(). NewFileInfo->Size
346 and NewFileInfo->FileName are ignored.
347
348 @retval EFI_SUCCESS No attributes had to be updated.
349
350 @retval EFI_SUCCESS The required set of attribute updates has been
351 determined and performed successfully.
352
353 @retval EFI_ACCESS_DENIED NewFileInfo requests an update to a property
354 different from the EFI_FILE_READ_ONLY bit in the
355 Attribute field, but VirtioFsFile is not open for
356 writing.
357
358 @return Error codes propagated from underlying functions.
359 **/
360 STATIC
361 EFI_STATUS
362 UpdateAttributes (
363 IN OUT VIRTIO_FS_FILE *VirtioFsFile,
364 IN EFI_FILE_INFO *NewFileInfo
365 )
366 {
367 VIRTIO_FS *VirtioFs;
368 EFI_STATUS Status;
369 VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
370 EFI_FILE_INFO FileInfo;
371 BOOLEAN UpdateFileSize;
372 UINT64 FileSize;
373 BOOLEAN UpdateAtime;
374 BOOLEAN UpdateMtime;
375 UINT64 Atime;
376 UINT64 Mtime;
377 BOOLEAN UpdateMode;
378 UINT32 Mode;
379
380 VirtioFs = VirtioFsFile->OwnerFs;
381
382 //
383 // Fetch the current attributes first, so we can build the difference between
384 // them and NewFileInfo.
385 //
386 Status = VirtioFsFuseGetAttr (VirtioFs, VirtioFsFile->NodeId, &FuseAttr);
387 if (EFI_ERROR (Status)) {
388 return Status;
389 }
390
391 Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, &FileInfo);
392 if (EFI_ERROR (Status)) {
393 return Status;
394 }
395
396 //
397 // Collect the updates.
398 //
399 if (VirtioFsFile->IsDirectory) {
400 UpdateFileSize = FALSE;
401 } else {
402 VirtioFsGetFuseSizeUpdate (
403 &FileInfo,
404 NewFileInfo,
405 &UpdateFileSize,
406 &FileSize
407 );
408 }
409
410 Status = VirtioFsGetFuseTimeUpdates (
411 &FileInfo,
412 NewFileInfo,
413 &UpdateAtime,
414 &UpdateMtime,
415 &Atime,
416 &Mtime
417 );
418 if (EFI_ERROR (Status)) {
419 return Status;
420 }
421
422 Status = VirtioFsGetFuseModeUpdate (
423 &FileInfo,
424 NewFileInfo,
425 &UpdateMode,
426 &Mode
427 );
428 if (EFI_ERROR (Status)) {
429 return Status;
430 }
431
432 //
433 // If no attribute updates are necessary, we're done.
434 //
435 if (!UpdateFileSize && !UpdateAtime && !UpdateMtime && !UpdateMode) {
436 return EFI_SUCCESS;
437 }
438
439 //
440 // If the file is not open for writing, then only Mode may be updated (for
441 // toggling EFI_FILE_READ_ONLY).
442 //
443 if (!VirtioFsFile->IsOpenForWriting &&
444 (UpdateFileSize || UpdateAtime || UpdateMtime))
445 {
446 return EFI_ACCESS_DENIED;
447 }
448
449 //
450 // Send the FUSE_SETATTR request now.
451 //
452 Status = VirtioFsFuseSetAttr (
453 VirtioFs,
454 VirtioFsFile->NodeId,
455 UpdateFileSize ? &FileSize : NULL,
456 UpdateAtime ? &Atime : NULL,
457 UpdateMtime ? &Mtime : NULL,
458 UpdateMode ? &Mode : NULL
459 );
460 return Status;
461 }
462
463 /**
464 Process an EFI_FILE_INFO setting request.
465 **/
466 STATIC
467 EFI_STATUS
468 SetFileInfo (
469 IN EFI_FILE_PROTOCOL *This,
470 IN UINTN BufferSize,
471 IN VOID *Buffer
472 )
473 {
474 VIRTIO_FS_FILE *VirtioFsFile;
475 EFI_STATUS Status;
476 EFI_FILE_INFO *FileInfo;
477
478 VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
479
480 //
481 // Validate if Buffer passes as EFI_FILE_INFO.
482 //
483 Status = ValidateInfoStructure (
484 BufferSize, // SizeByProtocolCaller
485 OFFSET_OF (
486 EFI_FILE_INFO,
487 FileName
488 ) + sizeof (CHAR16), // MinimumStructSize
489 TRUE, // IsSizeByInfoPresent
490 Buffer
491 );
492 if (EFI_ERROR (Status)) {
493 return Status;
494 }
495
496 FileInfo = Buffer;
497
498 //
499 // Perform the rename/move request, if any.
500 //
501 Status = Rename (VirtioFsFile, FileInfo->FileName);
502 if (EFI_ERROR (Status)) {
503 return Status;
504 }
505
506 //
507 // Update any attributes requested.
508 //
509 Status = UpdateAttributes (VirtioFsFile, FileInfo);
510 //
511 // The UEFI spec does not speak about partial failure in
512 // EFI_FILE_PROTOCOL.SetInfo(); we won't try to roll back the rename (if
513 // there was one) in case the attribute updates fail.
514 //
515 return Status;
516 }
517
518 /**
519 Process an EFI_FILE_SYSTEM_INFO setting request.
520 **/
521 STATIC
522 EFI_STATUS
523 SetFileSystemInfo (
524 IN EFI_FILE_PROTOCOL *This,
525 IN UINTN BufferSize,
526 IN VOID *Buffer
527 )
528 {
529 VIRTIO_FS_FILE *VirtioFsFile;
530 VIRTIO_FS *VirtioFs;
531 EFI_STATUS Status;
532 EFI_FILE_SYSTEM_INFO *FileSystemInfo;
533
534 VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
535 VirtioFs = VirtioFsFile->OwnerFs;
536
537 //
538 // Validate if Buffer passes as EFI_FILE_SYSTEM_INFO.
539 //
540 Status = ValidateInfoStructure (
541 BufferSize, // SizeByProtocolCaller
542 OFFSET_OF (
543 EFI_FILE_SYSTEM_INFO,
544 VolumeLabel
545 ) + sizeof (CHAR16), // MinimumStructSize
546 TRUE, // IsSizeByInfoPresent
547 Buffer
548 );
549 if (EFI_ERROR (Status)) {
550 return Status;
551 }
552
553 FileSystemInfo = Buffer;
554
555 //
556 // EFI_FILE_SYSTEM_INFO fields other than VolumeLabel cannot be changed, per
557 // spec.
558 //
559 // If the label is being changed to its current value, report success;
560 // otherwise, reject the request, as the Virtio Filesystem device does not
561 // support changing the label.
562 //
563 if (StrCmp (FileSystemInfo->VolumeLabel, VirtioFs->Label) == 0) {
564 return EFI_SUCCESS;
565 }
566
567 return EFI_WRITE_PROTECTED;
568 }
569
570 /**
571 Process an EFI_FILE_SYSTEM_VOLUME_LABEL setting request.
572 **/
573 STATIC
574 EFI_STATUS
575 SetFileSystemVolumeLabelInfo (
576 IN EFI_FILE_PROTOCOL *This,
577 IN UINTN BufferSize,
578 IN VOID *Buffer
579 )
580 {
581 VIRTIO_FS_FILE *VirtioFsFile;
582 VIRTIO_FS *VirtioFs;
583 EFI_STATUS Status;
584 EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel;
585
586 VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
587 VirtioFs = VirtioFsFile->OwnerFs;
588
589 //
590 // Validate if Buffer passes as EFI_FILE_SYSTEM_VOLUME_LABEL.
591 //
592 Status = ValidateInfoStructure (
593 BufferSize, // SizeByProtocolCaller
594 OFFSET_OF (
595 EFI_FILE_SYSTEM_VOLUME_LABEL,
596 VolumeLabel
597 ) + sizeof (CHAR16), // MinimumStructSize
598 FALSE, // IsSizeByInfoPresent
599 Buffer
600 );
601 if (EFI_ERROR (Status)) {
602 return Status;
603 }
604
605 FileSystemVolumeLabel = Buffer;
606
607 //
608 // If the label is being changed to its current value, report success;
609 // otherwise, reject the request, as the Virtio Filesystem device does not
610 // support changing the label.
611 //
612 if (StrCmp (FileSystemVolumeLabel->VolumeLabel, VirtioFs->Label) == 0) {
613 return EFI_SUCCESS;
614 }
615
616 return EFI_WRITE_PROTECTED;
617 }
618
619 EFI_STATUS
620 EFIAPI
621 VirtioFsSimpleFileSetInfo (
622 IN EFI_FILE_PROTOCOL *This,
623 IN EFI_GUID *InformationType,
624 IN UINTN BufferSize,
625 IN VOID *Buffer
626 )
627 {
628 if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
629 return SetFileInfo (This, BufferSize, Buffer);
630 }
631
632 if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
633 return SetFileSystemInfo (This, BufferSize, Buffer);
634 }
635
636 if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
637 return SetFileSystemVolumeLabelInfo (This, BufferSize, Buffer);
638 }
639
640 return EFI_UNSUPPORTED;
641 }