uboot/lib/efi_loader/efi_file.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * EFI_FILE_PROTOCOL
   4 *
   5 * Copyright (c) 2017 Rob Clark
   6 */
   7
   8#include <common.h>
   9#include <charset.h>
  10#include <efi_loader.h>
  11#include <log.h>
  12#include <malloc.h>
  13#include <mapmem.h>
  14#include <fs.h>
  15#include <part.h>
  16
  17/* GUID for file system information */
  18const efi_guid_t efi_file_system_info_guid = EFI_FILE_SYSTEM_INFO_GUID;
  19
  20/* GUID to obtain the volume label */
  21const efi_guid_t efi_system_volume_label_id = EFI_FILE_SYSTEM_VOLUME_LABEL_ID;
  22
  23struct file_system {
  24        struct efi_simple_file_system_protocol base;
  25        struct efi_device_path *dp;
  26        struct blk_desc *desc;
  27        int part;
  28};
  29#define to_fs(x) container_of(x, struct file_system, base)
  30
  31struct file_handle {
  32        struct efi_file_handle base;
  33        struct file_system *fs;
  34        loff_t offset;       /* current file position/cursor */
  35        int isdir;
  36        u64 open_mode;
  37
  38        /* for reading a directory: */
  39        struct fs_dir_stream *dirs;
  40        struct fs_dirent *dent;
  41
  42        char path[0];
  43};
  44#define to_fh(x) container_of(x, struct file_handle, base)
  45
  46static const struct efi_file_handle efi_file_handle_protocol;
  47
  48static char *basename(struct file_handle *fh)
  49{
  50        char *s = strrchr(fh->path, '/');
  51        if (s)
  52                return s + 1;
  53        return fh->path;
  54}
  55
  56static int set_blk_dev(struct file_handle *fh)
  57{
  58        return fs_set_blk_dev_with_part(fh->fs->desc, fh->fs->part);
  59}
  60
  61/**
  62 * is_dir() - check if file handle points to directory
  63 *
  64 * We assume that set_blk_dev(fh) has been called already.
  65 *
  66 * @fh:         file handle
  67 * Return:      true if file handle points to a directory
  68 */
  69static int is_dir(struct file_handle *fh)
  70{
  71        struct fs_dir_stream *dirs;
  72
  73        dirs = fs_opendir(fh->path);
  74        if (!dirs)
  75                return 0;
  76
  77        fs_closedir(dirs);
  78
  79        return 1;
  80}
  81
  82/*
  83 * Normalize a path which may include either back or fwd slashes,
  84 * double slashes, . or .. entries in the path, etc.
  85 */
  86static int sanitize_path(char *path)
  87{
  88        char *p;
  89
  90        /* backslash to slash: */
  91        p = path;
  92        while ((p = strchr(p, '\\')))
  93                *p++ = '/';
  94
  95        /* handle double-slashes: */
  96        p = path;
  97        while ((p = strstr(p, "//"))) {
  98                char *src = p + 1;
  99                memmove(p, src, strlen(src) + 1);
 100        }
 101
 102        /* handle extra /.'s */
 103        p = path;
 104        while ((p = strstr(p, "/."))) {
 105                /*
 106                 * You'd be tempted to do this *after* handling ".."s
 107                 * below to avoid having to check if "/." is start of
 108                 * a "/..", but that won't have the correct results..
 109                 * for example, "/foo/./../bar" would get resolved to
 110                 * "/foo/bar" if you did these two passes in the other
 111                 * order
 112                 */
 113                if (p[2] == '.') {
 114                        p += 2;
 115                        continue;
 116                }
 117                char *src = p + 2;
 118                memmove(p, src, strlen(src) + 1);
 119        }
 120
 121        /* handle extra /..'s: */
 122        p = path;
 123        while ((p = strstr(p, "/.."))) {
 124                char *src = p + 3;
 125
 126                p--;
 127
 128                /* find beginning of previous path entry: */
 129                while (true) {
 130                        if (p < path)
 131                                return -1;
 132                        if (*p == '/')
 133                                break;
 134                        p--;
 135                }
 136
 137                memmove(p, src, strlen(src) + 1);
 138        }
 139
 140        return 0;
 141}
 142
 143/**
 144 * efi_create_file() - create file or directory
 145 *
 146 * @fh:                 file handle
 147 * @attributes:         attributes for newly created file
 148 * Returns:             0 for success
 149 */
 150static int efi_create_file(struct file_handle *fh, u64 attributes)
 151{
 152        loff_t actwrite;
 153        void *buffer = &actwrite;
 154
 155        if (attributes & EFI_FILE_DIRECTORY)
 156                return fs_mkdir(fh->path);
 157        else
 158                return fs_write(fh->path, map_to_sysmem(buffer), 0, 0,
 159                                &actwrite);
 160}
 161
 162/**
 163 * file_open() - open a file handle
 164 *
 165 * @fs:                 file system
 166 * @parent:             directory relative to which the file is to be opened
 167 * @file_name:          path of the file to be opened. '\', '.', or '..' may
 168 *                      be used as modifiers. A leading backslash indicates an
 169 *                      absolute path.
 170 * @open_mode:          bit mask indicating the access mode (read, write,
 171 *                      create)
 172 * @attributes:         attributes for newly created file
 173 * Returns:             handle to the opened file or NULL
 174 */
 175static struct efi_file_handle *file_open(struct file_system *fs,
 176                struct file_handle *parent, u16 *file_name, u64 open_mode,
 177                u64 attributes)
 178{
 179        struct file_handle *fh;
 180        char f0[MAX_UTF8_PER_UTF16] = {0};
 181        int plen = 0;
 182        int flen = 0;
 183
 184        if (file_name) {
 185                utf16_to_utf8((u8 *)f0, file_name, 1);
 186                flen = u16_strlen(file_name);
 187        }
 188
 189        /* we could have a parent, but also an absolute path: */
 190        if (f0[0] == '\\') {
 191                plen = 0;
 192        } else if (parent) {
 193                plen = strlen(parent->path) + 1;
 194        }
 195
 196        /* +2 is for null and '/' */
 197        fh = calloc(1, sizeof(*fh) + plen + (flen * MAX_UTF8_PER_UTF16) + 2);
 198
 199        fh->open_mode = open_mode;
 200        fh->base = efi_file_handle_protocol;
 201        fh->fs = fs;
 202
 203        if (parent) {
 204                char *p = fh->path;
 205                int exists;
 206
 207                if (plen > 0) {
 208                        strcpy(p, parent->path);
 209                        p += plen - 1;
 210                        *p++ = '/';
 211                }
 212
 213                utf16_to_utf8((u8 *)p, file_name, flen);
 214
 215                if (sanitize_path(fh->path))
 216                        goto error;
 217
 218                /* check if file exists: */
 219                if (set_blk_dev(fh))
 220                        goto error;
 221
 222                exists = fs_exists(fh->path);
 223                /* fs_exists() calls fs_close(), so open file system again */
 224                if (set_blk_dev(fh))
 225                        goto error;
 226
 227                if (!exists) {
 228                        if (!(open_mode & EFI_FILE_MODE_CREATE) ||
 229                            efi_create_file(fh, attributes))
 230                                goto error;
 231                        if (set_blk_dev(fh))
 232                                goto error;
 233                }
 234
 235                /* figure out if file is a directory: */
 236                fh->isdir = is_dir(fh);
 237        } else {
 238                fh->isdir = 1;
 239                strcpy(fh->path, "");
 240        }
 241
 242        return &fh->base;
 243
 244error:
 245        free(fh);
 246        return NULL;
 247}
 248
 249efi_status_t efi_file_open_int(struct efi_file_handle *this,
 250                               struct efi_file_handle **new_handle,
 251                               u16 *file_name, u64 open_mode,
 252                               u64 attributes)
 253{
 254        struct file_handle *fh = to_fh(this);
 255        efi_status_t ret;
 256
 257        /* Check parameters */
 258        if (!this || !new_handle || !file_name) {
 259                ret = EFI_INVALID_PARAMETER;
 260                goto out;
 261        }
 262        if (open_mode != EFI_FILE_MODE_READ &&
 263            open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE) &&
 264            open_mode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE |
 265                         EFI_FILE_MODE_CREATE)) {
 266                ret = EFI_INVALID_PARAMETER;
 267                goto out;
 268        }
 269        /*
 270         * The UEFI spec requires that attributes are only set in create mode.
 271         * The SCT does not care about this and sets EFI_FILE_DIRECTORY in
 272         * read mode. EDK2 does not check that attributes are zero if not in
 273         * create mode.
 274         *
 275         * So here we only check attributes in create mode and do not check
 276         * that they are zero otherwise.
 277         */
 278        if ((open_mode & EFI_FILE_MODE_CREATE) &&
 279            (attributes & (EFI_FILE_READ_ONLY | ~EFI_FILE_VALID_ATTR))) {
 280                ret = EFI_INVALID_PARAMETER;
 281                goto out;
 282        }
 283
 284        /* Open file */
 285        *new_handle = file_open(fh->fs, fh, file_name, open_mode, attributes);
 286        if (*new_handle) {
 287                EFI_PRINT("file handle %p\n", *new_handle);
 288                ret = EFI_SUCCESS;
 289        } else {
 290                ret = EFI_NOT_FOUND;
 291        }
 292out:
 293        return ret;
 294}
 295
 296/**
 297 * efi_file_open_()
 298 *
 299 * This function implements the Open service of the File Protocol.
 300 * See the UEFI spec for details.
 301 *
 302 * @this:       EFI_FILE_PROTOCOL instance
 303 * @new_handle: on return pointer to file handle
 304 * @file_name:  file name
 305 * @open_mode:  mode to open the file (read, read/write, create/read/write)
 306 * @attributes: attributes for newly created file
 307 */
 308static efi_status_t EFIAPI efi_file_open(struct efi_file_handle *this,
 309                                         struct efi_file_handle **new_handle,
 310                                         u16 *file_name, u64 open_mode,
 311                                         u64 attributes)
 312{
 313        efi_status_t ret;
 314
 315        EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu", this, new_handle,
 316                  file_name, open_mode, attributes);
 317
 318        ret = efi_file_open_int(this, new_handle, file_name, open_mode,
 319                                attributes);
 320
 321        return EFI_EXIT(ret);
 322}
 323
 324/**
 325 * efi_file_open_ex() - open file asynchronously
 326 *
 327 * This function implements the OpenEx service of the File Protocol.
 328 * See the UEFI spec for details.
 329 *
 330 * @this:       EFI_FILE_PROTOCOL instance
 331 * @new_handle: on return pointer to file handle
 332 * @file_name:  file name
 333 * @open_mode:  mode to open the file (read, read/write, create/read/write)
 334 * @attributes: attributes for newly created file
 335 * @token:      transaction token
 336 */
 337static efi_status_t EFIAPI efi_file_open_ex(struct efi_file_handle *this,
 338                                            struct efi_file_handle **new_handle,
 339                                            u16 *file_name, u64 open_mode,
 340                                            u64 attributes,
 341                                            struct efi_file_io_token *token)
 342{
 343        efi_status_t ret;
 344
 345        EFI_ENTRY("%p, %p, \"%ls\", %llx, %llu, %p", this, new_handle,
 346                  file_name, open_mode, attributes, token);
 347
 348        if (!token) {
 349                ret = EFI_INVALID_PARAMETER;
 350                goto out;
 351        }
 352
 353        ret = efi_file_open_int(this, new_handle, file_name, open_mode,
 354                                attributes);
 355
 356        if (ret == EFI_SUCCESS && token->event) {
 357                token->status = EFI_SUCCESS;
 358                efi_signal_event(token->event);
 359        }
 360
 361out:
 362        return EFI_EXIT(ret);
 363}
 364
 365static efi_status_t file_close(struct file_handle *fh)
 366{
 367        fs_closedir(fh->dirs);
 368        free(fh);
 369        return EFI_SUCCESS;
 370}
 371
 372efi_status_t efi_file_close_int(struct efi_file_handle *file)
 373{
 374        struct file_handle *fh = to_fh(file);
 375
 376        return file_close(fh);
 377}
 378
 379static efi_status_t EFIAPI efi_file_close(struct efi_file_handle *file)
 380{
 381        EFI_ENTRY("%p", file);
 382        return EFI_EXIT(efi_file_close_int(file));
 383}
 384
 385static efi_status_t EFIAPI efi_file_delete(struct efi_file_handle *file)
 386{
 387        struct file_handle *fh = to_fh(file);
 388        efi_status_t ret = EFI_SUCCESS;
 389
 390        EFI_ENTRY("%p", file);
 391
 392        if (set_blk_dev(fh) || fs_unlink(fh->path))
 393                ret = EFI_WARN_DELETE_FAILURE;
 394
 395        file_close(fh);
 396        return EFI_EXIT(ret);
 397}
 398
 399/**
 400 * efi_get_file_size() - determine the size of a file
 401 *
 402 * @fh:         file handle
 403 * @file_size:  pointer to receive file size
 404 * Return:      status code
 405 */
 406static efi_status_t efi_get_file_size(struct file_handle *fh,
 407                                      loff_t *file_size)
 408{
 409        if (set_blk_dev(fh))
 410                return EFI_DEVICE_ERROR;
 411
 412        if (fs_size(fh->path, file_size))
 413                return EFI_DEVICE_ERROR;
 414
 415        return EFI_SUCCESS;
 416}
 417
 418/**
 419 * efi_file_size() - Get the size of a file using an EFI file handle
 420 *
 421 * @fh:         EFI file handle
 422 * @size:       buffer to fill in the discovered size
 423 *
 424 * Return:      size of the file
 425 */
 426efi_status_t efi_file_size(struct efi_file_handle *fh, efi_uintn_t *size)
 427{
 428        struct efi_file_info *info = NULL;
 429        efi_uintn_t bs = 0;
 430        efi_status_t ret;
 431
 432        *size = 0;
 433        ret = EFI_CALL(fh->getinfo(fh, (efi_guid_t *)&efi_file_info_guid, &bs,
 434                                   info));
 435        if (ret != EFI_BUFFER_TOO_SMALL) {
 436                ret = EFI_DEVICE_ERROR;
 437                goto out;
 438        }
 439
 440        info = malloc(bs);
 441        if (!info) {
 442                ret = EFI_OUT_OF_RESOURCES;
 443                goto out;
 444        }
 445        ret = EFI_CALL(fh->getinfo(fh, (efi_guid_t *)&efi_file_info_guid, &bs,
 446                                   info));
 447        if (ret != EFI_SUCCESS)
 448                goto out;
 449
 450        *size = info->file_size;
 451
 452out:
 453        free(info);
 454        return ret;
 455}
 456
 457static efi_status_t file_read(struct file_handle *fh, u64 *buffer_size,
 458                void *buffer)
 459{
 460        loff_t actread;
 461        efi_status_t ret;
 462        loff_t file_size;
 463
 464        if (!buffer) {
 465                ret = EFI_INVALID_PARAMETER;
 466                return ret;
 467        }
 468
 469        ret = efi_get_file_size(fh, &file_size);
 470        if (ret != EFI_SUCCESS)
 471                return ret;
 472        if (file_size < fh->offset) {
 473                ret = EFI_DEVICE_ERROR;
 474                return ret;
 475        }
 476
 477        if (set_blk_dev(fh))
 478                return EFI_DEVICE_ERROR;
 479        if (fs_read(fh->path, map_to_sysmem(buffer), fh->offset,
 480                    *buffer_size, &actread))
 481                return EFI_DEVICE_ERROR;
 482
 483        *buffer_size = actread;
 484        fh->offset += actread;
 485
 486        return EFI_SUCCESS;
 487}
 488
 489static void rtc2efi(struct efi_time *time, struct rtc_time *tm)
 490{
 491        memset(time, 0, sizeof(struct efi_time));
 492        time->year = tm->tm_year;
 493        time->month = tm->tm_mon;
 494        time->day = tm->tm_mday;
 495        time->hour = tm->tm_hour;
 496        time->minute = tm->tm_min;
 497        time->second = tm->tm_sec;
 498}
 499
 500static efi_status_t dir_read(struct file_handle *fh, u64 *buffer_size,
 501                void *buffer)
 502{
 503        struct efi_file_info *info = buffer;
 504        struct fs_dirent *dent;
 505        u64 required_size;
 506        u16 *dst;
 507
 508        if (set_blk_dev(fh))
 509                return EFI_DEVICE_ERROR;
 510
 511        if (!fh->dirs) {
 512                assert(fh->offset == 0);
 513                fh->dirs = fs_opendir(fh->path);
 514                if (!fh->dirs)
 515                        return EFI_DEVICE_ERROR;
 516                fh->dent = NULL;
 517        }
 518
 519        /*
 520         * So this is a bit awkward.  Since fs layer is stateful and we
 521         * can't rewind an entry, in the EFI_BUFFER_TOO_SMALL case below
 522         * we might have to return without consuming the dent.. so we
 523         * have to stash it for next call.
 524         */
 525        if (fh->dent) {
 526                dent = fh->dent;
 527        } else {
 528                dent = fs_readdir(fh->dirs);
 529        }
 530
 531        if (!dent) {
 532                /* no more files in directory */
 533                *buffer_size = 0;
 534                return EFI_SUCCESS;
 535        }
 536
 537        /* check buffer size: */
 538        required_size = sizeof(*info) +
 539                        2 * (utf8_utf16_strlen(dent->name) + 1);
 540        if (*buffer_size < required_size) {
 541                *buffer_size = required_size;
 542                fh->dent = dent;
 543                return EFI_BUFFER_TOO_SMALL;
 544        }
 545        if (!buffer)
 546                return EFI_INVALID_PARAMETER;
 547        fh->dent = NULL;
 548
 549        *buffer_size = required_size;
 550        memset(info, 0, required_size);
 551
 552        info->size = required_size;
 553        info->file_size = dent->size;
 554        info->physical_size = dent->size;
 555        info->attribute = dent->attr;
 556        rtc2efi(&info->create_time, &dent->create_time);
 557        rtc2efi(&info->modification_time, &dent->change_time);
 558        rtc2efi(&info->last_access_time, &dent->access_time);
 559
 560        if (dent->type == FS_DT_DIR)
 561                info->attribute |= EFI_FILE_DIRECTORY;
 562
 563        dst = info->file_name;
 564        utf8_utf16_strcpy(&dst, dent->name);
 565
 566        fh->offset++;
 567
 568        return EFI_SUCCESS;
 569}
 570
 571efi_status_t efi_file_read_int(struct efi_file_handle *this,
 572                               efi_uintn_t *buffer_size, void *buffer)
 573{
 574        struct file_handle *fh = to_fh(this);
 575        efi_status_t ret = EFI_SUCCESS;
 576        u64 bs;
 577
 578        if (!this || !buffer_size)
 579                return EFI_INVALID_PARAMETER;
 580
 581        bs = *buffer_size;
 582        if (fh->isdir)
 583                ret = dir_read(fh, &bs, buffer);
 584        else
 585                ret = file_read(fh, &bs, buffer);
 586        if (bs <= SIZE_MAX)
 587                *buffer_size = bs;
 588        else
 589                *buffer_size = SIZE_MAX;
 590
 591        return ret;
 592}
 593
 594/**
 595 * efi_file_read() - read file
 596 *
 597 * This function implements the Read() service of the EFI_FILE_PROTOCOL.
 598 *
 599 * See the Unified Extensible Firmware Interface (UEFI) specification for
 600 * details.
 601 *
 602 * @this:               file protocol instance
 603 * @buffer_size:        number of bytes to read
 604 * @buffer:             read buffer
 605 * Return:              status code
 606 */
 607static efi_status_t EFIAPI efi_file_read(struct efi_file_handle *this,
 608                                         efi_uintn_t *buffer_size, void *buffer)
 609{
 610        efi_status_t ret;
 611
 612        EFI_ENTRY("%p, %p, %p", this, buffer_size, buffer);
 613
 614        ret = efi_file_read_int(this, buffer_size, buffer);
 615
 616        return EFI_EXIT(ret);
 617}
 618
 619/**
 620 * efi_file_read_ex() - read file asynchonously
 621 *
 622 * This function implements the ReadEx() service of the EFI_FILE_PROTOCOL.
 623 *
 624 * See the Unified Extensible Firmware Interface (UEFI) specification for
 625 * details.
 626 *
 627 * @this:               file protocol instance
 628 * @token:              transaction token
 629 * Return:              status code
 630 */
 631static efi_status_t EFIAPI efi_file_read_ex(struct efi_file_handle *this,
 632                                            struct efi_file_io_token *token)
 633{
 634        efi_status_t ret;
 635
 636        EFI_ENTRY("%p, %p", this, token);
 637
 638        if (!token) {
 639                ret = EFI_INVALID_PARAMETER;
 640                goto out;
 641        }
 642
 643        ret = efi_file_read_int(this, &token->buffer_size, token->buffer);
 644
 645        if (ret == EFI_SUCCESS && token->event) {
 646                token->status = EFI_SUCCESS;
 647                efi_signal_event(token->event);
 648        }
 649
 650out:
 651        return EFI_EXIT(ret);
 652}
 653
 654static efi_status_t efi_file_write_int(struct efi_file_handle *this,
 655                                       efi_uintn_t *buffer_size, void *buffer)
 656{
 657        struct file_handle *fh = to_fh(this);
 658        efi_status_t ret = EFI_SUCCESS;
 659        loff_t actwrite;
 660
 661        if (!this || !buffer_size || !buffer) {
 662                ret = EFI_INVALID_PARAMETER;
 663                goto out;
 664        }
 665        if (fh->isdir) {
 666                ret = EFI_UNSUPPORTED;
 667                goto out;
 668        }
 669        if (!(fh->open_mode & EFI_FILE_MODE_WRITE)) {
 670                ret = EFI_ACCESS_DENIED;
 671                goto out;
 672        }
 673
 674        if (!*buffer_size)
 675                goto out;
 676
 677        if (set_blk_dev(fh)) {
 678                ret = EFI_DEVICE_ERROR;
 679                goto out;
 680        }
 681        if (fs_write(fh->path, map_to_sysmem(buffer), fh->offset, *buffer_size,
 682                     &actwrite)) {
 683                ret = EFI_DEVICE_ERROR;
 684                goto out;
 685        }
 686        *buffer_size = actwrite;
 687        fh->offset += actwrite;
 688
 689out:
 690        return ret;
 691}
 692
 693/**
 694 * efi_file_write() - write to file
 695 *
 696 * This function implements the Write() service of the EFI_FILE_PROTOCOL.
 697 *
 698 * See the Unified Extensible Firmware Interface (UEFI) specification for
 699 * details.
 700 *
 701 * @this:               file protocol instance
 702 * @buffer_size:        number of bytes to write
 703 * @buffer:             buffer with the bytes to write
 704 * Return:              status code
 705 */
 706static efi_status_t EFIAPI efi_file_write(struct efi_file_handle *this,
 707                                          efi_uintn_t *buffer_size,
 708                                          void *buffer)
 709{
 710        efi_status_t ret;
 711
 712        EFI_ENTRY("%p, %p, %p", this, buffer_size, buffer);
 713
 714        ret = efi_file_write_int(this, buffer_size, buffer);
 715
 716        return EFI_EXIT(ret);
 717}
 718
 719/**
 720 * efi_file_write_ex() - write to file
 721 *
 722 * This function implements the WriteEx() service of the EFI_FILE_PROTOCOL.
 723 *
 724 * See the Unified Extensible Firmware Interface (UEFI) specification for
 725 * details.
 726 *
 727 * @this:               file protocol instance
 728 * @token:              transaction token
 729 * Return:              status code
 730 */
 731static efi_status_t EFIAPI efi_file_write_ex(struct efi_file_handle *this,
 732                                             struct efi_file_io_token *token)
 733{
 734        efi_status_t ret;
 735
 736        EFI_ENTRY("%p, %p", this, token);
 737
 738        if (!token) {
 739                ret = EFI_INVALID_PARAMETER;
 740                goto out;
 741        }
 742
 743        ret = efi_file_write_int(this, &token->buffer_size, token->buffer);
 744
 745        if (ret == EFI_SUCCESS && token->event) {
 746                token->status = EFI_SUCCESS;
 747                efi_signal_event(token->event);
 748        }
 749
 750out:
 751        return EFI_EXIT(ret);
 752}
 753
 754/**
 755 * efi_file_getpos() - get current position in file
 756 *
 757 * This function implements the GetPosition service of the EFI file protocol.
 758 * See the UEFI spec for details.
 759 *
 760 * @file:       file handle
 761 * @pos:        pointer to file position
 762 * Return:      status code
 763 */
 764static efi_status_t EFIAPI efi_file_getpos(struct efi_file_handle *file,
 765                                           u64 *pos)
 766{
 767        efi_status_t ret = EFI_SUCCESS;
 768        struct file_handle *fh = to_fh(file);
 769
 770        EFI_ENTRY("%p, %p", file, pos);
 771
 772        if (fh->isdir) {
 773                ret = EFI_UNSUPPORTED;
 774                goto out;
 775        }
 776
 777        *pos = fh->offset;
 778out:
 779        return EFI_EXIT(ret);
 780}
 781
 782efi_status_t efi_file_setpos_int(struct efi_file_handle *file, u64 pos)
 783{
 784        struct file_handle *fh = to_fh(file);
 785        efi_status_t ret = EFI_SUCCESS;
 786
 787        if (fh->isdir) {
 788                if (pos != 0) {
 789                        ret = EFI_UNSUPPORTED;
 790                        goto error;
 791                }
 792                fs_closedir(fh->dirs);
 793                fh->dirs = NULL;
 794        }
 795
 796        if (pos == ~0ULL) {
 797                loff_t file_size;
 798
 799                ret = efi_get_file_size(fh, &file_size);
 800                if (ret != EFI_SUCCESS)
 801                        goto error;
 802                pos = file_size;
 803        }
 804
 805        fh->offset = pos;
 806
 807error:
 808        return ret;
 809}
 810
 811/**
 812 * efi_file_setpos() - set current position in file
 813 *
 814 * This function implements the SetPosition service of the EFI file protocol.
 815 * See the UEFI spec for details.
 816 *
 817 * @file:       file handle
 818 * @pos:        new file position
 819 * Return:      status code
 820 */
 821static efi_status_t EFIAPI efi_file_setpos(struct efi_file_handle *file,
 822                                           u64 pos)
 823{
 824        efi_status_t ret = EFI_SUCCESS;
 825
 826        EFI_ENTRY("%p, %llu", file, pos);
 827
 828        ret = efi_file_setpos_int(file, pos);
 829
 830        return EFI_EXIT(ret);
 831}
 832
 833static efi_status_t EFIAPI efi_file_getinfo(struct efi_file_handle *file,
 834                                            const efi_guid_t *info_type,
 835                                            efi_uintn_t *buffer_size,
 836                                            void *buffer)
 837{
 838        struct file_handle *fh = to_fh(file);
 839        efi_status_t ret = EFI_SUCCESS;
 840        u16 *dst;
 841
 842        EFI_ENTRY("%p, %pUs, %p, %p", file, info_type, buffer_size, buffer);
 843
 844        if (!file || !info_type || !buffer_size ||
 845            (*buffer_size && !buffer)) {
 846                ret = EFI_INVALID_PARAMETER;
 847                goto error;
 848        }
 849
 850        if (!guidcmp(info_type, &efi_file_info_guid)) {
 851                struct efi_file_info *info = buffer;
 852                char *filename = basename(fh);
 853                unsigned int required_size;
 854                loff_t file_size;
 855
 856                /* check buffer size: */
 857                required_size = sizeof(*info) +
 858                                2 * (utf8_utf16_strlen(filename) + 1);
 859                if (*buffer_size < required_size) {
 860                        *buffer_size = required_size;
 861                        ret = EFI_BUFFER_TOO_SMALL;
 862                        goto error;
 863                }
 864
 865                ret = efi_get_file_size(fh, &file_size);
 866                if (ret != EFI_SUCCESS)
 867                        goto error;
 868
 869                memset(info, 0, required_size);
 870
 871                info->size = required_size;
 872                info->file_size = file_size;
 873                info->physical_size = file_size;
 874
 875                if (fh->isdir)
 876                        info->attribute |= EFI_FILE_DIRECTORY;
 877
 878                dst = info->file_name;
 879                utf8_utf16_strcpy(&dst, filename);
 880        } else if (!guidcmp(info_type, &efi_file_system_info_guid)) {
 881                struct efi_file_system_info *info = buffer;
 882                struct disk_partition part;
 883                efi_uintn_t required_size;
 884                int r;
 885
 886                if (fh->fs->part >= 1)
 887                        r = part_get_info(fh->fs->desc, fh->fs->part, &part);
 888                else
 889                        r = part_get_info_whole_disk(fh->fs->desc, &part);
 890                if (r < 0) {
 891                        ret = EFI_DEVICE_ERROR;
 892                        goto error;
 893                }
 894                required_size = sizeof(*info) + 2;
 895                if (*buffer_size < required_size) {
 896                        *buffer_size = required_size;
 897                        ret = EFI_BUFFER_TOO_SMALL;
 898                        goto error;
 899                }
 900
 901                memset(info, 0, required_size);
 902
 903                info->size = required_size;
 904                /*
 905                 * TODO: We cannot determine if the volume can be written to.
 906                 */
 907                info->read_only = false;
 908                info->volume_size = part.size * part.blksz;
 909                /*
 910                 * TODO: We currently have no function to determine the free
 911                 * space. The volume size is the best upper bound we have.
 912                 */
 913                info->free_space = info->volume_size;
 914                info->block_size = part.blksz;
 915                /*
 916                 * TODO: The volume label is not available in U-Boot.
 917                 */
 918                info->volume_label[0] = 0;
 919        } else if (!guidcmp(info_type, &efi_system_volume_label_id)) {
 920                if (*buffer_size < 2) {
 921                        *buffer_size = 2;
 922                        ret = EFI_BUFFER_TOO_SMALL;
 923                        goto error;
 924                }
 925                *(u16 *)buffer = 0;
 926        } else {
 927                ret = EFI_UNSUPPORTED;
 928        }
 929
 930error:
 931        return EFI_EXIT(ret);
 932}
 933
 934static efi_status_t EFIAPI efi_file_setinfo(struct efi_file_handle *file,
 935                                            const efi_guid_t *info_type,
 936                                            efi_uintn_t buffer_size,
 937                                            void *buffer)
 938{
 939        struct file_handle *fh = to_fh(file);
 940        efi_status_t ret = EFI_UNSUPPORTED;
 941
 942        EFI_ENTRY("%p, %pUs, %zu, %p", file, info_type, buffer_size, buffer);
 943
 944        if (!guidcmp(info_type, &efi_file_info_guid)) {
 945                struct efi_file_info *info = (struct efi_file_info *)buffer;
 946                char *filename = basename(fh);
 947                char *new_file_name, *pos;
 948                loff_t file_size;
 949
 950                /* The buffer will always contain a file name. */
 951                if (buffer_size < sizeof(struct efi_file_info) + 2 ||
 952                    buffer_size < info->size) {
 953                        ret = EFI_BAD_BUFFER_SIZE;
 954                        goto out;
 955                }
 956                /* We cannot change the directory attribute */
 957                if (!fh->isdir != !(info->attribute & EFI_FILE_DIRECTORY)) {
 958                        ret = EFI_ACCESS_DENIED;
 959                        goto out;
 960                }
 961                /* Check for renaming */
 962                new_file_name = malloc(utf16_utf8_strlen(info->file_name) + 1);
 963                if (!new_file_name) {
 964                        ret = EFI_OUT_OF_RESOURCES;
 965                        goto out;
 966                }
 967                pos = new_file_name;
 968                utf16_utf8_strcpy(&pos, info->file_name);
 969                if (strcmp(new_file_name, filename)) {
 970                        /* TODO: we do not support renaming */
 971                        EFI_PRINT("Renaming not supported\n");
 972                        free(new_file_name);
 973                        ret = EFI_ACCESS_DENIED;
 974                        goto out;
 975                }
 976                free(new_file_name);
 977                /* Check for truncation */
 978                ret = efi_get_file_size(fh, &file_size);
 979                if (ret != EFI_SUCCESS)
 980                        goto out;
 981                if (file_size != info->file_size) {
 982                        /* TODO: we do not support truncation */
 983                        EFI_PRINT("Truncation not supported\n");
 984                        ret = EFI_ACCESS_DENIED;
 985                        goto out;
 986                }
 987                /*
 988                 * We do not care for the other attributes
 989                 * TODO: Support read only
 990                 */
 991                ret = EFI_SUCCESS;
 992        } else {
 993                /* TODO: We do not support changing the volume label */
 994                ret = EFI_UNSUPPORTED;
 995        }
 996out:
 997        return EFI_EXIT(ret);
 998}
 999
