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.

2908 lines
80 KiB

  1. /*++
  2. Copyright (c) 1990-2001 Microsoft Corporation
  3. Module Name:
  4. fatntfs.cxx
  5. Abstract:
  6. This module implements the conversion from FAT to NTFS
  7. Author:
  8. Ramon J. San Andres (ramonsa) Sep-19-1991
  9. Environment:
  10. ULIB, User Mode
  11. --*/
  12. #include <pch.cxx>
  13. //
  14. // ULIB include files
  15. //
  16. #define _NTAPI_ULIB_
  17. #include "ulib.hxx"
  18. #include "array.hxx"
  19. #include "drive.hxx"
  20. #include "hmem.hxx"
  21. #include "wstring.hxx"
  22. #include "rtmsg.h"
  23. //#define CONVERT_PERF_COUNTERS 1
  24. //
  25. // IFSUTIL include files
  26. //
  27. #include "secrun.hxx"
  28. #if defined ( _AUTOCONV_ )
  29. #include "ifssys.hxx"
  30. #else
  31. #if defined(CONVERT_PERF_COUNTERS)
  32. #include <stdio.h>
  33. #endif
  34. #endif
  35. //
  36. // CUFAT include files
  37. //
  38. #include "fatsa.hxx"
  39. #include "fatntfs.hxx"
  40. //
  41. // UFAT include files
  42. //
  43. #include "fatdent.hxx"
  44. #include "fatdir.hxx"
  45. #include "filedir.hxx"
  46. //
  47. // UNTFS include files
  48. //
  49. #include "attrib.hxx"
  50. #include "upfile.hxx"
  51. #include "logfile.hxx"
  52. #include "ntfssa.hxx"
  53. //
  54. // Number of clusters in boot sector
  55. //
  56. #define CLUSTERS_IN_BOOT ((BYTES_IN_BOOT_AREA + _ClusterFactor*_Drive->QuerySectorSize() - 1)/ \
  57. (_ClusterFactor*_Drive->QuerySectorSize()))
  58. //
  59. // Maximum ntfs cluster size to use during conversion
  60. //
  61. #define MAX_NTFS_CLUSTER_SIZE (1024*4) // 4K
  62. DEFINE_CONSTRUCTOR( FAT_NTFS, OBJECT );
  63. #if defined(CONVERT_PERF_COUNTERS)
  64. typedef struct _PERF_DATA {
  65. BOOLEAN first_call;
  66. LARGE_INTEGER t1;
  67. LARGE_INTEGER wwtime;
  68. LARGE_INTEGER wrtime;
  69. LARGE_INTEGER wctime;
  70. LARGE_INTEGER wscnt;
  71. LARGE_INTEGER wccnt;
  72. LARGE_INTEGER rrtime;
  73. LARGE_INTEGER rscnt;
  74. LARGE_INTEGER rccnt;
  75. } PERF_DATA;
  76. BOOLEAN
  77. CheckTime(
  78. PMESSAGE Message,
  79. PLOG_IO_DP_DRIVE pDrive,
  80. PERF_DATA *pData,
  81. PCHAR Title
  82. )
  83. {
  84. LARGE_INTEGER freq;
  85. LARGE_INTEGER wwtime;
  86. LARGE_INTEGER wrtime;
  87. LARGE_INTEGER wctime;
  88. LARGE_INTEGER wscnt;
  89. LARGE_INTEGER wccnt;
  90. LARGE_INTEGER rrtime;
  91. LARGE_INTEGER rscnt;
  92. LARGE_INTEGER rccnt;
  93. LARGE_INTEGER t2;
  94. if (pData->first_call) {
  95. pData->first_call = FALSE;
  96. pDrive->QueryPerformanceCounters(&pData->wwtime,
  97. &pData->wrtime,
  98. &pData->wctime,
  99. &pData->wscnt,
  100. &pData->wccnt,
  101. &pData->rrtime,
  102. &pData->rscnt,
  103. &pData->rccnt);
  104. Message->DisplayMsg(MSG_CHK_NTFS_MESSAGE, "%s%s", "VVVV ", Title);
  105. QueryPerformanceCounter(&pData->t1);
  106. } else {
  107. QueryPerformanceCounter(&t2);
  108. pDrive->QueryPerformanceCounters(&wwtime,
  109. &wrtime,
  110. &wctime,
  111. &wscnt,
  112. &wccnt,
  113. &rrtime,
  114. &rscnt,
  115. &rccnt);
  116. QueryPerformanceFrequency(&freq);
  117. Message->Set(MSG_CHK_NTFS_MESSAGE);
  118. Message->Display("%s%I64d", "^^^^ Elapsed time in ms: ",
  119. ((t2.QuadPart-pData->t1.QuadPart)*1000)/freq.QuadPart);
  120. Message->Display("%s%I64d", "^^^^ WWTime: ", ((wwtime.QuadPart-pData->wwtime.QuadPart)*1000)/freq.QuadPart);
  121. Message->Display("%s%I64d", "^^^^ WRTime: ", ((wrtime.QuadPart-pData->wrtime.QuadPart)*1000)/freq.QuadPart);
  122. Message->Display("%s%I64d", "^^^^ WCTime: ", ((wctime.QuadPart-pData->wctime.QuadPart)*1000)/freq.QuadPart);
  123. Message->Display("%s%I64d", "^^^^ WSCnt: ", wscnt.QuadPart -pData->wscnt.QuadPart);
  124. Message->Display("%s%I64d", "^^^^ WCCnt: ", wccnt.QuadPart -pData->wccnt.QuadPart);
  125. Message->Display("%s%I64d", "^^^^ RCTime: ", ((rrtime.QuadPart-pData->rrtime.QuadPart)*1000)/freq.QuadPart);
  126. Message->Display("%s%I64d", "^^^^ RSCnt: ", rscnt.QuadPart -pData->rscnt.QuadPart);
  127. Message->Display("%s%I64d", "^^^^ RCCnt: ", rccnt.QuadPart -pData->rccnt.QuadPart);
  128. pData->wwtime = wwtime;
  129. pData->wrtime = wrtime;
  130. pData->wctime = wctime;
  131. pData->wscnt = wscnt;
  132. pData->wccnt = wccnt;
  133. pData->rrtime = rrtime;
  134. pData->rscnt = rscnt;
  135. pData->rccnt = rccnt;
  136. Message->Display("%s%s", "VVVV ", Title);
  137. QueryPerformanceCounter(&pData->t1);
  138. }
  139. return TRUE;
  140. }
  141. #endif
  142. VOID
  143. FAT_NTFS::Construct (
  144. )
  145. /*++
  146. Routine Description:
  147. Constructs a FAT_NTFS object
  148. Arguments:
  149. None.
  150. Return Value:
  151. None.
  152. --*/
  153. {
  154. _FatSa = NULL;
  155. _Drive = NULL;
  156. _Message = NULL;
  157. _FileNameBuffer = NULL;
  158. }
  159. VOID
  160. FAT_NTFS::Destroy (
  161. )
  162. /*++
  163. Routine Description:
  164. Destroys a FAT_NTFS object
  165. Arguments:
  166. None.
  167. Return Value:
  168. None.
  169. --*/
  170. {
  171. DELETE( _FatSa );
  172. DELETE( _Drive );
  173. DELETE( _Message );
  174. DELETE( _FileNameBuffer );
  175. }
  176. FAT_NTFS::~FAT_NTFS (
  177. )
  178. /*++
  179. Routine Description:
  180. Destructor for FAT_NTFS.
  181. Arguments:
  182. None.
  183. Return Value:
  184. None.
  185. --*/
  186. {
  187. }
  188. BOOLEAN
  189. FAT_NTFS::Initialize(
  190. IN OUT PLOG_IO_DP_DRIVE Drive,
  191. IN OUT PREAL_FAT_SA FatSa,
  192. IN PCWSTRING CvtZoneFileName,
  193. IN OUT PMESSAGE Message,
  194. IN ULONG Flags
  195. )
  196. /*++
  197. Routine Description:
  198. Initializes a FAT_NTFS object
  199. Arguments:
  200. FatVol - Supplies the FAT volume
  201. FatSa - Supplies pointer to FAT superarea
  202. CvtZoneFileName - Supplies the name of the convert zone.
  203. Message - Supplies message object
  204. Flags - Supplies convert flags
  205. Return Value:
  206. BOOLEAN - TRUE if successfully initialized, FALSE otherwise
  207. --*/
  208. {
  209. ULONG fat_cluster_size;
  210. LCN cvt_zone_in_ntfs;
  211. BIG_INT cvt_zone_size_in_ntfs;
  212. DebugPtrAssert( Drive );
  213. DebugPtrAssert( FatSa );
  214. DebugPtrAssert( Message );
  215. //
  216. // Initialize the stuff passed as argument
  217. //
  218. _FatSa = FatSa;
  219. _Drive = Drive;
  220. _Message = Message;
  221. _Flags = Flags;
  222. // Floppies cannot be converted to NTFS.
  223. //
  224. if( _Drive->IsFloppy() ) {
  225. Message->Set(MSG_NTFS_FORMAT_NO_FLOPPIES);
  226. Message->Display();
  227. return FALSE;
  228. }
  229. // To protect ourselves from disk drivers that cannot
  230. // correctly determine the disk geometry, compare the
  231. // boot-code-critical values from the existing BPB with
  232. // the drive's values. If they don't match, we can't
  233. // convert this drive because if it's the system partition,
  234. // the system won't boot.
  235. //
  236. if( !CheckGeometryMatch( ) ) {
  237. _Message->Set( MSG_CONV_GEOMETRY_MISMATCH );
  238. _Message->Display( "%s", "NTFS" );
  239. return FALSE;
  240. }
  241. fat_cluster_size = FatSa->QuerySectorsPerCluster() * _Drive->QuerySectorSize();
  242. //
  243. // For volumes that are not data aligned, make cluster size
  244. // equals sector size.
  245. // For volumes that are data aligned, preserve the cluster size
  246. //
  247. if (FatSa->IsVolumeDataAligned()) {
  248. _ClusterFactor = min(FatSa->QuerySectorsPerCluster(),
  249. MAX_NTFS_CLUSTER_SIZE/_Drive->QuerySectorSize());
  250. _ClusterRatio = max(1, fat_cluster_size/MAX_NTFS_CLUSTER_SIZE);
  251. } else {
  252. _ClusterFactor = 1;
  253. _ClusterRatio = FatSa->QuerySectorsPerCluster();
  254. }
  255. if (SMALL_FRS_SIZE >= Drive->QuerySectorSize())
  256. _FrsSize = SMALL_FRS_SIZE;
  257. else
  258. _FrsSize = Drive->QuerySectorSize();
  259. // Set the default number of clusters per Index Allocation Buffer
  260. // to a useful value.
  261. //
  262. _ClustersPerIndexBuffer = NTFS_SA::QueryDefaultClustersPerIndexBuffer(
  263. _Drive, _ClusterFactor);
  264. //
  265. // _NumberOfFiles and _NumberOfDirectories are used to extend
  266. // the MFT when we know how many files there are in the volume.
  267. // Unless we do a volume census these values must be zero.
  268. //
  269. _NumberOfFiles = 0;
  270. _NumberOfDirectories = 0;
  271. if ( CvtZoneFileName->QueryChCount() ) {
  272. PFATDIR RootDir;
  273. FAT_DIRENT CvtZoneFileEntry;
  274. //
  275. // Get the root directory
  276. //
  277. RootDir = (PFATDIR)_FatSa->GetRootDir();
  278. if ( !RootDir ) {
  279. RootDir = (PFATDIR)_FatSa->GetFileDir();
  280. }
  281. DebugPtrAssert( RootDir );
  282. //
  283. // Locate the convert zone file. If it exists then remember its starting cluster
  284. // number and it's size.
  285. //
  286. // The starting cluster of the convert zone is remembered because it is used
  287. // later on for identifying the convert zone file while traversing the root directory.
  288. //
  289. if ( CvtZoneFileEntry.Initialize( RootDir->SearchForDirEntry( CvtZoneFileName ),
  290. FatSa->GetFileDir() ? FAT_TYPE_FAT32 : FAT_TYPE_EAS_OKAY )) {
  291. if (CvtZoneFileEntry.IsDirectory()) {
  292. Message->Set(MSG_CONV_CVTAREA_MUST_BE_FILE, ERROR_MESSAGE);
  293. Message->Display("%W", CvtZoneFileName);
  294. return FALSE;
  295. }
  296. _CvtZoneFileFirstCluster = CvtZoneFileEntry.QueryStartingCluster();
  297. _CvtZoneSize = (((BIG_INT)CvtZoneFileEntry.QueryFileSize() + fat_cluster_size - 1)/fat_cluster_size).GetLowPart();
  298. if (!FatSa->IsFileContiguous(_CvtZoneFileFirstCluster)) {
  299. Message->Set(MSG_CONV_CVTAREA_FILE_NOT_CONTIGUOUS, ERROR_MESSAGE);
  300. Message->Display("%W", CvtZoneFileName);
  301. return FALSE;
  302. }
  303. cvt_zone_in_ntfs = FatClusterToLcn(_CvtZoneFileFirstCluster)/_ClusterFactor;
  304. cvt_zone_size_in_ntfs = _CvtZoneSize*_ClusterRatio;
  305. } else {
  306. Message->Set(MSG_CONV_CVTAREA_FILE_MISSING, ERROR_MESSAGE);
  307. Message->Display("%W", CvtZoneFileName);
  308. return FALSE;
  309. }
  310. } else {
  311. _CvtZoneFileFirstCluster = 0;
  312. _CvtZoneSize = 0;
  313. cvt_zone_in_ntfs = 0;
  314. cvt_zone_size_in_ntfs = 0;
  315. }
  316. DebugAssert(cvt_zone_in_ntfs.GetHighPart() == 0);
  317. DebugAssert(cvt_zone_size_in_ntfs.GetHighPart() == 0);
  318. // Allocate space for the file name attribute buffer and initialize
  319. // the NTFS superarea and the Bad LCN stack.
  320. //
  321. if ( (
  322. _FileNameBuffer =
  323. (PFILE_NAME)MALLOC( sizeof(FILE_NAME) +
  324. sizeof(WCHAR) * NAMEBUFFERSIZE )) == NULL ||
  325. !_RootIndexName.Initialize( FileNameIndexNameData ) ||
  326. !_NtfsSa.Initialize( _Drive, _Message,
  327. cvt_zone_in_ntfs,
  328. cvt_zone_size_in_ntfs ) ||
  329. !_BadLcn.Initialize() ) {
  330. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  331. _Message->Display();
  332. return FALSE;
  333. }
  334. return TRUE;
  335. }
  336. BOOLEAN
  337. FAT_NTFS::Convert(
  338. OUT PCONVERT_STATUS Status
  339. )
  340. /*++
  341. Routine Description:
  342. Converts a FAT volume to NTFS
  343. Arguments:
  344. Status - Supplies pointer to conversion status
  345. Return Value:
  346. BOOLEAN - TRUE if successfully converted, FALSE otherwise
  347. --*/
  348. {
  349. BOOLEAN Converted;
  350. #if defined(CONVERT_PERF_COUNTERS)
  351. PERF_DATA pdata;
  352. pdata.first_call = TRUE;
  353. #endif
  354. DebugPtrAssert( Status );
  355. #if defined ( _AUTOCONV_ )
  356. _Message->Set( MSG_CONV_WILL_REBOOT );
  357. _Message->Display();
  358. #endif // _AUTOCONV_
  359. //
  360. // The conversion from FAT to NTFS consists of a sequence of well-defined
  361. // steps:
  362. //
  363. // 1.- Create holes (i.e. relocate FAT clusters) for fixed-location
  364. // NTFS structures and save FAT.
  365. //
  366. // 2.- Create NTFS elementary data structures in FAT free space
  367. //
  368. // 3.- Convert the File system, creating the NTFS file system in
  369. // the FAT free space.
  370. //
  371. // 4.- Mark as free in the NTFS bitmap those NTFS clusters being used
  372. // by FAT-specific structures.
  373. //
  374. // 5.- Write NTFS boot sector
  375. //
  376. //
  377. // Since a crash can occur at any time, we must minimize the chance of
  378. // disk corruption. Note that (almost) all writes are to FAT free space,
  379. // so a crash will preserve the FAT intact.
  380. //
  381. // The only times at which we write to non-free space, i.e. the times at
  382. // which a crash might cause problems are:
  383. //
  384. // a.- At the end of step 1, when we overwrite the FAT. The algorithm
  385. // for relocating clusters (in UFAT) guarantees that CHKDSK will be
  386. // able to fix the disk without any loss of data.
  387. //
  388. // b.- In step 5, while writting the boot sector. If a crash occurs during
  389. // this step, We're out of luck.
  390. //
  391. //
  392. Converted = (BOOLEAN)( //
  393. // Create holes for fixed-location structures
  394. //
  395. #if defined(CONVERT_PERF_COUNTERS)
  396. CheckTime(_Message, _Drive, &pdata, "CheckSpaceAndCreateHoles") &&
  397. #endif
  398. CheckSpaceAndCreateHoles( ) &&
  399. //
  400. // Initialize the bitmaps
  401. //
  402. #if defined(CONVERT_PERF_COUNTERS)
  403. CheckTime(_Message, _Drive, &pdata, "CreateBitmaps") &&
  404. #endif
  405. CreateBitmaps( ) &&
  406. //
  407. // Create the NTFS elementary data structures
  408. //
  409. #if defined(CONVERT_PERF_COUNTERS)
  410. CheckTime(_Message, _Drive, &pdata, "CreateElementary") &&
  411. #endif
  412. CreateElementary( ) &&
  413. //
  414. // Convert the file system
  415. //
  416. #if defined(CONVERT_PERF_COUNTERS)
  417. CheckTime(_Message, _Drive, &pdata, "ConvertFileSystem") &&
  418. #endif
  419. ConvertFileSystem( ) &&
  420. //
  421. // Mark the reserved sectors as free
  422. //
  423. #if defined(CONVERT_PERF_COUNTERS)
  424. CheckTime(_Message, _Drive, &pdata, "FreeReservedSectors") &&
  425. #endif
  426. FreeReservedSectors( ) &&
  427. //
  428. // Volume converted, write the boot code
  429. //
  430. #if defined(CONVERT_PERF_COUNTERS)
  431. CheckTime(_Message, _Drive, &pdata, "WriteBoot") &&
  432. #endif
  433. WriteBoot( ) &&
  434. #if defined(CONVERT_PERF_COUNTERS)
  435. CheckTime(_Message, _Drive, &pdata, "Done") &&
  436. #endif
  437. (TRUE));
  438. *Status = _Status;
  439. return Converted;
  440. }
  441. BOOLEAN
  442. FAT_NTFS::CheckSpaceAndCreateHoles (
  443. )
  444. /*++
  445. Routine Description:
  446. Determines free space requirements, makes sure that there is
  447. enough space for conversion, and makes holes. All this
  448. is done in one step so that we only have to traverse the
  449. file system once.
  450. Arguments:
  451. None
  452. Return Value:
  453. BOOLEAN - TRUE if there is enough space for conversion and
  454. the holes are in place.
  455. FALSE otherwise
  456. --*/
  457. {
  458. INTSTACK HoleStack; // Stack of holes
  459. CENSUS_REPORT CensusReport; // Census report
  460. PCENSUS_REPORT Census; // Pointer to census report
  461. BIG_INT SectorsTotal; // Number of sectors on the volume
  462. BIG_INT SectorsFree; // Free sectors on the volume
  463. BIG_INT SectorsNeeded; // Sectors needed by conversion
  464. BIG_INT KbytesTotal;
  465. BIG_INT KbytesFree;
  466. BIG_INT KbytesNeeded;
  467. BOOLEAN Relocated; // TRUE if relocated sectors
  468. #if !defined ( _AUTOCHECK_ )
  469. if(_FatSa->QueryVolumeFlags() & (FAT_BPB_RESERVED_DIRTY | FAT_BPB_RESERVED_TEST_SURFACE)) {
  470. //
  471. // The volume dirty bit is set??? Need to CHKDSK first.
  472. //
  473. _Message->Set( MSG_CONV_DISK_IS_DIRTY, ERROR_MESSAGE );
  474. _Message->Display();
  475. _Status = CONVERT_STATUS_DRIVE_IS_DIRTY;
  476. return FALSE;
  477. }
  478. #endif // !_AUTOCHECK_
  479. //
  480. // Identify all the "holes" that we need i.e. all those spots
  481. // that are used by NTFS structures that need to be at a fixed
  482. // location.
  483. //
  484. //
  485. if ( !QueryNeededHoles( &HoleStack ) ) {
  486. return FALSE;
  487. }
  488. SectorsTotal = _FatSa->QueryVirtualSectors();
  489. SectorsFree = _FatSa->QueryFreeSectors();
  490. // Census = ( SectorsFree > ( SectorsTotal / 2 ) ) ? NULL : &CensusReport;
  491. Census = &CensusReport;
  492. Relocated = FALSE;
  493. //
  494. // Create the holes and obtain the census if necessary
  495. //
  496. _Message->Set( MSG_CONV_CHECKING_SPACE );
  497. _Message->Display();
  498. if ( !_FatSa->QueryCensusAndRelocate( Census, &HoleStack, &Relocated )) {
  499. _Message->Set( MSG_CONV_NO_DISK_SPACE, ERROR_MESSAGE );
  500. _Message->Display();
  501. _Status = CONVERT_STATUS_INSUFFICIENT_FREE_SPACE;
  502. return FALSE;
  503. }
  504. #if defined ( _AUTOCONV_ )
  505. //
  506. // If relocated sectors, then we will be overwritting data that might
  507. // be needed by the system. In order to avoid this, we reboot so that
  508. // the system will read its stuff from the new locations.
  509. //
  510. if ( Relocated ) {
  511. _Drive->FlushCache();
  512. _Message->Set( MSG_CONV_REBOOT_AFTER_RELOCATION );
  513. _Message->Display();
  514. IFS_SYSTEM::Reboot();
  515. //
  516. // If we reach this point, the reboot failed and we should not
  517. // continue the conversion.
  518. //
  519. _Message->Set( MSG_CONV_CANNOT_RELOCATE, ERROR_MESSAGE );
  520. _Message->Display();
  521. _Status = CONVERT_STATUS_ERROR;
  522. return FALSE;
  523. }
  524. #endif
  525. //
  526. // Determine the number of sectors needed for the conversion
  527. //
  528. if ( Census ) {
  529. //
  530. // Estimate the number of sectors needed based on the
  531. // volume census.
  532. //
  533. QuerySectorsNeededForConversion( Census, &SectorsNeeded );
  534. } else {
  535. //
  536. // We'll say that we need half of the disk
  537. //
  538. SectorsNeeded = SectorsTotal / 2;
  539. }
  540. //
  541. // Take into account that the convert zone file is actually a free
  542. // space for NTFS to use to put system files
  543. //
  544. SectorsFree += (_CvtZoneSize * _FatSa->QuerySectorsPerCluster());
  545. KbytesTotal = SectorsTotal * _Drive->QuerySectorSize() / 1024;
  546. KbytesFree = SectorsFree * _Drive->QuerySectorSize() / 1024;
  547. KbytesNeeded = SectorsNeeded * _Drive->QuerySectorSize() / 1024;
  548. _Message->Set( MSG_CONV_KBYTES_TOTAL );
  549. _Message->Display( "%8d", KbytesTotal.GetLowPart() );
  550. _Message->Set( MSG_CONV_KBYTES_FREE );
  551. _Message->Display( "%8d", KbytesFree.GetLowPart() );
  552. _Message->Set( MSG_CONV_KBYTES_NEEDED );
  553. _Message->Display( "%8d", KbytesNeeded.GetLowPart() );
  554. if ( SectorsFree < SectorsNeeded ) {
  555. //
  556. // Not enough disk space for conversion
  557. //
  558. _Message->Set( MSG_CONV_NO_DISK_SPACE, ERROR_MESSAGE );
  559. _Message->Display();
  560. _Status = CONVERT_STATUS_INSUFFICIENT_FREE_SPACE;
  561. return FALSE;
  562. }
  563. //
  564. // The disk has enough disk space.
  565. //
  566. return TRUE;
  567. }
  568. BOOLEAN
  569. FAT_NTFS::ConvertRoot (
  570. IN PFATDIR Directory
  571. )
  572. /*++
  573. Routine Description:
  574. Converts the FAT root directory and recursively all its
  575. subdirectories.
  576. This is basically the same as the ConvertDirectory method, the
  577. differences being:
  578. 1.- The index for the root has already been created by
  579. NTFS_SA::CreateElementaryStructures()
  580. 2.- No FRS is created
  581. 3.- ConvertRoot does some extra checkings for
  582. EA file (which is at the root level only).
  583. Arguments:
  584. Directory - Supplies root directory.
  585. Return Value:
  586. BOOLEAN - TRUE if root directory successfully converted
  587. FALSE otherwise
  588. --*/
  589. {
  590. NTFS_FILE_RECORD_SEGMENT FrsOfRootIndex; // FRS of NTFS root index
  591. NTFS_INDEX_TREE RootIndex; // Root index
  592. BOOLEAN Converted;
  593. DebugPtrAssert( Directory );
  594. _Level = 0;
  595. //
  596. // Obtain the NTFS root index
  597. //
  598. if ( !FrsOfRootIndex.Initialize( ROOT_FILE_NAME_INDEX_NUMBER, &_Mft ) ||
  599. !FrsOfRootIndex.Read() ||
  600. !RootIndex.Initialize( _Drive,
  601. _ClusterFactor,
  602. &_VolumeBitmap,
  603. FrsOfRootIndex.GetUpcaseTable(),
  604. FrsOfRootIndex.QueryMaximumAttributeRecordSize()/2,
  605. &FrsOfRootIndex,
  606. &_RootIndexName )
  607. ) {
  608. _Message->Set( MSG_CONV_CANNOT_MAKE_INDEX, ERROR_MESSAGE );
  609. _Message->Display();
  610. _Status = CONVERT_STATUS_ERROR;
  611. return FALSE;
  612. }
  613. //
  614. // Convert the directory
  615. //
  616. if ( Converted = ConvertDir( Directory, &RootIndex, &FrsOfRootIndex )) {
  617. //
  618. // Save the index
  619. //
  620. if ( !RootIndex.Save( &FrsOfRootIndex ) ||
  621. !FrsOfRootIndex.Flush( &_VolumeBitmap ) ) {
  622. _Message->Set( MSG_CONV_CANNOT_WRITE, ERROR_MESSAGE );
  623. _Message->Display();
  624. _Status = CONVERT_STATUS_ERROR;
  625. Converted = FALSE;
  626. }
  627. }
  628. return Converted;
  629. }
  630. BOOLEAN
  631. FAT_NTFS::ConvertDirectory (
  632. IN PFATDIR Directory,
  633. IN PFAT_DIRENT DirEntry,
  634. IN OUT PNTFS_FILE_RECORD_SEGMENT FrsDir
  635. )
  636. /*++
  637. Routine Description:
  638. Converts a FAT directory and recursively all its subdirectories
  639. Arguments:
  640. Directory - Supplies directory to convert
  641. DirEntry - Supplies the directory entry of the directory
  642. FrsDir - Supplies pointer to FRS of directory
  643. Return Value:
  644. BOOLEAN - TRUE if directory successfully converted
  645. FALSE otherwise
  646. --*/
  647. {
  648. NTFS_INDEX_TREE Index; // NTFS index
  649. BOOLEAN Converted; // FALSE if error
  650. ULONG DirSize; // Dir size
  651. ULONG SectorsPerFatCluster; // Sectors per cluster
  652. ULONG Cluster; // Dir cluster number
  653. PFAT Fat; // Pointer to FAT
  654. LCN Lcn; // LCN
  655. DebugPtrAssert( Directory );
  656. DebugPtrAssert( DirEntry );
  657. DebugPtrAssert( FrsDir );
  658. //
  659. // Create an index for this directory:
  660. //
  661. if ( !Index.Initialize( $FILE_NAME,
  662. _Drive,
  663. _ClusterFactor,
  664. &_VolumeBitmap,
  665. FrsDir->GetUpcaseTable(),
  666. COLLATION_FILE_NAME,
  667. SMALL_INDEX_BUFFER_SIZE,
  668. FrsDir->QueryMaximumAttributeRecordSize() /2,
  669. &_RootIndexName
  670. )
  671. ) {
  672. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  673. _Message->Display();
  674. _Status = CONVERT_STATUS_ERROR;
  675. return FALSE;
  676. }
  677. _Level++;
  678. //
  679. // Convert the directory.
  680. //
  681. if ( Converted = ConvertDir( Directory, &Index, FrsDir ) ) {
  682. //
  683. // If the directory has extended attributes, convert them.
  684. //
  685. // Then save the index.
  686. //
  687. if ( Converted = (BOOLEAN)( !DirEntry->QueryEaHandle() ||
  688. ConvertExtendedAttributes( DirEntry, FrsDir ) ) ) {
  689. //
  690. // Mark the sectors used by this directory in the reserved
  691. // bitmap.
  692. //
  693. DirSize = DirEntry->QueryFileSize();
  694. SectorsPerFatCluster = _FatSa->QuerySectorsPerCluster();
  695. Cluster = DirEntry->QueryStartingCluster();
  696. Fat = _FatSa->GetFat();
  697. while ( TRUE ) {
  698. Lcn = FatClusterToLcn( Cluster )/_ClusterFactor;
  699. _ReservedBitmap.SetAllocated( Lcn, _ClusterRatio );
  700. if ( Fat->IsEndOfChain( Cluster )) {
  701. break;
  702. }
  703. Cluster = Fat->QueryEntry( Cluster );
  704. }
  705. //
  706. // Save the index for this directory
  707. //
  708. if ( !( Converted = (Index.Save( FrsDir ) ) ) ) {
  709. _Message->Set( MSG_CONV_CANNOT_WRITE, ERROR_MESSAGE );
  710. _Message->Display();
  711. _Status = CONVERT_STATUS_ERROR;
  712. }
  713. }
  714. }
  715. _Level--;
  716. return Converted;
  717. }
  718. BOOLEAN
  719. FAT_NTFS::ConvertDir (
  720. IN PFATDIR Directory,
  721. IN OUT PNTFS_INDEX_TREE Index,
  722. IN OUT PNTFS_FILE_RECORD_SEGMENT FrsDir
  723. )
  724. /*++
  725. Routine Description:
  726. Converts a FAT directory and recursively all its subdirectories
  727. Arguments:
  728. Return Value:
  729. BOOLEAN - TRUE if directory successfully converted
  730. FALSE otherwise
  731. --*/
  732. {
  733. FAT_DIRENT Entry; // Directory entry
  734. HMEM HMem; // Memory
  735. DSTRING DirName; // This dir's name
  736. DSTRING LongName; // the associated long name
  737. BOOLEAN UseLongName; // make one name attrib
  738. STANDARD_INFORMATION StandardInformation;// Std. Info
  739. ULONG EntryNumber; // Entry number counter
  740. BOOLEAN Converted; // FALSE if error
  741. NTFS_FILE_RECORD_SEGMENT Frs; // FRS of each entry
  742. VCN FileNumber; // File Number of child.
  743. USHORT FrsFlags;
  744. PVOID DirEntry;
  745. FILEDIR SubDir;
  746. BOOLEAN HasLongName;
  747. UCHAR FatType;
  748. CANNED_SECURITY_TYPE Sd;
  749. #if defined(CONVERT_PERF_COUNTERS)
  750. PERF_DATA pdata;
  751. #endif
  752. Converted = TRUE;
  753. EntryNumber = 0;
  754. if (_FatSa->GetFileDir())
  755. FatType = FAT_TYPE_FAT32;
  756. else
  757. FatType = FAT_TYPE_EAS_OKAY;
  758. DebugPtrAssert( Directory );
  759. DebugPtrAssert( Index );
  760. DebugPtrAssert( FrsDir );
  761. //
  762. // Traverse the directory, converting all its entries.
  763. //
  764. while ( Converted ) {
  765. //
  766. // Get next directory entry
  767. //
  768. if ( !(DirEntry = Directory->GetDirEntry( EntryNumber ))) {
  769. break;
  770. }
  771. if ( !Entry.Initialize( DirEntry, FatType))
  772. {
  773. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  774. _Message->Display();
  775. _Status = CONVERT_STATUS_ERROR;
  776. Converted = FALSE;
  777. DebugAssert(FALSE);
  778. break;
  779. }
  780. //
  781. // If end of directory, get out
  782. //
  783. if ( Entry.IsEndOfDirectory() ) {
  784. break;
  785. }
  786. //
  787. // Ignore the deleted, the "parent" and the "self" entries, the
  788. // volume label and the EA file.
  789. //
  790. if (( Entry.IsErased() ||
  791. Entry.IsDot() ||
  792. Entry.IsDotDot() ||
  793. Entry.IsVolumeLabel() ||
  794. Entry.IsLongEntry() ||
  795. (_CvtZoneFileFirstCluster != 0 &&
  796. Entry.QueryStartingCluster() == _CvtZoneFileFirstCluster) ||
  797. (_EAFileFirstCluster != 0 &&
  798. Entry.QueryStartingCluster() == _EAFileFirstCluster) ) ) {
  799. EntryNumber++;
  800. continue;
  801. }
  802. // Fill in the standard information for this file or
  803. // directory. Note that NTFS stores Universal Time,
  804. // whereas FAT stores Local Time, so the time has to
  805. // be converted.
  806. //
  807. LARGE_INTEGER FatTime, NtfsTime;
  808. Entry.QueryLastWriteTime( &FatTime );
  809. RtlLocalTimeToSystemTime( &FatTime, &NtfsTime );
  810. StandardInformation.LastModificationTime = NtfsTime;
  811. StandardInformation.LastChangeTime = NtfsTime;
  812. if (Entry.IsValidCreationTime()) {
  813. Entry.QueryCreationTime( &FatTime );
  814. RtlLocalTimeToSystemTime( &FatTime, &NtfsTime );
  815. StandardInformation.CreationTime = NtfsTime;
  816. } else {
  817. StandardInformation.CreationTime = StandardInformation.LastChangeTime;
  818. }
  819. if (Entry.IsValidLastAccessTime()) {
  820. Entry.QueryLastAccessTime( &FatTime );
  821. RtlLocalTimeToSystemTime( &FatTime, &NtfsTime );
  822. StandardInformation.LastAccessTime = NtfsTime;
  823. } else {
  824. StandardInformation.LastAccessTime = StandardInformation.LastChangeTime;
  825. }
  826. StandardInformation.FileAttributes = Entry.QueryAttributeByte();
  827. //
  828. // Get the WSTR name of the entry and fill in the FILE_NAME
  829. // structure for the file name attribute. If this entry
  830. // does not have an associated Long File Name, then its
  831. // name is both a valid DOS and NTFS name; if there is an
  832. // associated Long File Name, then the name is the DOS name
  833. // and the long name is the NTFS name.
  834. //
  835. // If the long name is identical to the short name, ignore
  836. // the long name.
  837. //
  838. Entry.QueryName( &DirName );
  839. if( !Directory->QueryLongName( EntryNumber, &LongName ) ) {
  840. DebugPrintTrace(( "CUFAT: QueryLongName failed.\n" ));
  841. _Status = CONVERT_STATUS_ERROR;
  842. Converted = FALSE;
  843. break;
  844. }
  845. HasLongName = (LongName.QueryChCount() != 0);
  846. //
  847. // If the long name is only a casewise permutation of the
  848. // short name, use the long name as the single ntfs name
  849. // attribute.
  850. //
  851. UseLongName = (HasLongName &&
  852. 0 == NtfsUpcaseCompare( DirName.GetWSTR(),
  853. DirName.QueryChCount(),
  854. LongName.GetWSTR(),
  855. LongName.QueryChCount(),
  856. FrsDir->GetUpcaseTable(),
  857. FALSE ));
  858. _FileNameBuffer->ParentDirectory = FrsDir->QuerySegmentReference();
  859. _FileNameBuffer->FileNameLength = (unsigned char)DirName.QueryChCount();
  860. if (UseLongName) {
  861. _FileNameBuffer->Flags = FILE_NAME_NTFS | FILE_NAME_DOS;
  862. if ( !LongName.QueryWSTR( 0, TO_END, NtfsFileNameGetName(_FileNameBuffer), NAMEBUFFERSIZE ) ) {
  863. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  864. _Message->Display();
  865. _Status = CONVERT_STATUS_ERROR;
  866. Converted = FALSE;
  867. DebugAssert(FALSE);
  868. break;
  869. }
  870. } else {
  871. _FileNameBuffer->Flags = HasLongName ?
  872. FILE_NAME_DOS :
  873. FILE_NAME_NTFS | FILE_NAME_DOS;
  874. if ( !DirName.QueryWSTR( 0, TO_END, NtfsFileNameGetName(_FileNameBuffer), NAMEBUFFERSIZE ) ) {
  875. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  876. _Message->Display();
  877. _Status = CONVERT_STATUS_ERROR;
  878. Converted = FALSE;
  879. DebugAssert(FALSE);
  880. break;
  881. }
  882. }
  883. // Allocate and create the FRS for this file or directory,
  884. // add its file name, and add an appropriate entry to the
  885. // index.
  886. //
  887. FrsFlags = (Entry.IsDirectory()) ? (USHORT)FILE_FILE_NAME_INDEX_PRESENT : (USHORT)0;
  888. if (_Flags & CONVERT_NOSECURITY_FLAG) {
  889. Sd = Entry.IsDirectory() ? EditWorldCannedDirSd : EditWorldCannedFileSd;
  890. } else {
  891. Sd = Entry.IsDirectory() ? NoAclCannedSd : NoAclCannedFileSd;
  892. }
  893. if ( !_Mft.AllocateFileRecordSegment( &FileNumber, FALSE ) ||
  894. !Frs.Initialize( FileNumber, &_Mft ) ||
  895. !Frs.Create( &StandardInformation, FrsFlags ) ||
  896. !Frs.AddFileNameAttribute( _FileNameBuffer ) ||
  897. !Frs.AddSecurityDescriptor( Sd, &_VolumeBitmap ) ||
  898. !Index->InsertEntry( NtfsFileNameGetLength( _FileNameBuffer ),
  899. _FileNameBuffer,
  900. Frs.QuerySegmentReference() ) ) {
  901. DebugPrint( "Can't create FRS in ConvertDirectory.\n" );
  902. Converted = FALSE;
  903. break;
  904. }
  905. // If the file has separate long name, add that entry to the FRS
  906. // and the index.
  907. //
  908. if( HasLongName && !UseLongName ) {
  909. _FileNameBuffer->ParentDirectory = FrsDir->QuerySegmentReference();
  910. _FileNameBuffer->FileNameLength = (unsigned char)LongName.QueryChCount();
  911. _FileNameBuffer->Flags = FILE_NAME_NTFS;
  912. if ( !LongName.QueryWSTR( 0, TO_END, NtfsFileNameGetName(_FileNameBuffer), NAMEBUFFERSIZE ) ) {
  913. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  914. _Message->Display();
  915. _Status = CONVERT_STATUS_ERROR;
  916. Converted = FALSE;
  917. DebugAssert(FALSE);
  918. break;
  919. }
  920. if ( !Frs.AddFileNameAttribute( _FileNameBuffer ) ||
  921. !Index->InsertEntry( NtfsFileNameGetLength( _FileNameBuffer ),
  922. _FileNameBuffer,
  923. Frs.QuerySegmentReference() ) ) {
  924. DebugPrint( "Can't create FRS in ConvertDirectory.\n" );
  925. Converted = FALSE;
  926. break;
  927. }
  928. }
  929. if ( _Flags & CONVERT_VERBOSE_FLAG ) {
  930. STATIC CHAR NameDisplayBuffer[128];
  931. ULONG NameStart = _Level * 4;
  932. PWSTRING Name;
  933. Name = (HasLongName ? &LongName : &DirName);
  934. memset(NameDisplayBuffer, ' ', NameStart);
  935. Name->QuerySTR( 0, TO_END, NameDisplayBuffer + NameStart,
  936. 128 - Name->QueryByteCount() );
  937. NameDisplayBuffer[NameStart + Name->QueryByteCount()] = 0;
  938. _Message->Set( MSG_ONE_STRING );
  939. _Message->Display( "%s", NameDisplayBuffer );
  940. }
  941. //
  942. // Determine if the entry is a directory or a file, and proccess it
  943. // accordingly.
  944. //
  945. if ( Entry.IsDirectory() ) {
  946. //
  947. // Directory
  948. //
  949. //
  950. // Convert the directory (and all its subdirectories)
  951. //
  952. if ( !HMem.Initialize() ||
  953. !SubDir.Initialize( &HMem,
  954. _Drive,
  955. _FatSa,
  956. _FatSa->GetFat(),
  957. Entry.QueryStartingCluster() ) ) {
  958. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  959. _Message->Display();
  960. _Status = CONVERT_STATUS_ERROR;
  961. DebugAssert(FALSE);
  962. return FALSE;
  963. }
  964. if ( !SubDir.Read() ) {
  965. _Message->Set( MSG_CONV_CANNOT_READ, ERROR_MESSAGE );
  966. _Message->Display( );
  967. _Status = CONVERT_STATUS_ERROR;
  968. return FALSE;
  969. }
  970. #if defined(CONVERT_PERF_COUNTERS)
  971. pdata.first_call = TRUE;
  972. CheckTime(_Message, _Drive, &pdata, "ConvertDirectory");
  973. _Message->Set(MSG_CHK_NTFS_MESSAGE);
  974. _Message->Display("%s%W", "VVVV Dir: ", &DirName);
  975. #endif
  976. if ( !ConvertDirectory( &SubDir, &Entry, &Frs ) ||
  977. !Frs.Flush( &_VolumeBitmap, Index ) ) {
  978. _Message->Set( MSG_CONV_CANNOT_CONVERT_DIRECTORY );
  979. _Message->Display( "%W", &DirName );
  980. Converted = FALSE;
  981. break;
  982. }
  983. #if defined(CONVERT_PERF_COUNTERS)
  984. CheckTime(_Message, _Drive, &pdata, "ConvertDirectory Done");
  985. #endif
  986. } else {
  987. //
  988. // File
  989. //
  990. DebugAssert( !Entry.IsVolumeLabel() );
  991. DebugAssert( !Entry.IsLongEntry() );
  992. DebugAssert( !_EAFileFirstCluster ||
  993. (Entry.QueryStartingCluster() != _EAFileFirstCluster) );
  994. //
  995. // Convert the file.
  996. //
  997. if ( !ConvertFile( &Entry, &Frs ) ||
  998. !Frs.Flush( &_VolumeBitmap, Index ) ) {
  999. Converted = FALSE;
  1000. break;
  1001. }
  1002. }
  1003. EntryNumber++;
  1004. }
  1005. return Converted;
  1006. }
  1007. BOOLEAN
  1008. FAT_NTFS::ConvertExtendedAttributes (
  1009. IN PFAT_DIRENT Dirent,
  1010. IN OUT PNTFS_FILE_RECORD_SEGMENT Frs
  1011. )
  1012. /*++
  1013. Routine Description:
  1014. Converts the extended attributes of a FAT directory entry.
  1015. Arguments:
  1016. Dirent - Supplies the directory entry
  1017. Frs - Supplies the file's FRS
  1018. Return Value:
  1019. BOOLEAN - TRUE if extended attributes converted
  1020. FALSE otherwise
  1021. --*/
  1022. {
  1023. USHORT EaHandle;
  1024. DebugPtrAssert( Dirent );
  1025. DebugPtrAssert( Frs );
  1026. EaHandle = Dirent->QueryEaHandle();
  1027. //
  1028. // If this entry has extended attributes, convert them
  1029. //
  1030. if ( EaHandle ) {
  1031. //
  1032. // Make sure that there is an EA file
  1033. //
  1034. if ( _EAFileFirstCluster == 0 ) {
  1035. _Message->Set( MSG_CONV_NO_EA_FILE, ERROR_MESSAGE );
  1036. _Message->Display( );
  1037. _Status = CONVERT_STATUS_ERROR;
  1038. return FALSE;
  1039. }
  1040. //
  1041. // Convert the attributes
  1042. //
  1043. return ConvertExtendedAttributes( Frs,
  1044. EaHandle );
  1045. }
  1046. return TRUE;
  1047. }
  1048. BOOLEAN
  1049. FAT_NTFS::ConvertExtendedAttributes (
  1050. IN OUT PNTFS_FILE_RECORD_SEGMENT Frs,
  1051. IN USHORT EaHandle
  1052. )
  1053. /*++
  1054. Routine Description:
  1055. Converts the extended attributes of a FAT directory entry.
  1056. Arguments:
  1057. Frs - Supplies the file's FRS
  1058. Eahandle - Supplies the EA handle
  1059. Return Value:
  1060. BOOLEAN - TRUE if extended attributes converted
  1061. FALSE otherwise
  1062. --*/
  1063. {
  1064. EA_INFORMATION EaInformation;
  1065. NTFS_ATTRIBUTE EaInformationAttribute;
  1066. NTFS_ATTRIBUTE EaDataAttribute;
  1067. EA_SET EaSet; // Extended attribute set
  1068. HMEM Mem; // Memory
  1069. ULONG Index; // EA Index
  1070. PEA Ea; // Pointer to EA
  1071. ULONG Cluster; // EA set cluster number
  1072. PBYTE UnpackedEaList;
  1073. ULONG PackedEaLength, UnpackedEaLength, PackedListLength,
  1074. UnpackedListLength, NeedEaCount, TargetOffset;
  1075. //
  1076. // Read in the EA set
  1077. //
  1078. Cluster = (_FatSa->GetFat())->QueryNthCluster( _EAFileFirstCluster,
  1079. _EAHeader.QueryEaSetClusterNumber( EaHandle ));
  1080. if ( !Mem.Initialize() ||
  1081. !EaSet.Initialize( &Mem,
  1082. _Drive,
  1083. _FatSa,
  1084. _FatSa->GetFat(),
  1085. Cluster )
  1086. ) {
  1087. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  1088. _Message->Display( );
  1089. _Status = CONVERT_STATUS_ERROR;
  1090. DebugAssert(FALSE);
  1091. return FALSE;
  1092. }
  1093. if ( !EaSet.Read() ) {
  1094. _Message->Set( MSG_CONV_CANNOT_READ, ERROR_MESSAGE );
  1095. _Message->Display( );
  1096. _Status = CONVERT_STATUS_ERROR;
  1097. return FALSE;
  1098. }
  1099. //
  1100. // Walk the list to determine the packed and unpacked length. The
  1101. // packed list is simply all the EA's concatenated together, so its
  1102. // length is the sum of the individual lengths. The unpacked list
  1103. // consists of a set of entries which in turn consist of a ULONG
  1104. // and a DWORD-aligned EA, so it length is a bit more complex to
  1105. // compute.
  1106. //
  1107. Index = 0;
  1108. PackedListLength = 0;
  1109. UnpackedListLength = 0;
  1110. NeedEaCount = 0;
  1111. while( (Ea = EaSet.GetEa( Index++ )) != NULL ) {
  1112. PackedEaLength = sizeof( EA ) + Ea->NameSize +
  1113. *(USHORT UNALIGNED *)Ea->ValueSize;
  1114. UnpackedEaLength = sizeof(ULONG) + DwordAlign( PackedEaLength );
  1115. PackedListLength += PackedEaLength;
  1116. UnpackedListLength += UnpackedEaLength;
  1117. if( Ea->Flag & NeedFlag ) {
  1118. NeedEaCount += 1;
  1119. }
  1120. }
  1121. //
  1122. // Allocate a buffer to hold the unpacked list.
  1123. //
  1124. if( (UnpackedEaList = (PBYTE)MALLOC( (unsigned int)UnpackedListLength )) == NULL ) {
  1125. return FALSE;
  1126. }
  1127. memset( UnpackedEaList, 0, (unsigned int)UnpackedListLength );
  1128. //
  1129. // Walk the list again, copying EA's into the packed list buffer.
  1130. //
  1131. Index = 0;
  1132. TargetOffset = 0;
  1133. while( (Ea = EaSet.GetEa( Index++ )) != NULL ) {
  1134. PackedEaLength = sizeof( EA ) + Ea->NameSize +
  1135. *(USHORT UNALIGNED *)Ea->ValueSize;
  1136. UnpackedEaLength = sizeof(ULONG) + DwordAlign( PackedEaLength );
  1137. memcpy( UnpackedEaList + TargetOffset,
  1138. &UnpackedEaLength,
  1139. sizeof( ULONG ) );
  1140. memcpy( UnpackedEaList + TargetOffset + sizeof( ULONG ),
  1141. Ea,
  1142. (unsigned int)PackedEaLength );
  1143. TargetOffset += UnpackedEaLength;
  1144. }
  1145. // Create the EA Information Attribute--fill in the fields of
  1146. // the EA information structure, put it into a resident attribute
  1147. // of type $EA_INFORMATION, and insert the attribute into the file.
  1148. //
  1149. EaInformation.PackedEaSize = (unsigned short)PackedListLength;
  1150. EaInformation.NeedEaCount = (unsigned short)NeedEaCount;
  1151. EaInformation.UnpackedEaSize = UnpackedListLength;
  1152. if( !EaInformationAttribute.Initialize( _Drive,
  1153. _Mft.QueryClusterFactor(),
  1154. &EaInformation,
  1155. sizeof( EA_INFORMATION ),
  1156. $EA_INFORMATION,
  1157. NULL,
  1158. 0 ) ||
  1159. !EaInformationAttribute.InsertIntoFile( Frs, &_VolumeBitmap ) ) {
  1160. FREE( UnpackedEaList );
  1161. return FALSE;
  1162. }
  1163. //
  1164. // Set up the Ea Data attribute. Start out with it resident; if
  1165. // it doesn't fit into the FRS, make it nonresident.
  1166. //
  1167. if( !EaDataAttribute.Initialize( _Drive,
  1168. _Mft.QueryClusterFactor(),
  1169. UnpackedEaList,
  1170. UnpackedListLength,
  1171. $EA_DATA,
  1172. NULL,
  1173. 0 ) ) {
  1174. DebugPrint( "Cannot initialize resident attribute for EA List.\n" );
  1175. FREE( UnpackedEaList );
  1176. return FALSE;
  1177. }
  1178. if( !EaDataAttribute.InsertIntoFile( Frs, &_VolumeBitmap ) ) {
  1179. // Couldn't insert it in resident form; make it nonresident.
  1180. if( !EaDataAttribute.MakeNonresident( &_VolumeBitmap ) ||
  1181. !EaDataAttribute.InsertIntoFile( Frs, &_VolumeBitmap ) ) {
  1182. // Can't insert it.
  1183. FREE( UnpackedEaList );
  1184. return FALSE;
  1185. }
  1186. }
  1187. //
  1188. // All the EAs have been converted
  1189. //
  1190. FREE( UnpackedEaList );
  1191. return TRUE;
  1192. }
  1193. BOOLEAN
  1194. FAT_NTFS::ConvertFile (
  1195. IN PFAT_DIRENT Dirent,
  1196. IN OUT PNTFS_FILE_RECORD_SEGMENT File
  1197. )
  1198. /*++
  1199. Routine Description:
  1200. Converts a file from FAT to NTFS
  1201. Arguments:
  1202. Dirent - Supplies the directory entry of the file to convert
  1203. ParentFrs - Supplies the FRS of the directory which contains this file
  1204. File - Supplies pointer to FRS of file
  1205. Return Value:
  1206. BOOLEAN - TRUE if file successfully converted
  1207. FALSE otherwise
  1208. --*/
  1209. {
  1210. DSTRING FileName; // File name
  1211. DebugPtrAssert( Dirent );
  1212. DebugPtrAssert( File );
  1213. Dirent->QueryName( &FileName );
  1214. //
  1215. // Convert the file data and extended attributes.
  1216. //
  1217. if ( !ConvertFileData( Dirent, File ) ||
  1218. !( !Dirent->QueryEaHandle() ||
  1219. ConvertExtendedAttributes( Dirent, File ))
  1220. ) {
  1221. _Message->Set( MSG_CONV_CANNOT_CONVERT_FILE, ERROR_MESSAGE );
  1222. _Message->Display( "%W", &FileName );
  1223. _Status = CONVERT_STATUS_ERROR;
  1224. return FALSE;
  1225. }
  1226. //
  1227. // File converted
  1228. //
  1229. return TRUE;
  1230. }
  1231. BOOLEAN
  1232. FAT_NTFS::ConvertFileData (
  1233. IN PFAT_DIRENT Dirent,
  1234. IN OUT PNTFS_FILE_RECORD_SEGMENT Frs
  1235. )
  1236. /*++
  1237. Routine Description:
  1238. Converts the file data from FAT to NTFS
  1239. Arguments:
  1240. Dirent - Supplies the directory entry of the file to convert
  1241. Frs - Supplies the FRS for the file
  1242. Return Value:
  1243. BOOLEAN - TRUE if file data successfully converted
  1244. FALSE otherwise
  1245. --*/
  1246. {
  1247. ULONG FileSize; // File size
  1248. ULONG SectorsPerFatCluster; // Sectors per cluster
  1249. DebugPtrAssert( Dirent );
  1250. DebugPtrAssert( Frs );
  1251. //
  1252. // Get the file size
  1253. //
  1254. FileSize = Dirent->QueryFileSize();
  1255. SectorsPerFatCluster = _FatSa->QuerySectorsPerCluster();
  1256. //
  1257. // If the data is small enough to fit in the FRS, we make it resident and free
  1258. // the cluster it occupies, otherwise we reuse its allocation and make it
  1259. // non-resident.
  1260. //
  1261. // Note that we only make the data resident if it is less than one FAT cluster
  1262. // long.
  1263. //
  1264. if ( ( Frs->QueryFreeSpace() > (FileSize + SIZE_OF_RESIDENT_HEADER ) ) &&
  1265. ( FileSize <= (SectorsPerFatCluster * _Drive->QuerySectorSize( )) )
  1266. ) {
  1267. return ConvertFileDataResident( Dirent, Frs );
  1268. } else {
  1269. DebugAssert( FileSize > 0 );
  1270. return ConvertFileDataNonResident( Dirent, Frs );
  1271. }
  1272. }
  1273. BOOLEAN
  1274. FAT_NTFS::ConvertFileDataNonResident (
  1275. IN PFAT_DIRENT Dirent,
  1276. IN OUT PNTFS_FILE_RECORD_SEGMENT Frs
  1277. )
  1278. /*++
  1279. Routine Description:
  1280. Converts the file data from FAT to NTFS in nonresident form
  1281. Arguments:
  1282. Dirent - Supplies the directory entry of the file to convert
  1283. Frs - Supplies the FRS for the file
  1284. Return Value:
  1285. BOOLEAN - TRUE if file data successfully converted
  1286. FALSE otherwise
  1287. --*/
  1288. {
  1289. ULONG Cluster; // File cluster number
  1290. NTFS_ATTRIBUTE DataAttribute; // File's $DATA attribute
  1291. NTFS_EXTENT_LIST ExtentList; // NTFS extent list
  1292. ULONG Length; // Length of extent
  1293. ULONG FileSize; // File size
  1294. ULONG ClusterSize; // NTFS cluster size
  1295. ULONG ClustersLeft; // NTFS clusters left to convert
  1296. ULONG SectorsPerCluster; // Sectors per FAT cluster
  1297. VCN Vcn; // VCN
  1298. LCN Lcn; // LCN
  1299. PFAT Fat; // Pointer to FAT
  1300. DebugPtrAssert( Dirent );
  1301. DebugPtrAssert( Frs );
  1302. //
  1303. // Get the file size
  1304. //
  1305. FileSize = Dirent->QueryFileSize();
  1306. SectorsPerCluster = _FatSa->QuerySectorsPerCluster();
  1307. DebugAssert( FileSize > 0 );
  1308. //
  1309. // First we generate an extent list mapping the file's data
  1310. // allocation. We just add all the clusters in the file as
  1311. // extents of size SectorsPerCluster. Note that we don't
  1312. // have to do anything special about consecutive clusters
  1313. // (the Extent List coallesces them for us).
  1314. //
  1315. // If there are unused sectors in the last cluster, we mark
  1316. // them in the ReservedBitmap.
  1317. //
  1318. if ( !ExtentList.Initialize( 0, 0 ) ) {
  1319. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  1320. _Message->Display( );
  1321. _Status = CONVERT_STATUS_ERROR;
  1322. DebugAssert(FALSE);
  1323. return FALSE;
  1324. }
  1325. Cluster = Dirent->QueryStartingCluster();
  1326. ClusterSize = _ClusterFactor*_Drive->QuerySectorSize();
  1327. ClustersLeft = (FileSize + ClusterSize - 1)/ClusterSize;
  1328. Fat = _FatSa->GetFat();
  1329. Vcn = 0;
  1330. // Add all the FAT clusters to the NTFS extent list. Note that in the last
  1331. // cluster we only add those sectors that contain file data, and the rest
  1332. // will become free after the conversion.
  1333. //
  1334. while ( ClustersLeft ) {
  1335. Lcn = FatClusterToLcn( Cluster )/_ClusterFactor;
  1336. Length = min( ClustersLeft, _ClusterRatio );
  1337. //DebugPrintTrace(( " Extent: Cluster %d Vcn %d Lcn %d Length %d Left %d\n",
  1338. // Cluster, Vcn.GetLowPart(), Lcn.GetLowPart(), Length,
  1339. // SectorsLeft ));
  1340. if ( !ExtentList.AddExtent( Vcn, Lcn, Length ) ) {
  1341. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  1342. _Message->Display( );
  1343. _Status = CONVERT_STATUS_ERROR;
  1344. DebugAssert(FALSE);
  1345. return FALSE;
  1346. }
  1347. Vcn += Length;
  1348. ClustersLeft -= Length;
  1349. DebugAssert((ClustersLeft > 0) || Fat->IsEndOfChain(Cluster));
  1350. Cluster = Fat->QueryEntry( Cluster );
  1351. }
  1352. //
  1353. // Unused sectors in the last cluster are marked in the
  1354. // ReservedBitmap.
  1355. //
  1356. if ( Length < _ClusterRatio ) {
  1357. _ReservedBitmap.SetAllocated( Lcn+Length,
  1358. _ClusterRatio - Length );
  1359. }
  1360. //
  1361. // Now put the file data in the $DATA attribute of the file
  1362. //
  1363. if ( !DataAttribute.Initialize( _Drive,
  1364. _ClusterFactor,
  1365. &ExtentList,
  1366. FileSize,
  1367. FileSize,
  1368. $DATA ) ) {
  1369. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  1370. _Message->Display( );
  1371. _Status = CONVERT_STATUS_ERROR;
  1372. DebugAssert(FALSE);
  1373. return FALSE;
  1374. }
  1375. if ( !DataAttribute.InsertIntoFile( Frs, &_VolumeBitmap ) ) {
  1376. _Message->Set( MSG_CONV_CANNOT_CONVERT_DATA, ERROR_MESSAGE );
  1377. _Message->Display( );
  1378. _Status = CONVERT_STATUS_ERROR;
  1379. return FALSE;
  1380. }
  1381. //
  1382. // File data converted
  1383. //
  1384. return TRUE;
  1385. }
  1386. BOOLEAN
  1387. FAT_NTFS::ConvertFileDataResident (
  1388. IN PFAT_DIRENT Dirent,
  1389. IN OUT PNTFS_FILE_RECORD_SEGMENT Frs
  1390. )
  1391. /*++
  1392. Routine Description:
  1393. Converts the file data from FAT to NTFS in resident form
  1394. Arguments:
  1395. Dirent - Supplies the directory entry of the file to convert
  1396. Frs - Supplies the FRS for the file
  1397. Return Value:
  1398. BOOLEAN - TRUE if file data successfully converted
  1399. FALSE otherwise
  1400. --*/
  1401. {
  1402. HMEM Hmem; // Memory
  1403. CLUSTER_CHAIN ClusterChain; // File cluster
  1404. NTFS_ATTRIBUTE DataAttribute; // File's $DATA attribute
  1405. ULONG FileSize; // File size
  1406. DebugPtrAssert( Dirent );
  1407. DebugPtrAssert( Frs );
  1408. //
  1409. // Get the file size
  1410. //
  1411. FileSize = Dirent->QueryFileSize();
  1412. if ( FileSize > 0 ) {
  1413. //
  1414. // Read the file data.
  1415. //
  1416. if ( !Hmem.Initialize() ||
  1417. !ClusterChain.Initialize( &Hmem,
  1418. _Drive,
  1419. _FatSa,
  1420. _FatSa->GetFat(),
  1421. Dirent->QueryStartingCluster(), 1 ) ) {
  1422. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  1423. _Message->Display( );
  1424. _Status = CONVERT_STATUS_ERROR;
  1425. DebugAssert(FALSE);
  1426. return FALSE;
  1427. }
  1428. if ( !ClusterChain.Read() ) {
  1429. //
  1430. // We cannot read the file data, possibly because there is a
  1431. // bad sector on the volume. We will try make the file data
  1432. // non-resident (that way we don't need to read the data, since
  1433. // all we do is generate the allocation info). Doing things
  1434. // this way does not change the state of the drive (i.e. it was
  1435. // bad before conversion, it is bad after the conversion).
  1436. //
  1437. return ConvertFileDataNonResident( Dirent, Frs );
  1438. }
  1439. }
  1440. //
  1441. // Now put the file data in the $DATA attribute of the file
  1442. //
  1443. if ( (FileSize > 0 && !ClusterChain.GetBuf()) ||
  1444. !DataAttribute.Initialize( _Drive,
  1445. _ClusterFactor,
  1446. (FileSize > 0) ? ClusterChain.GetBuf() : NULL,
  1447. FileSize,
  1448. $DATA ) ) {
  1449. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  1450. _Message->Display( );
  1451. _Status = CONVERT_STATUS_ERROR;
  1452. DebugAssert(FALSE);
  1453. return FALSE;
  1454. }
  1455. if ( !DataAttribute.InsertIntoFile( Frs, &_VolumeBitmap ) ) {
  1456. _Message->Set( MSG_CONV_CANNOT_CONVERT_DATA, ERROR_MESSAGE );
  1457. _Message->Display( );
  1458. _Status = CONVERT_STATUS_ERROR;
  1459. return FALSE;
  1460. }
  1461. //
  1462. // We can now mark the cluster with the file data in the
  1463. // ReservedBitmap, so it will be freed after the conversion.
  1464. //
  1465. // Note that we cannot mark it free in the VolumeBitmap because
  1466. // the conversion process must NOT overwrite it (or data may
  1467. // be lost if the conversion fails!).
  1468. //
  1469. if ( FileSize > 0 ) {
  1470. ReserveCluster( Dirent->QueryStartingCluster() );
  1471. }
  1472. //
  1473. // File data converted
  1474. //
  1475. return TRUE;
  1476. }
  1477. BOOLEAN
  1478. FAT_NTFS::ConvertFileSystem(
  1479. )
  1480. /*++
  1481. Routine Description:
  1482. Converts the existing FAT file system to NTFS. This is done by
  1483. traversing the file system tree and converting the FAT structures
  1484. (i.e. directories, files and EAs).
  1485. The space occupied by FAT-specific files (e.g. the EA file) is marked
  1486. in the ReservedBitmap so it will be freed up when the conversion is
  1487. done.
  1488. Note that Operating-System-specific files (e.g. IO.SYS, MSDOS.SYS) are
  1489. NOT removed by the conversion process. This is a File System conversion,
  1490. not an Operating System conversion (If this file system conversion is
  1491. being invoked by an operating system conversion program, then that
  1492. program is responsible for removing any system files).
  1493. Arguments:
  1494. None
  1495. Return Value:
  1496. BOOLEAN - TRUE if file system successfully converted
  1497. FALSE otherwise
  1498. --*/
  1499. {
  1500. PFATDIR RootDir; // FAT root directory
  1501. FAT_DIRENT EAFileDirEnt; // Entry for EA file
  1502. DSTRING EAFile; // Name of EA file
  1503. ULONG i; // Cluster index
  1504. PFAT Fat; // Pointer to FAT
  1505. UCHAR FatType;
  1506. _Message->Set( MSG_CONV_CONVERTING_FS );
  1507. _Message->Display();
  1508. //
  1509. // Get the root directory
  1510. //
  1511. RootDir = (PFATDIR)_FatSa->GetRootDir();
  1512. FatType = FAT_TYPE_EAS_OKAY;
  1513. if ( !RootDir ) {
  1514. RootDir = (PFATDIR)_FatSa->GetFileDir();
  1515. FatType = FAT_TYPE_FAT32;
  1516. }
  1517. DebugPtrAssert( RootDir );
  1518. //
  1519. // Locate the EA file. If it exists then remember its starting cluster
  1520. // number and initialize the EA header.
  1521. //
  1522. // The starting cluster of the EA file is remembered because it is used
  1523. // later on for identifying the EA file while traversing the root directory.
  1524. //
  1525. EAFile.Initialize( "EA DATA. SF" );
  1526. if ( (FAT_TYPE_FAT32 != FatType) && EAFileDirEnt.Initialize( RootDir->SearchForDirEntry( &EAFile )) )
  1527. {
  1528. _EAFileFirstCluster = EAFileDirEnt.QueryStartingCluster();
  1529. Fat = _FatSa->GetFat();
  1530. if ( !_EAMemory.Initialize() ||
  1531. !_EAHeader.Initialize( &_EAMemory,
  1532. _Drive,
  1533. _FatSa,
  1534. Fat,
  1535. _EAFileFirstCluster ) ||
  1536. !_EAHeader.Read()
  1537. ) {
  1538. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  1539. _Message->Display();
  1540. _Status = CONVERT_STATUS_ERROR;
  1541. DebugAssert(FALSE);
  1542. return FALSE;
  1543. }
  1544. //
  1545. // Mark all the EA file sectors in the ReservedBitmap, so the
  1546. // space will be freed up when the conversion is done.
  1547. //
  1548. i = _EAFileFirstCluster;
  1549. #if DBG
  1550. if ( _Flags & CONVERT_VERBOSE_FLAG ) {
  1551. DebugPrintTrace(( "EA file at cluster %X\n", _EAFileFirstCluster ));
  1552. }
  1553. #endif
  1554. while (TRUE) {
  1555. ReserveCluster( i );
  1556. if ( Fat->IsEndOfChain( i ) ) {
  1557. break;
  1558. }
  1559. i = Fat->QueryEntry( i );
  1560. }
  1561. } else {
  1562. #if DBG
  1563. if ( _Flags & CONVERT_VERBOSE_FLAG ) {
  1564. DebugPrintTrace(( "The volume contains no EA file\n" ));
  1565. }
  1566. #endif
  1567. _EAFileFirstCluster = 0;
  1568. }
  1569. //
  1570. // Convert the volume by recursively converting the root directory
  1571. //
  1572. return ConvertRoot( RootDir );
  1573. }
  1574. BOOLEAN
  1575. FAT_NTFS::CreateBitmaps(
  1576. )
  1577. /*++
  1578. Routine Description:
  1579. Creates the NTFS bitmaps for the volume and the bad block stack.
  1580. Two bitmaps are created:
  1581. _VolumeBitmap - Is the bitmap for the volume. Represents the volume at
  1582. seemed by NTFS.
  1583. _ReservedBitmap - Contains those NTFS clusters that are marked as "in use"
  1584. in the _VolumeBitmap during the conversion, but that must
  1585. be marked as "free" after the conversion. This is
  1586. required so that the conversion don't try to allocate
  1587. those clusters. These "reserved" clusters include all
  1588. the FAT structures that will be thrown away after the
  1589. conversion.
  1590. Arguments:
  1591. None
  1592. Return Value:
  1593. BOOLEAN - TRUE if bitmaps and bad block stack created.
  1594. FALSE otherwise
  1595. --*/
  1596. {
  1597. PFAT Fat; // The FAT
  1598. ULONG Cluster; // Used to traverse FAT
  1599. LCN Lcn; // Same as Cluster, but in sectors
  1600. ULONG ClusterCount; // Number of clusters in volume
  1601. LCN DataAreaStart; // Start of data area;
  1602. ULONG SectorsPerFatCluster; // Sector counter
  1603. BIG_INT NumberOfClusters;
  1604. //
  1605. // Initialize bitmaps
  1606. //
  1607. DebugAssert(_FatSa->IsVolumeDataAligned() || (_ClusterFactor == 1));
  1608. NumberOfClusters = (_Drive->QuerySectors() - 1)/_ClusterFactor;
  1609. DebugAssert(NumberOfClusters.GetHighPart() == 0);
  1610. if (!_VolumeBitmap.Initialize(NumberOfClusters, FALSE, _Drive, _ClusterFactor) ||
  1611. !_ReservedBitmap.Initialize(NumberOfClusters, FALSE, NULL, 0)) {
  1612. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  1613. _Message->Display();
  1614. _Status = CONVERT_STATUS_ERROR;
  1615. DebugAssert(FALSE);
  1616. return FALSE;
  1617. }
  1618. //
  1619. // The boot area must be free (it will become reserved when creating
  1620. // the elementary NTFS structures).
  1621. //
  1622. //
  1623. // The FAT ( From the end of the boot area up to the beginning of
  1624. // the data area) is reserved but will be freed at the end of the
  1625. // conversion.
  1626. //
  1627. DataAreaStart = _FatSa->QueryStartDataLbn();
  1628. DebugAssert(!_FatSa->IsVolumeDataAligned() || (DataAreaStart % _ClusterFactor == 0));
  1629. DataAreaStart = DataAreaStart / _ClusterFactor;
  1630. _VolumeBitmap.SetAllocated( CLUSTERS_IN_BOOT,
  1631. DataAreaStart - CLUSTERS_IN_BOOT );
  1632. _ReservedBitmap.SetAllocated( CLUSTERS_IN_BOOT,
  1633. DataAreaStart - CLUSTERS_IN_BOOT );
  1634. //
  1635. // Allocate the rest of the bitmap according to the FAT
  1636. //
  1637. Lcn = DataAreaStart;
  1638. Cluster = FirstDiskCluster;
  1639. ClusterCount = _FatSa->QueryClusterCount() - (ULONG)FirstDiskCluster;
  1640. Fat = _FatSa->GetFat();
  1641. SectorsPerFatCluster = _ClusterRatio * _ClusterFactor;
  1642. while ( ClusterCount-- ) {
  1643. //
  1644. // If the cluster is not free then allocate it if its OK, or
  1645. // push it onto the bad stack if it is bad.
  1646. //
  1647. if ( Fat->IsClusterFree( Cluster ) ) {
  1648. Lcn += _ClusterRatio;
  1649. } else if ( Fat->IsClusterBad( Cluster ) ) {
  1650. _BadLcn.Add( Lcn*_ClusterFactor, SectorsPerFatCluster );
  1651. Lcn += _ClusterRatio;
  1652. } else {
  1653. _VolumeBitmap.SetAllocated( Lcn, _ClusterRatio );
  1654. Lcn += _ClusterRatio;
  1655. }
  1656. Cluster++;
  1657. }
  1658. //
  1659. // Note that CLUSTERS_IN_BOOT are not really free (will be
  1660. // allocated later on).
  1661. //
  1662. _FreeSectorsBefore = ((BIG_INT)(_VolumeBitmap.QueryFreeClusters() - CLUSTERS_IN_BOOT))*
  1663. _Drive->QuerySectorSize();
  1664. return TRUE;
  1665. }
  1666. BOOLEAN
  1667. FAT_NTFS::CreateElementary(
  1668. )
  1669. /*++
  1670. Routine Description:
  1671. Creates the elementary NTFS data structures.
  1672. Arguments:
  1673. None
  1674. Return Value:
  1675. BOOLEAN - TRUE if elementary NTFS data structures created.
  1676. FALSE otherwise
  1677. --*/
  1678. {
  1679. NTFS_UPCASE_FILE UpcaseFile;
  1680. NTFS_ATTRIBUTE UpcaseAttribute;
  1681. NTFS_LOG_FILE LogFile;
  1682. DSTRING VolumeLabel; // Volume label
  1683. BOOLEAN Error;
  1684. DebugAssert( _VolumeBitmap.IsFree( 0, CLUSTERS_IN_BOOT ) );
  1685. //
  1686. // Get the volume label and create the elementary NTFS structures.
  1687. // Pass in zero for the initial log file size to indicate that
  1688. // CreateElementaryStructures should decide how big to make it.
  1689. //
  1690. if ( !_FatSa->QueryLabel( &VolumeLabel ) ||
  1691. !_NtfsSa.CreateElementaryStructures( &_VolumeBitmap,
  1692. _ClusterFactor,
  1693. _FrsSize,
  1694. SMALL_INDEX_BUFFER_SIZE,
  1695. 0,
  1696. &_BadLcn,
  1697. TRUE,
  1698. TRUE,
  1699. _Message,
  1700. _FatSa->GetBpb(),
  1701. &VolumeLabel ) ) {
  1702. _Message->Set( MSG_CONV_CANNOT_CREATE_ELEMENTARY, ERROR_MESSAGE );
  1703. _Message->Display();
  1704. _Status = CONVERT_STATUS_ERROR;
  1705. return FALSE;
  1706. }
  1707. //
  1708. // Now that we have the elementary structures, obtain the MFT, which is
  1709. // used later on during the conversion. Since we don't have an upcase
  1710. // table yet, pass in NULL for that parameter.
  1711. //
  1712. if ( !_Mft.Initialize( _Drive,
  1713. _NtfsSa.QueryMftStartingLcn(),
  1714. _ClusterFactor,
  1715. _FrsSize,
  1716. _NtfsSa.QueryVolumeSectors(),
  1717. &_VolumeBitmap,
  1718. NULL ) ||
  1719. !_Mft.Read() ) {
  1720. _Message->Set( MSG_CONV_CANNOT_READ, ERROR_MESSAGE );
  1721. _Message->Display();
  1722. _Status = CONVERT_STATUS_ERROR;
  1723. return FALSE;
  1724. }
  1725. // Tell the volume bitmap about the new Mft so it can add any
  1726. // bad clusters it finds to the bad cluster file.
  1727. _VolumeBitmap.SetMftPointer(_Mft.GetMasterFileTable());
  1728. // Get the upcase table.
  1729. //
  1730. if( !UpcaseFile.Initialize( _Mft.GetMasterFileTable() ) ||
  1731. !UpcaseFile.Read() ||
  1732. !UpcaseFile.QueryAttribute( &UpcaseAttribute, &Error, $DATA ) ||
  1733. !_UpcaseTable.Initialize( &UpcaseAttribute ) ) {
  1734. DebugPrint( "Can't get the upcase table.\n" );
  1735. return FALSE;
  1736. }
  1737. _Mft.SetUpcaseTable( &_UpcaseTable );
  1738. _Mft.GetMasterFileTable()->SetUpcaseTable( &_UpcaseTable );
  1739. //
  1740. // If we know how many files there are on the volume, extend the
  1741. // MFT so it is (sort of) contiguous.
  1742. //
  1743. if ( (_NumberOfFiles + _NumberOfDirectories) > 0 ) {
  1744. if ( !_Mft.Extend( _NumberOfFiles + _NumberOfDirectories + 20 ) ) {
  1745. DebugPrintTrace(( "Cannot extend MFT by %d segments\n", _NumberOfFiles + _NumberOfDirectories ));
  1746. _Message->Set( MSG_CONV_CANNOT_CREATE_ELEMENTARY, ERROR_MESSAGE );
  1747. _Message->Display();
  1748. _Status = CONVERT_STATUS_ERROR;
  1749. return FALSE;
  1750. }
  1751. }
  1752. // Flush the MFT now, so that it gets first claim to the FRS's
  1753. // at the beginning of the MFT.
  1754. //
  1755. if( !_Mft.Flush() ) {
  1756. DebugPrintTrace(( "CONVERT: Cannot flush the MFT\n" ));
  1757. _Message->Set( MSG_CONV_CANNOT_CREATE_ELEMENTARY, ERROR_MESSAGE );
  1758. _Message->Display();
  1759. _Status = CONVERT_STATUS_ERROR;
  1760. return FALSE;
  1761. }
  1762. // Sanity check: make sure that the log file doesn't have
  1763. // an attribute list. The file system will die horribly
  1764. // if Convert creates a log file with external attributes.
  1765. //
  1766. if( !LogFile.Initialize( _Mft.GetMasterFileTable() ) ||
  1767. !LogFile.Read() ||
  1768. LogFile.IsAttributePresent( $ATTRIBUTE_LIST ) ) {
  1769. _Message->Set( MSG_CONV_VOLUME_TOO_FRAGMENTED );
  1770. _Message->Display( "" );
  1771. return FALSE;
  1772. }
  1773. return TRUE;
  1774. }
  1775. BOOLEAN
  1776. FAT_NTFS::FreeReservedSectors (
  1777. )
  1778. /*++
  1779. Routine Description:
  1780. Frees up those sectors marked as "in use" in the _ReservedBitmap.
  1781. The _VolumeBitmap is updated and written to disk.
  1782. Arguments:
  1783. None
  1784. Return Value:
  1785. BOOLEAN - TRUE if sectors freed and _VolumeBitmap written.
  1786. FALSE otherwise.
  1787. --*/
  1788. {
  1789. NTFS_BITMAP_FILE BitmapFile; // NTFS bitmap file
  1790. NTFS_ATTRIBUTE Attribute; // $DATA attribute of bitmap file
  1791. LCN Lcn; // LCN
  1792. BOOLEAN Error;
  1793. PFILEDIR DirF32; // Pointer to FAT 32 Root dir
  1794. BIG_INT NumberOfLcn;
  1795. //
  1796. // Free the "reserved" clusters
  1797. //
  1798. NumberOfLcn = _Drive->QuerySectors()/_ClusterFactor;
  1799. for ( Lcn = 0; Lcn < NumberOfLcn; Lcn += 1 ) {
  1800. if ( !_ReservedBitmap.IsFree( Lcn, 1 ) ) {
  1801. _VolumeBitmap.SetFree( Lcn, 1 );
  1802. _ReservedBitmap.SetFree( Lcn, 1 );
  1803. }
  1804. }
  1805. DirF32 = _FatSa->GetFileDir();
  1806. if (DirF32) { // it's a fat32 drive..
  1807. ULONG clus;
  1808. PFAT Fat; // The FAT
  1809. ULONG SectorsPerCluster; // Sectors per cluster
  1810. LCN DataAreaStart; // Start of data area;
  1811. LCN Lcn; // Logical cluster number
  1812. SectorsPerCluster = _FatSa->QuerySectorsPerCluster();
  1813. Fat = _FatSa->GetFat();
  1814. DataAreaStart = _FatSa->QueryStartDataLbn();
  1815. //
  1816. // Mark the first 32 reserved sectors for later cleanup
  1817. //
  1818. _ReservedBitmap.SetAllocated( CLUSTERS_IN_BOOT, (32-1)/_ClusterFactor+1-CLUSTERS_IN_BOOT );
  1819. //
  1820. // Mark the root chain as "UN-used" in the NTFS bitmap.
  1821. // Mark those root directory clusters in the reserved bitmap for later cleanup
  1822. //
  1823. for (clus = DirF32->QueryStartingCluster(); !Fat->IsEndOfChain(clus); clus = Fat->QueryEntry(clus)) {
  1824. { // Free sectors under this Root Dir Cluster
  1825. Lcn = (DataAreaStart + ((clus-FirstDiskCluster)*SectorsPerCluster))/_ClusterFactor;
  1826. _VolumeBitmap.SetFree( Lcn, _ClusterRatio );
  1827. _ReservedBitmap.SetAllocated( Lcn, _ClusterRatio );
  1828. }
  1829. }
  1830. { // Free sectors under this Root Dir Cluster
  1831. Lcn = (DataAreaStart + ((clus-FirstDiskCluster)*SectorsPerCluster))/_ClusterFactor;
  1832. _VolumeBitmap.SetFree( Lcn, _ClusterRatio );
  1833. _ReservedBitmap.SetAllocated( Lcn, _ClusterRatio );
  1834. }
  1835. } else {
  1836. ULONG root_offset = _FatSa->QueryReservedSectors()+_FatSa->QueryFats()*_FatSa->QuerySectorsPerFat();
  1837. ULONG root_size = (_FatSa->QueryRootEntries()*BytesPerDirent - 1)/_Drive->QuerySectorSize() + 1;
  1838. DebugAssert( root_offset % _ClusterFactor == 0 );
  1839. DebugAssert( root_size % _ClusterFactor == 0 );
  1840. _ReservedBitmap.SetAllocated( root_offset/_ClusterFactor, root_size/_ClusterFactor );
  1841. }
  1842. //
  1843. // Update the Bitmap file.
  1844. //
  1845. if ( !BitmapFile.Initialize( _Mft.GetMasterFileTable() ) ||
  1846. !BitmapFile.Read() ||
  1847. !BitmapFile.QueryAttribute( &Attribute, &Error, $DATA )
  1848. ) {
  1849. _Message->Set( MSG_CONV_CANNOT_READ, ERROR_MESSAGE );
  1850. _Message->Display();
  1851. _Status = CONVERT_STATUS_ERROR;
  1852. return FALSE;
  1853. }
  1854. //
  1855. // Update the Bitmap file data attribute (i.e. the volume bitmap)
  1856. //
  1857. if ( !_VolumeBitmap.Write( &Attribute, &_VolumeBitmap ) ) {
  1858. _Message->Set( MSG_CONV_CANNOT_WRITE, ERROR_MESSAGE );
  1859. _Message->Display();
  1860. _Status = CONVERT_STATUS_ERROR;
  1861. return FALSE;
  1862. }
  1863. _FreeSectorsAfter = ((BIG_INT)_VolumeBitmap.QueryFreeClusters())*_ClusterFactor;
  1864. #if DBG
  1865. if ( _Flags & CONVERT_VERBOSE_FLAG ) {
  1866. DebugPrintTrace(( "Free sectors before conversion: %d\n", _FreeSectorsBefore.GetLowPart() ));
  1867. DebugPrintTrace(( "Free sectors after conversion: %d\n", _FreeSectorsAfter.GetLowPart() ));
  1868. }
  1869. #endif
  1870. return TRUE;
  1871. }
  1872. BOOLEAN
  1873. FAT_NTFS::QueryNeededHoles (
  1874. OUT PINTSTACK Stack
  1875. )
  1876. /*++
  1877. Routine Description:
  1878. Determines what holes are required and pushes the hole
  1879. information in the supplied stack.
  1880. Arguments:
  1881. Stack - Supplies the stack where the hole information is
  1882. passed
  1883. Return Value:
  1884. BOOLEAN - TRUE if all hole information is in stack
  1885. FALSE otherwise
  1886. --*/
  1887. {
  1888. BIG_INT HoleStart; // Starting sector of hole
  1889. BIG_INT HoleSize; // Size of the hole
  1890. BIG_INT BootSize; // Size of boot code
  1891. BIG_INT MftSize; // Size of MFT
  1892. BIG_INT MftReflectionSize; // Size of MFT reflection
  1893. ULONG sectorsize;
  1894. USHORT i;
  1895. //
  1896. // Initialize the hole stack
  1897. //
  1898. if ( !Stack->Initialize() ) {
  1899. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  1900. _Message->Display();
  1901. _Status = CONVERT_STATUS_ERROR;
  1902. DebugAssert(FALSE);
  1903. return FALSE;
  1904. }
  1905. //
  1906. // The only NTFS structure that needs a fixed location is
  1907. // the BOOT backup. Push the size and location of this sector
  1908. // onto the stack.
  1909. //
  1910. if ( !Stack->Push( 1 ) ||
  1911. !Stack->Push( _FatSa->QueryVirtualSectors() - 1 ) ) {
  1912. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  1913. _Message->Display();
  1914. _Status = CONVERT_STATUS_ERROR;
  1915. DebugAssert(FALSE);
  1916. return FALSE;
  1917. }
  1918. //
  1919. // We also want to create a hole big enough for the MFT and
  1920. // the MFT reflection. These don't need to be in a particular
  1921. // location, but they have to be contiguous (i.e. occupy a
  1922. // single "hole").
  1923. //
  1924. sectorsize = _Drive->QuerySectorSize();
  1925. MftSize = (_FrsSize * FIRST_USER_FILE_NUMBER + (sectorsize - 1)) / sectorsize;
  1926. MftReflectionSize = (_FrsSize * REFLECTED_MFT_SEGMENTS + (sectorsize - 1))
  1927. / sectorsize;
  1928. HoleSize = MftSize + MftReflectionSize;
  1929. HoleStart = _FatSa->QueryVirtualSectors() - 1 - HoleSize;
  1930. #if DBG
  1931. if ( _Flags & CONVERT_VERBOSE_FLAG ) {
  1932. DebugPrintTrace(( "Hole required: Sector %X, size %X\n",
  1933. HoleStart.GetLowPart(), HoleSize.GetLowPart() ));
  1934. }
  1935. #endif
  1936. //
  1937. // Make sure that the hole lies entirely in the FAT data area. Otherwise
  1938. // we won't be able to relocate the clusters in the hole.
  1939. //
  1940. if ( HoleStart < _FatSa->QueryStartDataLbn() ) {
  1941. _Message->Set( MSG_CONV_CANNOT_CONVERT_VOLUME, ERROR_MESSAGE );
  1942. _Message->Display( "%s%s", "NTFS", "FAT" );
  1943. _Status = CONVERT_STATUS_ERROR;
  1944. return FALSE;
  1945. }
  1946. //
  1947. // Push the hole data in the stack. Size goes first!
  1948. //
  1949. if ( !Stack->Push( HoleSize ) ||
  1950. !Stack->Push( HoleStart ) ) {
  1951. _Message->Set( MSG_CONV_NO_MEMORY, ERROR_MESSAGE );
  1952. _Message->Display();
  1953. _Status = CONVERT_STATUS_ERROR;
  1954. DebugAssert(FALSE);
  1955. return FALSE;
  1956. }
  1957. return TRUE;
  1958. }
  1959. VOID
  1960. FAT_NTFS::QuerySectorsNeededForConversion (
  1961. IN PCENSUS_REPORT Census,
  1962. OUT PBIG_INT SectorsNeeded
  1963. )
  1964. /*++
  1965. Routine Description:
  1966. Determines how many sectors are required for the conversion, given
  1967. the volume census.
  1968. Arguments:
  1969. Census - Supplies the volume census
  1970. SectorsNeeded - Supplies pointer to number of sectors needed
  1971. Return Value:
  1972. None
  1973. --*/
  1974. {
  1975. BIG_INT SectorsRequired;
  1976. BIG_INT BytesInIndices;
  1977. ULONG szTemp;
  1978. ULONG NtfsClusterSize; // Size of an NTFS cluster
  1979. ULONG sectorsize;
  1980. CONST AverageBytesPerIndexEntry = 128;
  1981. #if DBG
  1982. if ( _Flags & CONVERT_VERBOSE_FLAG ) {
  1983. DebugPrintTrace(( "\n" ));
  1984. DebugPrintTrace(( "---- Volume Census Data ----\n" ));
  1985. DebugPrintTrace(( "Number of dirs: %d\n", Census->DirEntriesCount ));
  1986. DebugPrintTrace(( "Number of files: %d\n", Census->FileEntriesCount ));
  1987. DebugPrintTrace(( "Clusters in dirs: %d\n", Census->DirClusters ));
  1988. DebugPrintTrace(( "Clusters in files: %d\n", Census->FileClusters ));
  1989. DebugPrintTrace(( "Clusters in EA file: %d\n", Census->EaClusters ));
  1990. DebugPrintTrace(( "\n\n" ));
  1991. }
  1992. #endif
  1993. NtfsClusterSize = _Drive->QuerySectorSize() * _ClusterFactor;
  1994. _NumberOfFiles = Census->FileEntriesCount;
  1995. _NumberOfDirectories = Census->DirEntriesCount;
  1996. SectorsRequired =
  1997. NTFS_SA::QuerySectorsInElementaryStructures( _Drive,
  1998. _ClusterFactor,
  1999. _FrsSize,
  2000. _ClustersPerIndexBuffer,
  2001. 0 );
  2002. //
  2003. // We will need _ClustersPerFrs clusters for each file or
  2004. // directory, plus enough index blocks to hold the required
  2005. // index entries. (Multiply the size of indices by two to
  2006. // reflect the fact that the average index block will be
  2007. // half full.)
  2008. //
  2009. sectorsize = _Drive->QuerySectorSize();
  2010. SectorsRequired += ( _NumberOfFiles + _NumberOfDirectories ) *
  2011. ((_FrsSize + (sectorsize - 1))/sectorsize);
  2012. BytesInIndices = ( _NumberOfFiles + _NumberOfDirectories ) *
  2013. AverageBytesPerIndexEntry * 2;
  2014. SectorsRequired += BytesInIndices / _Drive->QuerySectorSize();
  2015. //
  2016. // Extended attributes
  2017. //
  2018. if(Census->EaClusters) {
  2019. //
  2020. // With EAs each file will require one extra header for the EA itself
  2021. //
  2022. SectorsRequired += _NumberOfFiles *
  2023. ((_FrsSize + (sectorsize - 1))/sectorsize);
  2024. //
  2025. // Compute the "per file average EA size", round it up to
  2026. // the sector size and multiply it times the file count to get
  2027. // the projected EA size on NTFS.
  2028. //
  2029. szTemp = Census->EaClusters * _FatSa->QuerySectorsPerCluster();
  2030. szTemp = (szTemp + (_NumberOfFiles - 1)) / _NumberOfFiles; // sectors per file in EAs
  2031. if(szTemp == 0) {
  2032. szTemp = 1;
  2033. }
  2034. SectorsRequired += szTemp * _NumberOfFiles;
  2035. }
  2036. //
  2037. // In case of unreported bad sectors, we reserve 0.1% of the disk
  2038. //
  2039. SectorsRequired += _Drive->QuerySectors() / 1000;
  2040. // And that's that.
  2041. *SectorsNeeded = SectorsRequired;
  2042. }
  2043. BOOLEAN
  2044. FAT_NTFS::ReserveCluster (
  2045. IN ULONG Cluster
  2046. )
  2047. /*++
  2048. Routine Description:
  2049. "Reserves" all the sectors in the given clusters. This is done
  2050. by marking the sectors in the ReservedBitmap.
  2051. Arguments:
  2052. Cluster - Supplies cluster whose sectors are to be reserved
  2053. Return Value:
  2054. BOOLEAN - TRUE if all sectors in the cluster have been reserved
  2055. FALSE otherwise
  2056. --*/
  2057. {
  2058. LCN Lcn;
  2059. BIG_INT Clusters;
  2060. Clusters = ((ULONG)_FatSa->QuerySectorsPerCluster()) / _ClusterFactor;
  2061. if ( Cluster > 0 ) {
  2062. Lcn = FatClusterToLcn( Cluster )/_ClusterFactor;
  2063. _ReservedBitmap.SetAllocated( Lcn, Clusters );
  2064. return TRUE;
  2065. }
  2066. return FALSE;
  2067. }
  2068. NONVIRTUAL
  2069. BOOLEAN
  2070. FAT_NTFS::CheckGeometryMatch(
  2071. )
  2072. /*++
  2073. Routine Description:
  2074. This method checks that the geometry recorded in the
  2075. Bios Parameter Block agrees with the geometry reported
  2076. by the driver.
  2077. Arguments:
  2078. None.
  2079. Return Value:
  2080. TRUE if the geometry in the BPB matches that reported
  2081. by the driver; false if not. Note that the only field
  2082. which is checked is BytesPerSector.
  2083. --*/
  2084. {
  2085. USHORT SectorSize, SectorsPerTrack, Heads;
  2086. ULONG HiddenSectors;
  2087. _FatSa->QueryGeometry( &SectorSize,
  2088. &SectorsPerTrack,
  2089. &Heads,
  2090. &HiddenSectors );
  2091. if( SectorSize != _Drive->QuerySectorSize() ) {
  2092. return FALSE;
  2093. }
  2094. return TRUE;
  2095. }
  2096. BOOLEAN
  2097. FAT_NTFS::WriteBoot (
  2098. )
  2099. /*++
  2100. Routine Description:
  2101. Updates the boot sector and writes any other information (e.g.
  2102. partition data) so that the volume will be recognized as an NTFS
  2103. volume.
  2104. Arguments:
  2105. none
  2106. Return Value:
  2107. BOOLEAN - TRUE if boot sector updated
  2108. FALSE otherwise
  2109. --*/
  2110. {
  2111. FSTRING BootLogFileName;
  2112. PBYTE ZeroBuf;
  2113. ULONG Lcn;
  2114. BOOLEAN Done;
  2115. Done = (BOOLEAN)(_Mft.Flush() &&
  2116. _NtfsSa.Write( _Message ) &&
  2117. _NtfsSa.WriteRemainingBootCode());
  2118. if (Done) {
  2119. //
  2120. // Now that it passses the point of no return, we clean up
  2121. // any remaining fat32 reserved boot sectors and/or fat root directory.
  2122. //
  2123. ZeroBuf = (PBYTE)MALLOC( _Drive->QuerySectorSize()*_ClusterFactor );
  2124. if (ZeroBuf == NULL) {
  2125. _Message->Set( MSG_CONV_NO_MEMORY );
  2126. _Message->Display();
  2127. return FALSE;
  2128. }
  2129. memset( ZeroBuf, 0, _Drive->QuerySectorSize()*_ClusterFactor );
  2130. for ( Lcn = 0; Lcn < _ReservedBitmap.QuerySize(); Lcn +=1 ) {
  2131. if ( !_ReservedBitmap.IsFree( Lcn, 1 ) && _VolumeBitmap.IsFree( Lcn, 1 ) ) {
  2132. if (!_Drive->Write( Lcn*_ClusterFactor, 1, ZeroBuf )) {
  2133. DebugPrintTrace(("CUFAT: Failed to wipe clean cluster %x\n", Lcn));
  2134. }
  2135. }
  2136. }
  2137. FREE(ZeroBuf);
  2138. }
  2139. #if defined ( _AUTOCONV_ )
  2140. if ( Done ) {
  2141. //
  2142. // The volume is no longer FAT. We have to reboot so that the
  2143. // system recognizes it. Note that before we reboot, we
  2144. // must flush the drive's cache.
  2145. //
  2146. BootLogFileName.Initialize( L"bootex.log" );
  2147. if( _Message->IsLoggingEnabled() &&
  2148. !NTFS_SA::DumpMessagesToFile( &BootLogFileName,
  2149. &_Mft,
  2150. _Message ) ) {
  2151. DebugPrintTrace(( "CONVERT: Error writing messages to BOOTEX.LOG\n" ));
  2152. }
  2153. _Drive->FlushCache();
  2154. _Drive->InvalidateVolume();
  2155. //
  2156. // Unlock the volume and close our handle, to let the filesystem
  2157. // notice that things have changed.
  2158. //
  2159. DELETE(_Drive);
  2160. if ( _Flags & CONVERT_PAUSE_FLAG ) {
  2161. _Message->Set( MSG_CONV_PAUSE_BEFORE_REBOOT );
  2162. _Message->Display();
  2163. _Message->WaitForUserSignal();
  2164. } else {
  2165. _Message->Set( MSG_CONV_CONVERSION_COMPLETE );
  2166. _Message->Display();
  2167. }
  2168. //
  2169. // If we've paused for oem setup, we pass PowerOff = TRUE to
  2170. // Reboot.
  2171. //
  2172. IFS_SYSTEM::Reboot( (_Flags & CONVERT_PAUSE_FLAG) ? TRUE : FALSE );
  2173. }
  2174. #endif
  2175. return Done;
  2176. }