Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

728 lines
17 KiB

  1. /* lfn.c -
  2. *
  3. * This file contains code that combines winnet long filename API's and
  4. * the DOS INT 21h API's into a single interface. Thus, other parts of
  5. * Winfile call a single piece of code with no worries about the
  6. * underlying interface.
  7. */
  8. #include <nt.h>
  9. #include <ntrtl.h>
  10. #include <nturtl.h>
  11. #include "winfile.h"
  12. #include "object.h"
  13. #include "lfn.h" // lfn includes
  14. #include "dosfunc.h"
  15. #include "winnet.h"
  16. #include "wnetcaps.h" // WNetGetCaps()
  17. #include "wfcopy.h"
  18. BOOL APIENTRY IsFATName(LPSTR pName);
  19. #define CDRIVEMAX 26
  20. #define BUFSIZE 2048 // ff/fn buffer size
  21. #define MAXFILES 1024
  22. /* this is the internal buffer maintained for ff/fn operations
  23. */
  24. typedef struct _find {
  25. HANDLE hDir; // search handle
  26. WORD cbBuffer; // buffer size
  27. WORD nEntriesLeft; // remaining entries
  28. WORD ibEntry; // offset of next entry to return
  29. FILEFINDBUF2 rgFindBuf[1]; // array of find entries
  30. } FIND, * LPFIND;
  31. /* this structure contains an array of drives types (ie, unknown, FAT, LFN)
  32. * and a pointer to each of the driver functions. It is declared this way
  33. * in order to get function prototypes.
  34. */
  35. typedef struct _lfninfo {
  36. UINT hDriver;
  37. INT rgVolType[CDRIVEMAX];
  38. FARPROC lpfnQueryAbort;
  39. WORD ( APIENTRY *lpFindFirst)(LPSTR,WORD,LPINT,LPINT,WORD,PFILEFINDBUF2);
  40. WORD ( APIENTRY *lpFindNext)(HANDLE,LPINT,WORD,PFILEFINDBUF2);
  41. WORD ( APIENTRY *lpFindClose)(HANDLE);
  42. WORD ( APIENTRY *lpGetAttribute)(LPSTR,LPINT);
  43. WORD ( APIENTRY *lpSetAttribute)(LPSTR,WORD);
  44. WORD ( APIENTRY *lpCopy)(LPSTR,LPSTR,PQUERYPROC);
  45. WORD ( APIENTRY *lpMove)(LPSTR,LPSTR);
  46. WORD ( APIENTRY *lpDelete)(LPSTR);
  47. WORD ( APIENTRY *lpMKDir)(LPSTR);
  48. WORD ( APIENTRY *lpRMDir)(LPSTR);
  49. WORD ( APIENTRY *lpGetVolumeLabel)(WORD,LPSTR);
  50. WORD ( APIENTRY *lpSetVolumeLabel)(WORD,LPSTR);
  51. WORD ( APIENTRY *lpParse)(LPSTR,LPSTR,LPSTR);
  52. WORD ( APIENTRY *lpVolumeType)(WORD,LPINT);
  53. } LFNINFO, * PLFNINFO;
  54. /* pointer to lfn information, so we don't take up a lot of space on a
  55. * nonlfn system
  56. */
  57. PLFNINFO pLFN = NULL;
  58. VOID HandleSymbolicLink(HANDLE DirectoryHandle, PCHAR ObjectName);
  59. /* WFFindFirst -
  60. *
  61. * returns:
  62. * TRUE for success - lpFind->fd,hFindFileset,attrFilter set.
  63. * FALSE for failure
  64. *
  65. * Performs the FindFirst operation and the first WFFindNext.
  66. */
  67. BOOL
  68. APIENTRY
  69. WFFindFirst(
  70. LPLFNDTA lpFind,
  71. LPSTR lpName,
  72. DWORD dwAttrFilter
  73. )
  74. {
  75. // We OR these additional bits because of the way DosFindFirst works
  76. // in Windows. It returns the files that are specified by the attrfilter
  77. // and ORDINARY files too.
  78. #define BUFFERSIZE 1024
  79. #define Error(N,S) { \
  80. DbgPrint(#N); \
  81. DbgPrint(" Error %08lX\n", S); \
  82. }
  83. CHAR Buffer[BUFFERSIZE];
  84. NTSTATUS Status;
  85. HANDLE DirectoryHandle;
  86. ULONG Context = 0;
  87. ULONG ReturnedLength;
  88. POBJECT_DIRECTORY_INFORMATION DirInfo;
  89. POBJECT_NAME_INFORMATION NameInfo;
  90. INT length;
  91. lpFind->hFindFile = INVALID_HANDLE_VALUE;
  92. //DbgPrint("Find first : <%s>\n", lpName);
  93. // Remove drive letter
  94. while ((*lpName != 0) && (*lpName != '\\')) {
  95. lpName ++;
  96. }
  97. strcpy(Buffer, lpName);
  98. length = strlen(Buffer);
  99. length -= 4; // Remove '\'*.*
  100. if (length == 0) {
  101. length = 1; // Replace the '\'
  102. }
  103. Buffer[length] = 0; // Truncate the string at the appropriate point
  104. //DbgPrint("Find first modified : <%s>\n\r", Buffer);
  105. #define NEW
  106. #ifdef NEW
  107. //
  108. // Open the directory for list directory access
  109. //
  110. {
  111. OBJECT_ATTRIBUTES Attributes;
  112. ANSI_STRING DirectoryName;
  113. UNICODE_STRING UnicodeString;
  114. RtlInitAnsiString(&DirectoryName, Buffer);
  115. Status = RtlAnsiStringToUnicodeString( &UnicodeString,
  116. &DirectoryName,
  117. TRUE );
  118. ASSERT( NT_SUCCESS( Status ) );
  119. InitializeObjectAttributes( &Attributes,
  120. &UnicodeString,
  121. OBJ_CASE_INSENSITIVE,
  122. NULL,
  123. NULL );
  124. if (!NT_SUCCESS( Status = NtOpenDirectoryObject( &DirectoryHandle,
  125. STANDARD_RIGHTS_READ |
  126. DIRECTORY_QUERY |
  127. DIRECTORY_TRAVERSE,
  128. &Attributes ) )) {
  129. RtlFreeUnicodeString(&UnicodeString);
  130. if (Status == STATUS_OBJECT_TYPE_MISMATCH) {
  131. DbgPrint("%Z is not a valid Object Directory Object name\n",
  132. &DirectoryName );
  133. } else {
  134. DbgPrint("%Z - ", &DirectoryName );
  135. Error( OpenDirectory, Status );
  136. }
  137. return FALSE;
  138. }
  139. RtlFreeUnicodeString(&UnicodeString);
  140. }
  141. Status = NtQueryDirectoryObject( DirectoryHandle,
  142. &Buffer,
  143. BUFFERSIZE,
  144. TRUE,
  145. TRUE,
  146. &Context,
  147. &ReturnedLength );
  148. if (!NT_SUCCESS( Status )) {
  149. Error(Find_First_QueryDirectory, Status);
  150. return (FALSE);
  151. }
  152. //
  153. // For every record in the buffer type out the directory information
  154. //
  155. //
  156. // Point to the first record in the buffer, we are guaranteed to have
  157. // one otherwise Status would have been No More Files
  158. //
  159. DirInfo = (POBJECT_DIRECTORY_INFORMATION) &Buffer[0];
  160. //
  161. // Check if there is another record. If there isn't, then get out
  162. // of the loop now
  163. //
  164. if (DirInfo->Name.Length == 0) {
  165. DbgPrint("FindFirst - name length = 0\n\r");
  166. return (FALSE);
  167. }
  168. {
  169. ANSI_STRING AnsiString;
  170. AnsiString.Buffer = lpFind->fd.cFileName;
  171. AnsiString.MaximumLength = sizeof(lpFind->fd.cFileName);
  172. Status = RtlUnicodeStringToAnsiString(&AnsiString, &(DirInfo->Name), FALSE);
  173. ASSERT(NT_SUCCESS(Status));
  174. }
  175. //DbgPrint("FindFirst returning <%s>\n\r", lpFind->fd.cFileName);
  176. // Calculate the attribute field
  177. lpFind->fd.dwFileAttributes = CalcAttributes(&DirInfo->TypeName);
  178. #ifdef LATER
  179. if (lpFind->fd.dwFileAttributes == ATTR_SYMLINK) {
  180. HandleSymbolicLink(DirectoryHandle, lpFind->fd.cFileName);
  181. }
  182. // Label an unknown object type
  183. if (lpFind->fd.dwFileAttributes == 0) { // Unknown type
  184. strncat(lpFind->fd.cFileName, " (", MAX_PATH - strlen(lpFind->fd.cFileName));
  185. strncat(lpFind->fd.cFileName, DirInfo->TypeName.Buffer,
  186. MAX_PATH - strlen(lpFind->fd.cFileName));
  187. strncat(lpFind->fd.cFileName, ")", MAX_PATH - strlen(lpFind->fd.cFileName));
  188. }
  189. #endif
  190. // Save our search context
  191. lpFind->hFindFile = DirectoryHandle;
  192. lpFind->err = Context;
  193. return (TRUE);
  194. #else
  195. dwAttrFilter |= ATTR_ARCHIVE | ATTR_READONLY | ATTR_NORMAL;
  196. lpFind->hFindFile = FindFirstFile(lpName, &lpFind->fd);
  197. if (lpFind->hFindFile != (HANDLE)0xFFFFFFFF) {
  198. lpFind->dwAttrFilter = dwAttrFilter;
  199. if ((~dwAttrFilter & lpFind->fd.dwFileAttributes) == 0L ||
  200. WFFindNext(lpFind)) {
  201. PRINT(BF_PARMTRACE, "WFFindFirst:%s", &lpFind->fd.cFileName);
  202. return (TRUE);
  203. } else {
  204. lpFind->err = GetLastError();
  205. WFFindClose(lpFind);
  206. return (FALSE);
  207. }
  208. } else {
  209. return (FALSE);
  210. }
  211. #endif
  212. }
  213. /* WFFindNext -
  214. *
  215. * Performs a single file FindNext operation. Only returns TRUE if a
  216. * file matching the dwAttrFilter is found. On failure WFFindClose is
  217. * called.
  218. */
  219. BOOL
  220. APIENTRY
  221. WFFindNext(
  222. LPLFNDTA lpFind
  223. )
  224. {
  225. CHAR Buffer[BUFFERSIZE];
  226. NTSTATUS Status;
  227. HANDLE DirectoryHandle = lpFind->hFindFile;
  228. ULONG Context = lpFind->err;
  229. ULONG ReturnedLength;
  230. POBJECT_DIRECTORY_INFORMATION DirInfo;
  231. POBJECT_NAME_INFORMATION NameInfo;
  232. #ifdef NEW
  233. //ASSERT(lpFind->hFindFile != (HANDLE)0xFFFFFFFF);
  234. Status = NtQueryDirectoryObject( DirectoryHandle,
  235. &Buffer,
  236. BUFFERSIZE,
  237. TRUE,
  238. FALSE,
  239. &Context,
  240. &ReturnedLength );
  241. if (!NT_SUCCESS( Status )) {
  242. if (Status != STATUS_NO_MORE_ENTRIES) {
  243. Error(FindNext_QueryDirectory, Status);
  244. }
  245. return (FALSE);
  246. }
  247. //
  248. // For every record in the buffer type out the directory information
  249. //
  250. //
  251. // Point to the first record in the buffer, we are guaranteed to have
  252. // one otherwise Status would have been No More Files
  253. //
  254. DirInfo = (POBJECT_DIRECTORY_INFORMATION) &Buffer[0];
  255. //
  256. // Check if there is another record. If there isn't, then get out
  257. // of the loop now
  258. //
  259. if (DirInfo->Name.Length == 0) {
  260. DbgPrint("FindNext - name length = 0\n\r");
  261. return (FALSE);
  262. }
  263. {
  264. ANSI_STRING AnsiString;
  265. AnsiString.Buffer = lpFind->fd.cFileName;
  266. AnsiString.MaximumLength = sizeof(lpFind->fd.cFileName);
  267. Status = RtlUnicodeStringToAnsiString(&AnsiString, &(DirInfo->Name), FALSE);
  268. ASSERT(NT_SUCCESS(Status));
  269. }
  270. //DbgPrint("FindNext returning <%s>\n\r", lpFind->fd.cFileName);
  271. // Calculate the attribute field
  272. lpFind->fd.dwFileAttributes = CalcAttributes(&DirInfo->TypeName);
  273. #ifdef LATER
  274. if (lpFind->fd.dwFileAttributes == ATTR_SYMLINK) {
  275. HandleSymbolicLink(DirectoryHandle, lpFind->fd.cFileName);
  276. }
  277. // Label an unknown object type
  278. if (lpFind->fd.dwFileAttributes == 0) { // Unknown type
  279. strncat(lpFind->fd.cFileName, " (", MAX_PATH - strlen(lpFind->fd.cFileName));
  280. strncat(lpFind->fd.cFileName, DirInfo->TypeName.Buffer,
  281. MAX_PATH - strlen(lpFind->fd.cFileName));
  282. strncat(lpFind->fd.cFileName, ")", MAX_PATH - strlen(lpFind->fd.cFileName));
  283. }
  284. #endif
  285. // Save our search context
  286. lpFind->err = Context;
  287. return (TRUE);
  288. #else
  289. #ifdef DBG
  290. if (lpFind->hFindFile == (HANDLE)0xFFFFFFFF) {
  291. DebugBreak();
  292. return (FALSE);
  293. }
  294. #endif
  295. while (FindNextFile(lpFind->hFindFile, &lpFind->fd)) {
  296. if ((lpFind->fd.dwFileAttributes & ~lpFind->dwAttrFilter) != 0)
  297. continue; // only pick files that fit attr filter
  298. PRINT(BF_PARMTRACE, "WFFindNext:%s", &lpFind->fd.cFileName);
  299. return (TRUE);
  300. }
  301. lpFind->err = GetLastError();
  302. return (FALSE);
  303. #endif
  304. }
  305. /* WFFindClose -
  306. *
  307. * performs the find close operation
  308. */
  309. BOOL
  310. APIENTRY
  311. WFFindClose(
  312. LPLFNDTA lpFind
  313. )
  314. {
  315. HANDLE DirectoryHandle = lpFind->hFindFile;
  316. BOOL bRet;
  317. #ifdef NEW
  318. if (lpFind->hFindFile != INVALID_HANDLE_VALUE) {
  319. (VOID) NtClose( DirectoryHandle );
  320. lpFind->hFindFile = INVALID_HANDLE_VALUE;
  321. }
  322. return (TRUE);
  323. #else
  324. ENTER("WFFindClose");
  325. // ASSERT(lpFind->hFindFile != (HANDLE)0xFFFFFFFF);
  326. #ifdef DBG
  327. if (lpFind->hFindFile == (HANDLE)0xFFFFFFFF) {
  328. PRINT(BF_PARMTRACE, "WFFindClose:Invalid hFindFile = 0xFFFFFFFF","");
  329. return (FALSE);
  330. }
  331. #endif
  332. bRet = FindClose(lpFind->hFindFile);
  333. #ifdef DBG
  334. lpFind->hFindFile = (HANDLE)0xFFFFFFFF;
  335. #endif
  336. LEAVE("WFFindClose");
  337. return (bRet);
  338. #endif
  339. }
  340. VOID
  341. HandleSymbolicLink(
  342. HANDLE DirectoryHandle,
  343. PCHAR ObjectName
  344. ) // Assumes this points at a MAX_PATH length buffer
  345. {
  346. NTSTATUS Status;
  347. OBJECT_ATTRIBUTES Object_Attributes;
  348. HANDLE LinkHandle;
  349. STRING String;
  350. WCHAR UnicodeBuffer[MAX_PATH];
  351. UNICODE_STRING UnicodeString;
  352. INT Length;
  353. RtlInitString(&String, ObjectName);
  354. Status = RtlAnsiStringToUnicodeString( &UnicodeString,
  355. &String,
  356. TRUE );
  357. ASSERT( NT_SUCCESS( Status ) );
  358. InitializeObjectAttributes(&Object_Attributes,
  359. &UnicodeString,
  360. 0,
  361. DirectoryHandle,
  362. NULL
  363. );
  364. // Open the given symbolic link object
  365. Status = NtOpenSymbolicLinkObject(&LinkHandle,
  366. GENERIC_ALL,
  367. &Object_Attributes);
  368. RtlFreeUnicodeString(&UnicodeString);
  369. if (!NT_SUCCESS(Status)) {
  370. DbgPrint("HandleSymbolicLink : open symbolic link failed, status = %lx\n\r", Status);
  371. return;
  372. }
  373. strcat(ObjectName, " => ");
  374. Length = strlen(ObjectName);
  375. // Set up our String variable to point at the remains of the object name buffer
  376. String.Length = 0;
  377. String.MaximumLength = (USHORT)(MAX_PATH - Length);
  378. String.Buffer = &(ObjectName[Length]);
  379. // Go get the target of the symbolic link
  380. UnicodeString.Buffer = UnicodeBuffer;
  381. UnicodeString.MaximumLength = sizeof(UnicodeBuffer);
  382. Status = NtQuerySymbolicLinkObject(LinkHandle, &UnicodeString, NULL);
  383. NtClose(LinkHandle);
  384. if (!NT_SUCCESS(Status)) {
  385. DbgPrint("HandleSymbolicLink : query symbolic link failed, status = %lx\n\r", Status);
  386. return;
  387. }
  388. // Copy the symbolic target into return buffer
  389. Status = RtlUnicodeStringToAnsiString(&String, &UnicodeString, FALSE);
  390. ASSERT(NT_SUCCESS(Status));
  391. // Add NULL terminator
  392. String.Buffer[String.Length] = 0;
  393. return;
  394. }
  395. /* WFIsDir
  396. *
  397. * Determines if the specified path is a directory
  398. */
  399. BOOL
  400. APIENTRY
  401. WFIsDir(
  402. LPSTR lpDir
  403. )
  404. {
  405. DWORD attr = GetFileAttributes(lpDir);
  406. if (attr & 0x8000) // BUG: what is this constant???
  407. return FALSE;
  408. if (attr & ATTR_DIR)
  409. return TRUE;
  410. return FALSE;
  411. }
  412. /* LFNQueryAbort -
  413. *
  414. * wraps around WFQueryAbort and is exported/makeprocinstanced
  415. */
  416. BOOL
  417. APIENTRY
  418. LFNQueryAbort(
  419. VOID
  420. )
  421. {
  422. return WFQueryAbort();
  423. }
  424. /* LFNInit -
  425. *
  426. * Initializes stuff for LFN access
  427. */
  428. VOID
  429. APIENTRY
  430. LFNInit()
  431. {
  432. INT i;
  433. /* find out if long names are supported.
  434. */
  435. if (!(WNetGetCaps(WNNC_ADMIN) & WNNC_ADM_LONGNAMES))
  436. return;
  437. /* get the buffer
  438. */
  439. pLFN = (PLFNINFO)LocalAlloc(LPTR,sizeof(LFNINFO));
  440. if (!pLFN)
  441. return;
  442. /* get the handle to the driver
  443. */
  444. if (!(pLFN->hDriver = WNetGetCaps((WORD)0xFFFF))) {
  445. LocalFree((HANDLE)pLFN);
  446. pLFN = NULL;
  447. return;
  448. }
  449. /* set all the volume types to unknown
  450. */
  451. for (i = 0; i < CDRIVEMAX; i++) {
  452. pLFN->rgVolType[i] = -1;
  453. }
  454. }
  455. /* GetNameType -
  456. *
  457. * Shell around LFNParse. Classifies name.
  458. *
  459. * NOTE: this should work on unqualified names. currently this isn't
  460. * very useful.
  461. */
  462. WORD
  463. APIENTRY
  464. GetNameType(
  465. LPSTR lpName
  466. )
  467. {
  468. if (*(lpName+1) == ':') {
  469. if (!IsLFNDrive(lpName))
  470. return FILE_83_CI;
  471. } else if (IsFATName(lpName))
  472. return FILE_83_CI;
  473. return (FILE_LONG);
  474. }
  475. BOOL
  476. APIENTRY
  477. IsFATName(
  478. LPSTR pName
  479. )
  480. {
  481. INT cdots = 0;
  482. INT cb;
  483. INT i;
  484. INT iFirstDot;
  485. cb = lstrlen(pName);
  486. if (cb > 12) {
  487. return FALSE;
  488. } else {
  489. for (i = 0; i < cb; i++) {
  490. if (pName[i] == '.') {
  491. iFirstDot = cdots ? iFirstDot : i;
  492. cdots++;
  493. }
  494. }
  495. if (cdots == 0 && cb <= 8)
  496. return TRUE;
  497. else if (cdots != 1)
  498. return FALSE;
  499. else if (cdots == 1 && iFirstDot > 8)
  500. return FALSE;
  501. else
  502. return TRUE;
  503. }
  504. }
  505. BOOL
  506. APIENTRY
  507. IsLFN(
  508. LPSTR pName
  509. )
  510. {
  511. return !IsFATName(pName);
  512. }
  513. BOOL
  514. APIENTRY
  515. LFNMergePath(
  516. LPSTR pTo,
  517. LPSTR pFrom
  518. )
  519. {
  520. PRINT(BF_PARMTRACE, "LFNMergePath:basically a NOP", "");
  521. pTo; pFrom;
  522. return (FALSE);
  523. }
  524. /* InvalidateVolTypes -
  525. *
  526. * This function sets all drive types to unknown. It should be called
  527. * whenever the drive list is refreshed.
  528. */
  529. VOID
  530. APIENTRY
  531. InvalidateVolTypes( VOID )
  532. {
  533. INT i;
  534. if (!pLFN)
  535. return;
  536. for (i = 0; i < CDRIVEMAX; i++)
  537. pLFN->rgVolType[i] = -1;
  538. }
  539. /* WFCopy
  540. *
  541. * Copies files
  542. */
  543. WORD
  544. APIENTRY
  545. WFCopy(
  546. PSTR pszFrom,
  547. PSTR pszTo
  548. )
  549. {
  550. WORD wRet;
  551. Notify(hdlgProgress, IDS_COPYINGMSG, pszFrom, pszTo);
  552. wRet = FileCopy(pszFrom,pszTo);
  553. if (!wRet)
  554. ChangeFileSystem(FSC_CREATE,pszTo,NULL);
  555. return wRet;
  556. }
  557. /* WFRemove
  558. *
  559. * Deletes files
  560. */
  561. WORD
  562. APIENTRY
  563. WFRemove(
  564. PSTR pszFile
  565. )
  566. {
  567. WORD wRet;
  568. wRet = FileRemove(pszFile);
  569. if (!wRet)
  570. ChangeFileSystem(FSC_DELETE,pszFile,NULL);
  571. return wRet;
  572. }
  573. /* WFMove
  574. *
  575. * Moves files on a volume
  576. */
  577. WORD
  578. APIENTRY
  579. WFMove(
  580. PSTR pszFrom,
  581. PSTR pszTo
  582. )
  583. {
  584. WORD wRet;
  585. wRet = FileMove(pszFrom,pszTo);
  586. if (!wRet)
  587. ChangeFileSystem(FSC_RENAME,pszFrom,pszTo);
  588. return wRet;
  589. }