]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - EDK2/efiffs/mod/src/file.c
Update CI
[Ventoy.git] / EDK2 / efiffs / mod / 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 < sizeof(EFI_FILE_INFO)) {
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 if (Status == EFI_BUFFER_TOO_SMALL) {
356 *Len = MINIMUM_INFO_LENGTH;
357 } else {
358 PrintStatusError(Status, L"Directory listing failed");
359 }
360 return Status;
361 }
362
363 /* Our Index/FileSize must be reset */
364 Info->FileSize = 0;
365 Info->PhysicalSize = 0;
366
367 /* For regular files, we still need to fill the size */
368 if (!(Info->Attribute & EFI_FILE_DIRECTORY)) {
369 /* Open the file and read its size */
370 Status = GrubCreateFile(&TmpFile, File->FileSystem);
371 if (EFI_ERROR(Status)) {
372 PrintStatusError(Status, L"Unable to create temporary file");
373 return Status;
374 }
375 TmpFile->path = path;
376
377 Status = GrubOpen(TmpFile);
378 if (EFI_ERROR(Status)) {
379 // TODO: EFI_NO_MAPPING is returned for links...
380 PrintStatusError(Status, L"Unable to obtain the size of '%s'", Info->FileName);
381 /* Non fatal error */
382 } else {
383 Info->FileSize = GrubGetFileSize(TmpFile);
384 Info->PhysicalSize = GrubGetFileSize(TmpFile);
385 GrubClose(TmpFile);
386 }
387 GrubDestroyFile(TmpFile);
388 }
389
390 *Len = (UINTN) Info->Size;
391 /* Advance to the next entry */
392 File->DirIndex++;
393
394 // PrintInfo(L" Entry[%d]: '%s' %s\n", File->DirIndex-1, Info->FileName,
395 // (Info->Attribute&EFI_FILE_DIRECTORY)?L"<DIR>":L"");
396
397 return EFI_SUCCESS;
398 }
399
400 /**
401 * Read from file
402 *
403 * @v This File handle
404 * @v Len Length to read
405 * @v Data Data buffer
406 * @ret Status EFI status code
407 */
408 static EFI_STATUS EFIAPI
409 FileRead(EFI_FILE_HANDLE This, UINTN *Len, VOID *Data)
410 {
411 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
412
413 PrintInfo(L"Read(" PERCENT_P L"|'%s', %d) %s\n", (UINTN) This, FileName(File),
414 *Len, File->IsDir?L"<DIR>":L"");
415
416 /* If this is a directory, then fetch the directory entries */
417 if (File->IsDir)
418 return FileReadDir(File, Len, Data);
419
420 return GrubRead(File, Data, Len);
421 }
422
423 /* Ex version */
424 static EFI_STATUS EFIAPI
425 FileReadEx(IN EFI_FILE_PROTOCOL *This, IN OUT EFI_FILE_IO_TOKEN *Token)
426 {
427 return FileRead(This, &(Token->BufferSize), Token->Buffer);
428 }
429
430 /**
431 * Write to file
432 *
433 * @v This File handle
434 * @v Len Length to write
435 * @v Data Data buffer
436 * @ret Status EFI status code
437 */
438 static EFI_STATUS EFIAPI
439 FileWrite(EFI_FILE_HANDLE This, UINTN *Len, VOID *Data)
440 {
441 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
442
443 PrintError(L"Cannot write to '%s'\n", FileName(File));
444 return EFI_WRITE_PROTECTED;
445 }
446
447 /* Ex version */
448 static EFI_STATUS EFIAPI
449 FileWriteEx(IN EFI_FILE_PROTOCOL *This, IN OUT EFI_FILE_IO_TOKEN *Token)
450 {
451 return FileWrite(This, &(Token->BufferSize), Token->Buffer);
452 }
453
454 /**
455 * Set file position
456 *
457 * @v This File handle
458 * @v Position New file position
459 * @ret Status EFI status code
460 */
461 static EFI_STATUS EFIAPI
462 FileSetPosition(EFI_FILE_HANDLE This, UINT64 Position)
463 {
464 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
465 UINT64 FileSize;
466
467 PrintInfo(L"SetPosition(" PERCENT_P L"|'%s', %lld) %s\n", (UINTN) This,
468 FileName(File), Position, (File->IsDir)?L"<DIR>":L"");
469
470 /* If this is a directory, reset the Index to the start */
471 if (File->IsDir) {
472 if (Position != 0)
473 return EFI_INVALID_PARAMETER;
474 File->DirIndex = 0;
475 return EFI_SUCCESS;
476 }
477
478 /* Fail if we attempt to seek past the end of the file (since
479 * we do not support writes).
480 */
481 FileSize = GrubGetFileSize(File);
482 if (Position > FileSize) {
483 PrintError(L"'%s': Cannot seek to #%llx of %llx\n",
484 FileName(File), Position, FileSize);
485 return EFI_UNSUPPORTED;
486 }
487
488 /* Set position */
489 GrubSetFileOffset(File, Position);
490 PrintDebug(L"'%s': Position set to %llx\n",
491 FileName(File), Position);
492
493 return EFI_SUCCESS;
494 }
495
496 /**
497 * Get file position
498 *
499 * @v This File handle
500 * @ret Position New file position
501 * @ret Status EFI status code
502 */
503 static EFI_STATUS EFIAPI
504 FileGetPosition(EFI_FILE_HANDLE This, UINT64 *Position)
505 {
506 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
507
508 PrintInfo(L"GetPosition(" PERCENT_P L"|'%s', %lld)\n", (UINTN) This, FileName(File));
509
510 if (File->IsDir)
511 *Position = File->DirIndex;
512 else
513 *Position = GrubGetFileOffset(File);
514 return EFI_SUCCESS;
515 }
516
517 /**
518 * Get file information
519 *
520 * @v This File handle
521 * @v Type Type of information
522 * @v Len Buffer size
523 * @v Data Buffer
524 * @ret Status EFI status code
525 */
526 static EFI_STATUS EFIAPI
527 FileGetInfo(EFI_FILE_HANDLE This, EFI_GUID *Type, UINTN *Len, VOID *Data)
528 {
529 EFI_STATUS Status;
530 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
531 EFI_FILE_SYSTEM_INFO *FSInfo = (EFI_FILE_SYSTEM_INFO *) Data;
532 EFI_FILE_INFO *Info = (EFI_FILE_INFO *) Data;
533 EFI_FILE_SYSTEM_VOLUME_LABEL_INFO *VLInfo = (EFI_FILE_SYSTEM_VOLUME_LABEL_INFO *)Data;
534 EFI_TIME Time;
535 CHAR8* label;
536 UINTN tmpLen;
537
538 PrintInfo(L"GetInfo(" PERCENT_P L"|'%s', %d) %s\n", (UINTN) This,
539 FileName(File), *Len, File->IsDir?L"<DIR>":L"");
540
541 /* Determine information to return */
542 if (CompareMem(Type, &gEfiFileInfoGuid, sizeof(*Type)) == 0) {
543
544 /* Fill file information */
545 PrintExtra(L"Get regular file information\n");
546 if (*Len < sizeof(EFI_FILE_INFO)) {
547 *Len = MINIMUM_INFO_LENGTH;
548 return EFI_BUFFER_TOO_SMALL;
549 }
550
551 ZeroMem(Data, sizeof(EFI_FILE_INFO));
552
553 Info->Attribute = EFI_FILE_READ_ONLY;
554 GrubTimeToEfiTime(File->Mtime, &Time);
555 CopyMem(&Info->CreateTime, &Time, sizeof(Time));
556 CopyMem(&Info->LastAccessTime, &Time, sizeof(Time));
557 CopyMem(&Info->ModificationTime, &Time, sizeof(Time));
558
559 if (File->IsDir) {
560 Info->Attribute |= EFI_FILE_DIRECTORY;
561 } else {
562 Info->FileSize = GrubGetFileSize(File);
563 Info->PhysicalSize = GrubGetFileSize(File);
564 }
565
566 tmpLen = (UINTN)(Info->Size - sizeof(EFI_FILE_INFO) - 1);
567 Status = Utf8ToUtf16NoAllocUpdateLen(File->basename, Info->FileName, &tmpLen);
568 if (EFI_ERROR(Status)) {
569 if (Status != EFI_BUFFER_TOO_SMALL) {
570 PrintStatusError(Status, L"Could not convert basename to UTF-16");
571 } else {
572 *Len = MINIMUM_INFO_LENGTH;
573 }
574 return Status;
575 }
576
577 /* The Info struct size already accounts for the extra NUL */
578 Info->Size = sizeof(EFI_FILE_INFO) + tmpLen;
579 *Len = (INTN)Info->Size;
580 return EFI_SUCCESS;
581
582 } else if (CompareMem(Type, &gEfiFileSystemInfoGuid, sizeof(*Type)) == 0) {
583
584 /* Get file system information */
585 PrintExtra(L"Get file system information\n");
586 if (*Len < sizeof(EFI_FILE_SYSTEM_INFO)) {
587 *Len = MINIMUM_FS_INFO_LENGTH;
588 return EFI_BUFFER_TOO_SMALL;
589 }
590
591 ZeroMem(Data, sizeof(EFI_FILE_INFO));
592 FSInfo->Size = *Len;
593 FSInfo->ReadOnly = 1;
594 /* NB: This should really be cluster size, but we don't have access to that */
595 if (File->FileSystem->BlockIo2 != NULL) {
596 FSInfo->BlockSize = File->FileSystem->BlockIo2->Media->BlockSize;
597 } else {
598 FSInfo->BlockSize = File->FileSystem->BlockIo->Media->BlockSize;
599 }
600 if (FSInfo->BlockSize == 0) {
601 PrintWarning(L"Corrected Media BlockSize\n");
602 FSInfo->BlockSize = 512;
603 }
604 if (File->FileSystem->BlockIo2 != NULL) {
605 FSInfo->VolumeSize = (File->FileSystem->BlockIo2->Media->LastBlock + 1) *
606 FSInfo->BlockSize;
607 } else {
608 FSInfo->VolumeSize = (File->FileSystem->BlockIo->Media->LastBlock + 1) *
609 FSInfo->BlockSize;
610 }
611 /* No idea if we can easily get this for GRUB, and the device is RO anyway */
612 FSInfo->FreeSpace = 0;
613
614 Status = GrubLabel(File, &label);
615 if (EFI_ERROR(Status)) {
616 PrintStatusError(Status, L"Could not read disk label");
617 FSInfo->VolumeLabel[0] = 0;
618 *Len = sizeof(EFI_FILE_SYSTEM_INFO);
619 } else {
620 tmpLen = (INTN)(FSInfo->Size - sizeof(EFI_FILE_SYSTEM_INFO) - 1);
621 Status = Utf8ToUtf16NoAllocUpdateLen(label, FSInfo->VolumeLabel, &tmpLen);
622 if (EFI_ERROR(Status)) {
623 if (Status != EFI_BUFFER_TOO_SMALL) {
624 PrintStatusError(Status, L"Could not convert label to UTF-16");
625 } else {
626 *Len = MINIMUM_FS_INFO_LENGTH;
627 }
628 return Status;
629 }
630 FSInfo->Size = sizeof(EFI_FILE_SYSTEM_INFO) - 1 + tmpLen;
631 *Len = (INTN)FSInfo->Size;
632 }
633 return EFI_SUCCESS;
634
635 } else if (CompareMem(Type, &gEfiFileSystemVolumeLabelInfoIdGuid, sizeof(*Type)) == 0) {
636
637 /* Get the volume label */
638 Status = GrubLabel(File, &label);
639 if (EFI_ERROR(Status)) {
640 PrintStatusError(Status, L"Could not read disk label");
641 }
642 else {
643 Status = Utf8ToUtf16NoAllocUpdateLen(label, VLInfo->VolumeLabel, Len);
644 if (EFI_ERROR(Status)) {
645 if (Status != EFI_BUFFER_TOO_SMALL)
646 PrintStatusError(Status, L"Could not convert label to UTF-16");
647 return Status;
648 }
649 }
650 return EFI_SUCCESS;
651
652 } else {
653
654 Print(L"'%s': Cannot get information of type ", FileName(File));
655 PrintGuid(Type);
656 Print(L"\n");
657 return EFI_UNSUPPORTED;
658
659 }
660 }
661
662 /**
663 * Set file information
664 *
665 * @v This File handle
666 * @v Type Type of information
667 * @v Len Buffer size
668 * @v Data Buffer
669 * @ret Status EFI status code
670 */
671 static EFI_STATUS EFIAPI
672 FileSetInfo(EFI_FILE_HANDLE This, EFI_GUID *Type, UINTN Len, VOID *Data)
673 {
674 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
675
676 Print(L"Cannot set information of type ");
677 PrintGuid(Type);
678 Print(L" for file '%s'\n", FileName(File));
679
680 return EFI_WRITE_PROTECTED;
681 }
682
683 /**
684 * Flush file modified data
685 *
686 * @v This File handle
687 * @v Type Type of information
688 * @v Len Buffer size
689 * @v Data Buffer
690 * @ret Status EFI status code
691 */
692 static EFI_STATUS EFIAPI
693 FileFlush(EFI_FILE_HANDLE This)
694 {
695 EFI_GRUB_FILE *File = _CR(This, EFI_GRUB_FILE, EfiFile);
696
697 PrintInfo(L"Flush(" PERCENT_P L"|'%s')\n", (UINTN) This, FileName(File));
698 return EFI_SUCCESS;
699 }
700
701 /* Ex version */
702 static EFI_STATUS EFIAPI
703 FileFlushEx(EFI_FILE_HANDLE This, EFI_FILE_IO_TOKEN *Token)
704 {
705 return FileFlush(This);
706 }
707
708 /**
709 * Open root directory
710 *
711 * @v This EFI simple file system
712 * @ret Root File handle for the root directory
713 * @ret Status EFI status code
714 */
715 EFI_STATUS EFIAPI
716 FileOpenVolume(EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, EFI_FILE_HANDLE *Root)
717 {
718 EFI_FS *FSInstance = _CR(This, EFI_FS, FileIoInterface);
719
720 PrintInfo(L"OpenVolume\n");
721 *Root = &FSInstance->RootFile->EfiFile;
722
723 return EFI_SUCCESS;
724 }
725
726 /**
727 * Install the EFI simple file system protocol
728 * If successful this call instantiates a new FS#: drive, that is made
729 * available on the next 'map -r'. Note that all this call does is add
730 * the FS protocol. OpenVolume won't be called until a process tries
731 * to access a file or the root directory on the volume.
732 */
733 EFI_STATUS
734 FSInstall(EFI_FS *This, EFI_HANDLE ControllerHandle)
735 {
736 EFI_STATUS Status;
737
738 /* Check if it's a filesystem we can handle */
739 if (!GrubFSProbe(This))
740 return EFI_UNSUPPORTED;
741
742 PrintInfo(L"FSInstall: %s\n", This->DevicePathString);
743
744 /* Initialize the root handle */
745 Status = GrubCreateFile(&This->RootFile, This);
746 if (EFI_ERROR(Status)) {
747 PrintStatusError(Status, L"Could not create root file");
748 return Status;
749 }
750
751 /* Setup the EFI part */
752 This->RootFile->EfiFile.Revision = EFI_FILE_PROTOCOL_REVISION2;
753 This->RootFile->EfiFile.Open = FileOpen;
754 This->RootFile->EfiFile.Close = FileClose;
755 This->RootFile->EfiFile.Delete = FileDelete;
756 This->RootFile->EfiFile.Read = FileRead;
757 This->RootFile->EfiFile.Write = FileWrite;
758 This->RootFile->EfiFile.GetPosition = FileGetPosition;
759 This->RootFile->EfiFile.SetPosition = FileSetPosition;
760 This->RootFile->EfiFile.GetInfo = FileGetInfo;
761 This->RootFile->EfiFile.SetInfo = FileSetInfo;
762 This->RootFile->EfiFile.Flush = FileFlush;
763 This->RootFile->EfiFile.OpenEx = FileOpenEx;
764 This->RootFile->EfiFile.ReadEx = FileReadEx;
765 This->RootFile->EfiFile.WriteEx = FileWriteEx;
766 This->RootFile->EfiFile.FlushEx = FileFlushEx;
767
768 /* Setup the other attributes */
769 This->RootFile->path = "/";
770 This->RootFile->basename = &This->RootFile->path[1];
771 This->RootFile->IsDir = TRUE;
772
773 /* Install the simple file system protocol. */
774 Status = BS->InstallMultipleProtocolInterfaces(&ControllerHandle,
775 &gEfiSimpleFileSystemProtocolGuid, &This->FileIoInterface,
776 NULL);
777 if (EFI_ERROR(Status)) {
778 PrintStatusError(Status, L"Could not install simple file system protocol");
779 return Status;
780 }
781
782 return EFI_SUCCESS;
783 }
784
785 /* Uninstall EFI simple file system protocol */
786 VOID
787 FSUninstall(EFI_FS *This, EFI_HANDLE ControllerHandle)
788 {
789 PrintInfo(L"FSUninstall: %s\n", This->DevicePathString);
790
791 BS->UninstallMultipleProtocolInterfaces(ControllerHandle,
792 &gEfiSimpleFileSystemProtocolGuid, &This->FileIoInterface,
793 NULL);
794 }