1 /** @file
2   Handle operations in files and directories from UDF/ECMA-167 file systems.
3 
4   Copyright (C) 2014-2017 Paulo Alcantara <pcacjr@zytor.com>
5   Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
6 
7   SPDX-License-Identifier: BSD-2-Clause-Patent
8 **/
9 
10 #include "Udf.h"
11 
12 EFI_FILE_PROTOCOL gUdfFileIoOps = {
13   EFI_FILE_PROTOCOL_REVISION,
14   UdfOpen,
15   UdfClose,
16   UdfDelete,
17   UdfRead,
18   UdfWrite,
19   UdfGetPosition,
20   UdfSetPosition,
21   UdfGetInfo,
22   UdfSetInfo,
23   UdfFlush,
24   NULL,
25   NULL,
26   NULL,
27   NULL
28 };
29 
30 #define _ROOT_FILE(_PrivData) (_PrivData)->Root
31 #define _PARENT_FILE(_PrivData) \
32   ((_PrivData)->IsRootDirectory ? (_PrivData)->Root : &(_PrivData)->File)
33 #define _FILE(_PrivData) _PARENT_FILE(_PrivData)
34 
35 /**
36   Open the root directory on a volume.
37 
38   @param  This Protocol instance pointer.
39   @param  Root Returns an Open file handle for the root directory
40 
41   @retval EFI_SUCCESS          The device was opened.
42   @retval EFI_UNSUPPORTED      This volume does not support the file system.
43   @retval EFI_NO_MEDIA         The device has no media.
44   @retval EFI_DEVICE_ERROR     The device reported an error.
45   @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
46   @retval EFI_ACCESS_DENIED    The service denied access to the file.
47   @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of
48                                resources.
49 
50 **/
51 EFI_STATUS
52 EFIAPI
53 UdfOpenVolume (
54   IN   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  *This,
55   OUT  EFI_FILE_PROTOCOL                **Root
56   )
57 {
58   EFI_TPL                     OldTpl;
59   EFI_STATUS                  Status;
60   PRIVATE_UDF_SIMPLE_FS_DATA  *PrivFsData;
61   PRIVATE_UDF_FILE_DATA       *PrivFileData;
62 
63   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
64 
65   if (This == NULL || Root == NULL) {
66     Status = EFI_INVALID_PARAMETER;
67     goto Error_Invalid_Params;
68   }
69 
70   PrivFsData = PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS (This);
71 
72   if (PrivFsData->OpenFiles == 0) {
73     //
74     // There is no more open files. Read volume information again since it was
75     // cleaned up on the last UdfClose() call.
76     //
77     Status = ReadUdfVolumeInformation (
78       PrivFsData->BlockIo,
79       PrivFsData->DiskIo,
80       &PrivFsData->Volume
81       );
82     if (EFI_ERROR (Status)) {
83       goto Error_Read_Udf_Volume;
84     }
85   }
86 
87   CleanupFileInformation (&PrivFsData->Root);
88 
89   //
90   // Find root directory file.
91   //
92   Status = FindRootDirectory (
93     PrivFsData->BlockIo,
94     PrivFsData->DiskIo,
95     &PrivFsData->Volume,
96     &PrivFsData->Root
97     );
98   if (EFI_ERROR (Status)) {
99     goto Error_Find_Root_Dir;
100   }
101 
102   PrivFileData =
103     (PRIVATE_UDF_FILE_DATA *) AllocateZeroPool (sizeof (PRIVATE_UDF_FILE_DATA));
104   if (PrivFileData == NULL) {
105     Status = EFI_OUT_OF_RESOURCES;
106     goto Error_Alloc_Priv_File_Data;
107   }
108 
109   PrivFileData->Signature        = PRIVATE_UDF_FILE_DATA_SIGNATURE;
110   PrivFileData->SimpleFs         = This;
111   PrivFileData->Root             = &PrivFsData->Root;
112   PrivFileData->IsRootDirectory  = TRUE;
113 
114   CopyMem ((VOID *)&PrivFileData->FileIo, (VOID *)&gUdfFileIoOps,
115            sizeof (EFI_FILE_PROTOCOL));
116 
117   *Root = &PrivFileData->FileIo;
118 
119   PrivFsData->OpenFiles++;
120 
121   gBS->RestoreTPL (OldTpl);
122 
123   return EFI_SUCCESS;
124 
125 Error_Alloc_Priv_File_Data:
126   CleanupFileInformation (&PrivFsData->Root);
127 
128 Error_Find_Root_Dir:
129 
130 Error_Read_Udf_Volume:
131 Error_Invalid_Params:
132   gBS->RestoreTPL (OldTpl);
133 
134   return Status;
135 }
136 
137 /**
138   Opens a new file relative to the source file's location.
139 
140   @param  This       The protocol instance pointer.
141   @param  NewHandle  Returns File Handle for FileName.
142   @param  FileName   Null terminated string. "\", ".", and ".." are supported.
143   @param  OpenMode   Open mode for file.
144   @param  Attributes Only used for EFI_FILE_MODE_CREATE.
145 
146   @retval EFI_SUCCESS          The device was opened.
147   @retval EFI_NOT_FOUND        The specified file could not be found on the
148                                device.
149   @retval EFI_NO_MEDIA         The device has no media.
150   @retval EFI_MEDIA_CHANGED    The media has changed.
151   @retval EFI_DEVICE_ERROR     The device reported an error.
152   @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
153   @retval EFI_ACCESS_DENIED    The service denied access to the file.
154   @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of
155                                resources.
156   @retval EFI_VOLUME_FULL      The volume is full.
157 
158 **/
159 EFI_STATUS
160 EFIAPI
161 UdfOpen (
162   IN   EFI_FILE_PROTOCOL  *This,
163   OUT  EFI_FILE_PROTOCOL  **NewHandle,
164   IN   CHAR16             *FileName,
165   IN   UINT64             OpenMode,
166   IN   UINT64             Attributes
167   )
168 {
169   EFI_TPL                     OldTpl;
170   EFI_STATUS                  Status;
171   PRIVATE_UDF_FILE_DATA       *PrivFileData;
172   PRIVATE_UDF_SIMPLE_FS_DATA  *PrivFsData;
173   CHAR16                      FilePath[UDF_PATH_LENGTH];
174   UDF_FILE_INFO               File;
175   PRIVATE_UDF_FILE_DATA       *NewPrivFileData;
176   CHAR16                      *TempFileName;
177 
178   ZeroMem (FilePath, sizeof FilePath);
179   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
180 
181   if (This == NULL || NewHandle == NULL || FileName == NULL) {
182     Status = EFI_INVALID_PARAMETER;
183     goto Error_Invalid_Params;
184   }
185 
186   if (OpenMode != EFI_FILE_MODE_READ) {
187     Status = EFI_WRITE_PROTECTED;
188     goto Error_Invalid_Params;
189   }
190 
191   PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
192 
193   PrivFsData = PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS (PrivFileData->SimpleFs);
194 
195   //
196   // Build full path
197   //
198   if (*FileName == L'\\') {
199     StrCpyS (FilePath, UDF_PATH_LENGTH, FileName);
200   } else {
201     StrCpyS (FilePath, UDF_PATH_LENGTH, PrivFileData->AbsoluteFileName);
202     StrCatS (FilePath, UDF_PATH_LENGTH, L"\\");
203     StrCatS (FilePath, UDF_PATH_LENGTH, FileName);
204   }
205 
206   MangleFileName (FilePath);
207   if (FilePath[0] == L'\0') {
208     Status = EFI_NOT_FOUND;
209     goto Error_Bad_FileName;
210   }
211 
212   Status = FindFile (
213     PrivFsData->BlockIo,
214     PrivFsData->DiskIo,
215     &PrivFsData->Volume,
216     FilePath,
217     _ROOT_FILE (PrivFileData),
218     _PARENT_FILE (PrivFileData),
219     &_PARENT_FILE(PrivFileData)->FileIdentifierDesc->Icb,
220     &File
221     );
222   if (EFI_ERROR (Status)) {
223     goto Error_Find_File;
224   }
225 
226   NewPrivFileData =
227     (PRIVATE_UDF_FILE_DATA *)AllocateZeroPool (sizeof (PRIVATE_UDF_FILE_DATA));
228   if (NewPrivFileData == NULL) {
229     Status = EFI_OUT_OF_RESOURCES;
230     goto Error_Alloc_New_Priv_File_Data;
231   }
232 
233   CopyMem ((VOID *)NewPrivFileData, (VOID *)PrivFileData,
234            sizeof (PRIVATE_UDF_FILE_DATA));
235   CopyMem ((VOID *)&NewPrivFileData->File, &File, sizeof (UDF_FILE_INFO));
236 
237   NewPrivFileData->IsRootDirectory = FALSE;
238 
239   StrCpyS (NewPrivFileData->AbsoluteFileName, UDF_PATH_LENGTH, FilePath);
240   FileName = NewPrivFileData->AbsoluteFileName;
241 
242   while ((TempFileName = StrStr (FileName, L"\\")) != NULL) {
243     FileName = TempFileName + 1;
244   }
245 
246   StrCpyS (NewPrivFileData->FileName, UDF_FILENAME_LENGTH, FileName);
247 
248   Status = GetFileSize (
249     PrivFsData->BlockIo,
250     PrivFsData->DiskIo,
251     &PrivFsData->Volume,
252     &NewPrivFileData->File,
253     &NewPrivFileData->FileSize
254     );
255   if (EFI_ERROR (Status)) {
256     DEBUG ((
257       DEBUG_ERROR,
258       "%a: GetFileSize() fails with status - %r.\n",
259       __FUNCTION__, Status
260       ));
261     goto Error_Get_File_Size;
262   }
263 
264   NewPrivFileData->FilePosition = 0;
265   ZeroMem ((VOID *)&NewPrivFileData->ReadDirInfo,
266            sizeof (UDF_READ_DIRECTORY_INFO));
267 
268   *NewHandle = &NewPrivFileData->FileIo;
269 
270   PrivFsData->OpenFiles++;
271 
272   gBS->RestoreTPL (OldTpl);
273 
274   return Status;
275 
276 Error_Get_File_Size:
277   FreePool ((VOID *)NewPrivFileData);
278 
279 Error_Alloc_New_Priv_File_Data:
280   CleanupFileInformation (&File);
281 
282 Error_Find_File:
283 Error_Bad_FileName:
284 Error_Invalid_Params:
285   gBS->RestoreTPL (OldTpl);
286 
287   return Status;
288 }
289 
290 /**
291   Read data from the file.
292 
293   @param  This       Protocol instance pointer.
294   @param  BufferSize On input size of buffer, on output amount of data in
295                      buffer.
296   @param  Buffer     The buffer in which data is read.
297 
298   @retval EFI_SUCCESS          Data was read.
299   @retval EFI_NO_MEDIA         The device has no media.
300   @retval EFI_DEVICE_ERROR     The device reported an error.
301   @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
302   @retval EFI_BUFFER_TO_SMALL  BufferSize is too small. BufferSize contains
303                                required size.
304 
305 **/
306 EFI_STATUS
307 EFIAPI
308 UdfRead (
309   IN      EFI_FILE_PROTOCOL  *This,
310   IN OUT  UINTN              *BufferSize,
311   OUT     VOID               *Buffer
312   )
313 {
314   EFI_TPL                         OldTpl;
315   EFI_STATUS                      Status;
316   PRIVATE_UDF_FILE_DATA           *PrivFileData;
317   PRIVATE_UDF_SIMPLE_FS_DATA      *PrivFsData;
318   UDF_VOLUME_INFO                 *Volume;
319   UDF_FILE_INFO                   *Parent;
320   UDF_READ_DIRECTORY_INFO         *ReadDirInfo;
321   EFI_BLOCK_IO_PROTOCOL           *BlockIo;
322   EFI_DISK_IO_PROTOCOL            *DiskIo;
323   UDF_FILE_INFO                   FoundFile;
324   UDF_FILE_IDENTIFIER_DESCRIPTOR  *NewFileIdentifierDesc;
325   VOID                            *NewFileEntryData;
326   CHAR16                          FileName[UDF_FILENAME_LENGTH];
327   UINT64                          FileSize;
328   UINT64                          BufferSizeUint64;
329 
330   ZeroMem (FileName, sizeof FileName);
331   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
332 
333   if (This == NULL || BufferSize == NULL || (*BufferSize != 0 &&
334                                              Buffer == NULL)) {
335     Status = EFI_INVALID_PARAMETER;
336     goto Error_Invalid_Params;
337   }
338 
339   PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
340   PrivFsData = PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS (PrivFileData->SimpleFs);
341 
342   BlockIo                = PrivFsData->BlockIo;
343   DiskIo                 = PrivFsData->DiskIo;
344   Volume                 = &PrivFsData->Volume;
345   ReadDirInfo            = &PrivFileData->ReadDirInfo;
346   NewFileIdentifierDesc  = NULL;
347   NewFileEntryData       = NULL;
348 
349   Parent = _PARENT_FILE (PrivFileData);
350 
351   Status = EFI_VOLUME_CORRUPTED;
352 
353   if (IS_FID_NORMAL_FILE (Parent->FileIdentifierDesc)) {
354     if (PrivFileData->FilePosition > PrivFileData->FileSize) {
355       //
356       // File's position is beyond the EOF
357       //
358       Status = EFI_DEVICE_ERROR;
359       goto Error_File_Beyond_The_Eof;
360     }
361 
362     if (PrivFileData->FilePosition == PrivFileData->FileSize) {
363       *BufferSize = 0;
364       Status = EFI_SUCCESS;
365       goto Done;
366     }
367 
368     BufferSizeUint64 = *BufferSize;
369 
370     Status = ReadFileData (
371       BlockIo,
372       DiskIo,
373       Volume,
374       Parent,
375       PrivFileData->FileSize,
376       &PrivFileData->FilePosition,
377       Buffer,
378       &BufferSizeUint64
379       );
380     ASSERT (BufferSizeUint64 <= MAX_UINTN);
381     *BufferSize = (UINTN)BufferSizeUint64;
382   } else if (IS_FID_DIRECTORY_FILE (Parent->FileIdentifierDesc)) {
383     if (ReadDirInfo->FidOffset == 0 && PrivFileData->FilePosition > 0) {
384       Status = EFI_DEVICE_ERROR;
385       *BufferSize = 0;
386       goto Done;
387     }
388 
389     for (;;) {
390       Status = ReadDirectoryEntry (
391         BlockIo,
392         DiskIo,
393         Volume,
394         &Parent->FileIdentifierDesc->Icb,
395         Parent->FileEntry,
396         ReadDirInfo,
397         &NewFileIdentifierDesc
398         );
399       if (EFI_ERROR (Status)) {
400         if (Status == EFI_DEVICE_ERROR) {
401           FreePool (ReadDirInfo->DirectoryData);
402           ZeroMem ((VOID *)ReadDirInfo, sizeof (UDF_READ_DIRECTORY_INFO));
403 
404           *BufferSize = 0;
405           Status = EFI_SUCCESS;
406         }
407 
408         goto Done;
409       }
410       //
411       // After calling function ReadDirectoryEntry(), if 'NewFileIdentifierDesc'
412       // is NULL, then the 'Status' must be EFI_OUT_OF_RESOURCES. Hence, if the
413       // code reaches here, 'NewFileIdentifierDesc' must be not NULL.
414       //
415       // The ASSERT here is for addressing a false positive NULL pointer
416       // dereference issue raised from static analysis.
417       //
418       ASSERT (NewFileIdentifierDesc != NULL);
419 
420       if (!IS_FID_PARENT_FILE (NewFileIdentifierDesc)) {
421         break;
422       }
423 
424       FreePool ((VOID *)NewFileIdentifierDesc);
425     }
426 
427     Status = FindFileEntry (
428       BlockIo,
429       DiskIo,
430       Volume,
431       &NewFileIdentifierDesc->Icb,
432       &NewFileEntryData
433       );
434     if (EFI_ERROR (Status)) {
435       goto Error_Find_Fe;
436     }
437     ASSERT (NewFileEntryData != NULL);
438 
439     if (FE_ICB_FILE_TYPE (NewFileEntryData) == UdfFileEntrySymlink) {
440       Status = ResolveSymlink (
441         BlockIo,
442         DiskIo,
443         Volume,
444         Parent,
445         NewFileEntryData,
446         &FoundFile
447         );
448       if (EFI_ERROR (Status)) {
449         goto Error_Resolve_Symlink;
450       }
451 
452       FreePool ((VOID *)NewFileEntryData);
453       NewFileEntryData = FoundFile.FileEntry;
454 
455       Status = GetFileNameFromFid (NewFileIdentifierDesc, ARRAY_SIZE (FileName), FileName);
456       if (EFI_ERROR (Status)) {
457         FreePool ((VOID *)FoundFile.FileIdentifierDesc);
458         goto Error_Get_FileName;
459       }
460 
461       FreePool ((VOID *)NewFileIdentifierDesc);
462       NewFileIdentifierDesc = FoundFile.FileIdentifierDesc;
463     } else {
464       FoundFile.FileIdentifierDesc  = NewFileIdentifierDesc;
465       FoundFile.FileEntry           = NewFileEntryData;
466 
467       Status = GetFileNameFromFid (FoundFile.FileIdentifierDesc, ARRAY_SIZE (FileName), FileName);
468       if (EFI_ERROR (Status)) {
469         goto Error_Get_FileName;
470       }
471     }
472 
473     Status = GetFileSize (
474       BlockIo,
475       DiskIo,
476       Volume,
477       &FoundFile,
478       &FileSize
479       );
480     if (EFI_ERROR (Status)) {
481       goto Error_Get_File_Size;
482     }
483 
484     Status = SetFileInfo (
485       &FoundFile,
486       FileSize,
487       FileName,
488       BufferSize,
489       Buffer
490       );
491     if (EFI_ERROR (Status)) {
492       goto Error_Set_File_Info;
493     }
494 
495     PrivFileData->FilePosition++;
496     Status = EFI_SUCCESS;
497   } else if (IS_FID_DELETED_FILE (Parent->FileIdentifierDesc)) {
498     //
499     // Code should never reach here.
500     //
501     ASSERT (FALSE);
502     Status = EFI_DEVICE_ERROR;
503   }
504 
505 Error_Set_File_Info:
506 Error_Get_File_Size:
507 Error_Get_FileName:
508 Error_Resolve_Symlink:
509   if (NewFileEntryData != NULL) {
510     FreePool (NewFileEntryData);
511   }
512 
513 Error_Find_Fe:
514   if (NewFileIdentifierDesc != NULL) {
515     FreePool ((VOID *)NewFileIdentifierDesc);
516   }
517 
518 Done:
519 Error_File_Beyond_The_Eof:
520 Error_Invalid_Params:
521   gBS->RestoreTPL (OldTpl);
522 
523   return Status;
524 }
525 
526 /**
527   Close the file handle.
528 
529   @param  This Protocol instance pointer.
530 
531   @retval EFI_SUCCESS The file was closed.
532 
533 **/
534 EFI_STATUS
535 EFIAPI
536 UdfClose (
537   IN EFI_FILE_PROTOCOL *This
538   )
539 {
540   EFI_TPL                     OldTpl;
541   EFI_STATUS                  Status;
542   PRIVATE_UDF_FILE_DATA       *PrivFileData;
543 
544   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
545 
546   Status = EFI_SUCCESS;
547 
548   if (This == NULL) {
549     Status = EFI_INVALID_PARAMETER;
550     goto Exit;
551   }
552 
553   PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
554 
555   if (!PrivFileData->IsRootDirectory) {
556     CleanupFileInformation (&PrivFileData->File);
557 
558     if (PrivFileData->ReadDirInfo.DirectoryData != NULL) {
559       FreePool (PrivFileData->ReadDirInfo.DirectoryData);
560     }
561   }
562 
563   FreePool ((VOID *)PrivFileData);
564 
565 Exit:
566   gBS->RestoreTPL (OldTpl);
567 
568   return Status;
569 }
570 
571 /**
572   Close and delete the file handle.
573 
574   @param  This                     Protocol instance pointer.
575 
576   @retval EFI_SUCCESS              The file was closed and deleted.
577   @retval EFI_WARN_DELETE_FAILURE  The handle was closed but the file was not
578                                    deleted.
579 
580 **/
581 EFI_STATUS
582 EFIAPI
583 UdfDelete (
584   IN EFI_FILE_PROTOCOL  *This
585   )
586 {
587   PRIVATE_UDF_FILE_DATA *PrivFileData;
588 
589   if (This == NULL) {
590     return EFI_INVALID_PARAMETER;
591   }
592 
593   PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
594 
595   (VOID)PrivFileData->FileIo.Close(This);
596 
597   return EFI_WARN_DELETE_FAILURE;
598 }
599 
600 /**
601   Write data to a file.
602 
603   @param  This       Protocol instance pointer.
604   @param  BufferSize On input size of buffer, on output amount of data in
605                      buffer.
606   @param  Buffer     The buffer in which data to write.
607 
608   @retval EFI_SUCCESS          Data was written.
609   @retval EFI_UNSUPPORTED      Writes to Open directory are not supported.
610   @retval EFI_NO_MEDIA         The device has no media.
611   @retval EFI_DEVICE_ERROR     The device reported an error.
612   @retval EFI_DEVICE_ERROR     An attempt was made to write to a deleted file.
613   @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
614   @retval EFI_WRITE_PROTECTED  The device is write protected.
615   @retval EFI_ACCESS_DENIED    The file was open for read only.
616   @retval EFI_VOLUME_FULL      The volume is full.
617 
618 **/
619 EFI_STATUS
620 EFIAPI
621 UdfWrite (
622   IN      EFI_FILE_PROTOCOL  *This,
623   IN OUT  UINTN              *BufferSize,
624   IN      VOID               *Buffer
625   )
626 {
627   return EFI_UNSUPPORTED;
628 }
629 
630 /**
631   Get file's current position.
632 
633   @param  This      Protocol instance pointer.
634   @param  Position  Byte position from the start of the file.
635 
636   @retval EFI_SUCCESS      Position was updated.
637   @retval EFI_UNSUPPORTED  Seek request for directories is not valid.
638 
639 **/
640 EFI_STATUS
641 EFIAPI
642 UdfGetPosition (
643   IN   EFI_FILE_PROTOCOL  *This,
644   OUT  UINT64             *Position
645   )
646 {
647   PRIVATE_UDF_FILE_DATA *PrivFileData;
648 
649   if (This == NULL || Position == NULL) {
650     return EFI_INVALID_PARAMETER;
651   }
652 
653   PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
654 
655   //
656   // As per UEFI spec, if the file handle is a directory, then the current file
657   // position has no meaning and the operation is not supported.
658   //
659   if (IS_FID_DIRECTORY_FILE (PrivFileData->File.FileIdentifierDesc)) {
660     return  EFI_UNSUPPORTED;
661   }
662 
663   //
664   // The file is not a directory. So, return its position.
665   //
666   *Position = PrivFileData->FilePosition;
667 
668   return EFI_SUCCESS;
669 }
670 
671 /**
672   Set file's current position.
673 
674   @param  This      Protocol instance pointer.
675   @param  Position  Byte position from the start of the file.
676 
677   @retval EFI_SUCCESS      Position was updated.
678   @retval EFI_UNSUPPORTED  Seek request for non-zero is not valid on open.
679 
680 **/
681 EFI_STATUS
682 EFIAPI
683 UdfSetPosition (
684   IN EFI_FILE_PROTOCOL  *This,
685   IN UINT64             Position
686   )
687 {
688   EFI_STATUS                      Status;
689   PRIVATE_UDF_FILE_DATA           *PrivFileData;
690   UDF_FILE_IDENTIFIER_DESCRIPTOR  *FileIdentifierDesc;
691 
692   if (This == NULL) {
693     return EFI_INVALID_PARAMETER;
694   }
695 
696   Status = EFI_UNSUPPORTED;
697 
698   PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
699 
700   FileIdentifierDesc = _FILE (PrivFileData)->FileIdentifierDesc;
701   ASSERT (FileIdentifierDesc != NULL);
702   if (IS_FID_DIRECTORY_FILE (FileIdentifierDesc)) {
703     //
704     // If the file handle is a directory, the _only_ position that may be set is
705     // zero. This has no effect of starting the read proccess of the directory
706     // entries over.
707     //
708     if (Position == 0) {
709       PrivFileData->FilePosition = Position;
710       PrivFileData->ReadDirInfo.FidOffset = 0;
711       Status = EFI_SUCCESS;
712     }
713   } else if (IS_FID_NORMAL_FILE (FileIdentifierDesc)) {
714     //
715     // Seeking to position 0xFFFFFFFFFFFFFFFF causes the current position to be
716     // set to the EOF.
717     //
718     if (Position == 0xFFFFFFFFFFFFFFFF) {
719       PrivFileData->FilePosition = PrivFileData->FileSize;
720     } else {
721       PrivFileData->FilePosition = Position;
722     }
723 
724     Status = EFI_SUCCESS;
725   }
726 
727   return Status;
728 }
729 
730 /**
731   Get information about a file.
732 
733   @param  This            Protocol instance pointer.
734   @param  InformationType Type of information to return in Buffer.
735   @param  BufferSize      On input size of buffer, on output amount of data in
736                           buffer.
737   @param  Buffer          The buffer to return data.
738 
739   @retval EFI_SUCCESS          Data was returned.
740   @retval EFI_UNSUPPORTED      InformationType is not supported.
741   @retval EFI_NO_MEDIA         The device has no media.
742   @retval EFI_DEVICE_ERROR     The device reported an error.
743   @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
744   @retval EFI_WRITE_PROTECTED  The device is write protected.
745   @retval EFI_ACCESS_DENIED    The file was open for read only.
746   @retval EFI_BUFFER_TOO_SMALL Buffer was too small; required size returned in
747                                BufferSize.
748 
749 **/
750 EFI_STATUS
751 EFIAPI
752 UdfGetInfo (
753   IN      EFI_FILE_PROTOCOL  *This,
754   IN      EFI_GUID           *InformationType,
755   IN OUT  UINTN              *BufferSize,
756   OUT     VOID               *Buffer
757   )
758 {
759   EFI_STATUS                    Status;
760   PRIVATE_UDF_FILE_DATA         *PrivFileData;
761   PRIVATE_UDF_SIMPLE_FS_DATA    *PrivFsData;
762   EFI_FILE_SYSTEM_INFO          *FileSystemInfo;
763   UINTN                         FileSystemInfoLength;
764   UINT64                        VolumeSize;
765   UINT64                        FreeSpaceSize;
766   EFI_FILE_SYSTEM_VOLUME_LABEL  *FileSystemVolumeLabel;
767   UINTN                         FileSystemVolumeLabelLength;
768   CHAR16                        VolumeLabel[64];
769 
770   if (This == NULL || InformationType == NULL || BufferSize == NULL ||
771       (*BufferSize != 0 && Buffer == NULL)) {
772     return EFI_INVALID_PARAMETER;
773   }
774 
775   PrivFileData = PRIVATE_UDF_FILE_DATA_FROM_THIS (This);
776 
777   PrivFsData = PRIVATE_UDF_SIMPLE_FS_DATA_FROM_THIS (PrivFileData->SimpleFs);
778 
779   Status = EFI_UNSUPPORTED;
780 
781   if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
782     Status = SetFileInfo (
783       _FILE (PrivFileData),
784       PrivFileData->FileSize,
785       PrivFileData->FileName,
786       BufferSize,
787       Buffer
788       );
789   } else if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
790     Status = GetVolumeLabel (&PrivFsData->Volume, ARRAY_SIZE (VolumeLabel), VolumeLabel);
791     if (EFI_ERROR (Status)) {
792       return Status;
793     }
794 
795     FileSystemInfoLength = StrSize (VolumeLabel) +
796                            sizeof (EFI_FILE_SYSTEM_INFO);
797     if (*BufferSize < FileSystemInfoLength) {
798       *BufferSize = FileSystemInfoLength;
799       return EFI_BUFFER_TOO_SMALL;
800     }
801 
802     FileSystemInfo = (EFI_FILE_SYSTEM_INFO *)Buffer;
803     StrCpyS (
804       FileSystemInfo->VolumeLabel,
805       (*BufferSize - SIZE_OF_EFI_FILE_SYSTEM_INFO) / sizeof (CHAR16),
806       VolumeLabel
807       );
808     Status = GetVolumeSize (
809       PrivFsData->BlockIo,
810       PrivFsData->DiskIo,
811       &PrivFsData->Volume,
812       &VolumeSize,
813       &FreeSpaceSize
814       );
815     if (EFI_ERROR (Status)) {
816       return Status;
817     }
818 
819     FileSystemInfo->Size        = FileSystemInfoLength;
820     FileSystemInfo->ReadOnly    = TRUE;
821     FileSystemInfo->BlockSize   =
822       PrivFsData->Volume.LogicalVolDesc.LogicalBlockSize;
823     FileSystemInfo->VolumeSize  = VolumeSize;
824     FileSystemInfo->FreeSpace   = FreeSpaceSize;
825 
826     *BufferSize = FileSystemInfoLength;
827     Status = EFI_SUCCESS;
828   } else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
829     Status = GetVolumeLabel (&PrivFsData->Volume, ARRAY_SIZE (VolumeLabel), VolumeLabel);
830     if (EFI_ERROR (Status)) {
831       return Status;
832     }
833 
834     FileSystemVolumeLabelLength = StrSize (VolumeLabel) +
835                                   sizeof (EFI_FILE_SYSTEM_VOLUME_LABEL);
836     if (*BufferSize < FileSystemVolumeLabelLength) {
837       *BufferSize = FileSystemVolumeLabelLength;
838       return EFI_BUFFER_TOO_SMALL;
839     }
840 
841     FileSystemVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer;
842     StrCpyS (
843       FileSystemVolumeLabel->VolumeLabel,
844       (*BufferSize - SIZE_OF_EFI_FILE_SYSTEM_VOLUME_LABEL) / sizeof (CHAR16),
845       VolumeLabel
846       );
847     Status = EFI_SUCCESS;
848   }
849 
850   return Status;
851 }
852 
853 /**
854   Set information about a file.
855 
856   @param  This            Protocol instance pointer.
857   @param  InformationType Type of information in Buffer.
858   @param  BufferSize      Size of buffer.
859   @param  Buffer          The data to write.
860 
861   @retval EFI_SUCCESS          Data was set.
862   @retval EFI_UNSUPPORTED      InformationType is not supported.
863   @retval EFI_NO_MEDIA         The device has no media.
864   @retval EFI_DEVICE_ERROR     The device reported an error.
865   @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
866   @retval EFI_WRITE_PROTECTED  The device is write protected.
867   @retval EFI_ACCESS_DENIED    The file was open for read only.
868 
869 **/
870 EFI_STATUS
871 EFIAPI
872 UdfSetInfo (
873   IN EFI_FILE_PROTOCOL  *This,
874   IN EFI_GUID           *InformationType,
875   IN UINTN              BufferSize,
876   IN VOID               *Buffer
877   )
878 {
879   return EFI_WRITE_PROTECTED;
880 }
881 
882 /**
883   Flush data back for the file handle.
884 
885   @param  This Protocol instance pointer.
886 
887   @retval EFI_SUCCESS          Data was flushed.
888   @retval EFI_UNSUPPORTED      Writes to Open directory are not supported.
889   @retval EFI_NO_MEDIA         The device has no media.
890   @retval EFI_DEVICE_ERROR     The device reported an error.
891   @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
892   @retval EFI_WRITE_PROTECTED  The device is write protected.
893   @retval EFI_ACCESS_DENIED    The file was open for read only.
894   @retval EFI_VOLUME_FULL      The volume is full.
895 
896 **/
897 EFI_STATUS
898 EFIAPI
899 UdfFlush (
900   IN EFI_FILE_PROTOCOL *This
901   )
902 {
903   return EFI_WRITE_PROTECTED;
904 }
905