1000/**
1001 * efi_file_flush_int() - flush file
1002 *
1003 * This is the internal implementation of the Flush() and FlushEx() services of
1004 * the EFI_FILE_PROTOCOL.
1005 *
1006 * @this:       file protocol instance
1007 * Return:      status code
1008 */
1009static efi_status_t efi_file_flush_int(struct efi_file_handle *this)
1010{
1011        struct file_handle *fh = to_fh(this);
1012
1013        if (!this)
1014                return EFI_INVALID_PARAMETER;
1015
1016        if (!(fh->open_mode & EFI_FILE_MODE_WRITE))
1017                return EFI_ACCESS_DENIED;
1018
1019        /* TODO: flush for file position after end of file */
1020        return EFI_SUCCESS;
1021}
1022
1023/**
1024 * efi_file_flush() - flush file
1025 *
1026 * This function implements the Flush() service of the EFI_FILE_PROTOCOL.
1027 *
1028 * See the Unified Extensible Firmware Interface (UEFI) specification for
1029 * details.
1030 *
1031 * @this:       file protocol instance
1032 * Return:      status code
1033 */
1034static efi_status_t EFIAPI efi_file_flush(struct efi_file_handle *this)
1035{
1036        efi_status_t ret;
1037
1038        EFI_ENTRY("%p", this);
1039
1040        ret = efi_file_flush_int(this);
1041
1042        return EFI_EXIT(ret);
1043}
1044
1045/**
1046 * efi_file_flush_ex() - flush file
1047 *
1048 * This function implements the FlushEx() service of the EFI_FILE_PROTOCOL.
1049 *
1050 * See the Unified Extensible Firmware Interface (UEFI) specification for
1051 * details.
1052 *
1053 * @this:       file protocol instance
1054 * @token:      transaction token
1055 * Return:      status code
1056 */
1057static efi_status_t EFIAPI efi_file_flush_ex(struct efi_file_handle *this,
1058                                             struct efi_file_io_token *token)
1059{
1060        efi_status_t ret;
1061
1062        EFI_ENTRY("%p, %p", this, token);
1063
1064        if (!token) {
1065                ret = EFI_INVALID_PARAMETER;
1066                goto out;
1067        }
1068
1069        ret = efi_file_flush_int(this);
1070
1071        if (ret == EFI_SUCCESS && token->event) {
1072                token->status = EFI_SUCCESS;
1073                efi_signal_event(token->event);
1074        }
1075
1076out:
1077        return EFI_EXIT(ret);
1078}
1079
1080static const struct efi_file_handle efi_file_handle_protocol = {
1081        .rev = EFI_FILE_PROTOCOL_REVISION2,
1082        .open = efi_file_open,
1083        .close = efi_file_close,
1084        .delete = efi_file_delete,
1085        .read = efi_file_read,
1086        .write = efi_file_write,
1087        .getpos = efi_file_getpos,
1088        .setpos = efi_file_setpos,
1089        .getinfo = efi_file_getinfo,
1090        .setinfo = efi_file_setinfo,
1091        .flush = efi_file_flush,
1092        .open_ex = efi_file_open_ex,
1093        .read_ex = efi_file_read_ex,
1094        .write_ex = efi_file_write_ex,
1095        .flush_ex = efi_file_flush_ex,
1096};
1097
1098/**
1099 * efi_file_from_path() - open file via device path
1100 *
1101 * The device path @fp consists of the device path of the handle with the
1102 * simple file system protocol and one or more file path device path nodes.
1103 * The concatenation of all file path names provides the total file path.
1104 *
1105 * The code starts at the first file path node and tries to open that file or
1106 * directory. If there is a succeding file path node, the code opens it relative
1107 * to this directory and continues iterating until reaching the last file path
1108 * node.
1109 *
1110 * @fp:         device path
1111 * Return:      EFI_FILE_PROTOCOL for the file or NULL
1112 */
1113struct efi_file_handle *efi_file_from_path(struct efi_device_path *fp)
1114{
1115        struct efi_simple_file_system_protocol *v;
1116        struct efi_file_handle *f;
1117        efi_status_t ret;
1118
1119        v = efi_fs_from_path(fp);
1120        if (!v)
1121                return NULL;
1122
1123        EFI_CALL(ret = v->open_volume(v, &f));
1124        if (ret != EFI_SUCCESS)
1125                return NULL;
1126
1127        /* Skip over device-path nodes before the file path. */
1128        while (fp && !EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH))
1129                fp = efi_dp_next(fp);
1130
1131        /*
1132         * Step through the nodes of the directory path until the actual file
1133         * node is reached which is the final node in the device path.
1134         */
1135        while (fp) {
1136                struct efi_device_path_file_path *fdp =
1137                        container_of(fp, struct efi_device_path_file_path, dp);
1138                struct efi_file_handle *f2;
1139                u16 *filename;
1140                size_t filename_sz;
1141
1142                if (!EFI_DP_TYPE(fp, MEDIA_DEVICE, FILE_PATH)) {
1143                        printf("bad file path!\n");
1144                        EFI_CALL(f->close(f));
1145                        return NULL;
1146                }
1147
1148                /*
1149                 * UEFI specification requires pointers that are passed to
1150                 * protocol member functions to be aligned.  So memcpy it
1151                 * unconditionally
1152                 */
1153                if (fdp->dp.length <= offsetof(struct efi_device_path_file_path, str))
1154                        return NULL;
1155                filename_sz = fdp->dp.length -
1156                        offsetof(struct efi_device_path_file_path, str);
1157                filename = malloc(filename_sz);
1158                if (!filename)
1159                        return NULL;
1160                memcpy(filename, fdp->str, filename_sz);
1161                EFI_CALL(ret = f->open(f, &f2, filename,
1162                                       EFI_FILE_MODE_READ, 0));
1163                free(filename);
1164                if (ret != EFI_SUCCESS)
1165                        return NULL;
1166
1167                fp = efi_dp_next(fp);
1168
1169                EFI_CALL(f->close(f));
1170                f = f2;
1171        }
1172
1173        return f;
1174}
1175
1176efi_status_t efi_open_volume_int(struct efi_simple_file_system_protocol *this,
1177                                 struct efi_file_handle **root)
1178{
1179        struct file_system *fs = to_fs(this);
1180
1181        *root = file_open(fs, NULL, NULL, 0, 0);
1182
1183        return EFI_SUCCESS;
1184}
1185
1186static efi_status_t EFIAPI
1187efi_open_volume(struct efi_simple_file_system_protocol *this,
1188                struct efi_file_handle **root)
1189{
1190        EFI_ENTRY("%p, %p", this, root);
1191
1192        return EFI_EXIT(efi_open_volume_int(this, root));
1193}
1194
1195struct efi_simple_file_system_protocol *
1196efi_simple_file_system(struct blk_desc *desc, int part,
1197                       struct efi_device_path *dp)
1198{
1199        struct file_system *fs;
1200
1201        fs = calloc(1, sizeof(*fs));
1202        fs->base.rev = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION;
1203        fs->base.open_volume = efi_open_volume;
1204        fs->desc = desc;
1205        fs->part = part;
1206        fs->dp = dp;
1207
1208        return &fs->base;
1209}
1210