summaryrefslogtreecommitdiffstats
path: root/OvmfPkg/VirtioFsDxe/FuseLookup.c
blob: 5c9a825e172529f42bf16b93f9e3e5294cc1cc24 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/** @file
  FUSE_LOOKUP wrapper for the Virtio Filesystem device.

  Copyright (C) 2020, Red Hat, Inc.

  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include <Library/BaseLib.h> // AsciiStrSize()

#include "VirtioFsDxe.h"

/**
  Send a FUSE_LOOKUP request to the Virtio Filesystem device, for resolving a
  filename to an inode.

  The function returns EFI_NOT_FOUND exclusively if the Virtio Filesystem
  device explicitly responds with ENOENT -- "No such file or directory".

  The function may only be called after VirtioFsFuseInitSession() returns
  successfully and before VirtioFsUninit() is called.

  @param[in,out] VirtioFs  The Virtio Filesystem device to send the FUSE_LOOKUP
                           request to. On output, the FUSE request counter
                           "VirtioFs->RequestId" will have been incremented.

  @param[in] DirNodeId     The inode number of the directory in which Name
                           should be resolved to an inode.

  @param[in] Name          The single-component filename to resolve in the
                           directory identified by DirNodeId.

  @param[out] NodeId       The inode number which Name has been resolved to.

  @param[out] FuseAttr     The VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object
                           describing the properties of the resolved inode.

  @retval EFI_SUCCESS    Filename to inode resolution successful.

  @retval EFI_NOT_FOUND  The Virtio Filesystem device explicitly reported
                         ENOENT -- "No such file or directory".

  @return                The "errno" value mapped to an EFI_STATUS code, if the
                         Virtio Filesystem device explicitly reported an error
                         different from ENOENT. If said mapping resulted in
                         EFI_NOT_FOUND, it is remapped to EFI_DEVICE_ERROR.

  @return                Error codes propagated from VirtioFsSgListsValidate(),
                         VirtioFsFuseNewRequest(), VirtioFsSgListsSubmit(),
                         VirtioFsFuseCheckResponse(). EFI_NOT_FOUND is remapped
                         to EFI_DEVICE_ERROR.
**/
EFI_STATUS
VirtioFsFuseLookup (
  IN OUT VIRTIO_FS                          *VirtioFs,
  IN     UINT64                             DirNodeId,
  IN     CHAR8                              *Name,
     OUT UINT64                             *NodeId,
     OUT VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr
  )
{
  VIRTIO_FS_FUSE_REQUEST        CommonReq;
  VIRTIO_FS_IO_VECTOR           ReqIoVec[2];
  VIRTIO_FS_SCATTER_GATHER_LIST ReqSgList;
  VIRTIO_FS_FUSE_RESPONSE       CommonResp;
  VIRTIO_FS_FUSE_NODE_RESPONSE  NodeResp;
  VIRTIO_FS_IO_VECTOR           RespIoVec[3];
  VIRTIO_FS_SCATTER_GATHER_LIST RespSgList;
  EFI_STATUS                    Status;

  //
  // Set up the scatter-gather lists.
  //
  ReqIoVec[0].Buffer = &CommonReq;
  ReqIoVec[0].Size   = sizeof CommonReq;
  ReqIoVec[1].Buffer = Name;
  ReqIoVec[1].Size   = AsciiStrSize (Name);
  ReqSgList.IoVec    = ReqIoVec;
  ReqSgList.NumVec   = ARRAY_SIZE (ReqIoVec);

  RespIoVec[0].Buffer = &CommonResp;
  RespIoVec[0].Size   = sizeof CommonResp;
  RespIoVec[1].Buffer = &NodeResp;
  RespIoVec[1].Size   = sizeof NodeResp;
  RespIoVec[2].Buffer = FuseAttr;
  RespIoVec[2].Size   = sizeof *FuseAttr;
  RespSgList.IoVec    = RespIoVec;
  RespSgList.NumVec   = ARRAY_SIZE (RespIoVec);

  //
  // Validate the scatter-gather lists; calculate the total transfer sizes.
  //
  Status = VirtioFsSgListsValidate (VirtioFs, &ReqSgList, &RespSgList);
  if (EFI_ERROR (Status)) {
    goto Fail;
  }

  //
  // Populate the common request header.
  //
  Status = VirtioFsFuseNewRequest (VirtioFs, &CommonReq, ReqSgList.TotalSize,
             VirtioFsFuseOpLookup, DirNodeId);
  if (EFI_ERROR (Status)) {
    goto Fail;
  }

  //
  // Submit the request.
  //
  Status = VirtioFsSgListsSubmit (VirtioFs, &ReqSgList, &RespSgList);
  if (EFI_ERROR (Status)) {
    goto Fail;
  }

  //
  // Verify the response (all response buffers are fixed size).
  //
  Status = VirtioFsFuseCheckResponse (&RespSgList, CommonReq.Unique, NULL);
  if (EFI_ERROR (Status)) {
    if (Status == EFI_DEVICE_ERROR) {
      DEBUG ((
        ((CommonResp.Error == VIRTIO_FS_FUSE_ERRNO_ENOENT) ?
         DEBUG_VERBOSE :
         DEBUG_ERROR),
        "%a: Label=\"%s\" DirNodeId=%Lu Name=\"%a\" Errno=%d\n",
        __FUNCTION__,
        VirtioFs->Label,
        DirNodeId,
        Name,
        CommonResp.Error
        ));
      if (CommonResp.Error == VIRTIO_FS_FUSE_ERRNO_ENOENT) {
        return EFI_NOT_FOUND;
      }
      Status = VirtioFsErrnoToEfiStatus (CommonResp.Error);
    }
    goto Fail;
  }

  //
  // Output the NodeId to which Name has been resolved to.
  //
  *NodeId = NodeResp.NodeId;
  return EFI_SUCCESS;

Fail:
  return (Status == EFI_NOT_FOUND) ? EFI_DEVICE_ERROR : Status;
}