12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525 |
- /** @file
- Initialization and helper routines for the Virtio Filesystem device.
- Copyright (C) 2020, Red Hat, Inc.
- SPDX-License-Identifier: BSD-2-Clause-Patent
- **/
- #include <Library/BaseLib.h> // StrLen()
- #include <Library/BaseMemoryLib.h> // CopyMem()
- #include <Library/MemoryAllocationLib.h> // AllocatePool()
- #include <Library/TimeBaseLib.h> // EpochToEfiTime()
- #include <Library/VirtioLib.h> // Virtio10WriteFeatures()
- #include "VirtioFsDxe.h"
- /**
- Read the Virtio Filesystem device configuration structure in full.
- @param[in] Virtio The Virtio protocol underlying the VIRTIO_FS object.
- @param[out] Config The fully populated VIRTIO_FS_CONFIG structure.
- @retval EFI_SUCCESS Config has been filled in.
- @return Error codes propagated from Virtio->ReadDevice(). The
- contents of Config are indeterminate.
- **/
- STATIC
- EFI_STATUS
- VirtioFsReadConfig (
- IN VIRTIO_DEVICE_PROTOCOL *Virtio,
- OUT VIRTIO_FS_CONFIG *Config
- )
- {
- UINTN Idx;
- EFI_STATUS Status;
- for (Idx = 0; Idx < VIRTIO_FS_TAG_BYTES; Idx++) {
- Status = Virtio->ReadDevice (
- Virtio, // This
- OFFSET_OF (VIRTIO_FS_CONFIG, Tag[Idx]), // FieldOffset
- sizeof Config->Tag[Idx], // FieldSize
- sizeof Config->Tag[Idx], // BufferSize
- &Config->Tag[Idx] // Buffer
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
- }
- Status = Virtio->ReadDevice (
- Virtio, // This
- OFFSET_OF (VIRTIO_FS_CONFIG, NumReqQueues), // FieldOffset
- sizeof Config->NumReqQueues, // FieldSize
- sizeof Config->NumReqQueues, // BufferSize
- &Config->NumReqQueues // Buffer
- );
- return Status;
- }
- /**
- Configure the Virtio Filesystem device underlying VirtioFs.
- @param[in,out] VirtioFs The VIRTIO_FS object for which Virtio communication
- should be set up. On input, the caller is
- responsible for VirtioFs->Virtio having been
- initialized. On output, synchronous Virtio
- Filesystem commands (primitives) may be submitted to
- the device.
- @retval EFI_SUCCESS Virtio machinery has been set up.
- @retval EFI_UNSUPPORTED The host-side configuration of the Virtio Filesystem
- is not supported by this driver.
- @return Error codes from underlying functions.
- **/
- EFI_STATUS
- VirtioFsInit (
- IN OUT VIRTIO_FS *VirtioFs
- )
- {
- UINT8 NextDevStat;
- EFI_STATUS Status;
- UINT64 Features;
- VIRTIO_FS_CONFIG Config;
- UINTN Idx;
- UINT64 RingBaseShift;
- //
- // Execute virtio-v1.1-cs01-87fa6b5d8155, 3.1.1 Driver Requirements: Device
- // Initialization.
- //
- // 1. Reset the device.
- //
- NextDevStat = 0;
- Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- //
- // 2. Set the ACKNOWLEDGE status bit [...]
- //
- NextDevStat |= VSTAT_ACK;
- Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- //
- // 3. Set the DRIVER status bit [...]
- //
- NextDevStat |= VSTAT_DRIVER;
- Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- //
- // 4. Read device feature bits...
- //
- Status = VirtioFs->Virtio->GetDeviceFeatures (VirtioFs->Virtio, &Features);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- if ((Features & VIRTIO_F_VERSION_1) == 0) {
- Status = EFI_UNSUPPORTED;
- goto Failed;
- }
- //
- // No device-specific feature bits have been defined in file "virtio-fs.tex"
- // of the virtio spec at <https://github.com/oasis-tcs/virtio-spec.git>, as
- // of commit 87fa6b5d8155.
- //
- Features &= VIRTIO_F_VERSION_1 | VIRTIO_F_IOMMU_PLATFORM;
- //
- // ... and write the subset of feature bits understood by the [...] driver to
- // the device. [...]
- // 5. Set the FEATURES_OK status bit.
- // 6. Re-read device status to ensure the FEATURES_OK bit is still set [...]
- //
- Status = Virtio10WriteFeatures (VirtioFs->Virtio, Features, &NextDevStat);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- //
- // 7. Perform device-specific setup, including discovery of virtqueues for
- // the device, [...] reading [...] the device's virtio configuration space
- //
- Status = VirtioFsReadConfig (VirtioFs->Virtio, &Config);
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- //
- // 7.a. Convert the filesystem label from UTF-8 to UCS-2. Only labels with
- // printable ASCII code points (U+0020 through U+007E) are supported.
- // NUL-terminate at either the terminator we find, or right after the
- // original label.
- //
- for (Idx = 0; Idx < VIRTIO_FS_TAG_BYTES && Config.Tag[Idx] != '\0'; Idx++) {
- if ((Config.Tag[Idx] < 0x20) || (Config.Tag[Idx] > 0x7E)) {
- Status = EFI_UNSUPPORTED;
- goto Failed;
- }
- VirtioFs->Label[Idx] = Config.Tag[Idx];
- }
- VirtioFs->Label[Idx] = L'\0';
- //
- // 7.b. We need one queue for sending normal priority requests.
- //
- if (Config.NumReqQueues < 1) {
- Status = EFI_UNSUPPORTED;
- goto Failed;
- }
- //
- // 7.c. Fetch and remember the number of descriptors we can place on the
- // queue at once. We'll need two descriptors per request, as a minimum --
- // request header, response header.
- //
- Status = VirtioFs->Virtio->SetQueueSel (
- VirtioFs->Virtio,
- VIRTIO_FS_REQUEST_QUEUE
- );
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- Status = VirtioFs->Virtio->GetQueueNumMax (
- VirtioFs->Virtio,
- &VirtioFs->QueueSize
- );
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- if (VirtioFs->QueueSize < 2) {
- Status = EFI_UNSUPPORTED;
- goto Failed;
- }
- //
- // 7.d. [...] population of virtqueues [...]
- //
- Status = VirtioRingInit (
- VirtioFs->Virtio,
- VirtioFs->QueueSize,
- &VirtioFs->Ring
- );
- if (EFI_ERROR (Status)) {
- goto Failed;
- }
- Status = VirtioRingMap (
- VirtioFs->Virtio,
- &VirtioFs->Ring,
- &RingBaseShift,
- &VirtioFs->RingMap
- );
- if (EFI_ERROR (Status)) {
- goto ReleaseQueue;
- }
- Status = VirtioFs->Virtio->SetQueueAddress (
- VirtioFs->Virtio,
- &VirtioFs->Ring,
- RingBaseShift
- );
- if (EFI_ERROR (Status)) {
- goto UnmapQueue;
- }
- //
- // 8. Set the DRIVER_OK status bit.
- //
- NextDevStat |= VSTAT_DRIVER_OK;
- Status = VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
- if (EFI_ERROR (Status)) {
- goto UnmapQueue;
- }
- return EFI_SUCCESS;
- UnmapQueue:
- VirtioFs->Virtio->UnmapSharedBuffer (VirtioFs->Virtio, VirtioFs->RingMap);
- ReleaseQueue:
- VirtioRingUninit (VirtioFs->Virtio, &VirtioFs->Ring);
- Failed:
- //
- // If any of these steps go irrecoverably wrong, the driver SHOULD set the
- // FAILED status bit to indicate that it has given up on the device (it can
- // reset the device later to restart if desired). [...]
- //
- // Virtio access failure here should not mask the original error.
- //
- NextDevStat |= VSTAT_FAILED;
- VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, NextDevStat);
- return Status;
- }
- /**
- De-configure the Virtio Filesystem device underlying VirtioFs.
- @param[in] VirtioFs The VIRTIO_FS object for which Virtio communication
- should be torn down. On input, the caller is responsible
- for having called VirtioFsInit(). On output, Virtio
- Filesystem commands (primitives) must no longer be
- submitted to the device.
- **/
- VOID
- VirtioFsUninit (
- IN OUT VIRTIO_FS *VirtioFs
- )
- {
- //
- // Resetting the Virtio device makes it release its resources and forget its
- // configuration.
- //
- VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, 0);
- VirtioFs->Virtio->UnmapSharedBuffer (VirtioFs->Virtio, VirtioFs->RingMap);
- VirtioRingUninit (VirtioFs->Virtio, &VirtioFs->Ring);
- }
- /**
- ExitBootServices event notification function for a Virtio Filesystem object.
- This function resets the VIRTIO_FS.Virtio device, causing it to release all
- references to guest-side resources. The function may only be called after
- VirtioFsInit() returns successfully and before VirtioFsUninit() is called.
- @param[in] ExitBootEvent The VIRTIO_FS.ExitBoot event that has been
- signaled.
- @param[in] VirtioFsAsVoid Pointer to the VIRTIO_FS object, passed in as
- (VOID*).
- **/
- VOID
- EFIAPI
- VirtioFsExitBoot (
- IN EFI_EVENT ExitBootEvent,
- IN VOID *VirtioFsAsVoid
- )
- {
- VIRTIO_FS *VirtioFs;
- VirtioFs = VirtioFsAsVoid;
- DEBUG ((
- DEBUG_VERBOSE,
- "%a: VirtioFs=0x%p Label=\"%s\"\n",
- __FUNCTION__,
- VirtioFsAsVoid,
- VirtioFs->Label
- ));
- VirtioFs->Virtio->SetDeviceStatus (VirtioFs->Virtio, 0);
- }
- /**
- Validate two VIRTIO_FS_SCATTER_GATHER_LIST objects -- list of request
- buffers, list of response buffers -- together.
- On input, the caller is required to populate the following fields:
- - VIRTIO_FS_IO_VECTOR.Buffer,
- - VIRTIO_FS_IO_VECTOR.Size,
- - VIRTIO_FS_SCATTER_GATHER_LIST.IoVec,
- - VIRTIO_FS_SCATTER_GATHER_LIST.NumVec.
- On output (on successful return), the following fields will be
- zero-initialized:
- - VIRTIO_FS_IO_VECTOR.Mapped,
- - VIRTIO_FS_IO_VECTOR.MappedAddress,
- - VIRTIO_FS_IO_VECTOR.Mapping,
- - VIRTIO_FS_IO_VECTOR.Transferred.
- On output (on successful return), the following fields will be calculated:
- - VIRTIO_FS_SCATTER_GATHER_LIST.TotalSize.
- The function may only be called after VirtioFsInit() returns successfully and
- before VirtioFsUninit() is called.
- @param[in] VirtioFs The Virtio Filesystem device that the
- request-response exchange, expressed via
- RequestSgList and ResponseSgList, will be
- submitted to.
- @param[in,out] RequestSgList The scatter-gather list that describes the
- request part of the exchange -- the buffers
- that should be sent to the Virtio Filesystem
- device in the virtio transfer.
- @param[in,out] ResponseSgList The scatter-gather list that describes the
- response part of the exchange -- the buffers
- that the Virtio Filesystem device should
- populate in the virtio transfer. May be NULL
- if the exchange with the Virtio Filesystem
- device consists of a request only, with the
- response part omitted altogether.
- @retval EFI_SUCCESS RequestSgList and ResponseSgList have been
- validated, output fields have been set.
- @retval EFI_INVALID_PARAMETER RequestSgList is NULL.
- @retval EFI_INVALID_PARAMETER On input, a
- VIRTIO_FS_SCATTER_GATHER_LIST.IoVec field is
- NULL, or a
- VIRTIO_FS_SCATTER_GATHER_LIST.NumVec field is
- zero.
- @retval EFI_INVALID_PARAMETER On input, a VIRTIO_FS_IO_VECTOR.Buffer field
- is NULL, or a VIRTIO_FS_IO_VECTOR.Size field
- is zero.
- @retval EFI_UNSUPPORTED (RequestSgList->NumVec +
- ResponseSgList->NumVec) exceeds
- VirtioFs->QueueSize, meaning that the total
- list of buffers cannot be placed on the virtio
- queue in a single descriptor chain (with one
- descriptor per buffer).
- @retval EFI_UNSUPPORTED One of the
- VIRTIO_FS_SCATTER_GATHER_LIST.TotalSize fields
- would exceed MAX_UINT32.
- **/
- EFI_STATUS
- VirtioFsSgListsValidate (
- IN VIRTIO_FS *VirtioFs,
- IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *RequestSgList,
- IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList OPTIONAL
- )
- {
- VIRTIO_FS_SCATTER_GATHER_LIST *SgListParam[2];
- UINT16 DescriptorsNeeded;
- UINTN ListId;
- if (RequestSgList == NULL) {
- return EFI_INVALID_PARAMETER;
- }
- SgListParam[0] = RequestSgList;
- SgListParam[1] = ResponseSgList;
- DescriptorsNeeded = 0;
- for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {
- VIRTIO_FS_SCATTER_GATHER_LIST *SgList;
- UINT32 SgListTotalSize;
- UINTN IoVecIdx;
- SgList = SgListParam[ListId];
- if (SgList == NULL) {
- continue;
- }
- //
- // Sanity-check SgList -- it must provide at least one IO Vector.
- //
- if ((SgList->IoVec == NULL) || (SgList->NumVec == 0)) {
- return EFI_INVALID_PARAMETER;
- }
- //
- // Make sure that, for each IO Vector in this SgList, a virtio descriptor
- // can be added to the virtio queue, after the other descriptors added
- // previously.
- //
- if ((SgList->NumVec > (UINTN)(MAX_UINT16 - DescriptorsNeeded)) ||
- (DescriptorsNeeded + SgList->NumVec > VirtioFs->QueueSize))
- {
- return EFI_UNSUPPORTED;
- }
- DescriptorsNeeded += (UINT16)SgList->NumVec;
- SgListTotalSize = 0;
- for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {
- VIRTIO_FS_IO_VECTOR *IoVec;
- IoVec = &SgList->IoVec[IoVecIdx];
- //
- // Sanity-check this IoVec -- it must describe a non-empty buffer.
- //
- if ((IoVec->Buffer == NULL) || (IoVec->Size == 0)) {
- return EFI_INVALID_PARAMETER;
- }
- //
- // Make sure the cumulative size of all IO Vectors in this SgList remains
- // expressible as a UINT32.
- //
- if (IoVec->Size > MAX_UINT32 - SgListTotalSize) {
- return EFI_UNSUPPORTED;
- }
- SgListTotalSize += (UINT32)IoVec->Size;
- //
- // Initialize those fields in this IO Vector that will be updated in
- // relation to mapping / transfer.
- //
- IoVec->Mapped = FALSE;
- IoVec->MappedAddress = 0;
- IoVec->Mapping = NULL;
- IoVec->Transferred = 0;
- }
- //
- // Store the cumulative size of all IO Vectors that we have calculated in
- // this SgList.
- //
- SgList->TotalSize = SgListTotalSize;
- }
- return EFI_SUCCESS;
- }
- /**
- Submit a validated pair of (request buffer list, response buffer list) to the
- Virtio Filesystem device.
- On input, the pair of VIRTIO_FS_SCATTER_GATHER_LIST objects must have been
- validated together, using the VirtioFsSgListsValidate() function.
- On output (on successful return), the following fields will be re-initialized
- to zero (after temporarily setting them to different values):
- - VIRTIO_FS_IO_VECTOR.Mapped,
- - VIRTIO_FS_IO_VECTOR.MappedAddress,
- - VIRTIO_FS_IO_VECTOR.Mapping.
- On output (on successful return), the following fields will be calculated:
- - VIRTIO_FS_IO_VECTOR.Transferred.
- The function may only be called after VirtioFsInit() returns successfully and
- before VirtioFsUninit() is called.
- @param[in,out] VirtioFs The Virtio Filesystem device that the
- request-response exchange, expressed via
- RequestSgList and ResponseSgList, should now
- be submitted to.
- @param[in,out] RequestSgList The scatter-gather list that describes the
- request part of the exchange -- the buffers
- that should be sent to the Virtio Filesystem
- device in the virtio transfer.
- @param[in,out] ResponseSgList The scatter-gather list that describes the
- response part of the exchange -- the buffers
- that the Virtio Filesystem device should
- populate in the virtio transfer. May be NULL
- if and only if NULL was passed to
- VirtioFsSgListsValidate() as ResponseSgList.
- @retval EFI_SUCCESS Transfer complete. The caller should investigate
- the VIRTIO_FS_IO_VECTOR.Transferred fields in
- ResponseSgList, to ensure coverage of the relevant
- response buffers. Subsequently, the caller should
- investigate the contents of those buffers.
- @retval EFI_DEVICE_ERROR The Virtio Filesystem device reported populating
- more response bytes than ResponseSgList->TotalSize.
- @return Error codes propagated from
- VirtioMapAllBytesInSharedBuffer(), VirtioFlush(),
- or VirtioFs->Virtio->UnmapSharedBuffer().
- **/
- EFI_STATUS
- VirtioFsSgListsSubmit (
- IN OUT VIRTIO_FS *VirtioFs,
- IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *RequestSgList,
- IN OUT VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList OPTIONAL
- )
- {
- VIRTIO_FS_SCATTER_GATHER_LIST *SgListParam[2];
- VIRTIO_MAP_OPERATION SgListVirtioMapOp[ARRAY_SIZE (SgListParam)];
- UINT16 SgListDescriptorFlag[ARRAY_SIZE (SgListParam)];
- UINTN ListId;
- VIRTIO_FS_SCATTER_GATHER_LIST *SgList;
- UINTN IoVecIdx;
- VIRTIO_FS_IO_VECTOR *IoVec;
- EFI_STATUS Status;
- DESC_INDICES Indices;
- UINT32 TotalBytesWrittenByDevice;
- UINT32 BytesPermittedForWrite;
- SgListParam[0] = RequestSgList;
- SgListVirtioMapOp[0] = VirtioOperationBusMasterRead;
- SgListDescriptorFlag[0] = 0;
- SgListParam[1] = ResponseSgList;
- SgListVirtioMapOp[1] = VirtioOperationBusMasterWrite;
- SgListDescriptorFlag[1] = VRING_DESC_F_WRITE;
- //
- // Map all IO Vectors.
- //
- for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {
- SgList = SgListParam[ListId];
- if (SgList == NULL) {
- continue;
- }
- for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {
- IoVec = &SgList->IoVec[IoVecIdx];
- //
- // Map this IO Vector.
- //
- Status = VirtioMapAllBytesInSharedBuffer (
- VirtioFs->Virtio,
- SgListVirtioMapOp[ListId],
- IoVec->Buffer,
- IoVec->Size,
- &IoVec->MappedAddress,
- &IoVec->Mapping
- );
- if (EFI_ERROR (Status)) {
- goto Unmap;
- }
- IoVec->Mapped = TRUE;
- }
- }
- //
- // Compose the descriptor chain.
- //
- VirtioPrepare (&VirtioFs->Ring, &Indices);
- for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {
- SgList = SgListParam[ListId];
- if (SgList == NULL) {
- continue;
- }
- for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {
- UINT16 NextFlag;
- IoVec = &SgList->IoVec[IoVecIdx];
- //
- // Set VRING_DESC_F_NEXT on all except the very last descriptor.
- //
- NextFlag = VRING_DESC_F_NEXT;
- if ((ListId == ARRAY_SIZE (SgListParam) - 1) &&
- (IoVecIdx == SgList->NumVec - 1))
- {
- NextFlag = 0;
- }
- VirtioAppendDesc (
- &VirtioFs->Ring,
- IoVec->MappedAddress,
- (UINT32)IoVec->Size,
- SgListDescriptorFlag[ListId] | NextFlag,
- &Indices
- );
- }
- }
- //
- // Submit the descriptor chain.
- //
- Status = VirtioFlush (
- VirtioFs->Virtio,
- VIRTIO_FS_REQUEST_QUEUE,
- &VirtioFs->Ring,
- &Indices,
- &TotalBytesWrittenByDevice
- );
- if (EFI_ERROR (Status)) {
- goto Unmap;
- }
- //
- // Sanity-check: the Virtio Filesystem device should not have written more
- // bytes than what we offered buffers for.
- //
- if (ResponseSgList == NULL) {
- BytesPermittedForWrite = 0;
- } else {
- BytesPermittedForWrite = ResponseSgList->TotalSize;
- }
- if (TotalBytesWrittenByDevice > BytesPermittedForWrite) {
- Status = EFI_DEVICE_ERROR;
- goto Unmap;
- }
- //
- // Update the transfer sizes in the IO Vectors.
- //
- for (ListId = 0; ListId < ARRAY_SIZE (SgListParam); ListId++) {
- SgList = SgListParam[ListId];
- if (SgList == NULL) {
- continue;
- }
- for (IoVecIdx = 0; IoVecIdx < SgList->NumVec; IoVecIdx++) {
- IoVec = &SgList->IoVec[IoVecIdx];
- if (SgListVirtioMapOp[ListId] == VirtioOperationBusMasterRead) {
- //
- // We report that the Virtio Filesystem device has read all buffers in
- // the request.
- //
- IoVec->Transferred = IoVec->Size;
- } else {
- //
- // Regarding the response, calculate how much of the current IO Vector
- // has been populated by the Virtio Filesystem device. In
- // "TotalBytesWrittenByDevice", VirtioFlush() reported the total count
- // across all device-writeable descriptors, in the order they were
- // chained on the ring.
- //
- IoVec->Transferred = MIN (
- (UINTN)TotalBytesWrittenByDevice,
- IoVec->Size
- );
- TotalBytesWrittenByDevice -= (UINT32)IoVec->Transferred;
- }
- }
- }
- //
- // By now, "TotalBytesWrittenByDevice" has been exhausted.
- //
- ASSERT (TotalBytesWrittenByDevice == 0);
- //
- // We've succeeded; fall through.
- //
- Unmap:
- //
- // Unmap all mapped IO Vectors on both the success and the error paths. The
- // unmapping occurs in reverse order of mapping, in an attempt to avoid
- // memory fragmentation.
- //
- ListId = ARRAY_SIZE (SgListParam);
- while (ListId > 0) {
- --ListId;
- SgList = SgListParam[ListId];
- if (SgList == NULL) {
- continue;
- }
- IoVecIdx = SgList->NumVec;
- while (IoVecIdx > 0) {
- EFI_STATUS UnmapStatus;
- --IoVecIdx;
- IoVec = &SgList->IoVec[IoVecIdx];
- //
- // Unmap this IO Vector, if it has been mapped.
- //
- if (!IoVec->Mapped) {
- continue;
- }
- UnmapStatus = VirtioFs->Virtio->UnmapSharedBuffer (
- VirtioFs->Virtio,
- IoVec->Mapping
- );
- //
- // Re-set the following fields to the values they initially got from
- // VirtioFsSgListsValidate() -- the above unmapping attempt is considered
- // final, even if it fails.
- //
- IoVec->Mapped = FALSE;
- IoVec->MappedAddress = 0;
- IoVec->Mapping = NULL;
- //
- // If we are on the success path, but the unmapping failed, we need to
- // transparently flip to the failure path -- the caller must learn they
- // should not consult the response buffers.
- //
- // The branch below can be taken at most once.
- //
- if (!EFI_ERROR (Status) && EFI_ERROR (UnmapStatus)) {
- Status = UnmapStatus;
- }
- }
- }
- return Status;
- }
- /**
- Set up the fields of a new VIRTIO_FS_FUSE_REQUEST object.
- The function may only be called after VirtioFsInit() returns successfully and
- before VirtioFsUninit() is called.
- @param[in,out] VirtioFs The Virtio Filesystem device that the request is
- being prepared for. The "VirtioFs->RequestId" field
- will be copied into "Request->Unique". On output (on
- successful return), "VirtioFs->RequestId" will be
- incremented.
- @param[out] Request The VIRTIO_FS_FUSE_REQUEST object whose fields are to
- be set.
- @param[in] RequestSize The total size of the request, including
- sizeof(VIRTIO_FS_FUSE_REQUEST).
- @param[in] Opcode The VIRTIO_FS_FUSE_OPCODE that identifies the command
- to send.
- @param[in] NodeId The inode number of the file that the request refers
- to. When Opcode is VirtioFsFuseOpInit, NodeId is
- ignored by the Virtio Filesystem device.
- @retval EFI_INVALID_PARAMETER RequestSize is smaller than
- sizeof(VIRTIO_FS_FUSE_REQUEST).
- @retval EFI_OUT_OF_RESOURCES "VirtioFs->RequestId" is MAX_UINT64, and can
- be incremented no more.
- @retval EFI_SUCCESS Request has been populated,
- "VirtioFs->RequestId" has been incremented.
- **/
- EFI_STATUS
- VirtioFsFuseNewRequest (
- IN OUT VIRTIO_FS *VirtioFs,
- OUT VIRTIO_FS_FUSE_REQUEST *Request,
- IN UINT32 RequestSize,
- IN VIRTIO_FS_FUSE_OPCODE Opcode,
- IN UINT64 NodeId
- )
- {
- if (RequestSize < sizeof *Request) {
- return EFI_INVALID_PARAMETER;
- }
- if (VirtioFs->RequestId == MAX_UINT64) {
- return EFI_OUT_OF_RESOURCES;
- }
- Request->Len = RequestSize;
- Request->Opcode = Opcode;
- Request->Unique = VirtioFs->RequestId++;
- Request->NodeId = NodeId;
- Request->Uid = 0;
- Request->Gid = 0;
- Request->Pid = 1;
- Request->Padding = 0;
- return EFI_SUCCESS;
- }
- /**
- Check the common FUSE response format.
- The first buffer in the response scatter-gather list is assumed a
- VIRTIO_FS_FUSE_RESPONSE structure. Subsequent response buffers, if any, up to
- and excluding the last one, are assumed fixed size. The last response buffer
- may or may not be fixed size, as specified by the caller.
- This function may only be called after VirtioFsSgListsSubmit() returns
- successfully.
- @param[in] ResponseSgList The scatter-gather list that describes the
- response part of the exchange -- the buffers that
- the Virtio Filesystem device filled in during the
- virtio transfer.
- @param[in] RequestId The request identifier to which the response is
- expected to belong.
- @param[out] TailBufferFill If NULL, then the last buffer in ResponseSgList
- is considered fixed size. Otherwise, the last
- buffer is considered variable size, and on
- successful return, TailBufferFill reports the
- number of bytes in the last buffer.
- @retval EFI_INVALID_PARAMETER TailBufferFill is not NULL (i.e., the last
- buffer is considered variable size), and
- ResponseSgList->NumVec is 1.
- @retval EFI_INVALID_PARAMETER The allocated size of the first buffer does
- not match sizeof(VIRTIO_FS_FUSE_RESPONSE).
- @retval EFI_PROTOCOL_ERROR The VIRTIO_FS_FUSE_RESPONSE structure in the
- first buffer has not been fully populated.
- @retval EFI_PROTOCOL_ERROR "VIRTIO_FS_FUSE_RESPONSE.Len" in the first
- buffer does not equal the sum of the
- individual buffer sizes (as populated).
- @retval EFI_PROTOCOL_ERROR "VIRTIO_FS_FUSE_RESPONSE.Unique" in the first
- buffer does not equal RequestId.
- @retval EFI_PROTOCOL_ERROR "VIRTIO_FS_FUSE_RESPONSE.Error" in the first
- buffer is zero, but a subsequent fixed size
- buffer has not been fully populated.
- @retval EFI_DEVICE_ERROR "VIRTIO_FS_FUSE_RESPONSE.Error" in the first
- buffer is nonzero. The caller may investigate
- "VIRTIO_FS_FUSE_RESPONSE.Error". Note that the
- completeness of the subsequent fixed size
- buffers is not verified in this case.
- @retval EFI_SUCCESS Verification successful.
- **/
- EFI_STATUS
- VirtioFsFuseCheckResponse (
- IN VIRTIO_FS_SCATTER_GATHER_LIST *ResponseSgList,
- IN UINT64 RequestId,
- OUT UINTN *TailBufferFill
- )
- {
- UINTN NumFixedSizeVec;
- VIRTIO_FS_FUSE_RESPONSE *CommonResp;
- UINT32 TotalTransferred;
- UINTN Idx;
- //
- // Ensured by VirtioFsSgListsValidate().
- //
- ASSERT (ResponseSgList->NumVec > 0);
- if (TailBufferFill == NULL) {
- //
- // All buffers are considered fixed size.
- //
- NumFixedSizeVec = ResponseSgList->NumVec;
- } else {
- //
- // If the last buffer is variable size, then we need that buffer to be
- // different from the first buffer, which is considered a
- // VIRTIO_FS_FUSE_RESPONSE (fixed size) structure.
- //
- if (ResponseSgList->NumVec == 1) {
- return EFI_INVALID_PARAMETER;
- }
- NumFixedSizeVec = ResponseSgList->NumVec - 1;
- }
- //
- // The first buffer is supposed to carry a (fully populated)
- // VIRTIO_FS_FUSE_RESPONSE structure.
- //
- if (ResponseSgList->IoVec[0].Size != sizeof *CommonResp) {
- return EFI_INVALID_PARAMETER;
- }
- if (ResponseSgList->IoVec[0].Transferred != ResponseSgList->IoVec[0].Size) {
- return EFI_PROTOCOL_ERROR;
- }
- //
- // FUSE must report the same number of bytes, written by the Virtio
- // Filesystem device, as the virtio transport does.
- //
- CommonResp = ResponseSgList->IoVec[0].Buffer;
- TotalTransferred = 0;
- for (Idx = 0; Idx < ResponseSgList->NumVec; Idx++) {
- //
- // Integer overflow and truncation are not possible, based on
- // VirtioFsSgListsValidate() and VirtioFsSgListsSubmit().
- //
- TotalTransferred += (UINT32)ResponseSgList->IoVec[Idx].Transferred;
- }
- if (CommonResp->Len != TotalTransferred) {
- return EFI_PROTOCOL_ERROR;
- }
- //
- // Enforce that FUSE match our request ID in the response.
- //
- if (CommonResp->Unique != RequestId) {
- return EFI_PROTOCOL_ERROR;
- }
- //
- // If there is an explicit error report, skip checking the transfer
- // counts for the rest of the fixed size buffers.
- //
- if (CommonResp->Error != 0) {
- return EFI_DEVICE_ERROR;
- }
- //
- // There was no error reported, so we require that the Virtio Filesystem
- // device populate all fixed size buffers. We checked this for the very first
- // buffer above; let's check the rest (if any).
- //
- ASSERT (NumFixedSizeVec >= 1);
- for (Idx = 1; Idx < NumFixedSizeVec; Idx++) {
- if (ResponseSgList->IoVec[Idx].Transferred !=
- ResponseSgList->IoVec[Idx].Size)
- {
- return EFI_PROTOCOL_ERROR;
- }
- }
- //
- // If the last buffer is considered variable size, report its filled size.
- //
- if (TailBufferFill != NULL) {
- *TailBufferFill = ResponseSgList->IoVec[NumFixedSizeVec].Transferred;
- }
- return EFI_SUCCESS;
- }
- /**
- An ad-hoc function for mapping FUSE (well, Linux) "errno" values to
- EFI_STATUS.
- @param[in] Errno The "VIRTIO_FS_FUSE_RESPONSE.Error" value, returned by the
- Virtio Filesystem device. The value is expected to be
- negative.
- @return An EFI_STATUS error code that's deemed a passable
- mapping for the Errno value.
- @retval EFI_DEVICE_ERROR Fallback EFI_STATUS code for unrecognized Errno
- values.
- **/
- EFI_STATUS
- VirtioFsErrnoToEfiStatus (
- IN INT32 Errno
- )
- {
- switch (Errno) {
- case -1:// EPERM Operation not permitted
- return EFI_SECURITY_VIOLATION;
- case -2: // ENOENT No such file or directory
- case -3: // ESRCH No such process
- case -6: // ENXIO No such device or address
- case -10: // ECHILD No child processes
- case -19: // ENODEV No such device
- case -49: // EUNATCH Protocol driver not attached
- case -65: // ENOPKG Package not installed
- case -79: // ELIBACC Can not access a needed shared library
- case -126: // ENOKEY Required key not available
- return EFI_NOT_FOUND;
- case -4: // EINTR Interrupted system call
- case -11: // EAGAIN, EWOULDBLOCK Resource temporarily unavailable
- case -16: // EBUSY Device or resource busy
- case -26: // ETXTBSY Text file busy
- case -35: // EDEADLK, EDEADLOCK Resource deadlock avoided
- case -39: // ENOTEMPTY Directory not empty
- case -42: // ENOMSG No message of desired type
- case -61: // ENODATA No data available
- case -85: // ERESTART Interrupted system call should be restarted
- return EFI_NOT_READY;
- case -5: // EIO Input/output error
- case -45: // EL2NSYNC Level 2 not synchronized
- case -46: // EL3HLT Level 3 halted
- case -47: // EL3RST Level 3 reset
- case -51: // EL2HLT Level 2 halted
- case -121: // EREMOTEIO Remote I/O error
- case -133: // EHWPOISON Memory page has hardware error
- return EFI_DEVICE_ERROR;
- case -7: // E2BIG Argument list too long
- case -36: // ENAMETOOLONG File name too long
- case -90: // EMSGSIZE Message too long
- return EFI_BAD_BUFFER_SIZE;
- case -8: // ENOEXEC Exec format error
- case -15: // ENOTBLK Block device required
- case -18: // EXDEV Invalid cross-device link
- case -20: // ENOTDIR Not a directory
- case -21: // EISDIR Is a directory
- case -25: // ENOTTY Inappropriate ioctl for device
- case -27: // EFBIG File too large
- case -29: // ESPIPE Illegal seek
- case -38: // ENOSYS Function not implemented
- case -59: // EBFONT Bad font file format
- case -60: // ENOSTR Device not a stream
- case -83: // ELIBEXEC Cannot exec a shared library directly
- case -88: // ENOTSOCK Socket operation on non-socket
- case -91: // EPROTOTYPE Protocol wrong type for socket
- case -92: // ENOPROTOOPT Protocol not available
- case -93: // EPROTONOSUPPORT Protocol not supported
- case -94: // ESOCKTNOSUPPORT Socket type not supported
- case -95: // ENOTSUP, EOPNOTSUPP Operation not supported
- case -96: // EPFNOSUPPORT Protocol family not supported
- case -97: // EAFNOSUPPORT Address family not supported by protocol
- case -99: // EADDRNOTAVAIL Cannot assign requested address
- case -118: // ENOTNAM Not a XENIX named type file
- case -120: // EISNAM Is a named type file
- case -124: // EMEDIUMTYPE Wrong medium type
- return EFI_UNSUPPORTED;
- case -9: // EBADF Bad file descriptor
- case -14: // EFAULT Bad address
- case -44: // ECHRNG Channel number out of range
- case -48: // ELNRNG Link number out of range
- case -53: // EBADR Invalid request descriptor
- case -56: // EBADRQC Invalid request code
- case -57: // EBADSLT Invalid slot
- case -76: // ENOTUNIQ Name not unique on network
- case -84: // EILSEQ Invalid or incomplete multibyte or wide character
- return EFI_NO_MAPPING;
- case -12: // ENOMEM Cannot allocate memory
- case -23: // ENFILE Too many open files in system
- case -24: // EMFILE Too many open files
- case -31: // EMLINK Too many links
- case -37: // ENOLCK No locks available
- case -40: // ELOOP Too many levels of symbolic links
- case -50: // ENOCSI No CSI structure available
- case -55: // ENOANO No anode
- case -63: // ENOSR Out of streams resources
- case -82: // ELIBMAX Attempting to link in too many shared libraries
- case -87: // EUSERS Too many users
- case -105: // ENOBUFS No buffer space available
- case -109: // ETOOMANYREFS Too many references: cannot splice
- case -119: // ENAVAIL No XENIX semaphores available
- case -122: // EDQUOT Disk quota exceeded
- return EFI_OUT_OF_RESOURCES;
- case -13:// EACCES Permission denied
- return EFI_ACCESS_DENIED;
- case -17: // EEXIST File exists
- case -98: // EADDRINUSE Address already in use
- case -106: // EISCONN Transport endpoint is already connected
- case -114: // EALREADY Operation already in progress
- case -115: // EINPROGRESS Operation now in progress
- return EFI_ALREADY_STARTED;
- case -22: // EINVAL Invalid argument
- case -33: // EDOM Numerical argument out of domain
- return EFI_INVALID_PARAMETER;
- case -28: // ENOSPC No space left on device
- case -54: // EXFULL Exchange full
- return EFI_VOLUME_FULL;
- case -30:// EROFS Read-only file system
- return EFI_WRITE_PROTECTED;
- case -32: // EPIPE Broken pipe
- case -43: // EIDRM Identifier removed
- case -67: // ENOLINK Link has been severed
- case -68: // EADV Advertise error
- case -69: // ESRMNT Srmount error
- case -70: // ECOMM Communication error on send
- case -73: // EDOTDOT RFS specific error
- case -78: // EREMCHG Remote address changed
- case -86: // ESTRPIPE Streams pipe error
- case -102: // ENETRESET Network dropped connection on reset
- case -103: // ECONNABORTED Software caused connection abort
- case -104: // ECONNRESET Connection reset by peer
- case -116: // ESTALE Stale file handle
- case -125: // ECANCELED Operation canceled
- case -128: // EKEYREVOKED Key has been revoked
- case -129: // EKEYREJECTED Key was rejected by service
- case -130: // EOWNERDEAD Owner died
- case -131: // ENOTRECOVERABLE State not recoverable
- return EFI_ABORTED;
- case -34: // ERANGE Numerical result out of range
- case -75: // EOVERFLOW Value too large for defined data type
- return EFI_BUFFER_TOO_SMALL;
- case -52: // EBADE Invalid exchange
- case -108: // ESHUTDOWN Cannot send after transport endpoint shutdown
- case -111: // ECONNREFUSED Connection refused
- return EFI_END_OF_FILE;
- case -62: // ETIME Timer expired
- case -110: // ETIMEDOUT Connection timed out
- case -127: // EKEYEXPIRED Key has expired
- return EFI_TIMEOUT;
- case -64: // ENONET Machine is not on the network
- case -66: // EREMOTE Object is remote
- case -72: // EMULTIHOP Multihop attempted
- case -100: // ENETDOWN Network is down
- case -101: // ENETUNREACH Network is unreachable
- case -112: // EHOSTDOWN Host is down
- case -113: // EHOSTUNREACH No route to host
- case -123: // ENOMEDIUM No medium found
- case -132: // ERFKILL Operation not possible due to RF-kill
- return EFI_NO_MEDIA;
- case -71:// EPROTO Protocol error
- return EFI_PROTOCOL_ERROR;
- case -74: // EBADMSG Bad message
- case -77: // EBADFD File descriptor in bad state
- case -80: // ELIBBAD Accessing a corrupted shared library
- case -81: // ELIBSCN .lib section in a.out corrupted
- case -117: // EUCLEAN Structure needs cleaning
- return EFI_VOLUME_CORRUPTED;
- case -89: // EDESTADDRREQ Destination address required
- case -107: // ENOTCONN Transport endpoint is not connected
- return EFI_NOT_STARTED;
- default:
- break;
- }
- return EFI_DEVICE_ERROR;
- }
- //
- // Parser states for canonicalizing a POSIX pathname.
- //
- typedef enum {
- ParserInit, // just starting
- ParserEnd, // finished
- ParserSlash, // slash(es) seen
- ParserDot, // one dot seen since last slash
- ParserDotDot, // two dots seen since last slash
- ParserNormal, // a different sequence seen
- } PARSER_STATE;
- /**
- Strip the trailing slash from the parser's output buffer, unless the trailing
- slash stands for the root directory.
- @param[in] Buffer The parser's output buffer. Only used for
- sanity-checking.
- @param[in,out] Position On entry, points at the next character to produce
- (i.e., right past the end of the output written by
- the parser thus far). The last character in the
- parser's output buffer is a slash. On return, the
- slash is stripped, by decrementing Position by one.
- If this action would remove the slash character
- standing for the root directory, then the function
- has no effect.
- **/
- STATIC
- VOID
- ParserStripSlash (
- IN CHAR8 *Buffer,
- IN OUT UINTN *Position
- )
- {
- ASSERT (*Position >= 1);
- ASSERT (Buffer[*Position - 1] == '/');
- if (*Position == 1) {
- return;
- }
- (*Position)--;
- }
- /**
- Produce one character in the parser's output buffer.
- @param[out] Buffer The parser's output buffer. On return, Char8 will
- have been written.
- @param[in,out] Position On entry, points at the next character to produce
- (i.e., right past the end of the output written by
- the parser thus far). On return, Position is
- incremented by one.
- @param[in] Size Total allocated size of the parser's output buffer.
- Used for sanity-checking.
- @param[in] Char8 The character to place in the output buffer.
- **/
- STATIC
- VOID
- ParserCopy (
- OUT CHAR8 *Buffer,
- IN OUT UINTN *Position,
- IN UINTN Size,
- IN CHAR8 Char8
- )
- {
- ASSERT (*Position < Size);
- Buffer[(*Position)++] = Char8;
- }
- /**
- Rewind the last single-dot in the parser's output buffer.
- @param[in] Buffer The parser's output buffer. Only used for
- sanity-checking.
- @param[in,out] Position On entry, points at the next character to produce
- (i.e., right past the end of the output written by
- the parser thus far); the parser's output buffer
- ends with the characters ('/', '.'). On return, the
- dot is rewound by decrementing Position by one; a
- slash character will reside at the new end of the
- parser's output buffer.
- **/
- STATIC
- VOID
- ParserRewindDot (
- IN CHAR8 *Buffer,
- IN OUT UINTN *Position
- )
- {
- ASSERT (*Position >= 2);
- ASSERT (Buffer[*Position - 1] == '.');
- ASSERT (Buffer[*Position - 2] == '/');
- (*Position)--;
- }
- /**
- Rewind the last dot-dot in the parser's output buffer.
- @param[in] Buffer The parser's output buffer. Only used for
- sanity-checking.
- @param[in,out] Position On entry, points at the next character to produce
- (i.e., right past the end of the output written by
- the parser thus far); the parser's output buffer
- ends with the characters ('/', '.', '.'). On return,
- the ('.', '.') pair is rewound unconditionally, by
- decrementing Position by two; a slash character
- resides at the new end of the parser's output
- buffer.
- If this slash character stands for the root
- directory, then RootEscape is set to TRUE.
- Otherwise (i.e., if this slash character is not the
- one standing for the root directory), then the slash
- character, and the pathname component preceding it,
- are removed by decrementing Position further. In
- this case, the slash character preceding the removed
- pathname component will reside at the new end of the
- parser's output buffer.
- @param[out] RootEscape Set to TRUE on output if the dot-dot component tries
- to escape the root directory, as described above.
- Otherwise, RootEscape is not modified.
- **/
- STATIC
- VOID
- ParserRewindDotDot (
- IN CHAR8 *Buffer,
- IN OUT UINTN *Position,
- OUT BOOLEAN *RootEscape
- )
- {
- ASSERT (*Position >= 3);
- ASSERT (Buffer[*Position - 1] == '.');
- ASSERT (Buffer[*Position - 2] == '.');
- ASSERT (Buffer[*Position - 3] == '/');
- (*Position) -= 2;
- if (*Position == 1) {
- //
- // Root directory slash reached; don't try to climb higher.
- //
- *RootEscape = TRUE;
- return;
- }
- //
- // Skip slash.
- //
- (*Position)--;
- //
- // Scan until next slash to the left.
- //
- do {
- ASSERT (*Position > 0);
- (*Position)--;
- } while (Buffer[*Position] != '/');
- (*Position)++;
- }
- /**
- Append the UEFI-style RhsPath16 to the POSIX-style, canonical format
- LhsPath8. Output the POSIX-style, canonical format result in ResultPath, as a
- dynamically allocated string.
- Canonicalization (aka sanitization) establishes the following properties:
- - ResultPath is absolute (starts with "/"),
- - dot (.) and dot-dot (..) components are resolved/eliminated in ResultPath,
- with the usual semantics,
- - ResultPath uses forward slashes,
- - sequences of slashes are squashed in ResultPath,
- - the printable ASCII character set covers ResultPath,
- - CHAR8 encoding is used in ResultPath,
- - no trailing slash present in ResultPath except for the standalone root
- directory,
- - the length of ResultPath is at most VIRTIO_FS_MAX_PATHNAME_LENGTH.
- Any dot-dot in RhsPath16 that would remove the root directory is dropped, and
- reported through RootEscape, without failing the function call.
- @param[in] LhsPath8 Identifies the base directory. The caller is
- responsible for ensuring that LhsPath8 conform to
- the above canonical pathname format on entry.
- @param[in] RhsPath16 Identifies the desired file with a UEFI-style CHAR16
- pathname. If RhsPath16 starts with a backslash, then
- RhsPath16 is considered absolute, and LhsPath8 is
- ignored; RhsPath16 is sanitized in isolation, for
- producing ResultPath8. Otherwise (i.e., if RhsPath16
- is relative), RhsPath16 is transliterated to CHAR8,
- and naively appended to LhsPath8. The resultant
- fused pathname is then sanitized, to produce
- ResultPath8.
- @param[out] ResultPath8 The POSIX-style, canonical format pathname that
- leads to the file desired by the caller. After use,
- the caller is responsible for freeing ResultPath8.
- @param[out] RootEscape Set to TRUE if at least one dot-dot component in
- RhsPath16 attempted to escape the root directory;
- set to FALSE otherwise.
- @retval EFI_SUCCESS ResultPath8 has been produced. RootEscape has
- been output.
- @retval EFI_INVALID_PARAMETER RhsPath16 is zero-length.
- @retval EFI_INVALID_PARAMETER RhsPath16 failed the
- VIRTIO_FS_MAX_PATHNAME_LENGTH check.
- @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
- @retval EFI_OUT_OF_RESOURCES ResultPath8 would have failed the
- VIRTIO_FS_MAX_PATHNAME_LENGTH check.
- @retval EFI_UNSUPPORTED RhsPath16 contains a character that either
- falls outside of the printable ASCII set, or
- is a forward slash.
- **/
- EFI_STATUS
- VirtioFsAppendPath (
- IN CHAR8 *LhsPath8,
- IN CHAR16 *RhsPath16,
- OUT CHAR8 **ResultPath8,
- OUT BOOLEAN *RootEscape
- )
- {
- UINTN RhsLen;
- CHAR8 *RhsPath8;
- UINTN Idx;
- EFI_STATUS Status;
- UINTN SizeToSanitize;
- CHAR8 *BufferToSanitize;
- CHAR8 *SanitizedBuffer;
- PARSER_STATE State;
- UINTN SanitizedPosition;
- //
- // Appending an empty pathname is not allowed.
- //
- RhsLen = StrLen (RhsPath16);
- if (RhsLen == 0) {
- return EFI_INVALID_PARAMETER;
- }
- //
- // Enforce length restriction on RhsPath16.
- //
- if (RhsLen > VIRTIO_FS_MAX_PATHNAME_LENGTH) {
- return EFI_INVALID_PARAMETER;
- }
- //
- // Transliterate RhsPath16 to RhsPath8 by:
- // - rejecting RhsPath16 if a character outside of printable ASCII is seen,
- // - rejecting RhsPath16 if a forward slash is seen,
- // - replacing backslashes with forward slashes,
- // - casting the characters from CHAR16 to CHAR8.
- //
- RhsPath8 = AllocatePool (RhsLen + 1);
- if (RhsPath8 == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- for (Idx = 0; RhsPath16[Idx] != L'\0'; Idx++) {
- if ((RhsPath16[Idx] < 0x20) || (RhsPath16[Idx] > 0x7E) ||
- (RhsPath16[Idx] == L'/'))
- {
- Status = EFI_UNSUPPORTED;
- goto FreeRhsPath8;
- }
- RhsPath8[Idx] = (CHAR8)((RhsPath16[Idx] == L'\\') ? L'/' : RhsPath16[Idx]);
- }
- RhsPath8[Idx++] = '\0';
- //
- // Now prepare the input for the canonicalization (squashing of sequences of
- // forward slashes, and eliminating . (dot) and .. (dot-dot) pathname
- // components).
- //
- // The sanitized path can never be longer than the naive concatenation of the
- // left hand side and right hand side paths, so we'll use the catenated size
- // for allocating the sanitized output too.
- //
- if (RhsPath8[0] == '/') {
- //
- // If the right hand side path is absolute, then it is not appended to the
- // left hand side path -- it *replaces* the left hand side path.
- //
- SizeToSanitize = RhsLen + 1;
- BufferToSanitize = RhsPath8;
- } else {
- //
- // If the right hand side path is relative, then it is appended (naively)
- // to the left hand side.
- //
- UINTN LhsLen;
- LhsLen = AsciiStrLen (LhsPath8);
- SizeToSanitize = LhsLen + 1 + RhsLen + 1;
- BufferToSanitize = AllocatePool (SizeToSanitize);
- if (BufferToSanitize == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto FreeRhsPath8;
- }
- CopyMem (BufferToSanitize, LhsPath8, LhsLen);
- BufferToSanitize[LhsLen] = '/';
- CopyMem (BufferToSanitize + LhsLen + 1, RhsPath8, RhsLen + 1);
- }
- //
- // Allocate the output buffer.
- //
- SanitizedBuffer = AllocatePool (SizeToSanitize);
- if (SanitizedBuffer == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto FreeBufferToSanitize;
- }
- //
- // State machine for parsing the input and producing the canonical output
- // follows.
- //
- *RootEscape = FALSE;
- Idx = 0;
- State = ParserInit;
- SanitizedPosition = 0;
- do {
- CHAR8 Chr8;
- ASSERT (Idx < SizeToSanitize);
- Chr8 = BufferToSanitize[Idx++];
- switch (State) {
- case ParserInit: // just starting
- ASSERT (Chr8 == '/');
- ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
- State = ParserSlash;
- break;
- case ParserSlash: // slash(es) seen
- switch (Chr8) {
- case '\0':
- ParserStripSlash (SanitizedBuffer, &SanitizedPosition);
- ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
- State = ParserEnd;
- break;
- case '/':
- //
- // skip & stay in same state
- //
- break;
- case '.':
- ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
- State = ParserDot;
- break;
- default:
- ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
- State = ParserNormal;
- break;
- }
- break;
- case ParserDot: // one dot seen since last slash
- switch (Chr8) {
- case '\0':
- ParserRewindDot (SanitizedBuffer, &SanitizedPosition);
- ParserStripSlash (SanitizedBuffer, &SanitizedPosition);
- ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
- State = ParserEnd;
- break;
- case '/':
- ParserRewindDot (SanitizedBuffer, &SanitizedPosition);
- State = ParserSlash;
- break;
- case '.':
- ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
- State = ParserDotDot;
- break;
- default:
- ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
- State = ParserNormal;
- break;
- }
- break;
- case ParserDotDot: // two dots seen since last slash
- switch (Chr8) {
- case '\0':
- ParserRewindDotDot (SanitizedBuffer, &SanitizedPosition, RootEscape);
- ParserStripSlash (SanitizedBuffer, &SanitizedPosition);
- ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
- State = ParserEnd;
- break;
- case '/':
- ParserRewindDotDot (SanitizedBuffer, &SanitizedPosition, RootEscape);
- State = ParserSlash;
- break;
- case '.':
- //
- // fall through
- //
- default:
- ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
- State = ParserNormal;
- break;
- }
- break;
- case ParserNormal: // a different sequence seen
- switch (Chr8) {
- case '\0':
- ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
- State = ParserEnd;
- break;
- case '/':
- ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
- State = ParserSlash;
- break;
- case '.':
- //
- // fall through
- //
- default:
- //
- // copy and stay in same state
- //
- ParserCopy (SanitizedBuffer, &SanitizedPosition, SizeToSanitize, Chr8);
- break;
- }
- break;
- default:
- ASSERT (FALSE);
- break;
- }
- } while (State != ParserEnd);
- //
- // Ensure length invariant on ResultPath8.
- //
- ASSERT (SanitizedPosition >= 2);
- if (SanitizedPosition - 1 > VIRTIO_FS_MAX_PATHNAME_LENGTH) {
- Status = EFI_OUT_OF_RESOURCES;
- goto FreeSanitizedBuffer;
- }
- *ResultPath8 = SanitizedBuffer;
- SanitizedBuffer = NULL;
- Status = EFI_SUCCESS;
- //
- // Fall through.
- //
- FreeSanitizedBuffer:
- if (SanitizedBuffer != NULL) {
- FreePool (SanitizedBuffer);
- }
- FreeBufferToSanitize:
- if (RhsPath8[0] != '/') {
- FreePool (BufferToSanitize);
- }
- FreeRhsPath8:
- FreePool (RhsPath8);
- return Status;
- }
- /**
- For a given canonical pathname (as defined at VirtioFsAppendPath()), look up
- the NodeId of the most specific parent directory, plus output a pointer to
- the last pathname component (which is therefore a direct child of said parent
- 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 FUSE_LOOKUP
- and FUSE_FORGET requests to. On output, the FUSE
- request counter "VirtioFs->RequestId" will have
- been incremented several times.
- @param[in,out] Path The canonical pathname (as defined in the
- description of VirtioFsAppendPath()) to split.
- Path is modified in-place temporarily; however, on
- return (successful or otherwise), Path reassumes
- its original contents.
- @param[out] DirNodeId The NodeId of the most specific parent directory
- identified by Path. The caller is responsible for
- sending a FUSE_FORGET request to the Virtio
- Filesystem device for DirNodeId -- unless
- DirNodeId equals VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID
- --, when DirNodeId's use ends.
- @param[out] LastComponent A pointer into Path, pointing at the start of the
- last pathname component.
- @retval EFI_SUCCESS Splitting successful.
- @retval EFI_INVALID_PARAMETER Path is "/".
- @retval EFI_ACCESS_DENIED One of the components on Path before the last
- is not a directory.
- @return Error codes propagated from
- VirtioFsFuseLookup() and
- VirtioFsFuseAttrToEfiFileInfo().
- **/
- EFI_STATUS
- VirtioFsLookupMostSpecificParentDir (
- IN OUT VIRTIO_FS *VirtioFs,
- IN OUT CHAR8 *Path,
- OUT UINT64 *DirNodeId,
- OUT CHAR8 **LastComponent
- )
- {
- UINT64 ParentDirNodeId;
- CHAR8 *Slash;
- EFI_STATUS Status;
- UINT64 NextDirNodeId;
- if (AsciiStrCmp (Path, "/") == 0) {
- return EFI_INVALID_PARAMETER;
- }
- ParentDirNodeId = VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID;
- Slash = Path;
- for ( ; ;) {
- CHAR8 *NextSlash;
- VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE FuseAttr;
- EFI_FILE_INFO FileInfo;
- //
- // Find the slash (if any) that terminates the next pathname component.
- //
- NextSlash = AsciiStrStr (Slash + 1, "/");
- if (NextSlash == NULL) {
- break;
- }
- //
- // Temporarily replace the found slash character with a NUL in-place, for
- // easy construction of the single-component filename that we need to look
- // up.
- //
- *NextSlash = '\0';
- Status = VirtioFsFuseLookup (
- VirtioFs,
- ParentDirNodeId,
- Slash + 1,
- &NextDirNodeId,
- &FuseAttr
- );
- *NextSlash = '/';
- //
- // We're done with the directory inode that was the basis for the lookup.
- //
- if (ParentDirNodeId != VIRTIO_FS_FUSE_ROOT_DIR_NODE_ID) {
- VirtioFsFuseForget (VirtioFs, ParentDirNodeId);
- }
- //
- // If we couldn't look up the next *non-final* pathname component, bail.
- //
- if (EFI_ERROR (Status)) {
- return Status;
- }
- //
- // Lookup successful; now check if the next (non-final) component is a
- // directory. If not, bail.
- //
- Status = VirtioFsFuseAttrToEfiFileInfo (&FuseAttr, &FileInfo);
- if (EFI_ERROR (Status)) {
- goto ForgetNextDirNodeId;
- }
- if ((FileInfo.Attribute & EFI_FILE_DIRECTORY) == 0) {
- Status = EFI_ACCESS_DENIED;
- goto ForgetNextDirNodeId;
- }
- //
- // Advance.
- //
- ParentDirNodeId = NextDirNodeId;
- Slash = NextSlash;
- }
- //
- // ParentDirNodeId corresponds to the last containing directory. The
- // remaining single-component filename represents a direct child under that
- // directory. Said filename starts at (Slash + 1).
- //
- *DirNodeId = ParentDirNodeId;
- *LastComponent = Slash + 1;
- return EFI_SUCCESS;
- ForgetNextDirNodeId:
- VirtioFsFuseForget (VirtioFs, NextDirNodeId);
- return Status;
- }
- /**
- Format the last component of a canonical pathname into a caller-provided
- CHAR16 array.
- @param[in] Path The canonical pathname (as defined in the
- description of VirtioFsAppendPath()) to format
- the last component of.
- @param[out] Basename If BasenameSize is zero on input, Basename may
- be NULL. Otherwise, Basename is allocated by the
- caller. On successful return, Basename contains
- the last component of Path, formatted as a
- NUL-terminated CHAR16 string. When Path is "/"
- on input, Basename is L"" on output.
- @param[in,out] BasenameSize On input, the number of bytes the caller
- provides in Basename. On output, regardless of
- return value, the number of bytes required for
- formatting Basename, including the terminating
- L'\0'.
- @retval EFI_SUCCESS Basename has been filled in.
- @retval EFI_BUFFER_TOO_SMALL BasenameSize was too small on input; Basename
- has not been modified.
- **/
- EFI_STATUS
- VirtioFsGetBasename (
- IN CHAR8 *Path,
- OUT CHAR16 *Basename OPTIONAL,
- IN OUT UINTN *BasenameSize
- )
- {
- UINTN AllocSize;
- UINTN LastComponent;
- UINTN Idx;
- UINTN PathSize;
- AllocSize = *BasenameSize;
- LastComponent = MAX_UINTN;
- for (Idx = 0; Path[Idx] != '\0'; Idx++) {
- if (Path[Idx] == '/') {
- LastComponent = Idx;
- }
- }
- PathSize = Idx + 1;
- ASSERT (LastComponent < MAX_UINTN);
- LastComponent++;
- *BasenameSize = (PathSize - LastComponent) * sizeof Basename[0];
- if (*BasenameSize > AllocSize) {
- return EFI_BUFFER_TOO_SMALL;
- }
- for (Idx = LastComponent; Idx < PathSize; Idx++) {
- Basename[Idx - LastComponent] = Path[Idx];
- }
- return EFI_SUCCESS;
- }
- /**
- Format the destination of a rename/move operation as a dynamically allocated
- canonical pathname.
- Any dot-dot in RhsPath16 that would remove the root directory is dropped, and
- reported through RootEscape, without failing the function call.
- @param[in] LhsPath8 The source pathname operand of the rename/move
- operation, expressed as a canonical pathname (as
- defined in the description of VirtioFsAppendPath()).
- The root directory "/" cannot be renamed/moved, and
- will be rejected.
- @param[in] RhsPath16 The destination pathname operand expressed as a
- UEFI-style CHAR16 pathname.
- If RhsPath16 starts with a backslash, then RhsPath16
- is considered absolute. Otherwise, RhsPath16 is
- interpreted relative to the most specific parent
- directory found in LhsPath8.
- Independently, if RhsPath16 ends with a backslash
- (i.e., RhsPath16 is given in the "move into
- directory" convenience form), then RhsPath16 is
- interpreted with the basename of LhsPath8 appended.
- Otherwise, the last pathname component of RhsPath16
- is taken as the last pathname component of the
- rename/move destination.
- An empty RhsPath16 is rejected.
- @param[out] ResultPath8 The POSIX-style, canonical format pathname that
- leads to the renamed/moved file. After use, the
- caller is responsible for freeing ResultPath8.
- @param[out] RootEscape Set to TRUE if at least one dot-dot component in
- RhsPath16 attempted to escape the root directory;
- set to FALSE otherwise.
- @retval EFI_SUCCESS ResultPath8 has been produced. RootEscape has
- been output.
- @retval EFI_INVALID_PARAMETER LhsPath8 is "/".
- @retval EFI_INVALID_PARAMETER RhsPath16 is zero-length.
- @retval EFI_INVALID_PARAMETER RhsPath16 failed the
- VIRTIO_FS_MAX_PATHNAME_LENGTH check.
- @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
- @retval EFI_OUT_OF_RESOURCES ResultPath8 would have failed the
- VIRTIO_FS_MAX_PATHNAME_LENGTH check.
- @retval EFI_UNSUPPORTED RhsPath16 contains a character that either
- falls outside of the printable ASCII set, or
- is a forward slash.
- **/
- EFI_STATUS
- VirtioFsComposeRenameDestination (
- IN CHAR8 *LhsPath8,
- IN CHAR16 *RhsPath16,
- OUT CHAR8 **ResultPath8,
- OUT BOOLEAN *RootEscape
- )
- {
- //
- // Lengths are expressed as numbers of characters (CHAR8 or CHAR16),
- // excluding terminating NULs. Sizes are expressed as byte counts, including
- // the bytes taken up by terminating NULs.
- //
- UINTN RhsLen;
- UINTN LhsBasename16Size;
- EFI_STATUS Status;
- UINTN LhsBasenameLen;
- UINTN DestSuffix16Size;
- CHAR16 *DestSuffix16;
- CHAR8 *DestPrefix8;
- //
- // An empty destination operand for the rename/move operation is not allowed.
- //
- RhsLen = StrLen (RhsPath16);
- if (RhsLen == 0) {
- return EFI_INVALID_PARAMETER;
- }
- //
- // Enforce length restriction on RhsPath16.
- //
- if (RhsLen > VIRTIO_FS_MAX_PATHNAME_LENGTH) {
- return EFI_INVALID_PARAMETER;
- }
- //
- // Determine the length of the basename of LhsPath8.
- //
- LhsBasename16Size = 0;
- Status = VirtioFsGetBasename (LhsPath8, NULL, &LhsBasename16Size);
- ASSERT (Status == EFI_BUFFER_TOO_SMALL);
- ASSERT (LhsBasename16Size >= sizeof (CHAR16));
- ASSERT (LhsBasename16Size % sizeof (CHAR16) == 0);
- LhsBasenameLen = LhsBasename16Size / sizeof (CHAR16) - 1;
- if (LhsBasenameLen == 0) {
- //
- // The root directory cannot be renamed/moved.
- //
- return EFI_INVALID_PARAMETER;
- }
- //
- // Resolve the "move into directory" convenience form in RhsPath16.
- //
- if (RhsPath16[RhsLen - 1] == L'\\') {
- //
- // Append the basename of LhsPath8 as a CHAR16 string to RhsPath16.
- //
- DestSuffix16Size = RhsLen * sizeof (CHAR16) + LhsBasename16Size;
- DestSuffix16 = AllocatePool (DestSuffix16Size);
- if (DestSuffix16 == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- CopyMem (DestSuffix16, RhsPath16, RhsLen * sizeof (CHAR16));
- Status = VirtioFsGetBasename (
- LhsPath8,
- DestSuffix16 + RhsLen,
- &LhsBasename16Size
- );
- ASSERT_EFI_ERROR (Status);
- } else {
- //
- // Just create a copy of RhsPath16.
- //
- DestSuffix16Size = (RhsLen + 1) * sizeof (CHAR16);
- DestSuffix16 = AllocateCopyPool (DestSuffix16Size, RhsPath16);
- if (DestSuffix16 == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- }
- //
- // If the destination operand is absolute, it will be interpreted relative to
- // the root directory.
- //
- // Otherwise (i.e., if the destination operand is relative), then create the
- // canonical pathname that the destination operand is interpreted relatively
- // to; that is, the canonical pathname of the most specific parent directory
- // found in LhsPath8.
- //
- if (DestSuffix16[0] == L'\\') {
- DestPrefix8 = AllocateCopyPool (sizeof "/", "/");
- if (DestPrefix8 == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto FreeDestSuffix16;
- }
- } else {
- UINTN LhsLen;
- UINTN DestPrefixLen;
- //
- // Strip the basename of LhsPath8.
- //
- LhsLen = AsciiStrLen (LhsPath8);
- ASSERT (LhsBasenameLen < LhsLen);
- DestPrefixLen = LhsLen - LhsBasenameLen;
- ASSERT (LhsPath8[DestPrefixLen - 1] == '/');
- //
- // If we're not at the root directory, strip the slash too.
- //
- if (DestPrefixLen > 1) {
- DestPrefixLen--;
- }
- DestPrefix8 = AllocatePool (DestPrefixLen + 1);
- if (DestPrefix8 == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto FreeDestSuffix16;
- }
- CopyMem (DestPrefix8, LhsPath8, DestPrefixLen);
- DestPrefix8[DestPrefixLen] = '\0';
- }
- //
- // Now combine DestPrefix8 and DestSuffix16 into the final canonical
- // pathname.
- //
- Status = VirtioFsAppendPath (
- DestPrefix8,
- DestSuffix16,
- ResultPath8,
- RootEscape
- );
- FreePool (DestPrefix8);
- //
- // Fall through.
- //
- FreeDestSuffix16:
- FreePool (DestSuffix16);
- return Status;
- }
- /**
- Convert select fields of a VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
- corresponding fields in EFI_FILE_INFO.
- @param[in] FuseAttr The VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE object to
- convert the relevant fields from.
- @param[out] FileInfo The EFI_FILE_INFO structure to modify. Importantly, the
- FileInfo->Size and FileInfo->FileName fields are not
- overwritten.
- @retval EFI_SUCCESS Conversion successful.
- @retval EFI_UNSUPPORTED The allocated size of the file is inexpressible in
- EFI_FILE_INFO.
- @retval EFI_UNSUPPORTED One of the file access times is inexpressible in
- EFI_FILE_INFO.
- @retval EFI_UNSUPPORTED The file type is inexpressible in EFI_FILE_INFO.
- @retval EFI_UNSUPPORTED The file is a regular file that has multiple names
- on the host side (i.e., its hard link count is
- greater than one).
- **/
- EFI_STATUS
- VirtioFsFuseAttrToEfiFileInfo (
- IN VIRTIO_FS_FUSE_ATTRIBUTES_RESPONSE *FuseAttr,
- OUT EFI_FILE_INFO *FileInfo
- )
- {
- UINT64 EpochTime[3];
- EFI_TIME *ConvertedTime[ARRAY_SIZE (EpochTime)];
- UINTN Idx;
- FileInfo->FileSize = FuseAttr->Size;
- //
- // The unit for FuseAttr->Blocks is 512B.
- //
- if (FuseAttr->Blocks >= BIT55) {
- return EFI_UNSUPPORTED;
- }
- FileInfo->PhysicalSize = LShiftU64 (FuseAttr->Blocks, 9);
- //
- // Convert the timestamps. File creation time is not tracked by the Virtio
- // Filesystem device, so set FileInfo->CreateTime from FuseAttr->Mtime as
- // well.
- //
- EpochTime[0] = FuseAttr->Mtime;
- EpochTime[1] = FuseAttr->Atime;
- EpochTime[2] = FuseAttr->Mtime;
- ConvertedTime[0] = &FileInfo->CreateTime;
- ConvertedTime[1] = &FileInfo->LastAccessTime;
- ConvertedTime[2] = &FileInfo->ModificationTime;
- for (Idx = 0; Idx < ARRAY_SIZE (EpochTime); Idx++) {
- //
- // EpochToEfiTime() takes a UINTN for seconds.
- //
- if (EpochTime[Idx] > MAX_UINTN) {
- return EFI_UNSUPPORTED;
- }
- //
- // Set the following fields in the converted time: Year, Month, Day, Hour,
- // Minute, Second, Nanosecond.
- //
- EpochToEfiTime ((UINTN)EpochTime[Idx], ConvertedTime[Idx]);
- //
- // The times are all expressed in UTC. Consequently, they are not affected
- // by daylight saving.
- //
- ConvertedTime[Idx]->TimeZone = 0;
- ConvertedTime[Idx]->Daylight = 0;
- //
- // Clear the padding fields.
- //
- ConvertedTime[Idx]->Pad1 = 0;
- ConvertedTime[Idx]->Pad2 = 0;
- }
- //
- // Set the attributes.
- //
- switch (FuseAttr->Mode & VIRTIO_FS_FUSE_MODE_TYPE_MASK) {
- case VIRTIO_FS_FUSE_MODE_TYPE_DIR:
- FileInfo->Attribute = EFI_FILE_DIRECTORY;
- break;
- case VIRTIO_FS_FUSE_MODE_TYPE_REG:
- FileInfo->Attribute = 0;
- break;
- default:
- //
- // Other file types are not supported.
- //
- return EFI_UNSUPPORTED;
- }
- //
- // Report the regular file or directory as read-only if all classes lack
- // write permission.
- //
- if ((FuseAttr->Mode & (VIRTIO_FS_FUSE_MODE_PERM_WUSR |
- VIRTIO_FS_FUSE_MODE_PERM_WGRP |
- VIRTIO_FS_FUSE_MODE_PERM_WOTH)) == 0)
- {
- FileInfo->Attribute |= EFI_FILE_READ_ONLY;
- }
- //
- // A hard link count greater than 1 is not supported for regular files.
- //
- if (((FileInfo->Attribute & EFI_FILE_DIRECTORY) == 0) && (FuseAttr->Nlink > 1)) {
- return EFI_UNSUPPORTED;
- }
- return EFI_SUCCESS;
- }
- /**
- Convert a VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE filename to an EFI_FILE_INFO
- filename.
- @param[in] FuseDirent The VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE object to
- convert the filename byte array from. The caller is
- responsible for ensuring that FuseDirent->Namelen
- describe valid storage.
- @param[in,out] FileInfo The EFI_FILE_INFO structure to modify. On input, the
- caller is responsible for setting FileInfo->Size
- according to the allocated size. On successful
- return, FileInfo->Size is reduced to reflect the
- filename converted into FileInfo->FileName.
- FileInfo->FileName is set from the filename byte
- array that directly follows the FuseDirent header
- object. Fields other than FileInfo->Size and
- FileInfo->FileName are not modified.
- @retval EFI_SUCCESS Conversion successful.
- @retval EFI_INVALID_PARAMETER VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE()
- returns zero for FuseDirent->Namelen.
- @retval EFI_BUFFER_TOO_SMALL On input, FileInfo->Size does not provide
- enough room for converting the filename byte
- array from FuseDirent.
- @retval EFI_UNSUPPORTED The FuseDirent filename byte array contains a
- byte that falls outside of the printable ASCII
- range, or is a forward slash or a backslash.
- **/
- EFI_STATUS
- VirtioFsFuseDirentPlusToEfiFileInfo (
- IN VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE *FuseDirent,
- IN OUT EFI_FILE_INFO *FileInfo
- )
- {
- UINTN DirentSize;
- UINTN FileInfoSize;
- UINT8 *DirentName;
- UINT32 Idx;
- DirentSize = VIRTIO_FS_FUSE_DIRENTPLUS_RESPONSE_SIZE (FuseDirent->Namelen);
- if (DirentSize == 0) {
- return EFI_INVALID_PARAMETER;
- }
- //
- // We're now safe from overflow in the calculation below.
- //
- FileInfoSize = (OFFSET_OF (EFI_FILE_INFO, FileName) +
- ((UINTN)FuseDirent->Namelen + 1) * sizeof (CHAR16));
- if (FileInfoSize > FileInfo->Size) {
- return EFI_BUFFER_TOO_SMALL;
- }
- //
- // Convert the name.
- //
- DirentName = (UINT8 *)(FuseDirent + 1);
- for (Idx = 0; Idx < FuseDirent->Namelen; Idx++) {
- UINT8 NameByte;
- NameByte = DirentName[Idx];
- if ((NameByte < 0x20) || (NameByte > 0x7E) ||
- (NameByte == '/') || (NameByte == '\\'))
- {
- return EFI_UNSUPPORTED;
- }
- FileInfo->FileName[Idx] = (CHAR16)NameByte;
- }
- FileInfo->FileName[Idx++] = L'\0';
- //
- // Set the (possibly reduced) size.
- //
- FileInfo->Size = FileInfoSize;
- return EFI_SUCCESS;
- }
- /**
- Given an EFI_FILE_INFO object received in an EFI_FILE_PROTOCOL.SetInfo()
- call, determine whether updating the size of the file is necessary, relative
- to an EFI_FILE_INFO object describing the current state of the file.
- @param[in] Info The EFI_FILE_INFO describing the current state of the
- file. The caller is responsible for populating Info on
- input with VirtioFsFuseAttrToEfiFileInfo(), from the
- current FUSE attributes of the file. The Info->Size and
- Info->FileName members are ignored.
- @param[in] NewInfo The EFI_FILE_INFO object received in the
- EFI_FILE_PROTOCOL.SetInfo() call.
- @param[out] Update Set to TRUE on output if the file size needs to be
- updated. Set to FALSE otherwise.
- @param[out] Size If Update is set to TRUE, then Size provides the new file
- size to set. Otherwise, Size is not written to.
- **/
- VOID
- VirtioFsGetFuseSizeUpdate (
- IN EFI_FILE_INFO *Info,
- IN EFI_FILE_INFO *NewInfo,
- OUT BOOLEAN *Update,
- OUT UINT64 *Size
- )
- {
- BOOLEAN IsDirectory;
- IsDirectory = (BOOLEAN)((Info->Attribute & EFI_FILE_DIRECTORY) != 0);
- if (IsDirectory || (Info->FileSize == NewInfo->FileSize)) {
- *Update = FALSE;
- return;
- }
- *Update = TRUE;
- *Size = NewInfo->FileSize;
- }
- /**
- Given an EFI_FILE_INFO object received in an EFI_FILE_PROTOCOL.SetInfo()
- call, determine whether updating the last access time and/or the last
- modification time of the file is necessary, relative to an EFI_FILE_INFO
- object describing the current state of the file.
- @param[in] Info The EFI_FILE_INFO describing the current state of
- the file. The caller is responsible for populating
- Info on input with VirtioFsFuseAttrToEfiFileInfo(),
- from the current FUSE attributes of the file. The
- Info->Size and Info->FileName members are ignored.
- @param[in] NewInfo The EFI_FILE_INFO object received in the
- EFI_FILE_PROTOCOL.SetInfo() call.
- @param[out] UpdateAtime Set to TRUE on output if the last access time needs
- to be updated. Set to FALSE otherwise.
- @param[out] UpdateMtime Set to TRUE on output if the last modification time
- needs to be updated. Set to FALSE otherwise.
- @param[out] Atime If UpdateAtime is set to TRUE, then Atime provides
- the last access timestamp to set (as seconds since
- the Epoch). Otherwise, Atime is not written to.
- @param[out] Mtime If UpdateMtime is set to TRUE, then Mtime provides
- the last modification timestamp to set (as seconds
- since the Epoch). Otherwise, Mtime is not written
- to.
- @retval EFI_SUCCESS Output parameters have been set successfully.
- @retval EFI_INVALID_PARAMETER At least one of the CreateTime, LastAccessTime
- and ModificationTime fields in NewInfo
- represents an actual update relative to the
- current state of the file (expressed in Info),
- but does not satisfy the UEFI spec
- requirements on EFI_TIME.
- @retval EFI_ACCESS_DENIED NewInfo requests changing both CreateTime and
- ModificationTime, but to values that differ
- from each other. The Virtio Filesystem device
- does not support this.
- **/
- EFI_STATUS
- VirtioFsGetFuseTimeUpdates (
- IN EFI_FILE_INFO *Info,
- IN EFI_FILE_INFO *NewInfo,
- OUT BOOLEAN *UpdateAtime,
- OUT BOOLEAN *UpdateMtime,
- OUT UINT64 *Atime,
- OUT UINT64 *Mtime
- )
- {
- EFI_TIME *Time[3];
- EFI_TIME *NewTime[ARRAY_SIZE (Time)];
- UINTN Idx;
- STATIC CONST EFI_TIME ZeroTime = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- BOOLEAN Change[ARRAY_SIZE (Time)];
- UINT64 Seconds[ARRAY_SIZE (Time)];
- Time[0] = &Info->CreateTime;
- Time[1] = &Info->LastAccessTime;
- Time[2] = &Info->ModificationTime;
- NewTime[0] = &NewInfo->CreateTime;
- NewTime[1] = &NewInfo->LastAccessTime;
- NewTime[2] = &NewInfo->ModificationTime;
- //
- // Determine which timestamps differ from the current state. (A zero time
- // means "don't update", per UEFI spec.) For each timestamp that's being
- // changed, calculate the seconds since the Epoch.
- //
- for (Idx = 0; Idx < ARRAY_SIZE (Time); Idx++) {
- if ((CompareMem (NewTime[Idx], &ZeroTime, sizeof (EFI_TIME)) == 0) ||
- (CompareMem (NewTime[Idx], Time[Idx], sizeof (EFI_TIME)) == 0))
- {
- Change[Idx] = FALSE;
- } else {
- if (!IsTimeValid (NewTime[Idx])) {
- return EFI_INVALID_PARAMETER;
- }
- Change[Idx] = TRUE;
- Seconds[Idx] = EfiTimeToEpoch (NewTime[Idx]);
- }
- }
- //
- // If a change is requested for exactly one of CreateTime and
- // ModificationTime, we'll change the last modification time. If changes are
- // requested for both, and to the same timestamp, we'll similarly update the
- // last modification time. If changes are requested for both, but to
- // different timestamps, we reject the request.
- //
- if (Change[0] && Change[2] && (Seconds[0] != Seconds[2])) {
- return EFI_ACCESS_DENIED;
- }
- *UpdateAtime = FALSE;
- *UpdateMtime = FALSE;
- if (Change[0]) {
- *UpdateMtime = TRUE;
- *Mtime = Seconds[0];
- }
- if (Change[1]) {
- *UpdateAtime = TRUE;
- *Atime = Seconds[1];
- }
- if (Change[2]) {
- *UpdateMtime = TRUE;
- *Mtime = Seconds[2];
- }
- return EFI_SUCCESS;
- }
- /**
- Given an EFI_FILE_INFO object received in an EFI_FILE_PROTOCOL.SetInfo()
- call, determine whether updating the file mode bits of the file is necessary,
- relative to an EFI_FILE_INFO object describing the current state of the file.
- @param[in] Info The EFI_FILE_INFO describing the current state of the
- file. The caller is responsible for populating Info on
- input with VirtioFsFuseAttrToEfiFileInfo(), from the
- current FUSE attributes of the file. The Info->Size and
- Info->FileName members are ignored.
- @param[in] NewInfo The EFI_FILE_INFO object received in the
- EFI_FILE_PROTOCOL.SetInfo() call.
- @param[out] Update Set to TRUE on output if the file mode bits need to be
- updated. Set to FALSE otherwise.
- @param[out] Mode If Update is set to TRUE, then Mode provides the file
- mode bits to set. Otherwise, Mode is not written to.
- @retval EFI_SUCCESS Output parameters have been set successfully.
- @retval EFI_ACCESS_DENIED NewInfo requests toggling an unknown bit in the
- Attribute bitmask.
- @retval EFI_ACCESS_DENIED NewInfo requests toggling EFI_FILE_DIRECTORY in
- the Attribute bitmask.
- **/
- EFI_STATUS
- VirtioFsGetFuseModeUpdate (
- IN EFI_FILE_INFO *Info,
- IN EFI_FILE_INFO *NewInfo,
- OUT BOOLEAN *Update,
- OUT UINT32 *Mode
- )
- {
- UINT64 Toggle;
- BOOLEAN IsDirectory;
- BOOLEAN IsWriteable;
- BOOLEAN WillBeWriteable;
- Toggle = Info->Attribute ^ NewInfo->Attribute;
- if ((Toggle & ~EFI_FILE_VALID_ATTR) != 0) {
- //
- // Unknown attribute requested.
- //
- return EFI_ACCESS_DENIED;
- }
- if ((Toggle & EFI_FILE_DIRECTORY) != 0) {
- //
- // EFI_FILE_DIRECTORY cannot be toggled.
- //
- return EFI_ACCESS_DENIED;
- }
- IsDirectory = (BOOLEAN)((Info->Attribute & EFI_FILE_DIRECTORY) != 0);
- IsWriteable = (BOOLEAN)((Info->Attribute & EFI_FILE_READ_ONLY) == 0);
- WillBeWriteable = (BOOLEAN)((NewInfo->Attribute & EFI_FILE_READ_ONLY) == 0);
- if (IsWriteable == WillBeWriteable) {
- *Update = FALSE;
- return EFI_SUCCESS;
- }
- if (IsDirectory) {
- if (WillBeWriteable) {
- *Mode = (VIRTIO_FS_FUSE_MODE_PERM_RWXU |
- VIRTIO_FS_FUSE_MODE_PERM_RWXG |
- VIRTIO_FS_FUSE_MODE_PERM_RWXO);
- } else {
- *Mode = (VIRTIO_FS_FUSE_MODE_PERM_RUSR |
- VIRTIO_FS_FUSE_MODE_PERM_XUSR |
- VIRTIO_FS_FUSE_MODE_PERM_RGRP |
- VIRTIO_FS_FUSE_MODE_PERM_XGRP |
- VIRTIO_FS_FUSE_MODE_PERM_ROTH |
- VIRTIO_FS_FUSE_MODE_PERM_XOTH);
- }
- } else {
- if (WillBeWriteable) {
- *Mode = (VIRTIO_FS_FUSE_MODE_PERM_RUSR |
- VIRTIO_FS_FUSE_MODE_PERM_WUSR |
- VIRTIO_FS_FUSE_MODE_PERM_RGRP |
- VIRTIO_FS_FUSE_MODE_PERM_WGRP |
- VIRTIO_FS_FUSE_MODE_PERM_ROTH |
- VIRTIO_FS_FUSE_MODE_PERM_WOTH);
- } else {
- *Mode = (VIRTIO_FS_FUSE_MODE_PERM_RUSR |
- VIRTIO_FS_FUSE_MODE_PERM_RGRP |
- VIRTIO_FS_FUSE_MODE_PERM_ROTH);
- }
- }
- *Update = TRUE;
- return EFI_SUCCESS;
- }
|