SimpleFsSetInfo.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. /** @file
  2. EFI_FILE_PROTOCOL.SetInfo() 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 <Guid/FileSystemInfo.h> // gEfiFileSystemInfoGuid
  7. #include <Guid/FileSystemVolumeLabelInfo.h> // gEfiFileSystemVolumeLabelInfo...
  8. #include <Library/BaseLib.h> // StrCmp()
  9. #include <Library/BaseMemoryLib.h> // CompareGuid()
  10. #include <Library/MemoryAllocationLib.h> // FreePool()
  11. #include "VirtioFsDxe.h"
  12. /**
  13. Validate a buffer that the EFI_FILE_PROTOCOL.SetInfo() caller passes in for a
  14. particular InformationType GUID.
  15. The structure to be validated is supposed to end with a variable-length,
  16. NUL-terminated CHAR16 Name string.
  17. @param[in] SizeByProtocolCaller The BufferSize parameter as provided by the
  18. EFI_FILE_PROTOCOL.SetInfo() caller.
  19. @param[in] MinimumStructSize The minimum structure size that is required
  20. for the given InformationType GUID,
  21. including a single CHAR16 element from the
  22. trailing Name field.
  23. @param[in] IsSizeByInfoPresent TRUE if and only if the expected structure
  24. starts with a UINT64 Size field that reports
  25. the actual structure size.
  26. @param[in] Buffer The Buffer parameter as provided by the
  27. EFI_FILE_PROTOCOL.SetInfo() caller.
  28. @retval EFI_SUCCESS Validation successful, Buffer is well-formed.
  29. @retval EFI_BAD_BUFFER_SIZE The EFI_FILE_PROTOCOL.SetInfo()
  30. caller provided a BufferSize that is smaller
  31. than the minimum structure size required for
  32. the given InformationType GUID.
  33. @retval EFI_INVALID_PARAMETER IsSizeByInfoPresent is TRUE, and the leading
  34. UINT64 Size field does not match the
  35. EFI_FILE_PROTOCOL.SetInfo() caller-provided
  36. BufferSize.
  37. @retval EFI_INVALID_PARAMETER The trailing Name field does not consist of a
  38. whole multiple of CHAR16 elements.
  39. @retval EFI_INVALID_PARAMETER The trailing Name field is not NUL-terminated.
  40. **/
  41. STATIC
  42. EFI_STATUS
  43. ValidateInfoStructure (
  44. IN UINTN SizeByProtocolCaller,
  45. IN UINTN MinimumStructSize,
  46. IN BOOLEAN IsSizeByInfoPresent,
  47. IN VOID *Buffer
  48. )
  49. {
  50. UINTN NameFieldByteOffset;
  51. UINTN NameFieldBytes;
  52. UINTN NameFieldChar16s;
  53. CHAR16 *NameField;
  54. //
  55. // Make sure the internal function asking for validation passes in sane
  56. // values.
  57. //
  58. ASSERT (MinimumStructSize >= sizeof (CHAR16));
  59. NameFieldByteOffset = MinimumStructSize - sizeof (CHAR16);
  60. if (IsSizeByInfoPresent) {
  61. ASSERT (MinimumStructSize >= sizeof (UINT64) + sizeof (CHAR16));
  62. ASSERT (NameFieldByteOffset >= sizeof (UINT64));
  63. }
  64. //
  65. // Check whether the protocol caller provided enough bytes for the minimum
  66. // size of this info structure.
  67. //
  68. if (SizeByProtocolCaller < MinimumStructSize) {
  69. return EFI_BAD_BUFFER_SIZE;
  70. }
  71. //
  72. // If the info structure starts with a UINT64 Size field, check if that
  73. // agrees with the protocol caller-provided size.
  74. //
  75. if (IsSizeByInfoPresent) {
  76. UINT64 *SizeByInfo;
  77. SizeByInfo = Buffer;
  78. if (*SizeByInfo != SizeByProtocolCaller) {
  79. return EFI_INVALID_PARAMETER;
  80. }
  81. }
  82. //
  83. // The CHAR16 Name field at the end of the structure must have an even number
  84. // of bytes.
  85. //
  86. // The subtraction below cannot underflow, and yields at least
  87. // sizeof(CHAR16).
  88. //
  89. ASSERT (SizeByProtocolCaller >= NameFieldByteOffset);
  90. NameFieldBytes = SizeByProtocolCaller - NameFieldByteOffset;
  91. ASSERT (NameFieldBytes >= sizeof (CHAR16));
  92. if (NameFieldBytes % sizeof (CHAR16) != 0) {
  93. return EFI_INVALID_PARAMETER;
  94. }
  95. //
  96. // The CHAR16 Name field at the end of the structure must be NUL-terminated.
  97. //
  98. NameFieldChar16s = NameFieldBytes / sizeof (CHAR16);
  99. ASSERT (NameFieldChar16s >= 1);
  100. NameField = (CHAR16 *)((UINT8 *)Buffer + NameFieldByteOffset);
  101. if (NameField[NameFieldChar16s - 1] != L'\0') {
  102. return EFI_INVALID_PARAMETER;
  103. }
  104. return EFI_SUCCESS;
  105. }
  106. /**
  107. Rename a VIRTIO_FS_FILE as requested in EFI_FILE_INFO.FileName.
  108. @param[in,out] VirtioFsFile The VIRTIO_FS_FILE to rename.
  109. @param[in] NewFileName The new file name requested by
  110. EFI_FILE_PROTOCOL.SetInfo().
  111. @retval EFI_SUCCESS The canonical format destination path that is
  112. determined from the input value of
  113. VirtioFsFile->CanonicalPathname and from
  114. NewFileName is identical to the input value of
  115. VirtioFsFile->CanonicalPathname. This means that
  116. EFI_FILE_INFO does not constitute a rename
  117. request. VirtioFsFile has not been changed.
  118. @retval EFI_SUCCESS VirtioFsFile has been renamed.
  119. VirtioFsFile->CanonicalPathname has assumed the
  120. destination pathname in canonical format.
  121. @retval EFI_ACCESS_DENIED VirtioFsFile refers to the root directory, and
  122. NewFileName expresses an actual rename/move
  123. request.
  124. @retval EFI_ACCESS_DENIED VirtioFsFile is the (possibly indirect) parent
  125. directory of at least one other VIRTIO_FS_FILE
  126. that is open for the same Virtio Filesystem
  127. (identified by VirtioFsFile->OwnerFs). Renaming
  128. VirtioFsFile would invalidate the canonical
  129. pathnames of those VIRTIO_FS_FILE instances;
  130. therefore the request has been rejected.
  131. @retval EFI_ACCESS_DENIED VirtioFsFile is not open for writing, but
  132. NewFileName expresses an actual rename/move
  133. request.
  134. @retval EFI_NOT_FOUND At least one dot-dot component in NewFileName
  135. attempted to escape the root directory.
  136. @return Error codes propagated from underlying functions.
  137. **/
  138. STATIC
  139. EFI_STATUS
  140. Rename (
  141. IN OUT VIRTIO_FS_FILE *VirtioFsFile,
  142. IN CHAR16 *NewFileName
  143. )
  144. {
  145. VIRTIO_FS *VirtioFs;
  146. EFI_STATUS Status;
  147. CHAR8 *Destination;
  148. BOOLEAN RootEscape;
  149. UINT64 OldParentDirNodeId;
  150. CHAR8 *OldLastComponent;
  151. UINT64 NewParentDirNodeId;
  152. CHAR8 *NewLastComponent;
  153. VirtioFs = VirtioFsFile->OwnerFs;
  154. //
  155. // The root directory cannot be renamed.
  156. //
  157. if (AsciiStrCmp (VirtioFsFile->CanonicalPathname, "/") == 0) {
  158. if (StrCmp (NewFileName, L"") == 0) {
  159. //
  160. // Not a rename request anyway.
  161. //
  162. return EFI_SUCCESS;
  163. }
  164. return EFI_ACCESS_DENIED;
  165. }
  166. //
  167. // Compose the canonical pathname for the destination.
  168. //
  169. Status = VirtioFsComposeRenameDestination (
  170. VirtioFsFile->CanonicalPathname,
  171. NewFileName,
  172. &Destination,
  173. &RootEscape
  174. );
  175. if (EFI_ERROR (Status)) {
  176. return Status;
  177. }
  178. if (RootEscape) {
  179. Status = EFI_NOT_FOUND;
  180. goto FreeDestination;
  181. }
  182. //
  183. // If the rename would leave VirtioFsFile->CanonicalPathname unchanged, then
  184. // EFI_FILE_PROTOCOL.SetInfo() isn't asking for a rename actually.
  185. //
  186. if (AsciiStrCmp (VirtioFsFile->CanonicalPathname, Destination) == 0) {
  187. Status = EFI_SUCCESS;
  188. goto FreeDestination;
  189. }
  190. //
  191. // Check if the rename would break the canonical pathnames of other
  192. // VIRTIO_FS_FILE instances of the same VIRTIO_FS.
  193. //
  194. if (VirtioFsFile->IsDirectory) {
  195. UINTN PathLen;
  196. LIST_ENTRY *OpenFilesEntry;
  197. PathLen = AsciiStrLen (VirtioFsFile->CanonicalPathname);
  198. BASE_LIST_FOR_EACH (OpenFilesEntry, &VirtioFs->OpenFiles) {
  199. VIRTIO_FS_FILE *OtherFile;
  200. OtherFile = VIRTIO_FS_FILE_FROM_OPEN_FILES_ENTRY (OpenFilesEntry);
  201. if ((OtherFile != VirtioFsFile) &&
  202. (AsciiStrnCmp (
  203. VirtioFsFile->CanonicalPathname,
  204. OtherFile->CanonicalPathname,
  205. PathLen
  206. ) == 0) &&
  207. ((OtherFile->CanonicalPathname[PathLen] == '\0') ||
  208. (OtherFile->CanonicalPathname[PathLen] == '/')))
  209. {
  210. //
  211. // OtherFile refers to the same directory as VirtioFsFile, or is a
  212. // (possibly indirect) child of the directory referred to by
  213. // VirtioFsFile.
  214. //
  215. Status = EFI_ACCESS_DENIED;
  216. goto FreeDestination;
  217. }
  218. }
  219. }
  220. //
  221. // From this point on, the file needs to be open for writing.
  222. //
  223. if (!VirtioFsFile->IsOpenForWriting) {
  224. Status = EFI_ACCESS_DENIED;
  225. goto FreeDestination;
  226. }
  227. //
  228. // Split both source and destination canonical pathnames into (most specific
  229. // parent directory, last component) pairs.
  230. //
  231. Status = VirtioFsLookupMostSpecificParentDir (
  232. VirtioFs,
  233. VirtioFsFile->CanonicalPathname,
  234. &OldParentDirNodeId,
  235. &OldLastComponent
  236. );
  237. if (EFI_ERROR (Status)) {
  238. goto FreeDestination;
  239. }
  240. Status = VirtioFsLookupMostSpecificParentDir (
  241. VirtioFs,
  242. Destination,
  243. &NewParentDirNodeId,
  244. &NewLastComponent
  245. );
  246. if (EFI_ERROR (Status)) {
  247. goto ForgetOldParentDirNodeId;
  248. }
  249. //
  250. // Perform the rename. If the destination path exists, the rename will fail.
  251. //
  252. Status = VirtioFsFuseRename (
  253. VirtioFs,
  254. OldParentDirNodeId,
  255. OldLastComponent,
  256. NewParentDirNodeId,
  257. NewLastComponent
  258. );
  259. if (EFI_ERROR (Status)) {
  260. goto ForgetNewParentDirNodeId;
  261. }
  262. //
  263. // Swap in the new canonical pathname.
  264. //
  265. FreePool (VirtioFsFile->CanonicalPathname);
  266. VirtioFsFile->CanonicalPathname = Destination;
  267. Destination = NULL;
  268. Status = EFI_SUCCESS;
  269. //
  270. // Fall through.
  271. //
  272. ForgetNewParentDirNodeId:
  273. if (NewParentDirNodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
  274. VirtioFsFuseForget (VirtioFs, NewParentDirNodeId);
  275. }
  276. ForgetOldParentDirNodeId:
  277. if (OldParentDirNodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
  278. VirtioFsFuseForget (VirtioFs, OldParentDirNodeId);
  279. }
  280. FreeDestination:
  281. if (Destination != NULL) {
  282. FreePool (Destination);
  283. }
  284. return Status;
  285. }
  286. /**
  287. Update the attributes of a VIRTIO_FS_FILE as requested in EFI_FILE_INFO.
  288. @param[in,out] VirtioFsFile The VIRTIO_FS_FILE to update the attributes of.
  289. @param[in] NewFileInfo The new attributes requested by
  290. EFI_FILE_PROTOCOL.SetInfo(). NewFileInfo->Size
  291. and NewFileInfo->FileName are ignored.
  292. @retval EFI_SUCCESS No attributes had to be updated.
  293. @retval EFI_SUCCESS The required set of attribute updates has been
  294. determined and performed successfully.
  295. @retval EFI_ACCESS_DENIED NewFileInfo requests an update to a property
  296. different from the EFI_FILE_READ_ONLY bit in the
  297. Attribute field, but VirtioFsFile is not open for
  298. writing.
  299. @return Error codes propagated from underlying functions.
  300. **/
  301. STATIC
  302. EFI_STATUS
  303. UpdateAttributes (
  304. IN OUT VIRTIO_FS_FILE *VirtioFsFile,
  305. IN EFI_FILE_INFO *NewFileInfo
  306. )
  307. {
  308. VIRTIO_FS *VirtioFs;
  309. EFI_STATUS Status;
  310. VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
  311. EFI_FILE_INFO FileInfo;
  312. BOOLEAN UpdateFileSize;
  313. UINT64 FileSize;
  314. BOOLEAN UpdateAtime;
  315. BOOLEAN UpdateMtime;
  316. UINT64 Atime;
  317. UINT64 Mtime;
  318. BOOLEAN UpdateMode;
  319. UINT32 Mode;
  320. VirtioFs = VirtioFsFile->OwnerFs;
  321. //
  322. // Fetch the current attributes first, so we can build the difference between
  323. // them and NewFileInfo.
  324. //
  325. Status = VirtioFsFuseGetAttr (VirtioFs, VirtioFsFile->NodeId, &FuseAttr);
  326. if (EFI_ERROR (Status)) {
  327. return Status;
  328. }
  329. Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, &FileInfo);
  330. if (EFI_ERROR (Status)) {
  331. return Status;
  332. }
  333. //
  334. // Collect the updates.
  335. //
  336. if (VirtioFsFile->IsDirectory) {
  337. UpdateFileSize = FALSE;
  338. } else {
  339. VirtioFsGetFuseSizeUpdate (
  340. &FileInfo,
  341. NewFileInfo,
  342. &UpdateFileSize,
  343. &FileSize
  344. );
  345. }
  346. Status = VirtioFsGetFuseTimeUpdates (
  347. &FileInfo,
  348. NewFileInfo,
  349. &UpdateAtime,
  350. &UpdateMtime,
  351. &Atime,
  352. &Mtime
  353. );
  354. if (EFI_ERROR (Status)) {
  355. return Status;
  356. }
  357. Status = VirtioFsGetFuseModeUpdate (
  358. &FileInfo,
  359. NewFileInfo,
  360. &UpdateMode,
  361. &Mode
  362. );
  363. if (EFI_ERROR (Status)) {
  364. return Status;
  365. }
  366. //
  367. // If no attribute updates are necessary, we're done.
  368. //
  369. if (!UpdateFileSize && !UpdateAtime && !UpdateMtime && !UpdateMode) {
  370. return EFI_SUCCESS;
  371. }
  372. //
  373. // If the file is not open for writing, then only Mode may be updated (for
  374. // toggling EFI_FILE_READ_ONLY).
  375. //
  376. if (!VirtioFsFile->IsOpenForWriting &&
  377. (UpdateFileSize || UpdateAtime || UpdateMtime))
  378. {
  379. return EFI_ACCESS_DENIED;
  380. }
  381. //
  382. // Send the FUSE_SETATTR request now.
  383. //
  384. Status = VirtioFsFuseSetAttr (
  385. VirtioFs,
  386. VirtioFsFile->NodeId,
  387. UpdateFileSize ? &FileSize : NULL,
  388. UpdateAtime ? &Atime : NULL,
  389. UpdateMtime ? &Mtime : NULL,
  390. UpdateMode ? &Mode : NULL
  391. );
  392. return Status;
  393. }
  394. /**
  395. Process an EFI_FILE_INFO setting request.
  396. **/
  397. STATIC
  398. EFI_STATUS
  399. SetFileInfo (
  400. IN EFI_FILE_PROTOCOL *This,
  401. IN UINTN BufferSize,
  402. IN VOID *Buffer
  403. )
  404. {
  405. VIRTIO_FS_FILE *VirtioFsFile;
  406. EFI_STATUS Status;
  407. EFI_FILE_INFO *FileInfo;
  408. VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
  409. //
  410. // Validate if Buffer passes as EFI_FILE_INFO.
  411. //
  412. Status = ValidateInfoStructure (
  413. BufferSize, // SizeByProtocolCaller
  414. OFFSET_OF (
  415. EFI_FILE_INFO,
  416. FileName
  417. ) + sizeof (CHAR16), // MinimumStructSize
  418. TRUE, // IsSizeByInfoPresent
  419. Buffer
  420. );
  421. if (EFI_ERROR (Status)) {
  422. return Status;
  423. }
  424. FileInfo = Buffer;
  425. //
  426. // Perform the rename/move request, if any.
  427. //
  428. Status = Rename (VirtioFsFile, FileInfo->FileName);
  429. if (EFI_ERROR (Status)) {
  430. return Status;
  431. }
  432. //
  433. // Update any attributes requested.
  434. //
  435. Status = UpdateAttributes (VirtioFsFile, FileInfo);
  436. //
  437. // The UEFI spec does not speak about partial failure in
  438. // EFI_FILE_PROTOCOL.SetInfo(); we won't try to roll back the rename (if
  439. // there was one) in case the attribute updates fail.
  440. //
  441. return Status;
  442. }
  443. /**
  444. Process an EFI_FILE_SYSTEM_INFO setting request.
  445. **/
  446. STATIC
  447. EFI_STATUS
  448. SetFileSystemInfo (
  449. IN EFI_FILE_PROTOCOL *This,
  450. IN UINTN BufferSize,
  451. IN VOID *Buffer
  452. )
  453. {
  454. VIRTIO_FS_FILE *VirtioFsFile;
  455. VIRTIO_FS *VirtioFs;
  456. EFI_STATUS Status;
  457. EFI_FILE_SYSTEM_INFO *FileSystemInfo;
  458. VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
  459. VirtioFs = VirtioFsFile->OwnerFs;
  460. //
  461. // Validate if Buffer passes as EFI_FILE_SYSTEM_INFO.
  462. //
  463. Status = ValidateInfoStructure (
  464. BufferSize, // SizeByProtocolCaller
  465. OFFSET_OF (
  466. EFI_FILE_SYSTEM_INFO,
  467. VolumeLabel
  468. ) + sizeof (CHAR16), // MinimumStructSize
  469. TRUE, // IsSizeByInfoPresent
  470. Buffer
  471. );
  472. if (EFI_ERROR (Status)) {
  473. return Status;
  474. }
  475. FileSystemInfo = Buffer;
  476. //
  477. // EFI_FILE_SYSTEM_INFO fields other than VolumeLabel cannot be changed, per
  478. // spec.
  479. //
  480. // If the label is being changed to its current value, report success;
  481. // otherwise, reject the request, as the Virtio Filesystem device does not
  482. // support changing the label.
  483. //
  484. if (StrCmp (FileSystemInfo->VolumeLabel, VirtioFs->Label) == 0) {
  485. return EFI_SUCCESS;
  486. }
  487. return EFI_WRITE_PROTECTED;
  488. }
  489. /**
  490. Process an EFI_FILE_SYSTEM_VOLUME_LABEL setting request.
  491. **/
  492. STATIC
  493. EFI_STATUS
  494. SetFileSystemVolumeLabelInfo (
  495. IN EFI_FILE_PROTOCOL *This,
  496. IN UINTN BufferSize,
  497. IN VOID *Buffer
  498. )
  499. {
  500. VIRTIO_FS_FILE *VirtioFsFile;
  501. VIRTIO_FS *VirtioFs;
  502. EFI_STATUS Status;
  503. EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel;
  504. VirtioFsFile = VIRTIO_FS_FILE_FROM_SIMPLE_FILE (This);
  505. VirtioFs = VirtioFsFile->OwnerFs;
  506. //
  507. // Validate if Buffer passes as EFI_FILE_SYSTEM_VOLUME_LABEL.
  508. //
  509. Status = ValidateInfoStructure (
  510. BufferSize, // SizeByProtocolCaller
  511. OFFSET_OF (
  512. EFI_FILE_SYSTEM_VOLUME_LABEL,
  513. VolumeLabel
  514. ) + sizeof (CHAR16), // MinimumStructSize
  515. FALSE, // IsSizeByInfoPresent
  516. Buffer
  517. );
  518. if (EFI_ERROR (Status)) {
  519. return Status;
  520. }
  521. FileSystemVolumeLabel = Buffer;
  522. //
  523. // If the label is being changed to its current value, report success;
  524. // otherwise, reject the request, as the Virtio Filesystem device does not
  525. // support changing the label.
  526. //
  527. if (StrCmp (FileSystemVolumeLabel->VolumeLabel, VirtioFs->Label) == 0) {
  528. return EFI_SUCCESS;
  529. }
  530. return EFI_WRITE_PROTECTED;
  531. }
  532. EFI_STATUS
  533. EFIAPI
  534. VirtioFsSimpleFileSetInfo (
  535. IN EFI_FILE_PROTOCOL *This,
  536. IN EFI_GUID *InformationType,
  537. IN UINTN BufferSize,
  538. IN VOID *Buffer
  539. )
  540. {
  541. if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
  542. return SetFileInfo (This, BufferSize, Buffer);
  543. }
  544. if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
  545. return SetFileSystemInfo (This, BufferSize, Buffer);
  546. }
  547. if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
  548. return SetFileSystemVolumeLabelInfo (This, BufferSize, Buffer);
  549. }
  550. return EFI_UNSUPPORTED;
  551. }