SimpleFsOpen.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. /** @file
  2. EFI_FILE_PROTOCOL.Open() member function for the Virtio Filesystem driver.
  3. Copyright (C) 2020, Red Hat, Inc.
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. **/
  6. #include <Library/BaseLib.h> // AsciiStrCmp()
  7. #include <Library/MemoryAllocationLib.h> // AllocatePool()
  8. #include "VirtioFsDxe.h"
  9. /**
  10. Open the root directory, possibly for writing.
  11. @param[in,out] VirtioFs The Virtio Filesystem device whose root directory
  12. should be opened.
  13. @param[out] NewHandle The new EFI_FILE_PROTOCOL instance through which
  14. the root directory can be accessed.
  15. @param[in] OpenForWriting TRUE if the root directory should be opened for
  16. read-write access. FALSE if the root directory
  17. should be opened for read-only access. Opening the
  18. root directory for read-write access is useful for
  19. calling EFI_FILE_PROTOCOL.Flush() or
  20. EFI_FILE_PROTOCOL.SetInfo() later, for syncing or
  21. touching the root directory, respectively.
  22. @retval EFI_SUCCESS The root directory has been opened successfully.
  23. @retval EFI_ACCESS_DENIED OpenForWriting is TRUE, but the root directory is
  24. marked as read-only.
  25. @return Error codes propagated from underlying functions.
  26. **/
  27. STATIC
  28. EFI_STATUS
  29. OpenRootDirectory (
  30. IN OUT VIRTIO_FS *VirtioFs,
  31. OUT EFI_FILE_PROTOCOL **NewHandle,
  32. IN BOOLEAN OpenForWriting
  33. )
  34. {
  35. EFI_STATUS Status;
  36. VIRTIO_FS_FILE *NewVirtioFsFile;
  37. //
  38. // VirtioFsOpenVolume() opens the root directory for read-only access. If the
  39. // current request is to open the root directory for read-write access, so
  40. // that EFI_FILE_PROTOCOL.Flush() or EFI_FILE_PROTOCOL.SetInfo()+timestamps
  41. // can be used on the root directory later, then we have to check for write
  42. // permission first.
  43. //
  44. if (OpenForWriting) {
  45. VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
  46. EFI_FILE_INFO FileInfo;
  47. Status = VirtioFsFuseGetAttr (
  48. VirtioFs,
  49. VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID,
  50. &FuseAttr
  51. );
  52. if (EFI_ERROR (Status)) {
  53. return Status;
  54. }
  55. Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, &FileInfo);
  56. if (EFI_ERROR (Status)) {
  57. return Status;
  58. }
  59. if ((FileInfo.Attribute & EFI_FILE_READ_ONLY) != 0) {
  60. return EFI_ACCESS_DENIED;
  61. }
  62. }
  63. Status = VirtioFsOpenVolume (&VirtioFs->SimpleFs, NewHandle);
  64. if (EFI_ERROR (Status)) {
  65. return Status;
  66. }
  67. NewVirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (*NewHandle);
  68. NewVirtioFsFile->IsOpenForWriting = OpenForWriting;
  69. return EFI_SUCCESS;
  70. }
  71. /**
  72. Open an existent regular file or non-root directory.
  73. @param[in,out] VirtioFs The Virtio Filesystem device on which the
  74. regular file or directory should be opened.
  75. @param[in] DirNodeId The inode number of the immediate parent
  76. directory of the regular file or directory to
  77. open.
  78. @param[in] Name The single-component filename of the regular
  79. file or directory to open, under the immediate
  80. parent directory identified by DirNodeId.
  81. @param[in] OpenForWriting TRUE if the regular file or directory should be
  82. opened for read-write access. FALSE if the
  83. regular file or directory should be opened for
  84. read-only access. Opening a directory for
  85. read-write access is useful for deleting,
  86. renaming, syncing or touching the directory
  87. later.
  88. @param[out] NodeId The inode number of the regular file or
  89. directory, returned by the Virtio Filesystem
  90. device.
  91. @param[out] FuseHandle The open handle to the regular file or
  92. directory, returned by the Virtio Filesystem
  93. device.
  94. @param[out] NodeIsDirectory Set to TRUE on output if Name was found to refer
  95. to a directory. Set to FALSE if Name was found
  96. to refer to a regular file.
  97. @retval EFI_SUCCESS The regular file or directory has been looked up
  98. and opened successfully.
  99. @retval EFI_ACCESS_DENIED OpenForWriting is TRUE, but the regular file or
  100. directory is marked read-only.
  101. @retval EFI_NOT_FOUND A directory entry called Name was not found in the
  102. directory identified by DirNodeId. (EFI_NOT_FOUND
  103. is not returned for any other condition.)
  104. @return Errors propagated from underlying functions. If
  105. the error code to propagate were EFI_NOT_FOUND, it
  106. is remapped to EFI_DEVICE_ERROR.
  107. **/
  108. STATIC
  109. EFI_STATUS
  110. OpenExistentFileOrDirectory (
  111. IN OUT VIRTIO_FS *VirtioFs,
  112. IN UINT64 DirNodeId,
  113. IN CHAR8 *Name,
  114. IN BOOLEAN OpenForWriting,
  115. OUT UINT64 *NodeId,
  116. OUT UINT64 *FuseHandle,
  117. OUT BOOLEAN *NodeIsDirectory
  118. )
  119. {
  120. EFI_STATUS Status;
  121. UINT64 ResolvedNodeId;
  122. VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
  123. EFI_FILE_INFO FileInfo;
  124. BOOLEAN IsDirectory;
  125. UINT64 NewFuseHandle;
  126. Status = VirtioFsFuseLookup (
  127. VirtioFs,
  128. DirNodeId,
  129. Name,
  130. &ResolvedNodeId,
  131. &FuseAttr
  132. );
  133. if (EFI_ERROR (Status)) {
  134. return Status;
  135. }
  136. Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, &FileInfo);
  137. if (EFI_ERROR (Status)) {
  138. goto ForgetResolvedNodeId;
  139. }
  140. if (OpenForWriting && ((FileInfo.Attribute & EFI_FILE_READ_ONLY) != 0)) {
  141. Status = EFI_ACCESS_DENIED;
  142. goto ForgetResolvedNodeId;
  143. }
  144. IsDirectory = (BOOLEAN)((FileInfo.Attribute & EFI_FILE_DIRECTORY) != 0);
  145. if (IsDirectory) {
  146. //
  147. // If OpenForWriting is TRUE here, that's not passed to
  148. // VirtioFsFuseOpenDir(); it does not affect the FUSE_OPENDIR request we
  149. // send. OpenForWriting=TRUE will only permit attempts to delete, rename,
  150. // flush (sync), and touch the directory.
  151. //
  152. Status = VirtioFsFuseOpenDir (VirtioFs, ResolvedNodeId, &NewFuseHandle);
  153. } else {
  154. Status = VirtioFsFuseOpen (
  155. VirtioFs,
  156. ResolvedNodeId,
  157. OpenForWriting,
  158. &NewFuseHandle
  159. );
  160. }
  161. if (EFI_ERROR (Status)) {
  162. goto ForgetResolvedNodeId;
  163. }
  164. *NodeId = ResolvedNodeId;
  165. *FuseHandle = NewFuseHandle;
  166. *NodeIsDirectory = IsDirectory;
  167. return EFI_SUCCESS;
  168. ForgetResolvedNodeId:
  169. VirtioFsFuseForget (VirtioFs, ResolvedNodeId);
  170. return (Status == EFI_NOT_FOUND) ? EFI_DEVICE_ERROR : Status;
  171. }
  172. /**
  173. Create a directory.
  174. @param[in,out] VirtioFs The Virtio Filesystem device on which the directory
  175. should be created.
  176. @param[in] DirNodeId The inode number of the immediate parent directory
  177. of the directory to create.
  178. @param[in] Name The single-component filename of the directory to
  179. create, under the immediate parent directory
  180. identified by DirNodeId.
  181. @param[out] NodeId The inode number of the directory created, returned
  182. by the Virtio Filesystem device.
  183. @param[out] FuseHandle The open handle to the directory created, returned
  184. by the Virtio Filesystem device.
  185. @retval EFI_SUCCESS The directory has been created successfully.
  186. @return Errors propagated from underlying functions.
  187. **/
  188. STATIC
  189. EFI_STATUS
  190. CreateDirectory (
  191. IN OUT VIRTIO_FS *VirtioFs,
  192. IN UINT64 DirNodeId,
  193. IN CHAR8 *Name,
  194. OUT UINT64 *NodeId,
  195. OUT UINT64 *FuseHandle
  196. )
  197. {
  198. EFI_STATUS Status;
  199. UINT64 NewChildDirNodeId;
  200. UINT64 NewFuseHandle;
  201. Status = VirtioFsFuseMkDir (VirtioFs, DirNodeId, Name, &NewChildDirNodeId);
  202. if (EFI_ERROR (Status)) {
  203. return Status;
  204. }
  205. Status = VirtioFsFuseOpenDir (VirtioFs, NewChildDirNodeId, &NewFuseHandle);
  206. if (EFI_ERROR (Status)) {
  207. goto RemoveNewChildDir;
  208. }
  209. *NodeId = NewChildDirNodeId;
  210. *FuseHandle = NewFuseHandle;
  211. return EFI_SUCCESS;
  212. RemoveNewChildDir:
  213. VirtioFsFuseRemoveFileOrDir (VirtioFs, DirNodeId, Name, TRUE /* IsDir */);
  214. VirtioFsFuseForget (VirtioFs, NewChildDirNodeId);
  215. return Status;
  216. }
  217. /**
  218. Create a regular file.
  219. @param[in,out] VirtioFs The Virtio Filesystem device on which the regular
  220. file should be created.
  221. @param[in] DirNodeId The inode number of the immediate parent directory
  222. of the regular file to create.
  223. @param[in] Name The single-component filename of the regular file to
  224. create, under the immediate parent directory
  225. identified by DirNodeId.
  226. @param[out] NodeId The inode number of the regular file created,
  227. returned by the Virtio Filesystem device.
  228. @param[out] FuseHandle The open handle to the regular file created,
  229. returned by the Virtio Filesystem device.
  230. @retval EFI_SUCCESS The regular file has been created successfully.
  231. @return Errors propagated from underlying functions.
  232. **/
  233. STATIC
  234. EFI_STATUS
  235. CreateRegularFile (
  236. IN OUT VIRTIO_FS *VirtioFs,
  237. IN UINT64 DirNodeId,
  238. IN CHAR8 *Name,
  239. OUT UINT64 *NodeId,
  240. OUT UINT64 *FuseHandle
  241. )
  242. {
  243. return VirtioFsFuseOpenOrCreate (
  244. VirtioFs,
  245. DirNodeId,
  246. Name,
  247. NodeId,
  248. FuseHandle
  249. );
  250. }
  251. EFI_STATUS
  252. EFIAPI
  253. VirtioFsSimpleFileOpen (
  254. IN EFI_FILE_PROTOCOL *This,
  255. OUT EFI_FILE_PROTOCOL **NewHandle,
  256. IN CHAR16 *FileName,
  257. IN UINT64 OpenMode,
  258. IN UINT64 Attributes
  259. )
  260. {
  261. VIRTIO_FS_FILE *VirtioFsFile;
  262. VIRTIO_FS *VirtioFs;
  263. BOOLEAN OpenForWriting;
  264. BOOLEAN PermitCreation;
  265. BOOLEAN CreateDirectoryIfCreating;
  266. VIRTIO_FS_FILE *NewVirtioFsFile;
  267. EFI_STATUS Status;
  268. CHAR8 *NewCanonicalPath;
  269. BOOLEAN RootEscape;
  270. UINT64 DirNodeId;
  271. CHAR8 *LastComponent;
  272. UINT64 NewNodeId;
  273. UINT64 NewFuseHandle;
  274. BOOLEAN NewNodeIsDirectory;
  275. VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
  276. VirtioFs = VirtioFsFile->OwnerFs;
  277. //
  278. // Validate OpenMode.
  279. //
  280. switch (OpenMode) {
  281. case EFI_FILE_MODE_READ:
  282. OpenForWriting = FALSE;
  283. PermitCreation = FALSE;
  284. break;
  285. case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE:
  286. OpenForWriting = TRUE;
  287. PermitCreation = FALSE;
  288. break;
  289. case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE:
  290. OpenForWriting = TRUE;
  291. PermitCreation = TRUE;
  292. break;
  293. default:
  294. return EFI_INVALID_PARAMETER;
  295. }
  296. //
  297. // Set CreateDirectoryIfCreating to suppress incorrect compiler/analyzer
  298. // warnings.
  299. //
  300. CreateDirectoryIfCreating = FALSE;
  301. //
  302. // Validate the Attributes requested for the case when the file ends up being
  303. // created, provided creation is permitted.
  304. //
  305. if (PermitCreation) {
  306. if ((Attributes & ~EFI_FILE_VALID_ATTR) != 0) {
  307. //
  308. // Unknown attribute requested.
  309. //
  310. return EFI_INVALID_PARAMETER;
  311. }
  312. ASSERT (OpenForWriting);
  313. if ((Attributes & EFI_FILE_READ_ONLY) != 0) {
  314. DEBUG ((
  315. DEBUG_ERROR,
  316. ("%a: Label=\"%s\" CanonicalPathname=\"%a\" FileName=\"%s\" "
  317. "OpenMode=0x%Lx Attributes=0x%Lx: nonsensical request to possibly "
  318. "create a file marked read-only, for read-write access\n"),
  319. __FUNCTION__,
  320. VirtioFs->Label,
  321. VirtioFsFile->CanonicalPathname,
  322. FileName,
  323. OpenMode,
  324. Attributes
  325. ));
  326. return EFI_INVALID_PARAMETER;
  327. }
  328. CreateDirectoryIfCreating = (BOOLEAN)((Attributes &
  329. EFI_FILE_DIRECTORY) != 0);
  330. }
  331. //
  332. // Referring to a file relative to a regular file makes no sense (or at least
  333. // it cannot be implemented consistently with how a file is referred to
  334. // relative to a directory).
  335. //
  336. if (!VirtioFsFile->IsDirectory) {
  337. DEBUG ((
  338. DEBUG_ERROR,
  339. ("%a: Label=\"%s\" CanonicalPathname=\"%a\" FileName=\"%s\": "
  340. "nonsensical request to open a file or directory relative to a regular "
  341. "file\n"),
  342. __FUNCTION__,
  343. VirtioFs->Label,
  344. VirtioFsFile->CanonicalPathname,
  345. FileName
  346. ));
  347. return EFI_INVALID_PARAMETER;
  348. }
  349. //
  350. // Allocate the new VIRTIO_FS_FILE object.
  351. //
  352. NewVirtioFsFile = AllocatePool (sizeof *NewVirtioFsFile);
  353. if (NewVirtioFsFile == NULL) {
  354. return EFI_OUT_OF_RESOURCES;
  355. }
  356. //
  357. // Create the canonical pathname at which the desired file is expected to
  358. // exist.
  359. //
  360. Status = VirtioFsAppendPath (
  361. VirtioFsFile->CanonicalPathname,
  362. FileName,
  363. &NewCanonicalPath,
  364. &RootEscape
  365. );
  366. if (EFI_ERROR (Status)) {
  367. goto FreeNewVirtioFsFile;
  368. }
  369. if (RootEscape) {
  370. Status = EFI_ACCESS_DENIED;
  371. goto FreeNewCanonicalPath;
  372. }
  373. //
  374. // If the desired file is the root directory, just open the volume one more
  375. // time, without looking up anything.
  376. //
  377. if (AsciiStrCmp (NewCanonicalPath, "/") == 0) {
  378. FreePool (NewCanonicalPath);
  379. FreePool (NewVirtioFsFile);
  380. return OpenRootDirectory (VirtioFs, NewHandle, OpenForWriting);
  381. }
  382. //
  383. // Split the new canonical pathname into most specific parent directory
  384. // (given by DirNodeId) and last pathname component (i.e., immediate child
  385. // within that parent directory).
  386. //
  387. Status = VirtioFsLookupMostSpecificParentDir (
  388. VirtioFs,
  389. NewCanonicalPath,
  390. &DirNodeId,
  391. &LastComponent
  392. );
  393. if (EFI_ERROR (Status)) {
  394. goto FreeNewCanonicalPath;
  395. }
  396. //
  397. // Set NewNodeIsDirectory to suppress incorrect compiler/analyzer warnings.
  398. //
  399. NewNodeIsDirectory = FALSE;
  400. //
  401. // Try to open LastComponent directly under DirNodeId, as an existent regular
  402. // file or directory.
  403. //
  404. Status = OpenExistentFileOrDirectory (
  405. VirtioFs,
  406. DirNodeId,
  407. LastComponent,
  408. OpenForWriting,
  409. &NewNodeId,
  410. &NewFuseHandle,
  411. &NewNodeIsDirectory
  412. );
  413. //
  414. // If LastComponent could not be found under DirNodeId, but the request
  415. // allows us to create a new entry, attempt creating the requested regular
  416. // file or directory.
  417. //
  418. if ((Status == EFI_NOT_FOUND) && PermitCreation) {
  419. ASSERT (OpenForWriting);
  420. if (CreateDirectoryIfCreating) {
  421. Status = CreateDirectory (
  422. VirtioFs,
  423. DirNodeId,
  424. LastComponent,
  425. &NewNodeId,
  426. &NewFuseHandle
  427. );
  428. } else {
  429. Status = CreateRegularFile (
  430. VirtioFs,
  431. DirNodeId,
  432. LastComponent,
  433. &NewNodeId,
  434. &NewFuseHandle
  435. );
  436. }
  437. NewNodeIsDirectory = CreateDirectoryIfCreating;
  438. }
  439. //
  440. // Regardless of the branch taken, we're done with DirNodeId.
  441. //
  442. if (DirNodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
  443. VirtioFsFuseForget (VirtioFs, DirNodeId);
  444. }
  445. if (EFI_ERROR (Status)) {
  446. goto FreeNewCanonicalPath;
  447. }
  448. //
  449. // Populate the new VIRTIO_FS_FILE object.
  450. //
  451. NewVirtioFsFile->Signature = VIRTIO_FS_FILE_SIG;
  452. NewVirtioFsFile->SimpleFile.Revision = EFI_FILE_PROTOCOL_REVISION;
  453. NewVirtioFsFile->SimpleFile.Open = VirtioFsSimpleFileOpen;
  454. NewVirtioFsFile->SimpleFile.Close = VirtioFsSimpleFileClose;
  455. NewVirtioFsFile->SimpleFile.Delete = VirtioFsSimpleFileDelete;
  456. NewVirtioFsFile->SimpleFile.Read = VirtioFsSimpleFileRead;
  457. NewVirtioFsFile->SimpleFile.Write = VirtioFsSimpleFileWrite;
  458. NewVirtioFsFile->SimpleFile.GetPosition = VirtioFsSimpleFileGetPosition;
  459. NewVirtioFsFile->SimpleFile.SetPosition = VirtioFsSimpleFileSetPosition;
  460. NewVirtioFsFile->SimpleFile.GetInfo = VirtioFsSimpleFileGetInfo;
  461. NewVirtioFsFile->SimpleFile.SetInfo = VirtioFsSimpleFileSetInfo;
  462. NewVirtioFsFile->SimpleFile.Flush = VirtioFsSimpleFileFlush;
  463. NewVirtioFsFile->IsDirectory = NewNodeIsDirectory;
  464. NewVirtioFsFile->IsOpenForWriting = OpenForWriting;
  465. NewVirtioFsFile->OwnerFs = VirtioFs;
  466. NewVirtioFsFile->CanonicalPathname = NewCanonicalPath;
  467. NewVirtioFsFile->FilePosition = 0;
  468. NewVirtioFsFile->NodeId = NewNodeId;
  469. NewVirtioFsFile->FuseHandle = NewFuseHandle;
  470. NewVirtioFsFile->FileInfoArray = NULL;
  471. NewVirtioFsFile->SingleFileInfoSize = 0;
  472. NewVirtioFsFile->NumFileInfo = 0;
  473. NewVirtioFsFile->NextFileInfo = 0;
  474. //
  475. // One more file is now open for the filesystem.
  476. //
  477. InsertTailList (&VirtioFs->OpenFiles, &NewVirtioFsFile->OpenFilesEntry);
  478. *NewHandle = &NewVirtioFsFile->SimpleFile;
  479. return EFI_SUCCESS;
  480. FreeNewCanonicalPath:
  481. FreePool (NewCanonicalPath);
  482. FreeNewVirtioFsFile:
  483. FreePool (NewVirtioFsFile);
  484. return Status;
  485. }