LPFS.CPP
Upload User: linklycbj
Upload Date: 2009-11-12
Package Size: 447k
Code Size: 38k
Category:

Windows Develop

Development Platform:

WINDOWS

  1. // Lpfs.cpp -- Low Performance File System sample program
  2. // Copyright (C) 1996 by Walter Oney
  3. // All rights reserved
  4. #define NULL 0
  5. extern "C" {
  6. #define WANTVXDWRAPS
  7. #include <basedef.h>
  8. #include <vmm.h>
  9. #include <debug.h>
  10. #include <vxdwraps.h>
  11. #include <winerror.h>
  12. #include "iosdcls.h"
  13. #include "ifsmgr.h"
  14. } // extern "C"
  15. #pragma hdrstop
  16. #include "lpfs.h"
  17. void OnAsyncEvent(PAEP aep);
  18. CFileSystem* CreateInstance();
  19. ///////////////////////////////////////////////////////////////////////////////
  20. // Overrides for library new and delete operators.
  21. void* ::operator new(unsigned int size)
  22. {
  23. return _HeapAllocate(size, 0);
  24. }
  25. void ::operator delete(void* p)
  26. {
  27. if (p)
  28. _HeapFree(p, 0);
  29. }
  30. ///////////////////////////////////////////////////////////////////////////////
  31. // Static data
  32. #pragma VxD_LOCKED_DATA_SEG
  33. #pragma VxD_PAGEABLE_DATA_SEG
  34. ///////////////////////////////////////////////////////////////////////////////
  35. #pragma VxD_INIT_CODE_SEG
  36. #pragma VxD_INIT_DATA_SEG
  37. extern "C" extern DRP theDRP;
  38. SYSCTL BOOL OnDeviceInit(HVM hVM, DWORD refdata)
  39. { // OnDeviceInit
  40. if (!IOS_Get_Version())
  41. return FALSE; // IOS not loaded (?)
  42. theDRP.DRP_aer = (PVOID) OnAsyncEvent;
  43. IOS_Register(&theDRP);
  44. return CLocalFileSystem::Register(CLpfs::CreateNew);
  45. } // OnDeviceInit
  46. ///////////////////////////////////////////////////////////////////////////////
  47. #pragma VxD_LOCKED_CODE_SEG
  48. #pragma VxD_LOCKED_DATA_SEG
  49. void OnAsyncEvent(PAEP aep)
  50. { // OnAsyncEvent
  51. aep->AEP_result = AEP_SUCCESS;
  52. } // OnAsyncEvent
  53. CLpfs::CLpfs()
  54. { // CLpfs::CLpfs
  55. m_openfiles = NULL;
  56. } // CLpfs::CLpfs
  57. CLpfs::~CLpfs()
  58. { // CLpfs::~CLpfs
  59. } // CLpfs::~CLpfs
  60. // Static member function:
  61. CFileSystem* CLpfs::CreateNew()
  62. { // CreateInstance
  63. return new CLpfs();
  64. } // CreateInstance
  65. ///////////////////////////////////////////////////////////////////////////////
  66. // Is the volume associated with this request on of ours?
  67. BOOL CLpfs::OurVolume(pioreq pir)
  68. { // CLpfs::OurVolume
  69. PBYTE bootsec = ReadBootSector();
  70. if (!bootsec)
  71. { // couldn't read boot sector
  72. pir->ir_error = m_error;
  73. return FALSE;
  74. } // couldn't read boot sector
  75. BOOL ours = memcmp(bootsec + 3, "WALTONEY", 8) == 0;
  76. _HeapFree(bootsec, 0);
  77. if (!ours)
  78. return FALSE;
  79. // Dummy up a root directory in sector 64 that describes a file
  80. // named HelloWorld.txt
  81. DirectoryEntry e;
  82. e.created = e.modified = e.accessed = IFSMgr_Get_DOSTime();
  83. e.size = 0;
  84. e.sector = 65;
  85. e.attr = 0;
  86. char* longname = "HelloWorld.txt";
  87. e.namelen = (BYTE) BCSToUni(e.longname, (PBYTE) longname, strlen(longname), BCS_WANSI);
  88. CreateBasis(e.basename, e.longname, e.namelen);
  89. AppendBasisTail(e.basename, 1);
  90. e.namelen >>= 1; // length should be in characters
  91. WriteSectorNow(64, (PBYTE) &e);
  92. return TRUE;
  93. } // CLpfs::OurVolume
  94. ///////////////////////////////////////////////////////////////////////////////
  95. // Is the same volume still mounted on this device?
  96. BOOL CLpfs::SameVolume(CLocalFileSystem* fs)
  97. { // CLpfs::SameVolume
  98. return TRUE;
  99. } // CLpfs::SameVolume
  100. ///////////////////////////////////////////////////////////////////////////////
  101. void CLpfs::GetVolumeLabel(PDWORD pVolSer, char* pVolLabel)
  102. { // CLpfs::GetVolumeLabel
  103. PBYTE bootsec = ReadBootSector();
  104. if (!bootsec)
  105. return;
  106. *pVolSer = *(PDWORD) (bootsec + 0x39);
  107. memcpy(pVolLabel, bootsec + 0x43, 11);
  108. pVolLabel[11] = 0;
  109. _HeapFree(bootsec, 0);
  110. } // CLpfs::GetVolumeLabel
  111. ///////////////////////////////////////////////////////////////////////////////
  112. int CLpfs::DeleteFile(pioreq pir)
  113. { // CLpfs::DeleteFile
  114. #ifdef DEBUG
  115. BYTE path[MAX_PATH];
  116. int len = UniToBCSPath(path, pir->ir_ppath->pp_elements, sizeof(path)-1, BCS_OEM);
  117. if (len < 0)
  118. len = 0;
  119. path[len] = 0;
  120. Debug_Printf("LPFS: Delete %s (attr %X)rn", path, pir->ir_attr);
  121. #endif // DEBUG
  122. return pir->ir_error = ERROR_ACCESS_DENIED;
  123. } // CLpfs::DeleteFile
  124. ///////////////////////////////////////////////////////////////////////////////
  125. int CLpfs::Dir(pioreq pir)
  126. { // CLpfs::Dir
  127. #ifdef DEBUG
  128. static char *fname[] = {"CREATE_DIR", "DELETE_DIR", "CHECK_DIR",
  129. "QUERY83_DIR", "QUERYLONG_DIR"};
  130. BYTE path[MAX_PATH];
  131. int len = UniToBCSPath(path, pir->ir_ppath->pp_elements, sizeof(path)-1, BCS_OEM);
  132. if (len < 0)
  133. len = 0;
  134. path[len] = 0;
  135. Debug_Printf("LPFS: %s %srn", fname[pir->ir_flags], path);
  136. #endif // DEBUG
  137. switch (pir->ir_flags)
  138. { // select directory function
  139. ///////////////////////////////////////////////////////////////////////////
  140. // CREATE_DIR: Create a new directory with the specified name. LPFS
  141. // only has one directory, so we always fail this call.
  142. case CREATE_DIR:
  143. return pir->ir_error = ERROR_ACCESS_DENIED;
  144. ///////////////////////////////////////////////////////////////////////////
  145. // DELETE_DIR: Delete the directory with the specified name. LPFS doesn't
  146. // let you delete the root directory, so we always fail this call.
  147. case DELETE_DIR:
  148. return pir->ir_error = ERROR_ACCESS_DENIED;
  149. ///////////////////////////////////////////////////////////////////////////
  150. // CHECK_DIR: See if the specified directory exists. LPFS only has a root
  151. // directory, so fail a check request for anything else
  152. case CHECK_DIR:
  153. if (!IFSIsRoot(pir->ir_ppath))
  154. return pir->ir_error = ERROR_PATH_NOT_FOUND;
  155. return pir->ir_error = 0;
  156. ///////////////////////////////////////////////////////////////////////////
  157. // QUERY83_DIR: Convert the input path to a pure 8.3 version in ir_ppath2.
  158. // Since LPFS only has a root directory, this is pretty easy to do.
  159. case QUERY83_DIR:
  160. memcpy(pir->ir_ppath2, pir->ir_ppath, pir->ir_ppath->pp_totalLength);
  161. return pir->ir_error = 0;
  162. ///////////////////////////////////////////////////////////////////////////
  163. // QUERYLONG_DIR: Convert the input path to a pure long-name version in
  164. // ir_ppath2. This is also pretty trivial for us.
  165. case QUERYLONG_DIR:
  166. memcpy(pir->ir_ppath2, pir->ir_ppath, pir->ir_ppath->pp_totalLength);
  167. return pir->ir_error = 0;
  168. default:
  169. ASSERT(!"Invalid DIR function");
  170. return pir->ir_error = ERROR_INVALID_FUNCTION;
  171. } // select directory function
  172. } // CLpfs::Dir
  173. ///////////////////////////////////////////////////////////////////////////////
  174. int CLpfs::FileAttributes(pioreq pir)
  175. { // CLpfs::FileAttributes
  176. #ifdef DEBUG
  177. static char *fname[] = {
  178. "GET_ATTRIBUTES",
  179. "SET_ATTRIBUTES",
  180. "GET_ATTRIB_COMP_FILESIZE",
  181. "SET_ATTRIB_MODIFY_DATETIME",
  182. "GET_ATTRIB_MODIFY_DATETIME",
  183. "SET_ATTRIB_LAST_ACCESS_DATETIME",
  184. "GET_ATTRIB_LAST_ACCESS_DATETIME",
  185. "SET_ATTRIB_CREATION_DATETIME",
  186. "GET_ATTRIB_CREATION_DATETIME",
  187. "GET_ATTRIB_FIRST_CLUST",
  188. };
  189. BYTE path[MAX_PATH];
  190. int len = UniToBCSPath(path, pir->ir_ppath->pp_elements, sizeof(path)-1, BCS_OEM);
  191. if (len < 0)
  192. len = 0;
  193. path[len] = 0;
  194. Debug_Printf("LPFS: %s %srn", fname[pir->ir_flags], path);
  195. #endif // DEBUG
  196. DirectoryEntry e;
  197. ULONG sector;
  198. if (!FindDirectoryEntry(pir->ir_ppath, &e, sector))
  199. return pir->ir_error = m_error;
  200. m_error = 0;
  201. switch (pir->ir_flags)
  202. { // select subfunction
  203. case GET_ATTRIBUTES:
  204. pir->ir_attr = e.attr;
  205. break;
  206. case SET_ATTRIBUTES:
  207. e.attr = pir->ir_attr;
  208. if (ReadOnly())
  209. m_error = ERROR_WRITE_PROTECT;
  210. else
  211. m_error = WriteSectorNow(sector, (PBYTE) &e);
  212. break;
  213. case GET_ATTRIB_COMP_FILESIZE:
  214. pir->ir_size = e.size;
  215. break;
  216. case SET_ATTRIB_MODIFY_DATETIME:
  217. e.modified = pir->ir_dostime;
  218. if (ReadOnly())
  219. m_error = ERROR_WRITE_PROTECT;
  220. else
  221. m_error = WriteSectorNow(sector, (PBYTE) &e);
  222. break;
  223. case GET_ATTRIB_MODIFY_DATETIME:
  224. pir->ir_dostime = e.modified;
  225. break;
  226. case SET_ATTRIB_LAST_ACCESS_DATETIME:
  227. e.accessed = pir->ir_dostime;
  228. if (ReadOnly())
  229. m_error = ERROR_WRITE_PROTECT;
  230. else
  231. m_error = WriteSectorNow(sector, (PBYTE) &e);
  232. break;
  233. case GET_ATTRIB_LAST_ACCESS_DATETIME:
  234. pir->ir_dostime = e.accessed;
  235. break;
  236. case SET_ATTRIB_CREATION_DATETIME:
  237. e.created = pir->ir_dostime;
  238. if (ReadOnly())
  239. m_error = ERROR_WRITE_PROTECT;
  240. else
  241. m_error = WriteSectorNow(sector, (PBYTE) &e);
  242. break;
  243. case GET_ATTRIB_CREATION_DATETIME:
  244. pir->ir_dostime = e.created;
  245. break;
  246. case GET_ATTRIB_FIRST_CLUST:
  247. return pir->ir_error = ERROR_INVALID_FUNCTION;
  248. } // select subfunction
  249. return pir->ir_error = m_error;
  250. } // CLpfs::FileAttributes
  251. ///////////////////////////////////////////////////////////////////////////////
  252. BOOL CLpfs::FindDirectoryEntry(ParsedPath* path, DirectoryEntry* ep, ULONG& sector, BOOL wantfile /* = TRUE */)
  253. { // CLpfs::FindDirectoryEntry
  254. CPosition pos(path, wantfile);
  255. GetRootDirectoryEntry(ep, sector);
  256. while (!pos.AtEnd() && FindNextDirectoryEntry(pos, ep, sector))
  257. pos.Step(); // descend through pathname
  258. return pos.AtEnd();
  259. } // CLpfs::FindDirectoryEntry
  260. void CLpfs::GetRootDirectoryEntry(DirectoryEntry* ep, ULONG& sector)
  261. { // CLpfs::GetRootDirectoryEntry
  262. memset(ep, 0, sizeof(DirectoryEntry));
  263. ep->size = sizeof(DirectoryEntry); // LPFS only has one file
  264. ep->attr = FILE_ATTRIBUTE_DIRECTORY;
  265. ep->sector = 64; // LPFS has its root directory at sector 64
  266. sector = 0;
  267. } // CLpfs::GetRootDirectoryEntry
  268. BOOL CLpfs::FindNextDirectoryEntry(const CPosition pos, DirectoryEntry* ep, ULONG& sector)
  269. { // CLpfs::FindNextDirectoryEntry
  270. ASSERT(!pos.AtEnd());
  271. PathElement* element = pos.Current();
  272. int elsize = element->pe_length;
  273. BOOL islong = elsize > 12;
  274. USHORT basename[11];
  275. if (!islong)
  276. ShortToLossyFcb(basename, element->pe_unichars, elsize);
  277. // LPFS has a very non-optimal directory format in which each entry
  278. // occupies an entire sector and all the entries are contiguous on the
  279. // disk. You'd never design a real file system this way, of course.
  280. // It only works here because we only allow one file and one level
  281. // of directory.
  282. sector = ep->sector; // sector of 1st directory entry
  283. ULONG lastsector = sector + (ep->size / sizeof(DirectoryEntry));
  284. DirectoryEntry e;
  285. for (; sector < lastsector; ++sector)
  286. { // search directory
  287. if (m_error = ReadSectorNow(sector, (PBYTE) &e))
  288. return FALSE; // error reading directory entry
  289. // Check for a match between the path element and the upper-case
  290. // version of the long name in the directory entry
  291. if (e.namelen*2 == elsize-2)
  292. { // check for matching long name
  293. USHORT ucname[LFNMAXNAMELEN];
  294. UniToUpper(ucname, e.longname, e.namelen*2);
  295. if (memcmp(ucname, element->pe_unichars, elsize-2) == 0)
  296. break; // long names match
  297. } // check for matching long name
  298. // If the path element is in 8.3 format, also check to see if it's
  299. // the same as the short name recorded in the directory entry
  300. if (!islong && memcmp(ep->basename, basename, sizeof(basename)) == 0)
  301. break; // short names match
  302. } // search directory
  303. if (sector < lastsector)
  304. { // found the entry
  305. *ep = e;
  306. return TRUE;
  307. } // found the entry
  308. m_error = pos.AtEnd() ? ERROR_FILE_NOT_FOUND : ERROR_PATH_NOT_FOUND;
  309. return FALSE;
  310. } // CLpfs::FindNextDirectoryEntry
  311. ///////////////////////////////////////////////////////////////////////////////
  312. int CLpfs::FlushVolume(pioreq pir)
  313. { // CLpfs::FlushVolume
  314. #ifdef DEBUG
  315. Debug_Printf("LPFS: FLUSH");
  316. if (pir->ir_options & VOL_DISCARD_CACHE)
  317. Debug_Printf(" VOL_DISCARD_CACHE");
  318. if (pir->ir_options & VOL_REMOUNT)
  319. Debug_Printf(" VOL_REMOUNT");
  320. Debug_Printf("rn");
  321. #endif DEBUG
  322. ASSERT(m_outstanding == 0);
  323. if (pir->ir_options & VOL_REMOUNT)
  324. { // remount volume
  325. return pir->ir_error = ERROR_INVALID_FUNCTION; // TODO
  326. } // remount volume
  327. // If we were using VCACHE, we'd flush our cache buffers now.
  328. // In addition, we need to send our disk drive a flush command.
  329. PIOR ior = CreateIOR((pir->ir_options & VOL_DISCARD_CACHE)
  330. ? IOR_FLUSH_DRIVE_AND_DISCARD : IOR_FLUSH_DRIVE, 0);
  331. if (!ior)
  332. return pir->ir_error = ERROR_NOT_ENOUGH_MEMORY;
  333. SendCommandAndWait(ior);
  334. DestroyIOR(ior); // ignore any error
  335. return pir->ir_error = 0;
  336. } // CLpfs::FlushVolume
  337. ///////////////////////////////////////////////////////////////////////////////
  338. int CLpfs::GetDiskInfo(pioreq pir)
  339. { // CLpfs::GetDiskInfo
  340. #ifdef DEBUG
  341. Debug_Printf("LPFS: GETDISKINFOrn");
  342. #endif
  343. pir->ir_length = 512; // bytes per sector
  344. pir->ir_size = 4096; // total number of allocation units
  345. pir->ir_sectors = 1; // number of sectors per allocation unit
  346. pir->ir_numfree = 42; // number of free allocation units (a lie)
  347. return pir->ir_error = 0;
  348. } // CLpfs::GetDiskInfo
  349. ///////////////////////////////////////////////////////////////////////////////
  350. int CLpfs::OpenFile(pioreq pir)
  351. { // CLpfs::OpenFile
  352. #ifdef DEBUG
  353. BYTE path[MAX_PATH];
  354. int len = UniToBCSPath(path, pir->ir_ppath->pp_elements, sizeof(path)-1, BCS_OEM);
  355. if (len < 0)
  356. len = 0;
  357. path[len] = 0;
  358. Debug_Printf("LPFS: OPEN %s, access %X, action %X, attr %Xrn", path, pir->ir_flags, pir->ir_options, pir->ir_attr);
  359. #endif // DEBUG
  360. // Since LPFS only supports one file that can't be deleted or
  361. // renamed, it's always an error if we're asked to open a file
  362. // with some other name. A real file system would go on to
  363. // create a new file, etc.
  364. int mode = pir->ir_flags & ACCESS_MODE_MASK;
  365. int options = pir->ir_options;
  366. DirectoryEntry e;
  367. ULONG sector;
  368. if (!FindDirectoryEntry(pir->ir_ppath, &e, sector))
  369. { // file not found
  370. if (options & ACTION_NEXISTS_CREATE)
  371. m_error = ERROR_ACCESS_DENIED; // we're not going to create it
  372. return pir->ir_error = m_error;
  373. } // file not found
  374. if (!(options & (ACTION_EXISTS_OPEN | ACTION_TRUNCATE)))
  375. return pir->ir_error = ERROR_FILE_EXISTS; // doesn't want existing file
  376. // We're going to open the file, so capture the current time
  377. e.accessed = IFSMgr_Get_DOSTime();
  378. if (mode == ACCESS_WRITEONLY || mode == ACCESS_READWRITE)
  379. e.modified = e.accessed;
  380. if (m_error = WriteSectorNow(sector, (PBYTE) &e))
  381. return pir->ir_error = m_error;
  382. #undef vt
  383. #define vt(f) f##Thunk,
  384. static hndlmisc openmisc = {
  385. IFS_VERSION,
  386. IFS_REVISION,
  387. NUM_HNDLMISC, {
  388. vt(FileSeek)
  389. vt(CloseFile)
  390. vt(CommitFile)
  391. vt(LockFile)
  392. vt(FileDateTime)
  393. vt(EmptyFunc) // NamedPipeUNCRequest
  394. vt(EmptyFunc) // NamedPipeHandleInfo
  395. vt(EnumerateHandle)
  396. }};
  397. CFile* fp = new CFile(this, &e, sector);
  398. if (mode == ACCESS_WRITEONLY || mode == ACCESS_READWRITE)
  399. { // open for writing
  400. fp->m_flags |= CFile::FF_OUTPUT;
  401. if (options & ACTION_TRUNCATE)
  402. TruncateFile(fp);
  403. fp->m_pos = fp->m_size; // append from here
  404. } // open for writing
  405. pir->ir_fh = (fh_t) fp;
  406. pir->ir_dostime = e.modified;
  407. pir->ir_size = e.size;
  408. pir->ir_attr = e.attr;
  409. SetHandleFunc(pir, ReadFileThunk, WriteFileThunk, &openmisc);
  410. return pir->ir_error = 0;
  411. } // CLpfs::OpenFile
  412. ///////////////////////////////////////////////////////////////////////////////
  413. CLpfs::CFile::CFile(CLpfs* fs, CLpfs::DirectoryEntry* ep, ULONG dirsector)
  414. { // CFile::CFile
  415. m_next = fs->m_openfiles;
  416. m_prev = NULL;
  417. if (m_next)
  418. m_next->m_prev = this;
  419. fs->m_openfiles = this;
  420. m_fs = fs;
  421. m_direntry = dirsector;
  422. m_size = ep->size;
  423. m_pos = 0;
  424. m_sector = ep->sector;
  425. m_flags = 0;
  426. } // CFile::CFile
  427. CLpfs::CFile::~CFile()
  428. { // CFile::~CFile
  429. if (m_next)
  430. m_next->m_prev = m_prev;
  431. if (m_prev)
  432. m_prev->m_next = m_next;
  433. else
  434. m_fs->m_openfiles = m_next;
  435. } // CFile::~CFile
  436. ///////////////////////////////////////////////////////////////////////////////
  437. int CLpfs::RenameFile(pioreq pir)
  438. { // CLpfs::RenameFile
  439. #ifdef DEBUG
  440. BYTE path1[MAX_PATH], path2[MAX_PATH];
  441. int len = UniToBCSPath(path1, pir->ir_ppath->pp_elements, sizeof(path1)-1, BCS_OEM);
  442. if (len < 0)
  443. len = 0;
  444. path1[len] = 0;
  445. len = UniToBCSPath(path2, pir->ir_ppath2->pp_elements, sizeof(path2)-1, BCS_OEM);
  446. if (len < 0)
  447. len = 0;
  448. path2[len] = 0;
  449. Debug_Printf("LPFS: RENAME %s (attr %X) to %s (attr %X)rn", path1, pir->ir_attr, path2, pir->ir_attr2);
  450. #endif // DEBUG
  451. // Make sure we're not dealing with a read-only volume
  452. if (ReadOnly())
  453. return pir->ir_error = ERROR_WRITE_PROTECT;
  454. // Prepare for a wild-card search over the source directory. There can
  455. // only be wild cards if the source name is in 8.3 format, and LPFS
  456. // ignores the problem anyway when we finally get to the point of
  457. // constructing a new name, so this generality is actually pretty useless
  458. // here.
  459. int numrenamed = 0;
  460. CScanPos pos(this);
  461. DirectoryEntry e;
  462. if ((m_error = pos.Prepare(pir, &e)))
  463. return pir->ir_error = m_error;
  464. // Locate the target directory. In LPFS, it will always be the same as
  465. // the source directory (because there's only one directory).
  466. BOOL newlong = (pir->ir_attr2 & (FILE_FLAG_KEEP_CASE | FILE_FLAG_IS_LFN)) != 0;
  467. PathElement* pattern = IFSLastElement(pir->ir_ppath2);
  468. int elsize = pattern->pe_length;
  469. string_t newname;
  470. if (pir->ir_attr2 & FILE_FLAG_KEEP_CASE)
  471. newname = pir->ir_uFName;
  472. else
  473. newname = pattern->pe_unichars;
  474. if (elsize-2 > sizeof(e.longname))
  475. return pir->ir_error = ERROR_ACCESS_DENIED; // new name is too long
  476. ULONG junk;
  477. DirectoryEntry tde; // description of target directory
  478. if (!FindDirectoryEntry(pir->ir_ppath2, &tde, junk, FALSE))
  479. { // can't find target directory
  480. if (m_error == ERROR_FILE_NOT_FOUND)
  481. m_error = ERROR_PATH_NOT_FOUND;
  482. return pir->ir_error = m_error;
  483. } // can't find target directory
  484. BOOL samedir = TRUE; // always true for LPFS
  485. // Locate each of the files that's supposed to be renamed
  486. while ((m_error = pos.FindNext(&e)) == 0)
  487. { // for each target file
  488. ULONG esector = pos.sector; // where the current directory entry is
  489. // Determine the new name for this file. We'll only have wild cards
  490. // for an 8.3 name, which makes it relatively straightforward (but
  491. // still beyond the scope of this sample) to construct the new name.
  492. ; // I.e., just use unmodified "newname" & "pattern"
  493. // We'll scan the directory looking for the new name of the file.
  494. // As part of the process, also compute a numeric "tail" for the
  495. // new short name of the file
  496. USHORT basename[11];
  497. int maxtail = 0;
  498. CreateBasis(basename, newname, elsize-2);
  499. ULONG sector = tde.sector; // starting point for rescan
  500. ULONG lastsector = sector + (tde.size / sizeof(DirectoryEntry));
  501. DirectoryEntry dup;
  502. for (; sector < lastsector; ++sector)
  503. { // look for duplicate of new name
  504. if (samedir && sector == esector)
  505. continue; // skip looking at the target entry
  506. if ((m_error = ReadSectorNow(sector, (PBYTE) &dup)))
  507. return pir->ir_error = m_error;
  508. // To check for a duplicate long name, use the upper-case
  509. // version of the name in the directory because file
  510. // search operations are case-insensitive
  511. if (dup.namelen*2 == elsize-2)
  512. { // check for duplicate long name
  513. USHORT ucname[LFNMAXNAMELEN];
  514. UniToUpper(ucname, dup.longname, elsize-2);
  515. if (memcmp(ucname, pattern->pe_unichars, elsize-2) == 0)
  516. return pir->ir_error = ERROR_ACCESS_DENIED; // duplicate name
  517. } // check for duplicate long name
  518. // See if the short name of this file matches the new basis name
  519. // except for the numeric tail. If so, remember the largest
  520. // tail value we ever see.
  521. int tail = MatchBasisName(basename, dup.basename);
  522. if (tail > maxtail)
  523. maxtail = tail;
  524. if (tail == -1 && !newlong)
  525. return pir->ir_error = ERROR_ACCESS_DENIED; // duplicate name
  526. } // look for duplicate of new name
  527. // No duplicate, so rename the file. 
  528. memcpy(e.longname, newname, elsize-2);
  529. e.namelen = (elsize-2)/2;
  530. if (newlong)
  531. AppendBasisTail(basename, maxtail+1); // make up a short name
  532. memcpy(e.basename, basename, sizeof(e.basename));
  533. // In real life, we might need to allocate a new entry in the target
  534. // directory. In LPFS, we're always renaming in the source directory,
  535. // so just rewrite the entry we found earlier.
  536. if (samedir)
  537. { // rewrite directory entry
  538. m_error = WriteSectorNow(esector, (PBYTE) &e);
  539. if (m_error)
  540. return pir->ir_error = m_error;
  541. } // rewrite directory entry
  542. else
  543. ASSERT(FALSE); // can't happen in LPFS
  544. ++numrenamed;
  545. } // for each target file
  546. if (m_error == ERROR_NO_MORE_FILES)
  547. if (numrenamed)
  548. m_error = 0; // had at least one match
  549. else
  550. m_error = ERROR_FILE_NOT_FOUND;
  551. return pir->ir_error = m_error;
  552. } // CLpfs::RenameFile
  553. ///////////////////////////////////////////////////////////////////////////////
  554. int CLpfs::SearchFile(pioreq pir)
  555. { // CLpfs::SearchFile
  556. #ifdef DEBUG
  557. static char* fname[] = {"SEARCH_FIRST", "SEARCH_NEXT"};
  558. BYTE path[MAX_PATH];
  559. int len = UniToBCSPath(path, pir->ir_ppath->pp_elements, sizeof(path)-1, BCS_OEM);
  560. if (len < 0)
  561. len = 0;
  562. path[len] = 0;
  563. Debug_Printf("LPFS: %s %s (attr %X)rn", fname[pir->ir_flags], path, pir->ir_attr);
  564. #endif // DEBUG
  565. switch (pir->ir_flags)
  566. { // select subfunction
  567. case SEARCH_FIRST:
  568. { // SEARCH_FIRST
  569. srch_entry* sep = (srch_entry*) pir->ir_data;
  570. sep->se_key.sk_attr = pir->ir_attr;
  571. // Handle request for volume label as a special case
  572. if (LOBYTE(pir->ir_attr) == FILE_ATTRIBUTE_LABEL)
  573. { // looking for volume label
  574. PBYTE bootsec = ReadBootSector();
  575. if (bootsec)
  576. { // copy volume label
  577. memcpy(sep->se_name, bootsec + 43, 11);
  578. sep->se_name[11] = 0;
  579. _HeapFree(bootsec, 0);
  580. return pir->ir_error = 0;
  581. } // copy volume label
  582. return pir->ir_error = m_error;
  583. } // looking for volume label
  584. BYTE excludemask = (~pir->ir_attr) & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY);
  585. // Convert search name (which ought to be an 8.3 name, possibly
  586. // with wildcards) to an FCB format name for use with IFSMgr_MetaMatch
  587. PathElement* name = (PathElement*)((PBYTE) pir->ir_ppath + pir->ir_ppath->pp_prefixLength);
  588. ASSERT(!(pir->ir_attr & FILE_FLAG_IS_LFN));
  589. USHORT basename[11];
  590. ShortToLossyFcb(basename, name->pe_unichars, name->pe_length);
  591. int matchsemantics = UFLG_DOS;
  592. if (pir->ir_attr & FILE_FLAG_WILDCARDS)
  593. matchsemantics |= UFLG_META;
  594. // Find the directory entry for the subdirectory within which
  595. // we're supposed to search
  596. DirectoryEntry e;
  597. ULONG sector;
  598. if (!FindDirectoryEntry(pir->ir_ppath, &e, sector, FALSE))
  599. { // can't find directory
  600. if (m_error == ERROR_FILE_NOT_FOUND || m_error == ERROR_PATH_NOT_FOUND)
  601. m_error = ERROR_PATH_NOT_FOUND;
  602. return pir->ir_error = m_error;
  603. } // can't find directory
  604. // Search the directory for the first matching file
  605. sector = e.sector;
  606. ULONG lastsector = sector + (e.size / sizeof(DirectoryEntry));
  607. for (; sector < lastsector; ++sector)
  608. { // search directory
  609. if (m_error = ReadSectorNow(sector, (PBYTE) &e))
  610. return pir->ir_error = m_error;
  611. if (e.attr & excludemask)
  612. continue;
  613. if (IFSMgr_MetaMatch(basename, e.basename, matchsemantics))
  614. break; // found match
  615. } // search directory
  616. if (sector >= lastsector)
  617. return pir->ir_error = ERROR_NO_MORE_FILES;
  618. // We have a match. Fill in the DOS-format find-data area
  619. // at ir_data. In real life, we'd record enough info in the
  620. // 4 bytes at sk_localFSD to allow us to resume the search
  621. // at SEARCH_NEXT. Because of our poor choice of data structures,
  622. // 4 bytes wouldn't be enough, though.
  623. memcpy(sep->se_key.sk_pattern, basename, sizeof(basename));
  624. sep->se_attrib = e.attr;
  625. *(dos_time*)&sep->se_time = e.modified;
  626. sep->se_size = e.size;
  627. USHORT shortname[12];
  628. int len = FcbToShort(shortname, e.basename, FALSE);
  629. sep->se_name[UniToBCS((PBYTE) sep->se_name, shortname, len, 12, BCS_OEM)] = 0;
  630. return pir->ir_error = 0;
  631. } // SEARCH_FIRST
  632. case SEARCH_NEXT:
  633. { // SEARCH_FIRST
  634. return pir->ir_error = ERROR_NO_MORE_FILES; // because there's only one on the disk
  635. } // SEARCH_FIRST
  636. default:
  637. ASSERT(FALSE);
  638. return pir->ir_error = ERROR_INVALID_FUNCTION;
  639. } // select subfunction
  640. } // CLpfs::SearchFile
  641. ///////////////////////////////////////////////////////////////////////////////
  642. int CLpfs::QueryResourceInfo(pioreq pir)
  643. { // CLpfs::QueryResourceInfo
  644. #ifdef DEBUG
  645. BYTE path[MAX_PATH];
  646. if (pir->ir_options < 2)
  647. { // level 0 or 1 has name
  648. int len = UniToBCSPath(path, pir->ir_ppath->pp_elements, sizeof(path)-1, BCS_OEM);
  649. if (len < 0)
  650. len = 0;
  651. path[len] = 0;
  652. } // level 0 or 1 has name
  653. else
  654. path[0] = 0;
  655. Debug_Printf("LPFS: QUERY level %d %srn", pir->ir_options, path);
  656. #endif // DEBUG
  657. if (pir->ir_options == 2)
  658. { // answer level 2 query
  659. pir->ir_length = MAKELONG(sizeof(((DirectoryEntry*) NULL)->longname), 260);
  660. pir->ir_options = FS_CASE_IS_PRESERVED | FS_UNICODE_STORED_ON_DISK | FS_VOL_SUPPORTS_LONG_NAMES;
  661. return pir->ir_error = 0;
  662. } // answer level 2 query
  663. return pir->ir_error = ERROR_INVALID_FUNCTION;
  664. } // CLpfs::QueryResourceInfo
  665. ///////////////////////////////////////////////////////////////////////////////
  666. // It's okay for an FSD to fail a GetDiskParms request if it doesn't map
  667. // an MS-DOS drive.
  668. int CLpfs::GetDiskParms(pioreq pir)
  669. { // CLpfs::GetDiskParms
  670. #ifdef DEBUG
  671. Debug_Printf("LPFS: GETDISKPARMSrn");
  672. #endif
  673. return pir->ir_error = ERROR_INVALID_FUNCTION;
  674. } // CLpfs::GetDiskParms
  675. ///////////////////////////////////////////////////////////////////////////////
  676. int CLpfs::FindFirstFile(pioreq pir)
  677. { // CLpfs::FindFirstFile
  678. #ifdef DEBUG
  679. BYTE path[MAX_PATH];
  680. int len = UniToBCSPath(path, pir->ir_ppath->pp_elements, sizeof(path)-1, BCS_OEM);
  681. if (len < 0)
  682. len = 0;
  683. path[len] = 0;
  684. Debug_Printf("LPFS: FIND_FIRST %s (attr %X)rn", path, pir->ir_attr);
  685. #endif // DEBUG
  686. // Set output parameters for IFS to use in case we return success
  687. #undef vt
  688. #define vt(f) f##Thunk,
  689. static hndlmisc findmisc = {
  690. IFS_VERSION,
  691. IFS_REVISION,
  692. NUM_HNDLMISC, {
  693. vt(EmptyFunc) // HM_SEEK
  694. vt(FindClose) // HM_CLOSE
  695. vt(EmptyFunc) // HM_COMMIT
  696. vt(EmptyFunc) // HM_FILELOCKS
  697. vt(EmptyFunc) // HM_FILETIMES
  698. vt(EmptyFunc) // HM_PIPEREQUEST
  699. vt(EmptyFunc) // HM_HANDLEINFO
  700. vt(EnumerateHandle) // HM_ENUMHANDLE
  701. }};
  702. // It's not obvious from the doc, but ir_hfunc already points
  703. // to a "hndlfunc" structure that we're supposed to complete.
  704. SetHandleFunc(pir, FindNextFileThunk, EmptyFuncThunk, &findmisc);
  705. _WIN32_FIND_DATA* fdp = (_WIN32_FIND_DATA*) pir->ir_data;
  706. // Handle request for volume label as a special case
  707. if (LOBYTE(pir->ir_attr) == FILE_ATTRIBUTE_LABEL)
  708. { // looking for volume label
  709. PBYTE bootsec = ReadBootSector();
  710. if (bootsec)
  711. { // copy volume label
  712. BCSToUni(fdp->cFileName, bootsec + 43, 11, BCS_OEM);
  713. fdp->cFileName[11] = 0; // no-one will care about 8.3 version of label
  714. fdp->cAlternateFileName[0] = 0;
  715. fdp->dwFileAttributes = FILE_ATTRIBUTE_LABEL;
  716. pir->ir_fh = NULL; // should never need this
  717. _HeapFree(bootsec, 0);
  718. return pir->ir_error = 0;
  719. } // copy volume label
  720. return pir->ir_error = m_error;
  721. } // looking for volume label
  722. DirectoryEntry e;
  723. CScanPos* pos = new CScanPos(this);
  724. if ((m_error = pos->Prepare(pir, &e)) || (m_error = pos->FindNext(&e)))
  725. { // directory or file not found
  726. delete pos;
  727. return pir->ir_error = m_error;
  728. } // directory or file not found
  729. FindFill(fdp, &e);
  730. return pir->ir_error = 0;
  731. } // CLpfs::FindFirstFile
  732. ///////////////////////////////////////////////////////////////////////////////
  733. int CLpfs::FindNextFile(pioreq pir)
  734. { // CLpfs::FindNextFile
  735. #ifdef DEBUG
  736. Debug_Printf("LPFS: FINDNEXTrn");
  737. #endif
  738. CScanPos* pos = (CScanPos*) pir->ir_fh;
  739. DirectoryEntry e;
  740. if (!pos || (m_error = pos->FindNext(&e)))
  741. return pir->ir_error = ERROR_NO_MORE_FILES;
  742. FindFill((_WIN32_FIND_DATA*) pir->ir_data, &e);
  743. return pir->ir_error = 0;
  744. } // CLpfs::FindNextFile
  745. ///////////////////////////////////////////////////////////////////////////////
  746. int CLpfs::FindClose(pioreq pir)
  747. { // CLpfs::FindClose
  748. #ifdef DEBUG
  749. Debug_Printf("LPFS: FINDCLOSErn");
  750. #endif
  751. CScanPos* pos = (CScanPos*) pir->ir_fh;
  752. if (pos)
  753. delete pos;
  754. return pir->ir_error = 0;
  755. } // CLpfs::FindClose
  756. ///////////////////////////////////////////////////////////////////////////////
  757. void CLpfs::FindFill(_WIN32_FIND_DATA* fdp, DirectoryEntry* ep)
  758. { // CLpfs::FindFill
  759. fdp->dwFileAttributes = ep->attr;
  760. fdp->ftCreationTime = IFSMgr_DosToWin32Time(ep->created);
  761. fdp->ftLastAccessTime = IFSMgr_DosToWin32Time(ep->accessed);
  762. fdp->ftLastWriteTime = IFSMgr_DosToWin32Time(ep->modified);
  763. fdp->nFileSizeHigh = 0;
  764. fdp->nFileSizeLow = ep->size;
  765. memcpy(fdp->cFileName, ep->longname, ep->namelen*2);
  766. fdp->cFileName[ep->namelen] = 0;
  767. fdp->cAlternateFileName[FcbToShort(fdp->cAlternateFileName, ep->basename, FALSE)/2] = 0;
  768. } // CLpfs::FindFill
  769. ///////////////////////////////////////////////////////////////////////////////
  770. int CLpfs::EnumerateHandle(pioreq pir)
  771. { // CLpfs::EnumerateHandle
  772. #ifdef DEBUG
  773. static char *fname[] = {
  774. "ENUMH_GETFILEINFO",
  775. "ENUMH_GETFILENAME",
  776. "ENUMH_GETFINDINFO",
  777. "ENUMH_RESUMEFIND",
  778. "ENUMH_RESYNCFILEDIR",
  779. };
  780. Debug_Printf("LPFS: %s %Xrn", fname[pir->ir_flags], pir->ir_fh);
  781. #endif
  782. return pir->ir_error = ERROR_INVALID_FUNCTION;
  783. } // CLpfs::EnumerateHandle
  784. ///////////////////////////////////////////////////////////////////////////////
  785. int CLpfs::ReadFile(pioreq pir)
  786. { // CLpfs::ReadFile
  787. CFile* fp = (CFile*) pir->ir_fh;
  788. PBYTE dp = (PBYTE) pir->ir_data;
  789. ULONG nbytes = (ULONG) pir->ir_length;
  790. ULONG pos = (ULONG) pir->ir_pos;
  791. if (pos > fp->m_size)
  792. pos = fp->m_size;
  793. if (pos + nbytes > fp->m_size)
  794. nbytes = fp->m_size - pos;
  795. // In real life, you should build and execute an asynchronous read
  796. // for a realistic number of sectors, probably into a sector cache
  797. // maintained via VCACHE. In this simple example, we'll just do a
  798. // synchronous read for the one-and-only sector the file can
  799. // occupy.
  800. BYTE data[512];
  801. m_error = ReadSectorNow(fp->m_sector, data);
  802. memcpy(dp, data + pos, nbytes);
  803. pos += nbytes;
  804. fp->m_pos = pos;
  805. pir->ir_pos = pos;
  806. pir->ir_length = nbytes;
  807. return pir->ir_error = m_error;
  808. } // CLpfs::ReadFile
  809. ///////////////////////////////////////////////////////////////////////////////
  810. int CLpfs::WriteFile(pioreq pir)
  811. { // CLpfs::WriteFile
  812. if (ReadOnly())
  813. return pir->ir_error = ERROR_WRITE_PROTECT;
  814. CFile* fp = (CFile*) pir->ir_fh;
  815. ASSERT(fp->m_flags & CFile::FF_OUTPUT);
  816. PBYTE dp = (PBYTE) pir->ir_data;
  817. ULONG nbytes = (ULONG) pir->ir_length;
  818. ULONG pos = (ULONG) pir->ir_pos;
  819. // In real life, you'd extend the file so it's big enough to
  820. // hold the new data. You'd also schedule asynchronous writes,
  821. // probably through a cache maintained via VCACHE. In this example,
  822. // we'll just rewrite the data immediately.
  823. if (pos > 512)
  824. pos = 512;
  825. if (pos + nbytes > 512)
  826. nbytes = 512-pos;
  827. if (nbytes == 0)
  828. { // truncate file at this location
  829. fp->m_pos = pos;
  830. TruncateFile(fp);
  831. } // truncate file at this location
  832. else
  833. { // write some data
  834. BYTE data[512];
  835. if (m_error = ReadSectorNow(fp->m_sector, data))
  836. return pir->ir_error = m_error;
  837. memcpy(data + pos, dp, nbytes);
  838. if (m_error = WriteSectorNow(fp->m_sector, data))
  839. return pir->ir_error = m_error;
  840. } // write some data
  841. pos += nbytes;
  842. fp->m_pos = pos;
  843. if (pos > fp->m_size)
  844. fp->m_size = pos;
  845. pir->ir_length = nbytes;
  846. pir->ir_pos = pos;
  847. return pir->ir_error = 0;
  848. } // CLpfs::WriteFile
  849. ///////////////////////////////////////////////////////////////////////////////
  850. void CLpfs::TruncateFile(CLpfs::CFile* fp)
  851. { // CLpfs::TruncateFile
  852. fp->m_size = fp->m_pos;
  853. } // CLpfs::TruncateFile
  854. ///////////////////////////////////////////////////////////////////////////////
  855. int CLpfs::FileSeek(pioreq pir)
  856. { // CLpfs::FileSeek
  857. CFile* fp = (CFile*) pir->ir_fh;
  858. ULONG pos = pir->ir_pos;
  859. switch (pir->ir_flags)
  860. { // select on seek origin option
  861. case FILE_BEGIN:
  862. break; // relative to beginning
  863. case FILE_END:
  864. pos += fp->m_size; // relative to file size
  865. break;
  866. default:
  867. ASSERT(FALSE);
  868. break;
  869. } // select on seek origin option
  870. fp->m_pos = pos;
  871. pir->ir_pos = pos;
  872. return pir->ir_error = 0;
  873. } // CLpfs::FileSeek
  874. ///////////////////////////////////////////////////////////////////////////////
  875. int CLpfs::CloseFile(pioreq pir)
  876. { // CLpfs::CloseFile
  877. CFile* fp = (CFile*) pir->ir_fh;
  878. if (fp->m_flags & CFile::FF_OUTPUT)
  879. { // file was open for output
  880. DirectoryEntry e;
  881. if (m_error = ReadSectorNow(fp->m_direntry, (PBYTE) &e))
  882. return pir->ir_error = m_error;
  883. e.size = fp->m_size;
  884. m_error = WriteSectorNow(fp->m_direntry, (PBYTE) &e);
  885. } // file was open for output
  886. delete fp;
  887. pir->ir_pos = 0; // no file locks to remember
  888. return pir->ir_error = m_error;
  889. } // CLpfs::CloseFile
  890. ///////////////////////////////////////////////////////////////////////////////
  891. int CLpfs::FileDateTime(pioreq pir)
  892. { // CLpfs::FileDateTime
  893. CFile* fp = (CFile*) pir->ir_fh;
  894. DirectoryEntry e;
  895. if (m_error = ReadSectorNow(fp->m_direntry, (PBYTE) &e))
  896. return pir->ir_error = m_error;
  897. BOOL changed = FALSE;
  898. switch (pir->ir_flags)
  899. { // perform requested operation
  900. case GET_MODIFY_DATETIME:
  901. pir->ir_dostime = e.modified;
  902. pir->ir_options = 0;
  903. break;
  904. case SET_MODIFY_DATETIME:
  905. if (!(fp->m_flags & CFile::FF_OUTPUT))
  906. return pir->ir_error = ERROR_ACCESS_DENIED;
  907. e.modified = pir->ir_dostime;
  908. changed = TRUE;
  909. break;
  910. case GET_LAST_ACCESS_DATETIME:
  911. pir->ir_dostime = e.accessed;
  912. pir->ir_options = 0;
  913. break;
  914. case SET_LAST_ACCESS_DATETIME:
  915. if (!(fp->m_flags & CFile::FF_OUTPUT))
  916. return pir->ir_error = ERROR_ACCESS_DENIED;
  917. e.accessed = pir->ir_dostime;
  918. changed = TRUE;
  919. break;
  920. case GET_CREATION_DATETIME:
  921. pir->ir_dostime = e.created;
  922. pir->ir_options = 0;
  923. break;
  924. case SET_CREATION_DATETIME:
  925. if (!(fp->m_flags & CFile::FF_OUTPUT))
  926. return pir->ir_error = ERROR_ACCESS_DENIED;
  927. e.created = pir->ir_dostime;
  928. changed = TRUE;
  929. break;
  930. } // perform requested operation
  931. if (changed)
  932. m_error = WriteSectorNow(fp->m_direntry, (PBYTE) &e);
  933. else
  934. m_error = 0;
  935. return pir->ir_error = m_error;
  936. } // CLpfs::FileDateTime
  937. ///////////////////////////////////////////////////////////////////////////////
  938. int CLpfs::CScanPos::Prepare(pioreq pir, DirectoryEntry* ep)
  939. { // CLpfs::CScanPos::Prepare
  940. PathElement* name = IFSLastElement(pir->ir_ppath);
  941. namelen = name->pe_length;
  942. memcpy(pattern, name->pe_unichars, namelen); // copy & null terminate
  943. pattern[namelen/2] = 0;
  944. matchsemantics = 0;
  945. attr = pir->ir_attr;
  946. if (attr & (FILE_FLAG_KEEP_CASE | FILE_FLAG_IS_LFN))
  947. matchsemantics |= UFLG_NT;
  948.         else
  949.             matchsemantics |= UFLG_DOS;
  950. if (attr & FILE_FLAG_WILDCARDS)
  951. matchsemantics |= UFLG_META;
  952. hasdot = (attr & FILE_FLAG_HAS_DOT) != 0;
  953. excludemask = (BYTE) ((~attr) & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY));
  954. if (!fs->FindDirectoryEntry(pir->ir_ppath, ep, sector, FALSE))
  955. { // can't find directory
  956. if (fs->m_error == ERROR_FILE_NOT_FOUND || fs->m_error == ERROR_PATH_NOT_FOUND)
  957. fs->m_error = ERROR_PATH_NOT_FOUND;
  958. return fs->m_error;
  959. } // can't find directory
  960. sector = ep->sector - 1; // because FindNext will increment to start with
  961. lastsector = ep->sector + (ep->size / sizeof(DirectoryEntry));
  962. return 0;
  963. } // CLpfs::CScanPos::Prepare
  964. ///////////////////////////////////////////////////////////////////////////////
  965. int CLpfs::CScanPos::FindNext(DirectoryEntry* ep)
  966. { // CLpfs::CScanPos::FindNext
  967. for (++sector; sector < lastsector; ++sector)
  968. { // search directory
  969. if (fs->m_error = fs->ReadSectorNow(sector, (PBYTE) ep))
  970. return fs->m_error; // error reading directory entry
  971. // Modified version of TestMustMatch macro from IFS.H, for use
  972. // here since we don't have an ioreq with must-match flags during
  973. // a FindNextFile
  974. #undef TestMustMatch
  975. #define TestMustMatch(ir_attr, attr) 
  976. ((((ir_attr & (attr)<<8) ^ ir_attr) & FILE_ATTRIBUTE_MUSTMATCH) == 0)
  977. if (ep->attr & excludemask || !TestMustMatch(attr, ep->attr))
  978. continue;
  979. // IFSMgr_MetaMatch expects to compare two upper-cased, null
  980. // terminated Unicode strings (nice of them to tell us...)
  981. USHORT thisname[LFNMAXNAMELEN+1];
  982. thisname[UniToUpper(thisname, ep->longname, ep->namelen*2)/2] = 0;
  983. if (IFSMgr_MetaMatch(pattern, thisname, matchsemantics))
  984. return 0; // found match
  985. // No match under the long name. Try the short name
  986. thisname[FcbToShort(thisname, ep->basename, hasdot)/2] = 0;
  987. if (IFSMgr_MetaMatch(pattern, thisname, matchsemantics))
  988. return 0; // found match
  989. } // search directory
  990. return ERROR_NO_MORE_FILES;
  991. } // CLpfs::CScanPos::FindNext
  992. ///////////////////////////////////////////////////////////////////////////////
  993. ///////////////////////////////////////////////////////////////////////////////
  994. ///////////////////////////////////////////////////////////////////////////////
  995. ///////////////////////////////////////////////////////////////////////////////