]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - EDK2/efiffs/org/src/file.c
Added italian translation (#2580)
[Ventoy.git] / EDK2 / efiffs / org / src / file.c
1 /* file.c - SimpleFileIo Interface */
2 /*
3 * Copyright © 2014-2017 Pete Batard <pete@akeo.ie>
4 * Based on iPXE's efi_driver.c and efi_file.c:
5 * Copyright © 2011,2013 Michael Brown <mbrown@fensystems.co.uk>.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "driver.h"
22
23 /**
24 * Get EFI file name (for debugging)
25 *
26 * @v file EFI file
27 * @ret Name Name
28 */
29 static const CHAR16 *
30 FileName(EFI_GRUB_FILE *File)
31 {
32 EFI_STATUS Status;
33 static CHAR16 Path[MAX_PATH];
34
35 Status = Utf8ToUtf16NoAlloc(File->path, Path, sizeof(Path));
36 if (EFI_ERROR(Status)) {
37 PrintStatusError(Status, L"Could not convert filename to UTF16");
38 return NULL;
39 }
40
41 return Path;
42 }
43
44 /* Simple hook to populate the timestamp and directory flag when opening a file */
45 static INT32
46 InfoHook(const CHAR8 *name, const GRUB_DIRHOOK_INFO *Info, VOID *Data)
47 {
48 EFI_GRUB_FILE *File = (EFI_GRUB_FILE *) Data;
49
50 /* Look for a specific file */
51 if (strcmpa(name, File->basename) != 0)
52 return 0;
53
54 File->IsDir = (BOOLEAN) (Info->Dir);
55 if (Info->MtimeSet)
56 File->Mtime = Info->Mtime;
57
58 return 0;
59 }
60
61 /**
62 * Open file
63 *
64 * @v This File handle
65 * @ret new New file handle
66 * @v Name File name
67 * @v Mode File mode
68 * @v Attributes File attributes (for newly-created files)
69 * @ret Status EFI status code
70 */
71 static EFI_STATUS EFIAPI
72 FileOpen(EFI_FILE_HANDLE This, EFI_FILE_HANDLE *New,
73 CHAR16 *Name, UINT64 Mode, UINT64 Attributes)
74 {
75 EFI_STATUS Status;
76 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
77 EFI_GRUB_FILE *NewFile;
78
79 // TODO: Use dynamic buffers?
80 char path[MAX_PATH], clean_path[MAX_PATH], *dirname;
81 INTN i, len;
82 BOOLEAN AbsolutePath = (*Name == L'\\');
83
84 PrintInfo(L"Open(" PERCENT_P L"%s, \"%s\")\n", (UINTN) This,
85 IS_ROOT(File)?L" <ROOT>":L"", Name);
86
87 /* Fail unless opening read-only */
88 if (Mode != EFI_FILE_MODE_READ) {
89 PrintWarning(L"File '%s' can only be opened in read-only mode\n", Name);
90 return EFI_WRITE_PROTECTED;
91 }
92
93 /* Additional failures */
94 if ((StrCmp(Name, L"..") == 0) && IS_ROOT(File)) {
95 PrintInfo(L"Trying to open <ROOT>'s parent\n");
96 return EFI_NOT_FOUND;
97 }
98
99 /* See if we're trying to reopen current (which the EFI Shell insists on doing) */
100 if ((*Name == 0) || (StrCmp(Name, L".") == 0)) {
101 PrintInfo(L" Reopening %s\n", IS_ROOT(File)?L"<ROOT>":FileName(File));
102 File->RefCount++;
103 *New = This;
104 PrintInfo(L" RET: " PERCENT_P L"\n", (UINTN) *New);
105 return EFI_SUCCESS;
106 }
107
108 /* If we have an absolute path, don't bother completing with the parent */
109 if (AbsolutePath) {
110 len = 0;
111 } else {
112 strcpya(path, File->path);
113 len = strlena(path);
114 /* Add delimiter if needed */
115 if ((len == 0) || (path[len-1] != '/'))
116 path[len++] = '/';
117 }
118
119 /* Copy the rest of the path (converted to UTF-8) */
120 Status = Utf16ToUtf8NoAlloc(Name, &path[len], sizeof(path) - len);
121 if (EFI_ERROR(Status)) {
122 PrintStatusError(Status, L"Could not convert path to UTF-8");
123 return Status;
124 }
125 /* Convert the delimiters */
126 for (i = strlena(path) - 1 ; i >= len; i--) {
127 if (path[i] == '\\')
128 path[i] = '/';
129 }
130
131 /* We only want to handle with absolute paths */
132 clean_path[0] = '/';
133 /* Find out if we're dealing with root by removing the junk */
134 CopyPathRelative(&clean_path[1], path, MAX_PATH - 1);
135 if (clean_path[1] == 0) {
136 /* We're dealing with the root */
137 PrintInfo(L" Reopening <ROOT>\n");
138 *New = &File->FileSystem->RootFile->EfiFile;
139 /* Must make sure that DirIndex is reset too (NB: no concurrent access!) */
140 File->FileSystem->RootFile->DirIndex = 0;
141 PrintInfo(L" RET: " PERCENT_P L"\n", (UINTN) *New);
142 return EFI_SUCCESS;
143 }
144
145 // TODO: eventually we should seek for already opened files and increase RefCount
146 /* Allocate and initialise an instance of a file */
147 Status = GrubCreateFile(&NewFile, File->FileSystem);
148 if (EFI_ERROR(Status)) {
149 PrintStatusError(Status, L"Could not instantiate file");
150 return Status;
151 }
152
153 NewFile->path = AllocatePool(strlena(clean_path)+1);
154 if (NewFile->path == NULL) {
155 GrubDestroyFile(NewFile);
156 PrintError(L"Could not instantiate path\n");
157 return EFI_OUT_OF_RESOURCES;
158 }
159 strcpya(NewFile->path, clean_path);
160
161 /* Isolate the basename and dirname */
162 for (i = strlena(clean_path) - 1; i >= 0; i--) {
163 if (clean_path[i] == '/') {
164 clean_path[i] = 0;
165 break;
166 }
167 }
168 dirname = (i <= 0) ? "/" : clean_path;
169 NewFile->basename = &NewFile->path[i+1];
170
171 /* Find if we're working with a directory and fill the grub timestamp */
172 Status = GrubDir(NewFile, dirname, InfoHook, (VOID *) NewFile);
173 if (EFI_ERROR(Status)) {
174 if (Status != EFI_NOT_FOUND)
175 PrintStatusError(Status, L"Could not get file attributes for '%s'", Name);
176 FreePool(NewFile->path);
177 GrubDestroyFile(NewFile);
178 return Status;
179 }
180
181 /* Finally we can call on GRUB open() if it's a regular file */
182 if (!NewFile->IsDir) {
183 Status = GrubOpen(NewFile);
184 if (EFI_ERROR(Status)) {
185 if (Status != EFI_NOT_FOUND)
186 PrintStatusError(Status, L"Could not open file '%s'", Name);
187 FreePool(NewFile->path);
188 GrubDestroyFile(NewFile);
189 return Status;
190 }
191 }
192
193 NewFile->RefCount++;
194 *New = &NewFile->EfiFile;
195
196 PrintInfo(L" RET: " PERCENT_P L"\n", (UINTN) *New);
197 return EFI_SUCCESS;
198 }
199
200 /* Ex version */
201 static EFI_STATUS EFIAPI
202 FileOpenEx(EFI_FILE_HANDLE This, EFI_FILE_HANDLE *New, CHAR16 *Name,
203 UINT64 Mode, UINT64 Attributes, EFI_FILE_IO_TOKEN *Token)
204 {
205 return FileOpen(This, New, Name, Mode, Attributes);
206 }
207
208
209 /**
210 * Close file
211 *
212 * @v This File handle
213 * @ret Status EFI status code
214 */
215 static EFI_STATUS EFIAPI
216 FileClose(EFI_FILE_HANDLE This)
217 {
218 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
219
220 PrintInfo(L"Close(" PERCENT_P L"|'%s') %s\n", (UINTN) This, FileName(File),
221 IS_ROOT(File)?L"<ROOT>":L"");
222
223 /* Nothing to do it this is the root */
224 if (IS_ROOT(File))
225 return EFI_SUCCESS;
226
227 if (--File->RefCount == 0) {
228 /* Close the file if it's a regular one */
229 if (!File->IsDir)
230 GrubClose(File);
231 /* NB: basename points into File->path and does not need to be freed */
232 if (File->path != NULL)
233 FreePool(File->path);
234 GrubDestroyFile(File);
235 }
236
237 return EFI_SUCCESS;
238 }
239
240 /**
241 * Close and delete file
242 *
243 * @v This File handle
244 * @ret Status EFI status code
245 */
246 static EFI_STATUS EFIAPI
247 FileDelete(EFI_FILE_HANDLE This)
248 {
249 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
250
251 PrintError(L"Cannot delete '%s'\n", FileName(File));
252
253 /* Close file */
254 FileClose(This);
255
256 /* Warn of failure to delete */
257 return EFI_WARN_DELETE_FAILURE;
258 }
259
260 /* GRUB uses a callback for each directory entry, whereas EFI uses repeated
261 * firmware generated calls to FileReadDir() to get the info for each entry,
262 * so we have to reconcile the twos. For now, we'll re-issue a call to GRUB
263 * dir(), and run through all the entries (to find the one we
264 * are interested in) multiple times. Maybe later we'll try to optimize this
265 * by building a one-off chained list of entries that we can parse...
266 */
267 static INT32
268 DirHook(const CHAR8 *name, const GRUB_DIRHOOK_INFO *DirInfo, VOID *Data)
269 {
270 EFI_STATUS Status;
271 EFI_FILE_INFO *Info = (EFI_FILE_INFO *) Data;
272 INT64 *Index = (INT64 *) &Info->FileSize;
273 CHAR8 *filename = (CHAR8 *) (UINTN) Info->PhysicalSize;
274 EFI_TIME Time = { 1970, 01, 01, 00, 00, 00, 0, 0, 0, 0, 0};
275
276 // Eliminate '.' or '..'
277 if ((name[0] == '.') && ((name[1] == 0) || ((name[1] == '.') && (name[2] == 0))))
278 return 0;
279
280 /* Ignore any entry that doesn't match our index */
281 if ((*Index)-- != 0)
282 return 0;
283
284 strcpya(filename, name);
285
286 Status = Utf8ToUtf16NoAlloc(filename, Info->FileName, (INTN)(Info->Size - sizeof(EFI_FILE_INFO)));
287 if (EFI_ERROR(Status)) {
288 if (Status != EFI_BUFFER_TOO_SMALL)
289 PrintStatusError(Status, L"Could not convert directory entry to UTF-8");
290 return (INT32) Status;
291 }
292 /* The Info struct size already accounts for the extra NUL */
293 Info->Size = sizeof(*Info) + StrLen(Info->FileName) * sizeof(CHAR16);
294
295 // Oh, and of course GRUB uses a 32 bit signed mtime value (seriously, wtf guys?!?)
296 if (DirInfo->MtimeSet)
297 GrubTimeToEfiTime(DirInfo->Mtime, &Time);
298 CopyMem(&Info->CreateTime, &Time, sizeof(Time));
299 CopyMem(&Info->LastAccessTime, &Time, sizeof(Time));
300 CopyMem(&Info->ModificationTime, &Time, sizeof(Time));
301
302 Info->Attribute = EFI_FILE_READ_ONLY;
303 if (DirInfo->Dir)
304 Info->Attribute |= EFI_FILE_DIRECTORY;
305
306 return 0;
307 }
308
309 /**
310 * Read directory entry
311 *
312 * @v file EFI file
313 * @v Len Length to read
314 * @v Data Data buffer
315 * @ret Status EFI status code
316 */
317 static EFI_STATUS
318 FileReadDir(EFI_GRUB_FILE *File, UINTN *Len, VOID *Data)
319 {
320 EFI_FILE_INFO *Info = (EFI_FILE_INFO *) Data;
321 EFI_STATUS Status;
322 /* We temporarily repurpose the FileSize as a *signed* entry index */
323 INT64 *Index = (INT64 *) &Info->FileSize;
324 /* And PhysicalSize as a pointer to our filename */
325 CHAR8 **basename = (CHAR8 **) &Info->PhysicalSize;
326 CHAR8 path[MAX_PATH];
327 EFI_GRUB_FILE *TmpFile = NULL;
328 INTN len;
329
330 /* Unless we can fit our maximum size, forget it */
331 if (*Len < MINIMUM_INFO_LENGTH) {
332 *Len = MINIMUM_INFO_LENGTH;
333 return EFI_BUFFER_TOO_SMALL;
334 }
335
336 /* Populate our Info template */
337 ZeroMem(Data, *Len);
338 Info->Size = *Len;
339 *Index = File->DirIndex;
340 strcpya(path, File->path);
341 len = strlena(path);
342 if (path[len-1] != '/')
343 path[len++] = '/';
344 *basename = &path[len];
345
346 /* Invoke GRUB's directory listing */
347 Status = GrubDir(File, File->path, DirHook, Data);
348 if (*Index >= 0) {
349 /* No more entries */
350 *Len = 0;
351 return EFI_SUCCESS;
352 }
353
354 if (EFI_ERROR(Status)) {
355 PrintStatusError(Status, L"Directory listing failed");
356 return Status;
357 }
358
359 /* Our Index/FileSize must be reset */
360 Info->FileSize = 0;
361 Info->PhysicalSize = 0;
362
363 /* For regular files, we still need to fill the size */
364 if (!(Info->Attribute & EFI_FILE_DIRECTORY)) {
365 /* Open the file and read its size */
366 Status = GrubCreateFile(&TmpFile, File->FileSystem);
367 if (EFI_ERROR(Status)) {
368 PrintStatusError(Status, L"Unable to create temporary file");
369 return Status;
370 }
371 TmpFile->path = path;
372
373 Status = GrubOpen(TmpFile);
374 if (EFI_ERROR(Status)) {
375 // TODO: EFI_NO_MAPPING is returned for links...
376 PrintStatusError(Status, L"Unable to obtain the size of '%s'", Info->FileName);
377 /* Non fatal error */
378 } else {
379 Info->FileSize = GrubGetFileSize(TmpFile);
380 Info->PhysicalSize = GrubGetFileSize(TmpFile);
381 GrubClose(TmpFile);
382 }
383 GrubDestroyFile(TmpFile);
384 }
385
386 *Len = (UINTN) Info->Size;
387 /* Advance to the next entry */
388 File->DirIndex++;
389
390 // PrintInfo(L" Entry[%d]: '%s' %s\n", File->DirIndex-1, Info->FileName,
391 // (Info->Attribute&EFI_FILE_DIRECTORY)?L"<DIR>":L"");
392
393 return EFI_SUCCESS;
394 }
395
396 /**
397 * Read from file
398 *
399 * @v This File handle
400 * @v Len Length to read
401 * @v Data Data buffer
402 * @ret Status EFI status code
403 */
404 static EFI_STATUS EFIAPI
405 FileRead(EFI_FILE_HANDLE This, UINTN *Len, VOID *Data)
406 {
407 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
408
409 PrintInfo(L"Read(" PERCENT_P L"|'%s', %d) %s\n", (UINTN) This, FileName(File),
410 *Len, File->IsDir?L"<DIR>":L"");
411
412 /* If this is a directory, then fetch the directory entries */
413 if (File->IsDir)
414 return FileReadDir(File, Len, Data);
415
416 return GrubRead(File, Data, Len);
417 }
418
419 /* Ex version */
420 static EFI_STATUS EFIAPI
421 FileReadEx(IN EFI_FILE_PROTOCOL *This, IN OUT EFI_FILE_IO_TOKEN *Token)
422 {
423 return FileRead(This, &(Token->BufferSize), Token->Buffer);
424 }
425
426 /**
427 * Write to file
428 *
429 * @v This File handle
430 * @v Len Length to write
431 * @v Data Data buffer
432 * @ret Status EFI status code
433 */
434 static EFI_STATUS EFIAPI
435 FileWrite(EFI_FILE_HANDLE This, UINTN *Len, VOID *Data)
436 {
437 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
438
439 PrintError(L"Cannot write to '%s'\n", FileName(File));
440 return EFI_WRITE_PROTECTED;
441 }
442
443 /* Ex version */
444 static EFI_STATUS EFIAPI
445 FileWriteEx(IN EFI_FILE_PROTOCOL *This, IN OUT EFI_FILE_IO_TOKEN *Token)
446 {
447 return FileWrite(This, &(Token->BufferSize), Token->Buffer);
448 }
449
450 /**
451 * Set file position
452 *
453 * @v This File handle
454 * @v Position New file position
455 * @ret Status EFI status code
456 */
457 static EFI_STATUS EFIAPI
458 FileSetPosition(EFI_FILE_HANDLE This, UINT64 Position)
459 {
460 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
461 UINT64 FileSize;
462
463 PrintInfo(L"SetPosition(" PERCENT_P L"|'%s', %lld) %s\n", (UINTN) This,
464 FileName(File), Position, (File->IsDir)?L"<DIR>":L"");
465
466 /* If this is a directory, reset the Index to the start */
467 if (File->IsDir) {
468 if (Position != 0)
469 return EFI_INVALID_PARAMETER;
470 File->DirIndex = 0;
471 return EFI_SUCCESS;
472 }
473
474 /* Fail if we attempt to seek past the end of the file (since
475 * we do not support writes).
476 */
477 FileSize = GrubGetFileSize(File);
478 if (Position > FileSize) {
479 PrintError(L"'%s': Cannot seek to #%llx of %llx\n",
480 FileName(File), Position, FileSize);
481 return EFI_UNSUPPORTED;
482 }
483
484 /* Set position */
485 GrubSetFileOffset(File, Position);
486 PrintDebug(L"'%s': Position set to %llx\n",
487 FileName(File), Position);
488
489 return EFI_SUCCESS;
490 }
491
492 /**
493 * Get file position
494 *
495 * @v This File handle
496 * @ret Position New file position
497 * @ret Status EFI status code
498 */
499 static EFI_STATUS EFIAPI
500 FileGetPosition(EFI_FILE_HANDLE This, UINT64 *Position)
501 {
502 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
503
504 PrintInfo(L"GetPosition(" PERCENT_P L"|'%s', %lld)\n", (UINTN) This, FileName(File));
505
506 if (File->IsDir)
507 *Position = File->DirIndex;
508 else
509 *Position = GrubGetFileOffset(File);
510 return EFI_SUCCESS;
511 }
512
513 /**
514 * Get file information
515 *
516 * @v This File handle
517 * @v Type Type of information
518 * @v Len Buffer size
519 * @v Data Buffer
520 * @ret Status EFI status code
521 */
522 static EFI_STATUS EFIAPI
523 FileGetInfo(EFI_FILE_HANDLE This, EFI_GUID *Type, UINTN *Len, VOID *Data)
524 {
525 EFI_STATUS Status;
526 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
527 EFI_FILE_SYSTEM_INFO *FSInfo = (EFI_FILE_SYSTEM_INFO *) Data;
528 EFI_FILE_INFO *Info = (EFI_FILE_INFO *) Data;
529 EFI_FILE_SYSTEM_VOLUME_LABEL_INFO *VLInfo = (EFI_FILE_SYSTEM_VOLUME_LABEL_INFO *)Data;
530 EFI_TIME Time;
531 CHAR8* label;
532 UINTN tmpLen;
533
534 PrintInfo(L"GetInfo(" PERCENT_P L"|'%s', %d) %s\n", (UINTN) This,
535 FileName(File), *Len, File->IsDir?L"<DIR>":L"");
536
537 /* Determine information to return */
538 if (CompareMem(Type, &gEfiFileInfoGuid, sizeof(*Type)) == 0) {
539
540 /* Fill file information */
541 PrintExtra(L"Get regular file information\n");
542 if (*Len < MINIMUM_INFO_LENGTH) {
543 *Len = MINIMUM_INFO_LENGTH;
544 return EFI_BUFFER_TOO_SMALL;
545 }
546
547 ZeroMem(Data, sizeof(EFI_FILE_INFO));
548
549 Info->Attribute = EFI_FILE_READ_ONLY;
550 GrubTimeToEfiTime(File->Mtime, &Time);
551 CopyMem(&Info->CreateTime, &Time, sizeof(Time));
552 CopyMem(&Info->LastAccessTime, &Time, sizeof(Time));
553 CopyMem(&Info->ModificationTime, &Time, sizeof(Time));
554
555 if (File->IsDir) {
556 Info->Attribute |= EFI_FILE_DIRECTORY;
557 } else {
558 Info->FileSize = GrubGetFileSize(File);
559 Info->PhysicalSize = GrubGetFileSize(File);
560 }
561
562 tmpLen = (UINTN)(Info->Size - sizeof(EFI_FILE_INFO) - 1);
563 Status = Utf8ToUtf16NoAllocUpdateLen(File->basename, Info->FileName, &tmpLen);
564 if (EFI_ERROR(Status)) {
565 if (Status != EFI_BUFFER_TOO_SMALL)
566 PrintStatusError(Status, L"Could not convert basename to UTF-16");
567 return Status;
568 }
569
570 /* The Info struct size already accounts for the extra NUL */
571 Info->Size = sizeof(EFI_FILE_INFO) + tmpLen;
572 *Len = (INTN)Info->Size;
573 return EFI_SUCCESS;
574
575 } else if (CompareMem(Type, &gEfiFileSystemInfoGuid, sizeof(*Type)) == 0) {
576
577 /* Get file system information */
578 PrintExtra(L"Get file system information\n");
579 if (*Len < MINIMUM_FS_INFO_LENGTH) {
580 *Len = MINIMUM_FS_INFO_LENGTH;
581 return EFI_BUFFER_TOO_SMALL;
582 }
583
584 ZeroMem(Data, sizeof(EFI_FILE_INFO));
585 FSInfo->Size = *Len;
586 FSInfo->ReadOnly = 1;
587 /* NB: This should really be cluster size, but we don't have access to that */
588 if (File->FileSystem->BlockIo2 != NULL) {
589 FSInfo->BlockSize = File->FileSystem->BlockIo2->Media->BlockSize;
590 } else {
591 FSInfo->BlockSize = File->FileSystem->BlockIo->Media->BlockSize;
592 }
593 if (FSInfo->BlockSize == 0) {
594 PrintWarning(L"Corrected Media BlockSize\n");
595 FSInfo->BlockSize = 512;
596 }
597 if (File->FileSystem->BlockIo2 != NULL) {
598 FSInfo->VolumeSize = (File->FileSystem->BlockIo2->Media->LastBlock + 1) *
599 FSInfo->BlockSize;
600 } else {
601 FSInfo->VolumeSize = (File->FileSystem->BlockIo->Media->LastBlock + 1) *
602 FSInfo->BlockSize;
603 }
604 /* No idea if we can easily get this for GRUB, and the device is RO anyway */
605 FSInfo->FreeSpace = 0;
606
607 Status = GrubLabel(File, &label);
608 if (EFI_ERROR(Status)) {
609 PrintStatusError(Status, L"Could not read disk label");
610 FSInfo->VolumeLabel[0] = 0;
611 *Len = sizeof(EFI_FILE_SYSTEM_INFO);
612 } else {
613 tmpLen = (INTN)(FSInfo->Size - sizeof(EFI_FILE_SYSTEM_INFO) - 1);
614 Status = Utf8ToUtf16NoAllocUpdateLen(label, FSInfo->VolumeLabel, &tmpLen);
615 if (EFI_ERROR(Status)) {
616 if (Status != EFI_BUFFER_TOO_SMALL)
617 PrintStatusError(Status, L"Could not convert label to UTF-16");
618 return Status;
619 }
620 FSInfo->Size = sizeof(EFI_FILE_SYSTEM_INFO) - 1 + tmpLen;
621 *Len = (INTN)FSInfo->Size;
622 }
623 return EFI_SUCCESS;
624
625 } else if (CompareMem(Type, &gEfiFileSystemVolumeLabelInfoIdGuid, sizeof(*Type)) == 0) {
626
627 /* Get the volume label */
628 Status = GrubLabel(File, &label);
629 if (EFI_ERROR(Status)) {
630 PrintStatusError(Status, L"Could not read disk label");
631 }
632 else {
633 Status = Utf8ToUtf16NoAllocUpdateLen(label, VLInfo->VolumeLabel, Len);
634 if (EFI_ERROR(Status)) {
635 if (Status != EFI_BUFFER_TOO_SMALL)
636 PrintStatusError(Status, L"Could not convert label to UTF-16");
637 return Status;
638 }
639 }
640 return EFI_SUCCESS;
641
642 } else {
643
644 Print(L"'%s': Cannot get information of type ", FileName(File));
645 PrintGuid(Type);
646 Print(L"\n");
647 return EFI_UNSUPPORTED;
648
649 }
650 }
651
652 /**
653 * Set file information
654 *
655 * @v This File handle
656 * @v Type Type of information
657 * @v Len Buffer size
658 * @v Data Buffer
659 * @ret Status EFI status code
660 */
661 static EFI_STATUS EFIAPI
662 FileSetInfo(EFI_FILE_HANDLE This, EFI_GUID *Type, UINTN Len, VOID *Data)
663 {
664 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
665
666 Print(L"Cannot set information of type ");
667 PrintGuid(Type);
668 Print(L" for file '%s'\n", FileName(File));
669
670 return EFI_WRITE_PROTECTED;
671 }
672
673 /**
674 * Flush file modified data
675 *
676 * @v This File handle
677 * @v Type Type of information
678 * @v Len Buffer size
679 * @v Data Buffer
680 * @ret Status EFI status code
681 */
682 static EFI_STATUS EFIAPI
683 FileFlush(EFI_FILE_HANDLE This)
684 {
685 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
686
687 PrintInfo(L"Flush(" PERCENT_P L"|'%s')\n", (UINTN) This, FileName(File));
688 return EFI_SUCCESS;
689 }
690
691 /* Ex version */
692 static EFI_STATUS EFIAPI
693 FileFlushEx(EFI_FILE_HANDLE This, EFI_FILE_IO_TOKEN *Token)
694 {
695 return FileFlush(This);
696 }
697
698 /**
699 * Open root directory
700 *
701 * @v This EFI simple file system
702 * @ret Root File handle for the root directory
703 * @ret Status EFI status code
704 */
705 EFI_STATUS EFIAPI
706 FileOpenVolume(EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, EFI_FILE_HANDLE *Root)
707 {
708 EFI_FS *FSInstance = _CR(This, EFI_FS, FileIoInterface);
709
710 PrintInfo(L"OpenVolume\n");
711 *Root = &FSInstance->RootFile->EfiFile;
712
713 return EFI_SUCCESS;
714 }
715
716 /**
717 * Install the EFI simple file system protocol
718 * If successful this call instantiates a new FS#: drive, that is made
719 * available on the next 'map -r'. Note that all this call does is add
720 * the FS protocol. OpenVolume won't be called until a process tries
721 * to access a file or the root directory on the volume.
722 */
723 EFI_STATUS
724 FSInstall(EFI_FS *This, EFI_HANDLE ControllerHandle)
725 {
726 EFI_STATUS Status;
727
728 /* Check if it's a filesystem we can handle */
729 if (!GrubFSProbe(This))
730 return EFI_UNSUPPORTED;
731
732 PrintInfo(L"FSInstall: %s\n", This->DevicePathString);
733
734 /* Initialize the root handle */
735 Status = GrubCreateFile(&This->RootFile, This);
736 if (EFI_ERROR(Status)) {
737 PrintStatusError(Status, L"Could not create root file");
738 return Status;
739 }
740
741 /* Setup the EFI part */
742 This->RootFile->EfiFile.Revision = EFI_FILE_PROTOCOL_REVISION2;
743 This->RootFile->EfiFile.Open = FileOpen;
744 This->RootFile->EfiFile.Close = FileClose;
745 This->RootFile->EfiFile.Delete = FileDelete;
746 This->RootFile->EfiFile.Read = FileRead;
747 This->RootFile->EfiFile.Write = FileWrite;
748 This->RootFile->EfiFile.GetPosition = FileGetPosition;
749 This->RootFile->EfiFile.SetPosition = FileSetPosition;
750 This->RootFile->EfiFile.GetInfo = FileGetInfo;
751 This->RootFile->EfiFile.SetInfo = FileSetInfo;
752 This->RootFile->EfiFile.Flush = FileFlush;
753 This->RootFile->EfiFile.OpenEx = FileOpenEx;
754 This->RootFile->EfiFile.ReadEx = FileReadEx;
755 This->RootFile->EfiFile.WriteEx = FileWriteEx;
756 This->RootFile->EfiFile.FlushEx = FileFlushEx;
757
758 /* Setup the other attributes */
759 This->RootFile->path = "/";
760 This->RootFile->basename = &This->RootFile->path[1];
761 This->RootFile->IsDir = TRUE;
762
763 /* Install the simple file system protocol. */
764 Status = BS->InstallMultipleProtocolInterfaces(&ControllerHandle,
765 &gEfiSimpleFileSystemProtocolGuid, &This->FileIoInterface,
766 NULL);
767 if (EFI_ERROR(Status)) {
768 PrintStatusError(Status, L"Could not install simple file system protocol");
769 return Status;
770 }
771
772 return EFI_SUCCESS;
773 }
774
775 /* Uninstall EFI simple file system protocol */
776 VOID
777 FSUninstall(EFI_FS *This, EFI_HANDLE ControllerHandle)
778 {
779 PrintInfo(L"FSUninstall: %s\n", This->DevicePathString);
780
781 BS->UninstallMultipleProtocolInterfaces(ControllerHandle,
782 &gEfiSimpleFileSystemProtocolGuid, &This->FileIoInterface,
783 NULL);
784 }