Something went wrong. Try again.
Reactos
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS system libraries * FILE: lib/rtl/path.c * PURPOSE: Path and current directory functions * PROGRAMMERS: Wine team * Thomas Weidenmueller * Gunnar Dalsnes * Alex Ionescu (alex.ionescu@reactos.org) * Pierre Schweitzer (pierre@reactos.org) */
/* INCLUDES *******************************************************************/
#include <rtl.h>
#define NDEBUG#include <debug.h>
/* DEFINITONS and MACROS ******************************************************/
#define MAX_PFX_SIZE 16
#define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
#define RTL_CURDIR_IS_REMOVABLE 0x1#define RTL_CURDIR_DROP_OLD_HANDLE 0x2#define RTL_CURDIR_ALL_FLAGS (RTL_CURDIR_DROP_OLD_HANDLE | RTL_CURDIR_IS_REMOVABLE) // 0x3C_ASSERT(RTL_CURDIR_ALL_FLAGS == OBJ_HANDLE_TAGBITS);
/* GLOBALS ********************************************************************/
const UNICODE_STRING DeviceRootString = RTL_CONSTANT_STRING(L"\\\\.\\");
const UNICODE_STRING RtlpDosDevicesUncPrefix = RTL_CONSTANT_STRING(L"\\??\\UNC\\");const UNICODE_STRING RtlpWin32NtRootSlash = RTL_CONSTANT_STRING(L"\\\\?\\");const UNICODE_STRING RtlpDosSlashCONDevice = RTL_CONSTANT_STRING(L"\\\\.\\CON");const UNICODE_STRING RtlpDosDevicesPrefix = RTL_CONSTANT_STRING(L"\\??\\");
const UNICODE_STRING RtlpDosLPTDevice = RTL_CONSTANT_STRING(L"LPT");const UNICODE_STRING RtlpDosCOMDevice = RTL_CONSTANT_STRING(L"COM");const UNICODE_STRING RtlpDosPRNDevice = RTL_CONSTANT_STRING(L"PRN");const UNICODE_STRING RtlpDosAUXDevice = RTL_CONSTANT_STRING(L"AUX");const UNICODE_STRING RtlpDosCONDevice = RTL_CONSTANT_STRING(L"CON");const UNICODE_STRING RtlpDosNULDevice = RTL_CONSTANT_STRING(L"NUL");
const UNICODE_STRING RtlpDoubleSlashPrefix = RTL_CONSTANT_STRING(L"\\\\");
static const UNICODE_STRING RtlpDefaultExtension = RTL_CONSTANT_STRING(L".DLL");static const UNICODE_STRING RtlpDotLocal = RTL_CONSTANT_STRING(L".Local\\");static const UNICODE_STRING RtlpPathDividers = RTL_CONSTANT_STRING(L"\\/");
PRTLP_CURDIR_REF RtlpCurDirRef;
/* PRIVATE FUNCTIONS **********************************************************/
RTL_PATH_TYPENTAPIRtlDetermineDosPathNameType_Ustr(IN PCUNICODE_STRING PathString){ PWCHAR Path; ULONG Chars;
Path = PathString->Buffer; Chars = PathString->Length / sizeof(WCHAR);
/* Return if there are no characters */ if (!Chars) return RtlPathTypeRelative;
/* * The algorithm is similar to RtlDetermineDosPathNameType_U but here we * actually check for the path length before touching the characters */ if (IS_PATH_SEPARATOR(Path[0])) { if ((Chars < 2) || !(IS_PATH_SEPARATOR(Path[1]))) return RtlPathTypeRooted; /* \x */ if ((Chars < 3) || ((Path[2] != L'.') && (Path[2] != L'?'))) return RtlPathTypeUncAbsolute;/* \\x */ if ((Chars >= 4) && (IS_PATH_SEPARATOR(Path[3]))) return RtlPathTypeLocalDevice; /* \\.\x or \\?\x */ if (Chars != 3) return RtlPathTypeUncAbsolute; /* \\.x or \\?x */ return RtlPathTypeRootLocalDevice; /* \\. or \\? */ } else { if ((Chars < 2) || (Path[1] != L':')) return RtlPathTypeRelative; /* x */ if ((Chars < 3) || !(IS_PATH_SEPARATOR(Path[2]))) return RtlPathTypeDriveRelative; /* x: */ return RtlPathTypeDriveAbsolute; /* x:\ */ }}
ULONGNTAPIRtlIsDosDeviceName_Ustr(IN PCUNICODE_STRING PathString){ UNICODE_STRING PathCopy; PWCHAR Start, End; USHORT PathChars, ColonCount = 0; USHORT ReturnOffset = 0, ReturnLength, OriginalLength; WCHAR c;
/* Validate the input */ if (!PathString) return 0;
/* Check what type of path this is */ switch (RtlDetermineDosPathNameType_Ustr(PathString)) { /* Fail for UNC or unknown paths */ case RtlPathTypeUnknown: case RtlPathTypeUncAbsolute: return 0;
/* Make special check for the CON device */ case RtlPathTypeLocalDevice: if (RtlEqualUnicodeString(PathString, &RtlpDosSlashCONDevice, TRUE)) { /* This should return 0x80006 */ return MAKELONG(RtlpDosCONDevice.Length, DeviceRootString.Length); } return 0;
default: break; }
/* Make a copy of the string */ PathCopy = *PathString; OriginalLength = PathString->Length;
/* Return if there's no characters */ PathChars = PathCopy.Length / sizeof(WCHAR); if (!PathChars) return 0;
/* Check for drive path and truncate */ if (PathCopy.Buffer[PathChars - 1] == L':') { /* Fixup the lengths */ PathCopy.Length -= sizeof(WCHAR); if (!--PathChars) return 0;
/* Remember this for later */ ColonCount = 1; }
/* Check for extension or space, and truncate */ do { /* Stop if we hit something else than a space or period */ c = PathCopy.Buffer[PathChars - 1]; if ((c != L'.') && (c != L' ')) break;
/* Fixup the lengths */ PathCopy.Length -= sizeof(WCHAR);
/* Remember this for later */ ColonCount++; } while (--PathChars);
/* Anything still left? */ if (PathChars) { /* Loop from the end */ for (End = &PathCopy.Buffer[PathChars - 1]; End >= PathCopy.Buffer; --End) { /* Check if the character is a path or drive separator */ c = *End; if (IS_PATH_SEPARATOR(c) || ((c == L':') && (End == PathCopy.Buffer + 1))) { /* Get the next lower case character */ End++; c = RtlpDowncaseUnicodeChar(*End);
/* Check if it's a DOS device (LPT, COM, PRN, AUX, or NUL) */ if ((End < &PathCopy.Buffer[OriginalLength / sizeof(WCHAR)]) && ((c == L'l') || (c == L'c') || (c == L'p') || (c == L'a') || (c == L'n'))) { /* Calculate the offset */ ReturnOffset = (USHORT)((PCHAR)End - (PCHAR)PathCopy.Buffer);
/* Build the final string */ PathCopy.Length = OriginalLength - ReturnOffset - (ColonCount * sizeof(WCHAR)); PathCopy.Buffer = End;
/* Save new amount of chars in the path */ PathChars = PathCopy.Length / sizeof(WCHAR);
break; } else { return 0; } } }
/* Get the next lower case character and check if it's a DOS device */ c = RtlpDowncaseUnicodeChar(*PathCopy.Buffer); if ((c != L'l') && (c != L'c') && (c != L'p') && (c != L'a') && (c != L'n')) { /* Not LPT, COM, PRN, AUX, or NUL */ return 0; } }
/* Now skip past any extra extension or drive letter characters */ Start = PathCopy.Buffer; End = &Start[PathChars]; while (Start < End) { c = *Start; if ((c == L'.') || (c == L':')) break; Start++; }
/* And then go backwards to get rid of spaces */ while ((Start > PathCopy.Buffer) && (Start[-1] == L' ')) --Start;
/* Finally see how many characters are left, and that's our size */ PathChars = (USHORT)(Start - PathCopy.Buffer); PathCopy.Length = PathChars * sizeof(WCHAR);
/* Check if this is a COM or LPT port, which has a digit after it */ if ((PathChars == 4) && (iswdigit(PathCopy.Buffer[3]) && (PathCopy.Buffer[3] != L'0'))) { /* Don't compare the number part, just check for LPT or COM */ PathCopy.Length -= sizeof(WCHAR); if ((RtlEqualUnicodeString(&PathCopy, &RtlpDosLPTDevice, TRUE)) || (RtlEqualUnicodeString(&PathCopy, &RtlpDosCOMDevice, TRUE))) { /* Found it */ ReturnLength = sizeof(L"COM1") - sizeof(WCHAR); return MAKELONG(ReturnLength, ReturnOffset); } } else if ((PathChars == 3) && ((RtlEqualUnicodeString(&PathCopy, &RtlpDosPRNDevice, TRUE)) || (RtlEqualUnicodeString(&PathCopy, &RtlpDosAUXDevice, TRUE)) || (RtlEqualUnicodeString(&PathCopy, &RtlpDosNULDevice, TRUE)) || (RtlEqualUnicodeString(&PathCopy, &RtlpDosCONDevice, TRUE)))) { /* Otherwise this was something like AUX, NUL, PRN, or CON */ ReturnLength = sizeof(L"AUX") - sizeof(WCHAR); return MAKELONG(ReturnLength, ReturnOffset); }
/* Otherwise, this is not a valid DOS device */ return 0;}
NTSTATUSNTAPIRtlpCheckDeviceName(IN PUNICODE_STRING FileName, IN ULONG Length, OUT PBOOLEAN NameInvalid){ PWCHAR Buffer; NTSTATUS Status;
/* Allocate a large enough buffer */ Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, FileName->Length); if (Buffer) { /* Assume failure */ *NameInvalid = TRUE;
/* Copy the filename */ RtlCopyMemory(Buffer, FileName->Buffer, FileName->Length);
/* And add a dot at the end */ Buffer[Length / sizeof(WCHAR)] = L'.'; Buffer[(Length / sizeof(WCHAR)) + 1] = UNICODE_NULL;
/* Check if the file exists or not */ *NameInvalid = RtlDoesFileExists_U(Buffer) ? FALSE: TRUE;
/* Get rid of the buffer now */ Status = RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); } else { /* Assume the name is ok, but fail the call */ *NameInvalid = FALSE; Status = STATUS_NO_MEMORY; }
/* Return the status */ return Status;}
/****************************************************************** * RtlpCollapsePath (from WINE) * * Helper for RtlGetFullPathName_U * * 1) Converts slashes into backslashes and gets rid of duplicated ones; * 2) Gets rid of . and .. components in the path. * * Returns the full path length without its terminating NULL character. */static ULONGRtlpCollapsePath(PWSTR Path, /* ULONG PathBufferSize, ULONG PathLength, */ ULONG mark, BOOLEAN SkipTrailingPathSeparators){ PWSTR p, next;
// FIXME: Do not suppose NULL-terminated strings!!
SIZE_T PathLength = wcslen(Path); PWSTR EndBuffer = Path + PathLength; // Path + PathBufferSize / sizeof(WCHAR); PWSTR EndPath;
/* Convert slashes into backslashes */ for (p = Path; *p; p++) { if (*p == L'/') *p = L'\\'; }
/* Collapse duplicate backslashes */ next = Path + max( 1, mark ); for (p = next; *p; p++) { if (*p != L'\\' || next[-1] != L'\\') *next++ = *p; } *next = UNICODE_NULL; EndPath = next;
p = Path + mark; while (*p) { if (*p == L'.') { switch (p[1]) { case UNICODE_NULL: /* final . */ if (p > Path + mark) p--; *p = UNICODE_NULL; EndPath = p; continue;
case L'\\': /* .\ component */ next = p + 2; // ASSERT(EndPath - next == wcslen(next)); RtlMoveMemory(p, next, (EndPath - next + 1) * sizeof(WCHAR)); EndPath -= (next - p); continue;
case L'.': if (p[2] == L'\\') /* ..\ component */ { next = p + 3; if (p > Path + mark) { p--; while (p > Path + mark && p[-1] != L'\\') p--; } // ASSERT(EndPath - next == wcslen(next)); RtlMoveMemory(p, next, (EndPath - next + 1) * sizeof(WCHAR)); EndPath -= (next - p); continue; } else if (p[2] == UNICODE_NULL) /* final .. */ { if (p > Path + mark) { p--; while (p > Path + mark && p[-1] != L'\\') p--; if (p > Path + mark) p--; } *p = UNICODE_NULL; EndPath = p; continue; } break; } }
/* Skip to the next component */ while (*p && *p != L'\\') p++; if (*p == L'\\') { /* Remove last dot in previous dir name */ if (p > Path + mark && p[-1] == L'.') { // ASSERT(EndPath - p == wcslen(p)); RtlMoveMemory(p - 1, p, (EndPath - p + 1) * sizeof(WCHAR)); EndPath--; } else { p++; } } }
/* Remove trailing backslashes if needed (after the UNC part if it exists) */ if (SkipTrailingPathSeparators) { while (p > Path + mark && IS_PATH_SEPARATOR(p[-1])) p--; }
/* Remove trailing spaces and dots (for all the path) */ while (p > Path && (p[-1] == L' ' || p[-1] == L'.')) p--;
/* * Zero-out the discarded buffer zone, starting just after * the path string and going up to the end of the buffer. * It also NULL-terminate the path string. */ ASSERT(EndBuffer >= p); RtlZeroMemory(p, (EndBuffer - p + 1) * sizeof(WCHAR));
/* Return the real path length */ PathLength = (p - Path); // ASSERT(PathLength == wcslen(Path)); return (PathLength * sizeof(WCHAR));}
/****************************************************************** * RtlpSkipUNCPrefix (from WINE) * * Helper for RtlGetFullPathName_U * * Skips the \\share\dir part of a file name and returns the new position * (which can point on the last backslash of "dir\"). */static SIZE_TRtlpSkipUNCPrefix(PCWSTR FileNameBuffer){ PCWSTR UncPath = FileNameBuffer + 2; DPRINT("RtlpSkipUNCPrefix(%S)\n", FileNameBuffer);
while (*UncPath && !IS_PATH_SEPARATOR(*UncPath)) UncPath++; /* share name */ while (IS_PATH_SEPARATOR(*UncPath)) UncPath++; while (*UncPath && !IS_PATH_SEPARATOR(*UncPath)) UncPath++; /* dir name */ /* while (IS_PATH_SEPARATOR(*UncPath)) UncPath++; */
return (UncPath - FileNameBuffer);}
NTSTATUSNTAPIRtlpApplyLengthFunction(IN ULONG Flags, IN ULONG Type, IN PVOID UnicodeStringOrUnicodeStringBuffer, IN NTSTATUS(NTAPI* LengthFunction)(ULONG, PUNICODE_STRING, PULONG)){ NTSTATUS Status; PUNICODE_STRING String; ULONG Length;
if (Flags || UnicodeStringOrUnicodeStringBuffer == NULL || LengthFunction == NULL) { DPRINT1("ERROR: Flags=0x%x, UnicodeStringOrUnicodeStringBuffer=%p, LengthFunction=%p\n", Flags, UnicodeStringOrUnicodeStringBuffer, LengthFunction); return STATUS_INVALID_PARAMETER; }
if (Type == sizeof(UNICODE_STRING)) { String = (PUNICODE_STRING)UnicodeStringOrUnicodeStringBuffer; } else if (Type == sizeof(RTL_UNICODE_STRING_BUFFER)) { String = &((PRTL_UNICODE_STRING_BUFFER)UnicodeStringOrUnicodeStringBuffer)->String; } else { DPRINT1("ERROR: Type = %u\n", Type); return STATUS_INVALID_PARAMETER; }
Length = 0; Status = LengthFunction(0, String, &Length); if (!NT_SUCCESS(Status)) return Status;
if (Length > UNICODE_STRING_MAX_CHARS) return STATUS_NAME_TOO_LONG;
String->Length = (USHORT)(Length * sizeof(WCHAR));
if (Type == sizeof(RTL_UNICODE_STRING_BUFFER)) { String->Buffer[Length] = UNICODE_NULL; }
return STATUS_SUCCESS;}
NTSTATUSNTAPIRtlGetLengthWithoutLastFullDosOrNtPathElement(IN ULONG Flags, IN PCUNICODE_STRING Path, OUT PULONG LengthOut){ static const UNICODE_STRING PathDividers = RTL_CONSTANT_STRING(L"\\/"); USHORT Position; RTL_PATH_TYPE PathType;
/* All failure paths have this in common, so simplify code */ if (LengthOut) *LengthOut = 0;
if (Flags || !Path || !LengthOut) { return STATUS_INVALID_PARAMETER; }
if ((Path->Length / sizeof(WCHAR)) == 0) { /* Nothing to do here */ return STATUS_SUCCESS; }
PathType = RtlDetermineDosPathNameType_Ustr(Path); switch (PathType) { case RtlPathTypeLocalDevice: // Handle \\\\?\\C:\\ with the last ':' or '\\' missing: if (Path->Length / sizeof(WCHAR) < 7 || Path->Buffer[5] != ':' || !IS_PATH_SEPARATOR(Path->Buffer[6])) { return STATUS_INVALID_PARAMETER; } break; case RtlPathTypeRooted: // "\\??\\" break; case RtlPathTypeUncAbsolute: // "\\\\" break; case RtlPathTypeDriveAbsolute: // "C:\\" break; default: return STATUS_INVALID_PARAMETER; }
/* Find the last path separator */ if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END, Path, &PathDividers, &Position))) Position = 0;
/* Is it the last char of the string? */ if (Position && Position + sizeof(WCHAR) == Path->Length) { UNICODE_STRING Tmp = *Path; Tmp.Length = Position;
/* Keep walking path separators to eliminate multiple next to eachother */ while (Tmp.Length > sizeof(WCHAR) && IS_PATH_SEPARATOR(Tmp.Buffer[Tmp.Length / sizeof(WCHAR)])) Tmp.Length -= sizeof(WCHAR);
/* Find the previous occurence */ if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END, &Tmp, &PathDividers, &Position))) Position = 0; }
/* Simplify code by working in chars instead of bytes */ if (Position) Position /= sizeof(WCHAR);
if (Position) { // Keep walking path separators to eliminate multiple next to eachother, but ensure we leave one in place! while (Position > 1 && IS_PATH_SEPARATOR(Path->Buffer[Position - 1])) Position--; }
if (Position > 0) { /* Return a length, not an index */ *LengthOut = Position + 1; }
return STATUS_SUCCESS;}
NTSTATUSNTAPIRtlComputePrivatizedDllName_U( _In_ PUNICODE_STRING DllName, _Inout_ PUNICODE_STRING RealName, _Inout_ PUNICODE_STRING LocalName){ static const UNICODE_STRING ExtensionChar = RTL_CONSTANT_STRING(L".");
USHORT Position; UNICODE_STRING ImagePathName, DllNameOnly, CopyRealName, CopyLocalName; BOOLEAN HasExtension; ULONG RequiredSize; NTSTATUS Status; C_ASSERT(sizeof(UNICODE_NULL) == sizeof(WCHAR));
CopyRealName = *RealName; CopyLocalName = *LocalName;
/* Get the image path */ ImagePathName = RtlGetCurrentPeb()->ProcessParameters->ImagePathName;
/* Check if it's not normalized */ if (!(RtlGetCurrentPeb()->ProcessParameters->Flags & RTL_USER_PROCESS_PARAMETERS_NORMALIZED)) { /* Normalize it */ ImagePathName.Buffer = (PWSTR)((ULONG_PTR)ImagePathName.Buffer + (ULONG_PTR)RtlGetCurrentPeb()->ProcessParameters); }
if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END, DllName, &RtlpPathDividers, &Position))) { DllNameOnly = *DllName; } else { /* Just keep the dll name, ignore path components */ Position += sizeof(WCHAR); DllNameOnly.Buffer = DllName->Buffer + Position / sizeof(WCHAR); DllNameOnly.Length = DllName->Length - Position; DllNameOnly.MaximumLength = DllName->MaximumLength - Position; }
if (!NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END, &DllNameOnly, &ExtensionChar, &Position))) { Position = 0; }
HasExtension = Position > 1;
/* First we create the c:\path\processname.exe.Local\something.dll path */ RequiredSize = ImagePathName.Length + RtlpDotLocal.Length + DllNameOnly.Length + (HasExtension ? 0 : RtlpDefaultExtension.Length) + sizeof(UNICODE_NULL);
/* This is not going to work out */ if (RequiredSize > UNICODE_STRING_MAX_BYTES) return STATUS_NAME_TOO_LONG;
/* We need something extra */ if (RequiredSize > CopyLocalName.MaximumLength) { CopyLocalName.Buffer = RtlpAllocateStringMemory(RequiredSize, TAG_USTR); if (CopyLocalName.Buffer == NULL) return STATUS_NO_MEMORY; CopyLocalName.MaximumLength = RequiredSize; } /* Now build the entire path */ CopyLocalName.Length = 0; Status = RtlAppendUnicodeStringToString(&CopyLocalName, &ImagePathName); ASSERT(NT_SUCCESS(Status)); if (NT_SUCCESS(Status)) { Status = RtlAppendUnicodeStringToString(&CopyLocalName, &RtlpDotLocal); ASSERT(NT_SUCCESS(Status)); } if (NT_SUCCESS(Status)) { Status = RtlAppendUnicodeStringToString(&CopyLocalName, &DllNameOnly); ASSERT(NT_SUCCESS(Status)); } /* Do we need to append an extension? */ if (NT_SUCCESS(Status) && !HasExtension) { Status = RtlAppendUnicodeStringToString(&CopyLocalName, &RtlpDefaultExtension); ASSERT(NT_SUCCESS(Status)); }
if (NT_SUCCESS(Status)) { /* then we create the c:\path\something.dll path */ if (NT_SUCCESS(RtlFindCharInUnicodeString(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END, &ImagePathName, &RtlpPathDividers, &Position))) { ImagePathName.Length = Position + sizeof(WCHAR); }
RequiredSize = ImagePathName.Length + DllNameOnly.Length + (HasExtension ? 0 : RtlpDefaultExtension.Length) + sizeof(UNICODE_NULL);
if (RequiredSize >= UNICODE_STRING_MAX_BYTES) { Status = STATUS_NAME_TOO_LONG; } else { if (RequiredSize > CopyRealName.MaximumLength) { CopyRealName.Buffer = RtlpAllocateStringMemory(RequiredSize, TAG_USTR); if (CopyRealName.Buffer == NULL) Status = STATUS_NO_MEMORY; CopyRealName.MaximumLength = RequiredSize; } CopyRealName.Length = 0; if (NT_SUCCESS(Status)) { Status = RtlAppendUnicodeStringToString(&CopyRealName, &ImagePathName); ASSERT(NT_SUCCESS(Status)); } if (NT_SUCCESS(Status)) { Status = RtlAppendUnicodeStringToString(&CopyRealName, &DllNameOnly); ASSERT(NT_SUCCESS(Status)); } if (NT_SUCCESS(Status) && !HasExtension) { Status = RtlAppendUnicodeStringToString(&CopyRealName, &RtlpDefaultExtension); ASSERT(NT_SUCCESS(Status)); } } }
if (!NT_SUCCESS(Status)) { if (CopyRealName.Buffer && CopyRealName.Buffer != RealName->Buffer) RtlpFreeStringMemory(CopyRealName.Buffer, TAG_USTR); if (CopyLocalName.Buffer && CopyLocalName.Buffer != LocalName->Buffer) RtlpFreeStringMemory(CopyLocalName.Buffer, TAG_USTR); return Status; }
*RealName = CopyRealName; *LocalName = CopyLocalName; return STATUS_SUCCESS;}
ULONGNTAPIRtlGetFullPathName_Ustr( _In_ PUNICODE_STRING FileName, _In_ ULONG Size, _Out_z_bytecap_(Size) PWSTR Buffer, _Out_opt_ PCWSTR *ShortName, _Out_opt_ PBOOLEAN InvalidName, _Out_ RTL_PATH_TYPE *PathType){ NTSTATUS Status; PWCHAR FileNameBuffer; ULONG FileNameLength, FileNameChars, DosLength, DosLengthOffset, FullLength; BOOLEAN SkipTrailingPathSeparators; WCHAR c;
ULONG reqsize = 0; PCWSTR ptr;
PCUNICODE_STRING CurDirName; UNICODE_STRING EnvVarName, EnvVarValue; WCHAR EnvVarNameBuffer[4];
ULONG PrefixCut = 0; // Where the path really starts (after the skipped prefix) PWCHAR Prefix = NULL; // pointer to the string to be inserted as the new path prefix ULONG PrefixLength = 0; PWCHAR Source; ULONG SourceLength;
/* For now, assume the name is valid */ DPRINT("Filename: %wZ\n", FileName); DPRINT("Size and buffer: %lx %p\n", Size, Buffer); if (InvalidName) *InvalidName = FALSE;
/* Handle initial path type and failure case */ *PathType = RtlPathTypeUnknown; if ((FileName->Length == 0) || (FileName->Buffer[0] == UNICODE_NULL)) return 0;
/* Break filename into component parts */ FileNameBuffer = FileName->Buffer; FileNameLength = FileName->Length; FileNameChars = FileNameLength / sizeof(WCHAR);
/* Kill trailing spaces */ c = FileNameBuffer[FileNameChars - 1]; while ((FileNameLength != 0) && (c == L' ')) { /* Keep going, ignoring the spaces */ FileNameLength -= sizeof(WCHAR); if (FileNameLength != 0) c = FileNameBuffer[FileNameLength / sizeof(WCHAR) - 1]; }
/* Check if anything is left */ if (FileNameLength == 0) return 0;
/* * Check whether we'll need to skip trailing path separators in the * computed full path name. If the original file name already contained * trailing separators, then we keep them in the full path name. On the * other hand, if the original name didn't contain any trailing separators * then we'll skip it in the full path name. */ SkipTrailingPathSeparators = !IS_PATH_SEPARATOR(FileNameBuffer[FileNameChars - 1]);
/* Check if this is a DOS name */ DosLength = RtlIsDosDeviceName_Ustr(FileName); DPRINT("DOS length for filename: %lx %wZ\n", DosLength, FileName); if (DosLength != 0) { /* Zero out the short name */ if (ShortName) *ShortName = NULL;
/* See comment for RtlIsDosDeviceName_Ustr if this is confusing... */ DosLengthOffset = HIWORD(DosLength); DosLength = LOWORD(DosLength);
/* Do we have a DOS length, and does the caller want validity? */ if (InvalidName && (DosLengthOffset != 0)) { /* Do the check */ Status = RtlpCheckDeviceName(FileName, DosLengthOffset, InvalidName);
/* If the check failed, or the name is invalid, fail here */ if (!NT_SUCCESS(Status)) return 0; if (*InvalidName) return 0; }
/* Add the size of the device root and check if it fits in the size */ FullLength = DosLength + DeviceRootString.Length; if (FullLength < Size) { /* Add the device string */ RtlMoveMemory(Buffer, DeviceRootString.Buffer, DeviceRootString.Length);
/* Now add the DOS device name */ RtlMoveMemory((PCHAR)Buffer + DeviceRootString.Length, (PCHAR)FileNameBuffer + DosLengthOffset, DosLength);
/* Null terminate */ *(PWCHAR)((ULONG_PTR)Buffer + FullLength) = UNICODE_NULL; return FullLength; }
/* Otherwise, there's no space, so return the buffer size needed */ if ((FullLength + sizeof(UNICODE_NULL)) > UNICODE_STRING_MAX_BYTES) return 0; return FullLength + sizeof(UNICODE_NULL); }
/* Zero-out the destination buffer. FileName must be different from Buffer */ RtlZeroMemory(Buffer, Size);
/* Get the path type */ *PathType = RtlDetermineDosPathNameType_U(FileNameBuffer);
/********************************************** ** CODE REWRITING IS HAPPENING THERE ** **********************************************/ Source = FileNameBuffer; SourceLength = FileNameLength; EnvVarValue.Buffer = NULL;
/* Lock the PEB to get the current directory */ RtlAcquirePebLock(); CurDirName = &NtCurrentPeb()->ProcessParameters->CurrentDirectory.DosPath;
switch (*PathType) { case RtlPathTypeUncAbsolute: /* \\foo */ { PrefixCut = RtlpSkipUNCPrefix(FileNameBuffer); break; }
case RtlPathTypeLocalDevice: /* \\.\foo */ { PrefixCut = 4; break; }
case RtlPathTypeDriveAbsolute: /* c:\foo */ { ASSERT(FileNameBuffer[1] == L':'); ASSERT(IS_PATH_SEPARATOR(FileNameBuffer[2]));
// FileNameBuffer[0] = RtlpUpcaseUnicodeChar(FileNameBuffer[0]); Prefix = FileNameBuffer; PrefixLength = 3 * sizeof(WCHAR); Source += 3; SourceLength -= 3 * sizeof(WCHAR);
PrefixCut = 3; break; }
case RtlPathTypeDriveRelative: /* c:foo */ { WCHAR CurDrive, NewDrive;
Source += 2; SourceLength -= 2 * sizeof(WCHAR);
CurDrive = RtlpUpcaseUnicodeChar(CurDirName->Buffer[0]); NewDrive = RtlpUpcaseUnicodeChar(FileNameBuffer[0]);
if ((NewDrive != CurDrive) || CurDirName->Buffer[1] != L':') { EnvVarNameBuffer[0] = L'='; EnvVarNameBuffer[1] = NewDrive; EnvVarNameBuffer[2] = L':'; EnvVarNameBuffer[3] = UNICODE_NULL;
EnvVarName.Length = 3 * sizeof(WCHAR); EnvVarName.MaximumLength = EnvVarName.Length + sizeof(WCHAR); EnvVarName.Buffer = EnvVarNameBuffer;
// FIXME: Is it possible to use the user-given buffer ? // RtlInitEmptyUnicodeString(&EnvVarValue, NULL, Size); EnvVarValue.Length = 0; EnvVarValue.MaximumLength = (USHORT)Size; EnvVarValue.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Size); if (EnvVarValue.Buffer == NULL) { Prefix = NULL; PrefixLength = 0; goto Quit; }
Status = RtlQueryEnvironmentVariable_U(NULL, &EnvVarName, &EnvVarValue); switch (Status) { case STATUS_SUCCESS: /* * (From Wine) * FIXME: Win2k seems to check that the environment * variable actually points to an existing directory. * If not, root of the drive is used (this seems also * to be the only place in RtlGetFullPathName that the * existence of a part of a path is checked). */ EnvVarValue.Buffer[EnvVarValue.Length / sizeof(WCHAR)] = L'\\'; Prefix = EnvVarValue.Buffer; PrefixLength = EnvVarValue.Length + sizeof(WCHAR); /* Append trailing '\\' */ break;
case STATUS_BUFFER_TOO_SMALL: reqsize = EnvVarValue.Length + SourceLength + sizeof(UNICODE_NULL); goto Quit;
default: DPRINT1("RtlQueryEnvironmentVariable_U(\"%wZ\") returned 0x%08lx\n", &EnvVarName, Status);
EnvVarNameBuffer[0] = NewDrive; EnvVarNameBuffer[1] = L':'; EnvVarNameBuffer[2] = L'\\'; EnvVarNameBuffer[3] = UNICODE_NULL; Prefix = EnvVarNameBuffer; PrefixLength = 3 * sizeof(WCHAR);
RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue.Buffer); EnvVarValue.Buffer = NULL; break; } PrefixCut = 3; break; } /* Fall through */ DPRINT("RtlPathTypeDriveRelative - Using fall-through to RtlPathTypeRelative\n"); }
case RtlPathTypeRelative: /* foo */ { Prefix = CurDirName->Buffer; PrefixLength = CurDirName->Length; if (CurDirName->Buffer[1] != L':') { PrefixCut = RtlpSkipUNCPrefix(CurDirName->Buffer); } else { PrefixCut = 3; } break; }
case RtlPathTypeRooted: /* \xxx */ { if (CurDirName->Buffer[1] == L':') { // The path starts with "C:\" ASSERT(CurDirName->Buffer[1] == L':'); ASSERT(IS_PATH_SEPARATOR(CurDirName->Buffer[2]));
Prefix = CurDirName->Buffer; PrefixLength = 3 * sizeof(WCHAR); // Skip "C:\"
PrefixCut = 3; // Source index location incremented of + 3 } else { PrefixCut = RtlpSkipUNCPrefix(CurDirName->Buffer); PrefixLength = PrefixCut * sizeof(WCHAR); Prefix = CurDirName->Buffer; } break; }
case RtlPathTypeRootLocalDevice: /* \\. */ { Prefix = DeviceRootString.Buffer; PrefixLength = DeviceRootString.Length; Source += 3; SourceLength -= 3 * sizeof(WCHAR);
PrefixCut = 4; break; }
case RtlPathTypeUnknown: goto Quit; }
/* Do we have enough space for storing the full path? */ reqsize = PrefixLength; if (reqsize + SourceLength + sizeof(WCHAR) > Size) { /* Not enough space, return needed size (including terminating '\0') */ reqsize += SourceLength + sizeof(WCHAR); goto Quit; }
/* * Build the full path */ /* Copy the path's prefix */ if (PrefixLength) RtlMoveMemory(Buffer, Prefix, PrefixLength); /* Copy the remaining part of the path */ RtlMoveMemory(Buffer + PrefixLength / sizeof(WCHAR), Source, SourceLength + sizeof(WCHAR));
/* Some cleanup */ Prefix = NULL; if (EnvVarValue.Buffer) { RtlFreeHeap(RtlGetProcessHeap(), 0, EnvVarValue.Buffer); EnvVarValue.Buffer = NULL; }
/* * Finally, put the path in canonical form (remove redundant . and .., * (back)slashes...) and retrieve the length of the full path name * (without its terminating null character) (in chars). */ reqsize = RtlpCollapsePath(Buffer, /* Size, reqsize, */ PrefixCut, SkipTrailingPathSeparators);
/* Find the file part, which is present after the last path separator */ if (ShortName) { ptr = wcsrchr(Buffer, L'\\'); if (ptr) ++ptr; // Skip it
/* * For UNC paths, the file part is after the \\share\dir part of the path. */ PrefixCut = (*PathType == RtlPathTypeUncAbsolute ? PrefixCut : 3);
if (ptr && *ptr && (ptr >= Buffer + PrefixCut)) { *ShortName = ptr; } else { /* Zero-out the short name */ *ShortName = NULL; } }
Quit: /* Release PEB lock */ RtlReleasePebLock();
return reqsize;}
NTSTATUSNTAPIRtlpWin32NTNameToNtPathName_U(IN PUNICODE_STRING DosPath, OUT PUNICODE_STRING NtPath, OUT PCWSTR *PartName, OUT PRTL_RELATIVE_NAME_U RelativeName){ ULONG DosLength; PWSTR NewBuffer, p;
/* Validate the input */ if (!DosPath) return STATUS_OBJECT_NAME_INVALID;
/* Validate the DOS length */ DosLength = DosPath->Length; if (DosLength >= UNICODE_STRING_MAX_BYTES) return STATUS_NAME_TOO_LONG;
/* Make space for the new path */ NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, DosLength + sizeof(UNICODE_NULL)); if (!NewBuffer) return STATUS_NO_MEMORY;
/* Copy the prefix, and then the rest of the DOS path, and NULL-terminate */ RtlCopyMemory(NewBuffer, RtlpDosDevicesPrefix.Buffer, RtlpDosDevicesPrefix.Length); RtlCopyMemory((PCHAR)NewBuffer + RtlpDosDevicesPrefix.Length, DosPath->Buffer + RtlpDosDevicesPrefix.Length / sizeof(WCHAR), DosPath->Length - RtlpDosDevicesPrefix.Length); NewBuffer[DosLength / sizeof(WCHAR)] = UNICODE_NULL;
/* Did the caller send a relative name? */ if (RelativeName) { /* Zero initialize it */ RtlInitEmptyUnicodeString(&RelativeName->RelativeName, NULL, 0); RelativeName->ContainingDirectory = NULL; RelativeName->CurDirRef = 0; }
/* Did the caller request a partial name? */ if (PartName) { /* Loop from the back until we find a path separator */ p = &NewBuffer[DosLength / sizeof(WCHAR)]; while (--p > NewBuffer) { /* We found a path separator, move past it */ if (*p == OBJ_NAME_PATH_SEPARATOR) { ++p; break; } }
/* Check whether a separator was found and if something remains */ if ((p > NewBuffer) && *p) { /* What follows the path separator is the partial name */ *PartName = p; } else { /* The path ends with a path separator, no partial name */ *PartName = NULL; } }
/* Build the final NT path string */ NtPath->Buffer = NewBuffer; NtPath->Length = (USHORT)DosLength; NtPath->MaximumLength = (USHORT)DosLength + sizeof(UNICODE_NULL); return STATUS_SUCCESS;}
NTSTATUSNTAPIRtlpDosPathNameToRelativeNtPathName_Ustr(IN BOOLEAN HaveRelative, IN PCUNICODE_STRING DosName, OUT PUNICODE_STRING NtName, OUT PCWSTR *PartName, OUT PRTL_RELATIVE_NAME_U RelativeName){ WCHAR BigBuffer[MAX_PATH + 1]; PWCHAR PrefixBuffer, NewBuffer, Buffer; ULONG MaxLength, PathLength, PrefixLength, PrefixCut, LengthChars, Length; UNICODE_STRING CapturedDosName, PartNameString, FullPath; BOOLEAN QuickPath; RTL_PATH_TYPE InputPathType, BufferPathType; NTSTATUS Status; BOOLEAN NameInvalid; PCURDIR CurrentDirectory;
/* Assume MAX_PATH for now */ DPRINT("Relative: %lx DosName: %wZ NtName: %p, PartName: %p, RelativeName: %p\n", HaveRelative, DosName, NtName, PartName, RelativeName); MaxLength = sizeof(BigBuffer);
/* Validate the input */ if (!DosName) return STATUS_OBJECT_NAME_INVALID;
/* Capture input string */ CapturedDosName = *DosName;
/* Check for the presence or absence of the NT prefix "\\?\" form */ // if (!RtlPrefixUnicodeString(&RtlpWin32NtRootSlash, &CapturedDosName, FALSE)) if ((CapturedDosName.Length <= RtlpWin32NtRootSlash.Length) || (CapturedDosName.Buffer[0] != RtlpWin32NtRootSlash.Buffer[0]) || (CapturedDosName.Buffer[1] != RtlpWin32NtRootSlash.Buffer[1]) || (CapturedDosName.Buffer[2] != RtlpWin32NtRootSlash.Buffer[2]) || (CapturedDosName.Buffer[3] != RtlpWin32NtRootSlash.Buffer[3])) { /* NT prefix not present */
/* Quick path won't be used */ QuickPath = FALSE;
/* Use the static buffer */ Buffer = BigBuffer; MaxLength += RtlpDosDevicesUncPrefix.Length;
/* Allocate a buffer to hold the path */ NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, MaxLength); DPRINT("MaxLength: %lx\n", MaxLength); if (!NewBuffer) return STATUS_NO_MEMORY; } else { /* NT prefix present */
/* Use the optimized path after acquiring the lock */ QuickPath = TRUE; NewBuffer = NULL; }
/* Lock the PEB and check if the quick path can be used */ RtlAcquirePebLock(); if (QuickPath) { /* Some simple fixups will get us the correct path */ DPRINT("Quick path\n"); Status = RtlpWin32NTNameToNtPathName_U(&CapturedDosName, NtName, PartName, RelativeName);
/* Release the lock, we're done here */ RtlReleasePebLock(); return Status; }
/* Call the main function to get the full path name and length */ PathLength = RtlGetFullPathName_Ustr(&CapturedDosName, MAX_PATH * sizeof(WCHAR), Buffer, PartName, &NameInvalid, &InputPathType); if ((NameInvalid) || !(PathLength) || (PathLength > (MAX_PATH * sizeof(WCHAR)))) { /* Invalid name, fail */ DPRINT("Invalid name: %lx Path Length: %lx\n", NameInvalid, PathLength); RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer); RtlReleasePebLock(); return STATUS_OBJECT_NAME_INVALID; }
/* Start by assuming the path starts with \??\ (DOS Devices Path) */ PrefixLength = RtlpDosDevicesPrefix.Length; PrefixBuffer = RtlpDosDevicesPrefix.Buffer; PrefixCut = 0;
/* Check where it really is */ BufferPathType = RtlDetermineDosPathNameType_U(Buffer); DPRINT("Buffer: %S Type: %lx\n", Buffer, BufferPathType); switch (BufferPathType) { /* It's actually a UNC path in \??\UNC\ */ case RtlPathTypeUncAbsolute: PrefixLength = RtlpDosDevicesUncPrefix.Length; PrefixBuffer = RtlpDosDevicesUncPrefix.Buffer; PrefixCut = 2; break;
case RtlPathTypeLocalDevice: /* We made a good guess, go with it but skip the \??\ */ PrefixCut = 4; break;
case RtlPathTypeDriveAbsolute: case RtlPathTypeDriveRelative: case RtlPathTypeRooted: case RtlPathTypeRelative: /* Our guess was good, roll with it */ break;
/* Nothing else is expected */ default: ASSERT(FALSE); }
/* Now copy the prefix and the buffer */ RtlCopyMemory(NewBuffer, PrefixBuffer, PrefixLength); RtlCopyMemory((PCHAR)NewBuffer + PrefixLength, Buffer + PrefixCut, PathLength - (PrefixCut * sizeof(WCHAR)));
/* Compute the length */ Length = PathLength + PrefixLength - PrefixCut * sizeof(WCHAR); LengthChars = Length / sizeof(WCHAR);
/* Setup the actual NT path string and terminate it */ NtName->Buffer = NewBuffer; NtName->Length = (USHORT)Length; NtName->MaximumLength = (USHORT)MaxLength; NewBuffer[LengthChars] = UNICODE_NULL; DPRINT("New buffer: %S\n", NewBuffer); DPRINT("NT Name: %wZ\n", NtName);
/* Check if a partial name was requested */ if ((PartName) && (*PartName)) { /* Convert to Unicode */ Status = RtlInitUnicodeStringEx(&PartNameString, *PartName); if (NT_SUCCESS(Status)) { /* Set the partial name */ *PartName = &NewBuffer[LengthChars - (PartNameString.Length / sizeof(WCHAR))]; } else { /* Fail */ RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer); RtlReleasePebLock(); return Status; } }
/* Check if a relative name was asked for */ if (RelativeName) { /* Setup the structure */ RtlInitEmptyUnicodeString(&RelativeName->RelativeName, NULL, 0); RelativeName->ContainingDirectory = NULL; RelativeName->CurDirRef = NULL;
/* Check if the input path itself was relative */ if (InputPathType == RtlPathTypeRelative) { /* Get current directory */ CurrentDirectory = &(NtCurrentPeb()->ProcessParameters->CurrentDirectory); if (CurrentDirectory->Handle) { Status = RtlInitUnicodeStringEx(&FullPath, Buffer); if (!NT_SUCCESS(Status)) { RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer); RtlReleasePebLock(); return Status; }
/* If current directory is bigger than full path, there's no way */ if (CurrentDirectory->DosPath.Length > FullPath.Length) { RtlReleasePebLock(); return Status; }
/* File is in current directory */ if (RtlEqualUnicodeString(&FullPath, &CurrentDirectory->DosPath, TRUE)) { /* Make relative name string */ RelativeName->RelativeName.Buffer = (PWSTR)((ULONG_PTR)NewBuffer + PrefixLength + FullPath.Length - PrefixCut * sizeof(WCHAR)); RelativeName->RelativeName.Length = (USHORT)(PathLength - FullPath.Length); /* If relative name starts with \, skip it */ if (RelativeName->RelativeName.Buffer[0] == OBJ_NAME_PATH_SEPARATOR) { RelativeName->RelativeName.Buffer++; RelativeName->RelativeName.Length -= sizeof(WCHAR); } RelativeName->RelativeName.MaximumLength = RelativeName->RelativeName.Length; DPRINT("RelativeName: %wZ\n", &(RelativeName->RelativeName));
if (!HaveRelative) { RelativeName->ContainingDirectory = CurrentDirectory->Handle; return Status; }
/* Give back current directory data & reference counter */ RelativeName->CurDirRef = RtlpCurDirRef; if (RelativeName->CurDirRef) { InterlockedIncrement(&RtlpCurDirRef->RefCount); }
RelativeName->ContainingDirectory = CurrentDirectory->Handle; } } } }
/* Done */ RtlReleasePebLock(); return STATUS_SUCCESS;}
NTSTATUSNTAPIRtlpDosPathNameToRelativeNtPathName_U(IN BOOLEAN HaveRelative, IN PCWSTR DosName, OUT PUNICODE_STRING NtName, OUT PCWSTR *PartName, OUT PRTL_RELATIVE_NAME_U RelativeName){ NTSTATUS Status; UNICODE_STRING NameString;
/* Create the unicode name */ Status = RtlInitUnicodeStringEx(&NameString, DosName); if (NT_SUCCESS(Status)) { /* Call the unicode function */ Status = RtlpDosPathNameToRelativeNtPathName_Ustr(HaveRelative, &NameString, NtName, PartName, RelativeName); }
/* Return status */ return Status;}
BOOLEANNTAPIRtlDosPathNameToRelativeNtPathName_Ustr(IN PCUNICODE_STRING DosName, OUT PUNICODE_STRING NtName, OUT PCWSTR *PartName, OUT PRTL_RELATIVE_NAME_U RelativeName){ /* Call the internal function */ ASSERT(RelativeName); return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_Ustr(TRUE, DosName, NtName, PartName, RelativeName));}
BOOLEANNTAPIRtlDoesFileExists_UstrEx(IN PCUNICODE_STRING FileName, IN BOOLEAN SucceedIfBusy){ BOOLEAN Result; RTL_RELATIVE_NAME_U RelativeName; UNICODE_STRING NtPathName; PVOID Buffer; OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS Status; FILE_BASIC_INFORMATION BasicInformation;
/* Get the NT Path */ Result = RtlDosPathNameToRelativeNtPathName_Ustr(FileName, &NtPathName, NULL, &RelativeName); if (!Result) return FALSE;
/* Save the buffer */ Buffer = NtPathName.Buffer;
/* Check if we have a relative name */ if (RelativeName.RelativeName.Length) { /* Use it */ NtPathName = RelativeName.RelativeName; } else { /* Otherwise ignore it */ RelativeName.ContainingDirectory = NULL; }
/* Initialize the object attributes */ InitializeObjectAttributes(&ObjectAttributes, &NtPathName, OBJ_CASE_INSENSITIVE, RelativeName.ContainingDirectory, NULL);
/* Query the attributes and free the buffer now */ Status = ZwQueryAttributesFile(&ObjectAttributes, &BasicInformation); RtlReleaseRelativeName(&RelativeName); RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
/* Check if we failed */ if (!NT_SUCCESS(Status)) { /* Check if we failed because the file is in use */ if ((Status == STATUS_SHARING_VIOLATION) || (Status == STATUS_ACCESS_DENIED)) { /* Check if the caller wants this to be considered OK */ Result = SucceedIfBusy ? TRUE : FALSE; } else { /* A failure because the file didn't exist */ Result = FALSE; } } else { /* The file exists */ Result = TRUE; }
/* Return the result */ return Result;}
BOOLEANNTAPIRtlDoesFileExists_UStr(IN PUNICODE_STRING FileName){ /* Call the updated API */ return RtlDoesFileExists_UstrEx(FileName, TRUE);}
BOOLEANNTAPIRtlDoesFileExists_UEx(IN PCWSTR FileName, IN BOOLEAN SucceedIfBusy){ UNICODE_STRING NameString;
/* Create the unicode name*/ if (NT_SUCCESS(RtlInitUnicodeStringEx(&NameString, FileName))) { /* Call the unicode function */ return RtlDoesFileExists_UstrEx(&NameString, SucceedIfBusy); }
/* Fail */ return FALSE;}
/* PUBLIC FUNCTIONS ***********************************************************/
/* * @implemented */VOIDNTAPIRtlReleaseRelativeName(IN PRTL_RELATIVE_NAME_U RelativeName){ /* Check if a directory reference was grabbed */ if (RelativeName->CurDirRef) { /* Decrease reference count */ if (!InterlockedDecrement(&RelativeName->CurDirRef->RefCount)) { /* If no one uses it any longer, close handle & free */ NtClose(RelativeName->CurDirRef->Handle); RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeName->CurDirRef); } RelativeName->CurDirRef = NULL; }}
/* * @implemented */ULONGNTAPIRtlGetLongestNtPathLength(VOID){ /* * The longest NT path is a DOS path that actually sits on a UNC path (ie: * a mapped network drive), which is accessed through the DOS Global?? path. * This is, and has always been equal to, 269 characters, except in Wine * which claims this is 277. Go figure. */ return MAX_PATH + RtlpDosDevicesUncPrefix.Length / sizeof(WCHAR) + sizeof(ANSI_NULL);}
/* * @implemented * @note: the export is called RtlGetLengthWithoutTrailingPathSeperators * (with a 'e' instead of a 'a' in "Seperators"). */NTSTATUSNTAPIRtlGetLengthWithoutTrailingPathSeparators(IN ULONG Flags, IN PCUNICODE_STRING PathString, OUT PULONG Length){ ULONG NumChars;
/* Parameters validation */ if (Length == NULL) return STATUS_INVALID_PARAMETER;
*Length = 0;
if (PathString == NULL) return STATUS_INVALID_PARAMETER;
/* No flags are supported yet */ if (Flags != 0) return STATUS_INVALID_PARAMETER;
NumChars = PathString->Length / sizeof(WCHAR);
/* * Notice that we skip the last character, therefore: * - if we have: "some/path/f" we test for: "some/path/" * - if we have: "some/path/" we test for: "some/path" * - if we have: "s" we test for: "" * - if we have: "" then NumChars was already zero and we aren't there */
while (NumChars > 0 && IS_PATH_SEPARATOR(PathString->Buffer[NumChars - 1])) { --NumChars; }
*Length = NumChars; return STATUS_SUCCESS;}
/* * @implemented */RTL_PATH_TYPENTAPIRtlDetermineDosPathNameType_U(IN PCWSTR Path){ DPRINT("RtlDetermineDosPathNameType_U %S\n", Path);
/* Unlike the newer RtlDetermineDosPathNameType_U we assume 4 characters */ if (IS_PATH_SEPARATOR(Path[0])) { if (!IS_PATH_SEPARATOR(Path[1])) return RtlPathTypeRooted; /* \x */ if ((Path[2] != L'.') && (Path[2] != L'?')) return RtlPathTypeUncAbsolute;/* \\x */ if (IS_PATH_SEPARATOR(Path[3])) return RtlPathTypeLocalDevice; /* \\.\x or \\?\x */ if (Path[3]) return RtlPathTypeUncAbsolute; /* \\.x or \\?x */ return RtlPathTypeRootLocalDevice; /* \\. or \\? */ } else { if (!(Path[0]) || (Path[1] != L':')) return RtlPathTypeRelative; /* x */ if (IS_PATH_SEPARATOR(Path[2])) return RtlPathTypeDriveAbsolute; /* x:\ */ return RtlPathTypeDriveRelative; /* x: */ }}
/* * @implemented */ULONGNTAPIRtlIsDosDeviceName_U(IN PCWSTR Path){ UNICODE_STRING PathString; NTSTATUS Status;
/* Build the string */ Status = RtlInitUnicodeStringEx(&PathString, Path); if (!NT_SUCCESS(Status)) return 0;
/* * Returns 0 if name is not valid DOS device name, or DWORD with * offset in bytes to DOS device name from beginning of buffer in high word * and size in bytes of DOS device name in low word */ return RtlIsDosDeviceName_Ustr(&PathString);}
/* * @implemented */ULONGNTAPIRtlGetCurrentDirectory_U( _In_ ULONG MaximumLength, _Out_bytecap_(MaximumLength) PWSTR Buffer){ ULONG Length, Bytes; PCURDIR CurDir; PWSTR CurDirName; DPRINT("RtlGetCurrentDirectory %lu %p\n", MaximumLength, Buffer);
/* Lock the PEB to get the current directory */ RtlAcquirePebLock(); CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory;
/* Get the buffer and character length */ CurDirName = CurDir->DosPath.Buffer; Length = CurDir->DosPath.Length / sizeof(WCHAR); ASSERT((CurDirName != NULL) && (Length > 0));
/* * DosPath.Buffer should always have a trailing slash. There is an assert * below which checks for this. * * This function either returns x:\ for a root (keeping the original buffer) * or it returns x:\path\foo for a directory (replacing the trailing slash * with a NULL. */ Bytes = Length * sizeof(WCHAR); if ((Length <= 1) || (CurDirName[Length - 2] == L':')) { /* Check if caller does not have enough space */ if (MaximumLength <= Bytes) { /* Call has no space for it, fail, add the trailing slash */ RtlReleasePebLock(); return Bytes + sizeof(OBJ_NAME_PATH_SEPARATOR); } } else { /* Check if caller does not have enough space */ if (MaximumLength < Bytes) { /* Call has no space for it, fail */ RtlReleasePebLock(); return Bytes; } }
/* Copy the buffer since we seem to have space */ RtlCopyMemory(Buffer, CurDirName, Bytes);
/* The buffer should end with a path separator */ ASSERT(Buffer[Length - 1] == OBJ_NAME_PATH_SEPARATOR);
/* Again check for our two cases (drive root vs path) */ if ((Length <= 1) || (Buffer[Length - 2] != L':')) { /* Replace the trailing slash with a null */ Buffer[Length - 1] = UNICODE_NULL; --Length; } else { /* Append the null char since there's no trailing slash */ Buffer[Length] = UNICODE_NULL; }
/* Release PEB lock */ RtlReleasePebLock(); DPRINT("CurrentDirectory %S\n", Buffer); return Length * sizeof(WCHAR);}
/* * @implemented */NTSTATUSNTAPIRtlSetCurrentDirectory_U(IN PUNICODE_STRING Path){ PCURDIR CurDir; NTSTATUS Status; RTL_PATH_TYPE PathType; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING FullPath, NtName; PRTLP_CURDIR_REF OldCurDir = NULL; OBJECT_ATTRIBUTES ObjectAttributes; FILE_FS_DEVICE_INFORMATION FileFsDeviceInfo; ULONG SavedLength, CharLength, FullPathLength; HANDLE OldHandle = NULL, CurDirHandle = NULL, OldCurDirHandle = NULL;
DPRINT("RtlSetCurrentDirectory_U %wZ\n", Path);
/* Initialize for failure case */ RtlInitEmptyUnicodeString(&NtName, NULL, 0);
/* Can't set current directory on DOS device */ if (RtlIsDosDeviceName_Ustr(Path)) { return STATUS_NOT_A_DIRECTORY; }
/* Get current directory */ RtlAcquirePebLock(); CurDir = &NtCurrentPeb()->ProcessParameters->CurrentDirectory;
/* Check if we have to drop current handle */ if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_DROP_OLD_HANDLE) { OldHandle = CurDir->Handle; CurDir->Handle = NULL; }
/* Allocate a buffer for full path (using max possible length */ FullPath.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, CurDir->DosPath.MaximumLength); if (!FullPath.Buffer) { Status = STATUS_NO_MEMORY; goto Leave; }
/* Init string */ FullPath.Length = 0; FullPath.MaximumLength = CurDir->DosPath.MaximumLength;
/* Get new directory full path */ FullPathLength = RtlGetFullPathName_Ustr(Path, FullPath.MaximumLength, FullPath.Buffer, NULL, NULL, &PathType); if (!FullPathLength) { Status = STATUS_OBJECT_NAME_INVALID; goto Leave; }
SavedLength = FullPath.MaximumLength; CharLength = FullPathLength / sizeof(WCHAR);
if (FullPathLength > FullPath.MaximumLength) { Status = STATUS_NAME_TOO_LONG; goto Leave; }
/* Translate it to NT name */ if (!RtlDosPathNameToNtPathName_U(FullPath.Buffer, &NtName, NULL, NULL)) { Status = STATUS_OBJECT_NAME_INVALID; goto Leave; }
InitializeObjectAttributes(&ObjectAttributes, &NtName, OBJ_CASE_INSENSITIVE | OBJ_INHERIT, NULL, NULL);
/* If previous current directory was removable, then check it for dropping */ if (((ULONG_PTR)(CurDir->Handle) & RTL_CURDIR_ALL_FLAGS) == RTL_CURDIR_ALL_FLAGS) { /* Get back normal handle */ CurDirHandle = (HANDLE)((ULONG_PTR)(CurDir->Handle) & ~RTL_CURDIR_ALL_FLAGS); CurDir->Handle = NULL;
/* Get device information */ Status = NtQueryVolumeInformationFile(CurDirHandle, &IoStatusBlock, &FileFsDeviceInfo, sizeof(FileFsDeviceInfo), FileFsDeviceInformation); /* Retry without taking care of removable device */ if (!NT_SUCCESS(Status)) { Status = RtlSetCurrentDirectory_U(Path); goto Leave; } } else { /* Open directory */ Status = NtOpenFile(&CurDirHandle, SYNCHRONIZE | FILE_TRAVERSE, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT); if (!NT_SUCCESS(Status)) goto Leave;
/* Get device information */ Status = NtQueryVolumeInformationFile(CurDirHandle, &IoStatusBlock, &FileFsDeviceInfo, sizeof(FileFsDeviceInfo), FileFsDeviceInformation); if (!NT_SUCCESS(Status)) goto Leave; }
/* If device is removable, mark handle */ if (FileFsDeviceInfo.Characteristics & FILE_REMOVABLE_MEDIA) { CurDirHandle = (HANDLE)((ULONG_PTR)CurDirHandle | RTL_CURDIR_IS_REMOVABLE); }
FullPath.Length = (USHORT)FullPathLength;
/* If full path isn't \ terminated, do it */ if (FullPath.Buffer[CharLength - 1] != OBJ_NAME_PATH_SEPARATOR) { if ((CharLength + 1) * sizeof(WCHAR) > SavedLength) { Status = STATUS_NAME_TOO_LONG; goto Leave; }
FullPath.Buffer[CharLength] = OBJ_NAME_PATH_SEPARATOR; FullPath.Buffer[CharLength + 1] = UNICODE_NULL; FullPath.Length += sizeof(WCHAR); }
/* If we have previous current directory with only us as reference, save it */ if (RtlpCurDirRef != NULL && RtlpCurDirRef->RefCount == 1) { OldCurDirHandle = RtlpCurDirRef->Handle; } else { /* Allocate new current directory struct saving previous one */ OldCurDir = RtlpCurDirRef; RtlpCurDirRef = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(RTLP_CURDIR_REF)); if (!RtlpCurDirRef) { RtlpCurDirRef = OldCurDir; OldCurDir = NULL; Status = STATUS_NO_MEMORY; goto Leave; }
/* Set reference to 1 (us) */ RtlpCurDirRef->RefCount = 1; }
/* Save new data */ CurDir->Handle = CurDirHandle; RtlpCurDirRef->Handle = CurDirHandle; CurDirHandle = NULL;
/* Copy full path */ RtlCopyMemory(CurDir->DosPath.Buffer, FullPath.Buffer, FullPath.Length + sizeof(WCHAR)); CurDir->DosPath.Length = FullPath.Length;
Status = STATUS_SUCCESS;
Leave: RtlReleasePebLock();
if (FullPath.Buffer) { RtlFreeHeap(RtlGetProcessHeap(), 0, FullPath.Buffer); }
if (NtName.Buffer) { RtlFreeHeap(RtlGetProcessHeap(), 0, NtName.Buffer); }
if (CurDirHandle) NtClose(CurDirHandle);
if (OldHandle) NtClose(OldHandle);
if (OldCurDirHandle) NtClose(OldCurDirHandle);
if (OldCurDir && InterlockedDecrement(&OldCurDir->RefCount) == 0) { NtClose(OldCurDir->Handle); RtlFreeHeap(RtlGetProcessHeap(), 0, OldCurDir); }
return Status;}
/* * @implemented */ULONGNTAPIRtlGetFullPathName_UEx( _In_ PWSTR FileName, _In_ ULONG BufferLength, _Out_writes_bytes_(BufferLength) PWSTR Buffer, _Out_opt_ PWSTR *FilePart, _Out_opt_ RTL_PATH_TYPE *InputPathType){ UNICODE_STRING FileNameString; NTSTATUS status;
if (InputPathType) *InputPathType = 0;
/* Build the string */ status = RtlInitUnicodeStringEx(&FileNameString, FileName); if (!NT_SUCCESS(status)) return 0;
/* Call the extended function */ return RtlGetFullPathName_Ustr( &FileNameString, BufferLength, Buffer, (PCWSTR*)FilePart, NULL, InputPathType);}
/****************************************************************** * RtlGetFullPathName_U (NTDLL.@) * * Returns the number of bytes written to buffer (not including the * terminating NULL) if the function succeeds, or the required number of bytes * (including the terminating NULL) if the buffer is too small. * * file_part will point to the filename part inside buffer (except if we use * DOS device name, in which case file_in_buf is NULL) * * @implemented */
/* * @implemented */ULONGNTAPIRtlGetFullPathName_U( _In_ PCWSTR FileName, _In_ ULONG Size, _Out_z_bytecap_(Size) PWSTR Buffer, _Out_opt_ PWSTR *ShortName){ RTL_PATH_TYPE PathType;
/* Call the extended function */ return RtlGetFullPathName_UEx((PWSTR)FileName, Size, Buffer, ShortName, &PathType);}
/* * @implemented */BOOLEANNTAPIRtlDosPathNameToNtPathName_U(IN PCWSTR DosName, OUT PUNICODE_STRING NtName, OUT PCWSTR *PartName, OUT PRTL_RELATIVE_NAME_U RelativeName){ /* Call the internal function */ return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(FALSE, DosName, NtName, PartName, RelativeName));}
/* * @implemented */NTSTATUSNTAPIRtlDosPathNameToNtPathName_U_WithStatus(IN PCWSTR DosName, OUT PUNICODE_STRING NtName, OUT PCWSTR *PartName, OUT PRTL_RELATIVE_NAME_U RelativeName){ /* Call the internal function */ return RtlpDosPathNameToRelativeNtPathName_U(FALSE, DosName, NtName, PartName, RelativeName);}
/* * @implemented */BOOLEANNTAPIRtlDosPathNameToRelativeNtPathName_U(IN PCWSTR DosName, OUT PUNICODE_STRING NtName, OUT PCWSTR *PartName, OUT PRTL_RELATIVE_NAME_U RelativeName){ /* Call the internal function */ ASSERT(RelativeName); return NT_SUCCESS(RtlpDosPathNameToRelativeNtPathName_U(TRUE, DosName, NtName, PartName, RelativeName));}
/* * @implemented */NTSTATUSNTAPIRtlDosPathNameToRelativeNtPathName_U_WithStatus(IN PCWSTR DosName, OUT PUNICODE_STRING NtName, OUT PCWSTR *PartName, OUT PRTL_RELATIVE_NAME_U RelativeName){ /* Call the internal function */ ASSERT(RelativeName); return RtlpDosPathNameToRelativeNtPathName_U(TRUE, DosName, NtName, PartName, RelativeName);}
/* * @implemented */NTSTATUS NTAPI RtlNtPathNameToDosPathName(IN ULONG Flags, IN OUT PRTL_UNICODE_STRING_BUFFER Path, OUT PULONG PathType, PULONG Unknown){ PCUNICODE_STRING UsePrefix = NULL, AlternatePrefix = NULL;
if (PathType) *PathType = 0;
if (!Path || Flags) return STATUS_INVALID_PARAMETER;
/* The initial check is done on Path->String */ if (RtlPrefixUnicodeString(&RtlpDosDevicesUncPrefix, &Path->String, TRUE)) { UsePrefix = &RtlpDosDevicesUncPrefix; AlternatePrefix = &RtlpDoubleSlashPrefix; if (PathType) *PathType = RTL_CONVERTED_UNC_PATH; } else if (RtlPrefixUnicodeString(&RtlpDosDevicesPrefix, &Path->String, FALSE)) { UsePrefix = &RtlpDosDevicesPrefix; if (PathType) *PathType = RTL_CONVERTED_NT_PATH; }
if (UsePrefix) { NTSTATUS Status;
USHORT Len = Path->String.Length - UsePrefix->Length; if (AlternatePrefix) Len += AlternatePrefix->Length;
Status = RtlEnsureBufferSize(0, &Path->ByteBuffer, Len); if (!NT_SUCCESS(Status)) return Status;
if (Len + sizeof(UNICODE_NULL) <= Path->ByteBuffer.Size) { /* Then, the contents of Path->ByteBuffer are always used... */ if (AlternatePrefix) { memcpy(Path->ByteBuffer.Buffer, AlternatePrefix->Buffer, AlternatePrefix->Length); memmove(Path->ByteBuffer.Buffer + AlternatePrefix->Length, Path->ByteBuffer.Buffer + UsePrefix->Length, Len - AlternatePrefix->Length); } else { memmove(Path->ByteBuffer.Buffer, Path->ByteBuffer.Buffer + UsePrefix->Length, Len); } Path->String.Buffer = (PWSTR)Path->ByteBuffer.Buffer; Path->String.Length = Len; Path->String.MaximumLength = Path->ByteBuffer.Size; Path->String.Buffer[Len / sizeof(WCHAR)] = UNICODE_NULL; } return STATUS_SUCCESS; }
if (PathType) { switch (RtlDetermineDosPathNameType_Ustr(&Path->String)) { case RtlPathTypeUncAbsolute: case RtlPathTypeDriveAbsolute: case RtlPathTypeLocalDevice: case RtlPathTypeRootLocalDevice: *PathType = RTL_UNCHANGED_DOS_PATH; break; case RtlPathTypeUnknown: case RtlPathTypeDriveRelative: case RtlPathTypeRooted: case RtlPathTypeRelative: *PathType = RTL_UNCHANGED_UNK_PATH; break; } }
return STATUS_SUCCESS;}
/* * @implemented */ULONGNTAPIRtlDosSearchPath_U(IN PCWSTR Path, IN PCWSTR FileName, IN PCWSTR Extension, IN ULONG Size, IN PWSTR Buffer, OUT PWSTR *PartName){ NTSTATUS Status; ULONG ExtensionLength, Length, FileNameLength, PathLength; UNICODE_STRING TempString; PWCHAR NewBuffer, BufferStart; PCWSTR p;
/* Check if this is an absolute path */ if (RtlDetermineDosPathNameType_U(FileName) != RtlPathTypeRelative) { /* Check if the file exists */ if (RtlDoesFileExists_UEx(FileName, TRUE)) { /* Get the full name, which does the DOS lookup */ return RtlGetFullPathName_U(FileName, Size, Buffer, PartName); }
/* Doesn't exist, so fail */ return 0; }
/* Scan the filename */ p = FileName; while (*p) { /* Looking for an extension */ if (*p == L'.') { /* No extension string needed -- it's part of the filename */ Extension = NULL; break; }
/* Next character */ p++; }
/* Do we have an extension? */ if (!Extension) { /* Nope, don't worry about one */ ExtensionLength = 0; } else { /* Build a temporary string to get the extension length */ Status = RtlInitUnicodeStringEx(&TempString, Extension); if (!NT_SUCCESS(Status)) return 0; ExtensionLength = TempString.Length; }
/* Build a temporary string to get the path length */ Status = RtlInitUnicodeStringEx(&TempString, Path); if (!NT_SUCCESS(Status)) return 0; PathLength = TempString.Length;
/* Build a temporary string to get the filename length */ Status = RtlInitUnicodeStringEx(&TempString, FileName); if (!NT_SUCCESS(Status)) return 0; FileNameLength = TempString.Length;
/* Allocate the buffer for the new string name */ NewBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, FileNameLength + ExtensionLength + PathLength + 3 * sizeof(WCHAR)); if (!NewBuffer) { /* Fail the call */ DbgPrint("%s: Failing due to out of memory (RtlAllocateHeap failure)\n", __FUNCTION__); return 0; }
/* Final loop to build the path */ while (TRUE) { /* Check if we have a valid character */ BufferStart = NewBuffer; if (*Path) { /* Loop as long as there's no semicolon */ while (*Path != L';') { /* Copy the next character */ *BufferStart++ = *Path++; if (!*Path) break; }
/* We found a semi-colon, to stop path processing on this loop */ if (*Path == L';') ++Path; }
/* Add a terminating slash if needed */ if ((BufferStart != NewBuffer) && (BufferStart[-1] != OBJ_NAME_PATH_SEPARATOR)) { *BufferStart++ = OBJ_NAME_PATH_SEPARATOR; }
/* Bail out if we reached the end */ if (!*Path) Path = NULL;
/* Copy the file name and check if an extension is needed */ RtlCopyMemory(BufferStart, FileName, FileNameLength); if (ExtensionLength) { /* Copy the extension too */ RtlCopyMemory((PCHAR)BufferStart + FileNameLength, Extension, ExtensionLength + sizeof(WCHAR)); } else { /* Just NULL-terminate */ *(PWCHAR)((PCHAR)BufferStart + FileNameLength) = UNICODE_NULL; }
/* Now, does this file exist? */ if (RtlDoesFileExists_UEx(NewBuffer, FALSE)) { /* Call the full-path API to get the length */ Length = RtlGetFullPathName_U(NewBuffer, Size, Buffer, PartName); break; }
/* If we got here, path doesn't exist, so fail the call */ Length = 0; if (!Path) break; }
/* Free the allocation and return the length */ RtlFreeHeap(RtlGetProcessHeap(), 0, NewBuffer); return Length;}
/* * @implemented */NTSTATUSNTAPIRtlGetFullPathName_UstrEx(IN PUNICODE_STRING FileName, IN PUNICODE_STRING StaticString, IN PUNICODE_STRING DynamicString, IN PUNICODE_STRING *StringUsed, IN PSIZE_T FilePartSize, OUT PBOOLEAN NameInvalid, OUT RTL_PATH_TYPE* PathType, OUT PSIZE_T LengthNeeded){ NTSTATUS Status; PWCHAR StaticBuffer; PCWCH ShortName; ULONG Length; USHORT StaticLength; UNICODE_STRING TempDynamicString;
/* Initialize all our locals */ ShortName = NULL; StaticBuffer = NULL; TempDynamicString.Buffer = NULL;
/* Initialize the input parameters */ if (StringUsed) *StringUsed = NULL; if (LengthNeeded) *LengthNeeded = 0; if (FilePartSize) *FilePartSize = 0;
/* Check for invalid parameters */ if ((DynamicString) && !(StringUsed) && (StaticString)) { return STATUS_INVALID_PARAMETER; }
/* Check if we did not get an input string */ if (!StaticString) { /* Allocate one */ StaticLength = MAX_PATH * sizeof(WCHAR); StaticBuffer = RtlpAllocateStringMemory(MAX_PATH * sizeof(WCHAR), TAG_USTR); if (!StaticBuffer) return STATUS_NO_MEMORY; } else { /* Use the one we received */ StaticBuffer = StaticString->Buffer; StaticLength = StaticString->MaximumLength; }
/* Call the lower-level function */ Length = RtlGetFullPathName_Ustr(FileName, StaticLength, StaticBuffer, &ShortName, NameInvalid, PathType); DPRINT("Length: %u StaticBuffer: %S\n", Length, StaticBuffer); if (!Length) { /* Fail if it failed */ DbgPrint("%s(%d) - RtlGetFullPathName_Ustr() returned 0\n", __FUNCTION__, __LINE__); Status = STATUS_OBJECT_NAME_INVALID; goto Quickie; }
/* Check if it fits inside our static string */ if ((StaticString) && (Length < StaticLength)) { /* Set the final length */ StaticString->Length = (USHORT)Length;
/* Set the file part size */ if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0;
/* Return the static string if requested */ if (StringUsed) *StringUsed = StaticString;
/* We are done with success */ Status = STATUS_SUCCESS; goto Quickie; }
/* Did we not have an input dynamic string ?*/ if (!DynamicString) { /* Return the length we need */ if (LengthNeeded) *LengthNeeded = Length;
/* And fail such that the caller can try again */ Status = STATUS_BUFFER_TOO_SMALL; goto Quickie; }
/* Check if it fits in our static buffer */ if ((StaticBuffer) && (Length < StaticLength)) { /* NULL-terminate it */ StaticBuffer[Length / sizeof(WCHAR)] = UNICODE_NULL;
/* Set the settings for the dynamic string the caller sent */ DynamicString->MaximumLength = StaticLength; DynamicString->Length = (USHORT)Length; DynamicString->Buffer = StaticBuffer;
/* Set the part size */ if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticBuffer) : 0;
/* Return the dynamic string if requested */ if (StringUsed) *StringUsed = DynamicString;
/* Do not free the static buffer on exit, and return success */ StaticBuffer = NULL; Status = STATUS_SUCCESS; goto Quickie; }
/* Now try again under the PEB lock */ RtlAcquirePebLock(); Length = RtlGetFullPathName_Ustr(FileName, StaticLength, StaticBuffer, &ShortName, NameInvalid, PathType); if (!Length) { /* It failed */ DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n", __FUNCTION__, __LINE__); Status = STATUS_OBJECT_NAME_INVALID; goto Release; }
/* Check if it fits inside our static string now */ if ((StaticString) && (Length < StaticLength)) { /* Set the final length */ StaticString->Length = (USHORT)Length;
/* Set the file part size */ if (FilePartSize) *FilePartSize = ShortName ? (ShortName - StaticString->Buffer) : 0;
/* Return the static string if requested */ if (StringUsed) *StringUsed = StaticString;
/* We are done with success */ Status = STATUS_SUCCESS; goto Release; }
/* Check if the path won't even fit in a real string */ if ((Length + sizeof(WCHAR)) > UNICODE_STRING_MAX_BYTES) { /* Name is way too long, fail */ Status = STATUS_NAME_TOO_LONG; goto Release; }
/* Allocate the string to hold the path name now */ TempDynamicString.Buffer = RtlpAllocateStringMemory(Length + sizeof(WCHAR), TAG_USTR); if (!TempDynamicString.Buffer) { /* Out of memory, fail */ Status = STATUS_NO_MEMORY; goto Release; }
/* Add space for a NULL terminator, and now check the full path */ TempDynamicString.MaximumLength = (USHORT)Length + sizeof(UNICODE_NULL); Length = RtlGetFullPathName_Ustr(FileName, Length, TempDynamicString.Buffer, &ShortName, NameInvalid, PathType); if (!Length) { /* Some path error, so fail out */ DbgPrint("%s line %d: RtlGetFullPathName_Ustr() returned 0\n", __FUNCTION__, __LINE__); Status = STATUS_OBJECT_NAME_INVALID; goto Release; }
/* It should fit in the string we just allocated */ ASSERT(Length < (TempDynamicString.MaximumLength - sizeof(WCHAR))); if (Length > TempDynamicString.MaximumLength) { /* This is really weird and would mean some kind of race */ Status = STATUS_INTERNAL_ERROR; goto Release; }
/* Return the file part size */ if (FilePartSize) *FilePartSize = ShortName ? (ShortName - TempDynamicString.Buffer) : 0;
/* Terminate the whole string now */ TempDynamicString.Buffer[Length / sizeof(WCHAR)] = UNICODE_NULL;
/* Finalize the string and return it to the user */ DynamicString->Buffer = TempDynamicString.Buffer; DynamicString->Length = (USHORT)Length; DynamicString->MaximumLength = TempDynamicString.MaximumLength; if (StringUsed) *StringUsed = DynamicString;
/* Return success and make sure we don't free the buffer on exit */ TempDynamicString.Buffer = NULL; Status = STATUS_SUCCESS;
Release: /* Release the PEB lock */ RtlReleasePebLock();
Quickie: /* Free any buffers we should be freeing */ DPRINT("Status: %lx %S %S\n", Status, StaticBuffer, TempDynamicString.Buffer); if ((StaticString) && (StaticBuffer) && (StaticBuffer != StaticString->Buffer)) { RtlpFreeStringMemory(StaticBuffer, TAG_USTR); } if (TempDynamicString.Buffer) { RtlpFreeStringMemory(TempDynamicString.Buffer, TAG_USTR); }
/* Print out any unusual errors */ if ((NT_ERROR(Status)) && (Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL)) { DbgPrint("RTL: %s - failing on filename %wZ with status %08lx\n", __FUNCTION__, FileName, Status); }
/* Return, we're all done */ return Status;}
/* * @implemented */NTSTATUSNTAPIRtlDosSearchPath_Ustr(IN ULONG Flags, IN PUNICODE_STRING PathString, IN PUNICODE_STRING FileNameString, IN PUNICODE_STRING ExtensionString, IN PUNICODE_STRING CallerBuffer, IN OUT PUNICODE_STRING DynamicString OPTIONAL, OUT PUNICODE_STRING* FullNameOut OPTIONAL, OUT PSIZE_T FilePartSize OPTIONAL, OUT PSIZE_T LengthNeeded OPTIONAL){ WCHAR StaticCandidateBuffer[MAX_PATH]; UNICODE_STRING StaticCandidateString; NTSTATUS Status; RTL_PATH_TYPE PathType; PWCHAR p, End, CandidateEnd, SegmentEnd; SIZE_T WorstCaseLength, NamePlusExtLength, SegmentSize, ByteCount, PathSize, MaxPathSize = 0; USHORT ExtensionLength = 0; PUNICODE_STRING FullIsolatedPath; DPRINT("DOS Path Search: %lx %wZ %wZ %wZ %wZ %wZ\n", Flags, PathString, FileNameString, ExtensionString, CallerBuffer, DynamicString);
/* Initialize the input string */ RtlInitEmptyUnicodeString(&StaticCandidateString, StaticCandidateBuffer, sizeof(StaticCandidateBuffer));
/* Initialize optional arguments */ if (FullNameOut ) *FullNameOut = NULL; if (FilePartSize) *FilePartSize = 0; if (LengthNeeded) *LengthNeeded = 0; if (DynamicString) { DynamicString->Length = DynamicString->MaximumLength = 0; DynamicString->Buffer = NULL; }
/* Check for invalid parameters */ if ((Flags & ~7) || !(PathString) || !(FileNameString) || ((CallerBuffer) && (DynamicString) && !(FullNameOut))) { /* Fail */ DbgPrint("%s: Invalid parameters passed\n", __FUNCTION__); Status = STATUS_INVALID_PARAMETER; goto Quickie; }
/* First check what kind of path this is */ PathType = RtlDetermineDosPathNameType_Ustr(FileNameString);
/* Check if the caller wants to prevent relative .\ and ..\ paths */ if ((Flags & 2) && (PathType == RtlPathTypeRelative) && (FileNameString->Length >= (2 * sizeof(WCHAR))) && (FileNameString->Buffer[0] == L'.') && ((IS_PATH_SEPARATOR(FileNameString->Buffer[1])) || ((FileNameString->Buffer[1] == L'.') && ((FileNameString->Length >= (3 * sizeof(WCHAR))) && (IS_PATH_SEPARATOR(FileNameString->Buffer[2])))))) { /* Yes, and this path is like that, so make it seem unknown */ PathType = RtlPathTypeUnknown; }
/* Now check relative vs non-relative paths */ if (PathType == RtlPathTypeRelative) { /* Does the caller want SxS? */ if (Flags & 1) { /* Apply the SxS magic */ FullIsolatedPath = NULL; Status = RtlDosApplyFileIsolationRedirection_Ustr(TRUE, FileNameString, ExtensionString, CallerBuffer, DynamicString, &FullIsolatedPath, NULL, FilePartSize, LengthNeeded); if (NT_SUCCESS(Status)) { /* We found the SxS path, return it */ if (FullNameOut) *FullNameOut = FullIsolatedPath; goto Quickie; } else if (Status != STATUS_SXS_KEY_NOT_FOUND) { /* Critical SxS error, fail */ DbgPrint("%s: Failing because call to " "RtlDosApplyIsolationRedirection_Ustr(%wZ) failed with " "status 0x%08lx\n", __FUNCTION__, FileNameString, Status); goto Quickie; } }
/* No SxS key found, or not requested, check if there's an extension */ if (ExtensionString) { /* Save the extension length, and check if there's a file name */ ExtensionLength = ExtensionString->Length; if (FileNameString->Length) { /* Start parsing the file name */ End = &FileNameString->Buffer[FileNameString->Length / sizeof(WCHAR)]; while (End > FileNameString->Buffer) { /* If we find a path separator, there's no extension */ --End; if (IS_PATH_SEPARATOR(*End)) break;
/* Otherwise, did we find an extension dot? */ if (*End == L'.') { /* Ignore what the caller sent it, use the filename's */ ExtensionString = NULL; ExtensionLength = 0; break; } } } }
/* Check if we got a path */ if (PathString->Length) { /* Start parsing the path name, looking for path separators */ End = &PathString->Buffer[PathString->Length / sizeof(WCHAR)]; p = End; while (p > PathString->Buffer) { if (*--p == L';') { /* This is the size of the path -- handle a trailing slash */ PathSize = End - p - 1; if ((PathSize) && !(IS_PATH_SEPARATOR(*(End - 1)))) PathSize++;
/* Check if we found a bigger path than before */ if (PathSize > MaxPathSize) MaxPathSize = PathSize;
/* Keep going with the path after this path separator */ End = p; } }
/* This is the trailing path, run the same code as above */ PathSize = End - p; if ((PathSize) && !(IS_PATH_SEPARATOR(*(End - 1)))) PathSize++; if (PathSize > MaxPathSize) MaxPathSize = PathSize;
/* Finally, convert the largest path size into WCHAR */ MaxPathSize *= sizeof(WCHAR); }
/* Use the extension, the file name, and the largest path as the size */ WorstCaseLength = ExtensionLength + FileNameString->Length + (USHORT)MaxPathSize + sizeof(UNICODE_NULL); if (WorstCaseLength > UNICODE_STRING_MAX_BYTES) { /* It has to fit in a registry string, if not, fail here */ DbgPrint("%s returning STATUS_NAME_TOO_LONG because the computed " "worst case file name length is %Iu bytes\n", __FUNCTION__, WorstCaseLength); Status = STATUS_NAME_TOO_LONG; goto Quickie; }
/* Scan the path now, to see if we can find the file */ p = PathString->Buffer; End = &p[PathString->Length / sizeof(WCHAR)]; while (p < End) { /* Find out where this path ends */ for (SegmentEnd = p; ((SegmentEnd != End) && (*SegmentEnd != L';')); SegmentEnd++);
/* Compute the size of this path */ ByteCount = SegmentSize = (SegmentEnd - p) * sizeof(WCHAR);
/* Handle trailing slash if there isn't one */ if ((SegmentSize) && !(IS_PATH_SEPARATOR(*(SegmentEnd - 1)))) { /* Add space for one */ SegmentSize += sizeof(OBJ_NAME_PATH_SEPARATOR); }
/* Now check if our initial static buffer is too small */ if (StaticCandidateString.MaximumLength < (SegmentSize + ExtensionLength + FileNameString->Length + sizeof(UNICODE_NULL))) { /* At this point we should've been using our static buffer */ ASSERT(StaticCandidateString.Buffer == StaticCandidateBuffer); if (StaticCandidateString.Buffer != StaticCandidateBuffer) { /* Something is really messed up if this was the dynamic string */ DbgPrint("%s: internal error #1; " "CandidateString.Buffer = %p; " "StaticCandidateBuffer = %p\n", __FUNCTION__, StaticCandidateString.Buffer, StaticCandidateBuffer); Status = STATUS_INTERNAL_ERROR; goto Quickie; }
/* We checked before that the maximum possible size shoudl fit! */ ASSERT((SegmentSize + FileNameString->Length + ExtensionLength) < UNICODE_STRING_MAX_BYTES); if ((SegmentSize + ExtensionLength + FileNameString->Length) > (UNICODE_STRING_MAX_BYTES - sizeof(WCHAR))) { /* For some reason it's not fitting anymore. Something messed up */ DbgPrint("%s: internal error #2; SegmentSize = %u, " "FileName->Length = %u, DefaultExtensionLength = %u\n", __FUNCTION__, SegmentSize, FileNameString->Length, ExtensionLength); Status = STATUS_INTERNAL_ERROR; goto Quickie; }
/* Now allocate the dynamic string */ StaticCandidateString.MaximumLength = WorstCaseLength; StaticCandidateString.Buffer = RtlpAllocateStringMemory(WorstCaseLength, TAG_USTR); if (!StaticCandidateString.Buffer) { /* Out of memory, fail */ DbgPrint("%s: Unable to allocate %u byte buffer for path candidate\n", __FUNCTION__, StaticCandidateString.MaximumLength); Status = STATUS_NO_MEMORY; goto Quickie; } }
/* Copy the path in the string */ RtlCopyMemory(StaticCandidateString.Buffer, p, ByteCount);
/* Get to the end of the string, and add the trailing slash if missing */ CandidateEnd = &StaticCandidateString.Buffer[ByteCount / sizeof(WCHAR)]; if ((SegmentSize) && (SegmentSize != ByteCount)) { *CandidateEnd++ = OBJ_NAME_PATH_SEPARATOR; }
/* Copy the filename now */ RtlCopyMemory(CandidateEnd, FileNameString->Buffer, FileNameString->Length); CandidateEnd += (FileNameString->Length / sizeof(WCHAR));
/* Check if there was an extension */ if (ExtensionString) { /* Copy the extension too */ RtlCopyMemory(CandidateEnd, ExtensionString->Buffer, ExtensionString->Length); CandidateEnd += (ExtensionString->Length / sizeof(WCHAR)); }
/* We are done, terminate it */ *CandidateEnd = UNICODE_NULL;
/* Now set the final length of the string so it becomes valid */ StaticCandidateString.Length = (USHORT)(CandidateEnd - StaticCandidateString.Buffer) * sizeof(WCHAR); ASSERT(StaticCandidateString.Length < StaticCandidateString.MaximumLength);
/* Check if this file exists */ DPRINT("BUFFER: %S\n", StaticCandidateString.Buffer); if (RtlDoesFileExists_UEx(StaticCandidateString.Buffer, FALSE)) { /* Awesome, it does, now get the full path */ Status = RtlGetFullPathName_UstrEx(&StaticCandidateString, CallerBuffer, DynamicString, (PUNICODE_STRING*)FullNameOut, FilePartSize, NULL, &PathType, LengthNeeded); if (!(NT_SUCCESS(Status)) && ((Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL))) { DbgPrint("%s: Failing because we thought we found %wZ on " "the search path, but RtlGetfullPathNameUStrEx() " "returned %08lx\n", __FUNCTION__, &StaticCandidateString, Status); } DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer); goto Quickie; } else { /* Otherwise, move to the next path */ if (SegmentEnd != End) { /* Handle the case of the path separator trailing */ p = SegmentEnd + 1; } else { p = SegmentEnd; } } }
/* Loop finished and we didn't break out -- fail */ Status = STATUS_NO_SUCH_FILE; } else { /* We have a full path, so check if it does exist */ DPRINT("%wZ\n", FileNameString); if (!RtlDoesFileExists_UstrEx(FileNameString, TRUE)) { /* It doesn't exist, did we have an extension? */ if (!(ExtensionString) || !(ExtensionString->Length)) { /* No extension, so just fail */ Status = STATUS_NO_SUCH_FILE; goto Quickie; }
/* There was an extension, check if the filename already had one */ if (!(Flags & 4) && (FileNameString->Length)) { /* Parse the filename */ p = FileNameString->Buffer; End = &p[FileNameString->Length / sizeof(WCHAR)]; while (End > p) { /* If there's a path separator, there's no extension */ --End; if (IS_PATH_SEPARATOR(*End)) break;
/* Othwerwise, did we find an extension dot? */ if (*End == L'.') { /* File already had an extension, so fail */ Status = STATUS_NO_SUCH_FILE; goto Quickie; } } }
/* So there is an extension, we'll try again by adding it */ NamePlusExtLength = FileNameString->Length + ExtensionString->Length + sizeof(UNICODE_NULL); if (NamePlusExtLength > UNICODE_STRING_MAX_BYTES) { /* It won't fit in any kind of valid string, so fail */ DbgPrint("%s: Failing because filename plus extension (%Iu bytes) is too big\n", __FUNCTION__, NamePlusExtLength); Status = STATUS_NAME_TOO_LONG; goto Quickie; }
/* Fill it fit in our temporary string? */ if (NamePlusExtLength > StaticCandidateString.MaximumLength) { /* It won't fit anymore, allocate a dynamic string for it */ StaticCandidateString.MaximumLength = NamePlusExtLength; StaticCandidateString.Buffer = RtlpAllocateStringMemory(NamePlusExtLength, TAG_USTR); if (!StaticCandidateString.Buffer) { /* Ran out of memory, so fail */ DbgPrint("%s: Failing because allocating the dynamic filename buffer failed\n", __FUNCTION__); Status = STATUS_NO_MEMORY; goto Quickie; } }
/* Copy the filename */ RtlCopyUnicodeString(&StaticCandidateString, FileNameString);
/* Copy the extension */ RtlAppendUnicodeStringToString(&StaticCandidateString, ExtensionString);
DPRINT("SB: %wZ\n", &StaticCandidateString);
/* And check if this file now exists */ if (!RtlDoesFileExists_UstrEx(&StaticCandidateString, TRUE)) { /* Still no joy, fail out */ Status = STATUS_NO_SUCH_FILE; goto Quickie; }
/* File was found, get the final full path */ Status = RtlGetFullPathName_UstrEx(&StaticCandidateString, CallerBuffer, DynamicString, (PUNICODE_STRING*)FullNameOut, FilePartSize, NULL, &PathType, LengthNeeded); if (!(NT_SUCCESS(Status)) && (Status != STATUS_NO_SUCH_FILE)) { DbgPrint("%s: Failing on \"%wZ\" because RtlGetfullPathNameUStrEx() " "failed with status %08lx\n", __FUNCTION__, &StaticCandidateString, Status); } DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer); } else { /* File was found on the first try, get the final full path */ Status = RtlGetFullPathName_UstrEx(FileNameString, CallerBuffer, DynamicString, (PUNICODE_STRING*)FullNameOut, FilePartSize, NULL, &PathType, LengthNeeded); if (!(NT_SUCCESS(Status)) && ((Status != STATUS_NO_SUCH_FILE) && (Status != STATUS_BUFFER_TOO_SMALL))) { DbgPrint("%s: Failing because RtlGetfullPathNameUStrEx() on %wZ " "failed with status %08lx\n", __FUNCTION__, FileNameString, Status); } DPRINT("Status: %lx BUFFER: %S\n", Status, CallerBuffer->Buffer); } }
Quickie: /* Anything that was not an error, turn into STATUS_SUCCESS */ if (NT_SUCCESS(Status)) Status = STATUS_SUCCESS;
/* Check if we had a dynamic string */ if ((StaticCandidateString.Buffer) && (StaticCandidateString.Buffer != StaticCandidateBuffer)) { /* Free it */ RtlFreeUnicodeString(&StaticCandidateString); }
/* Return the status */ return Status;}
/* * @implemented */BOOLEANNTAPIRtlDoesFileExists_U(IN PCWSTR FileName){ /* Call the new function */ return RtlDoesFileExists_UEx(FileName, TRUE);}
/* EOF */