コード例 #1
0
        public void ShortNames()
        {
            NtfsFileSystem ntfs = FileSystemSource.NtfsFileSystem();

            // Check we can find a short name in the same directory
            using (Stream s = ntfs.OpenFile("ALongFileName.txt", FileMode.CreateNew)) {}
            ntfs.SetShortName("ALongFileName.txt", "ALONG~01.TXT");
            Assert.Equal("ALONG~01.TXT", ntfs.GetShortName("ALongFileName.txt"));
            Assert.True(ntfs.FileExists("ALONG~01.TXT"));

            // Check path handling
            ntfs.CreateDirectory("DIR");
            using (Stream s = ntfs.OpenFile(@"DIR\ALongFileName2.txt", FileMode.CreateNew)) { }
            ntfs.SetShortName(@"DIR\ALongFileName2.txt", "ALONG~02.TXT");
            Assert.Equal("ALONG~02.TXT", ntfs.GetShortName(@"DIR\ALongFileName2.txt"));
            Assert.True(ntfs.FileExists(@"DIR\ALONG~02.TXT"));

            // Check we can open a file by the short name
            using (Stream s = ntfs.OpenFile("ALONG~01.TXT", FileMode.Open)) { }

            // Delete the long name, and make sure the file is gone
            ntfs.DeleteFile("ALONG~01.TXT");
            Assert.False(ntfs.FileExists("ALONG~01.TXT"));

            // Delete the short name, and make sure the file is gone
            ntfs.DeleteFile(@"DIR\ALONG~02.TXT");
            Assert.False(ntfs.FileExists(@"DIR\ALongFileName2.txt"));
        }
コード例 #2
0
        public void GetFileLength()
        {
            NtfsFileSystem ntfs = FileSystemSource.NtfsFileSystem();

            ntfs.OpenFile(@"AFILE.TXT", FileMode.Create).Dispose();
            Assert.Equal(0, ntfs.GetFileLength("AFILE.TXT"));

            using (var stream = ntfs.OpenFile(@"AFILE.TXT", FileMode.Open))
            {
                stream.Write(new byte[14325], 0, 14325);
            }
            Assert.Equal(14325, ntfs.GetFileLength("AFILE.TXT"));

            using (var attrStream = ntfs.OpenFile(@"AFILE.TXT:altstream", FileMode.Create))
            {
                attrStream.Write(new byte[122], 0, 122);
            }
            Assert.Equal(122, ntfs.GetFileLength("AFILE.TXT:altstream"));


            // Test NTFS options for hardlink behaviour
            ntfs.CreateDirectory("Dir");
            ntfs.CreateHardLink("AFILE.TXT", @"Dir\OtherLink.txt");

            using (var stream = ntfs.OpenFile("AFILE.TXT", FileMode.Open, FileAccess.ReadWrite))
            {
                stream.SetLength(50);
            }
            Assert.Equal(50, ntfs.GetFileLength("AFILE.TXT"));
            Assert.Equal(14325, ntfs.GetFileLength(@"Dir\OtherLink.txt"));

            ntfs.NtfsOptions.FileLengthFromDirectoryEntries = false;

            Assert.Equal(50, ntfs.GetFileLength(@"Dir\OtherLink.txt"));
        }
コード例 #3
0
        public void Fragmented()
        {
            NtfsFileSystem ntfs = FileSystemSource.NtfsFileSystem();

            ntfs.CreateDirectory(@"DIR");

            byte[] buffer = new byte[4096];

            for (int i = 0; i < 2500; ++i)
            {
                using (var stream = ntfs.OpenFile(@"DIR\file" + i + ".bin", FileMode.Create, FileAccess.ReadWrite))
                {
                    stream.Write(buffer, 0, buffer.Length);
                }

                using (var stream = ntfs.OpenFile(@"DIR\" + i + ".bin", FileMode.Create, FileAccess.ReadWrite))
                {
                    stream.Write(buffer, 0, buffer.Length);
                }
            }

            for (int i = 0; i < 2500; ++i)
            {
                ntfs.DeleteFile(@"DIR\file" + i + ".bin");
            }

            // Create fragmented file (lots of small writes)
            using (var stream = ntfs.OpenFile(@"DIR\fragmented.bin", FileMode.Create, FileAccess.ReadWrite))
            {
                for (int i = 0; i < 2500; ++i)
                {
                    stream.Write(buffer, 0, buffer.Length);
                }
            }

            // Try a large write
            byte[] largeWriteBuffer = new byte[200 * 1024];
            for (int i = 0; i < largeWriteBuffer.Length / 4096; ++i)
            {
                largeWriteBuffer[i * 4096] = (byte)i;
            }
            using (var stream = ntfs.OpenFile(@"DIR\fragmented.bin", FileMode.OpenOrCreate, FileAccess.ReadWrite))
            {
                stream.Position = stream.Length - largeWriteBuffer.Length;
                stream.Write(largeWriteBuffer, 0, largeWriteBuffer.Length);
            }

            // And a large read
            byte[] largeReadBuffer = new byte[largeWriteBuffer.Length];
            using (var stream = ntfs.OpenFile(@"DIR\fragmented.bin", FileMode.OpenOrCreate, FileAccess.ReadWrite))
            {
                stream.Position = stream.Length - largeReadBuffer.Length;
                stream.Read(largeReadBuffer, 0, largeReadBuffer.Length);
            }

            Assert.Equal(largeWriteBuffer, largeReadBuffer);
        }
コード例 #4
0
        public void AclInheritance()
        {
            NtfsFileSystem ntfs = FileSystemSource.NtfsFileSystem();

            RawSecurityDescriptor sd = new RawSecurityDescriptor("O:BAG:BAD:(A;OICINP;GA;;;BA)");

            ntfs.CreateDirectory("dir");
            ntfs.SetSecurity("dir", sd);

            ntfs.CreateDirectory(@"dir\subdir");
            RawSecurityDescriptor inheritedSd = ntfs.GetSecurity(@"dir\subdir");

            Assert.NotNull(inheritedSd);
            Assert.Equal("O:BAG:BAD:(A;ID;GA;;;BA)", inheritedSd.GetSddlForm(AccessControlSections.All));

            using (ntfs.OpenFile(@"dir\subdir\file", FileMode.Create, FileAccess.ReadWrite)) { }
            inheritedSd = ntfs.GetSecurity(@"dir\subdir\file");
            Assert.NotNull(inheritedSd);
            Assert.Equal("O:BAG:BAD:", inheritedSd.GetSddlForm(AccessControlSections.All));
        }
コード例 #5
0
        public void ReparsePoints_NonEmpty()
        {
            NtfsFileSystem ntfs = FileSystemSource.NtfsFileSystem();

            ntfs.CreateDirectory("dir");
            ntfs.SetReparsePoint("dir", new ReparsePoint(123, new byte[] { 4, 5, 6 }));

            ReparsePoint rp = ntfs.GetReparsePoint("dir");

            Assert.Equal(123, rp.Tag);
            Assert.NotNull(rp.Content);
            Assert.Equal(3, rp.Content.Length);
        }
コード例 #6
0
        public void DeleteShortNameDir()
        {
            NtfsFileSystem ntfs = FileSystemSource.NtfsFileSystem();

            ntfs.CreateDirectory(@"\TestLongName1\TestLongName2");
            ntfs.SetShortName(@"\TestLongName1\TestLongName2", "TESTLO~1");

            Assert.True(ntfs.DirectoryExists(@"\TestLongName1\TESTLO~1"));
            Assert.True(ntfs.DirectoryExists(@"\TestLongName1\TestLongName2"));

            ntfs.DeleteDirectory(@"\TestLongName1", true);

            Assert.False(ntfs.DirectoryExists(@"\TestLongName1"));
        }
コード例 #7
0
        public void HardLinkCount()
        {
            NtfsFileSystem ntfs = FileSystemSource.NtfsFileSystem();

            using (Stream s = ntfs.OpenFile("ALongFileName.txt", FileMode.CreateNew)) { }
            Assert.Equal(1, ntfs.GetHardLinkCount("ALongFileName.txt"));

            ntfs.CreateHardLink("ALongFileName.txt", "AHardLink.TXT");
            Assert.Equal(2, ntfs.GetHardLinkCount("ALongFileName.txt"));

            ntfs.CreateDirectory("DIR");
            ntfs.CreateHardLink(@"ALongFileName.txt", @"DIR\SHORTLNK.TXT");
            Assert.Equal(3, ntfs.GetHardLinkCount("ALongFileName.txt"));

            // If we enumerate short names, then the initial long name results in two 'hardlinks'
            ntfs.NtfsOptions.HideDosFileNames = false;
            Assert.Equal(4, ntfs.GetHardLinkCount("ALongFileName.txt"));
        }
コード例 #8
0
        public void MoveLongName()
        {
            NtfsFileSystem ntfs = FileSystemSource.NtfsFileSystem();

            using (Stream s = ntfs.OpenFile("ALongFileName.txt", FileMode.CreateNew)) { }

            Assert.True(ntfs.FileExists("ALONGF~1.TXT"));

            ntfs.MoveFile("ALongFileName.txt", "ADifferentLongFileName.txt");

            Assert.False(ntfs.FileExists("ALONGF~1.TXT"));
            Assert.True(ntfs.FileExists("ADIFFE~1.TXT"));

            ntfs.CreateDirectory("ALongDirectoryName");
            Assert.True(ntfs.DirectoryExists("ALONGD~1"));

            ntfs.MoveDirectory("ALongDirectoryName", "ADifferentLongDirectoryName");
            Assert.False(ntfs.DirectoryExists("ALONGD~1"));
            Assert.True(ntfs.DirectoryExists("ADIFFE~1"));
        }
コード例 #9
0
        public NtfsFileSystem Format(Stream stream)
        {
            _context                      = new NtfsContext();
            _context.Options              = new NtfsOptions();
            _context.RawStream            = stream;
            _context.AttributeDefinitions = new AttributeDefinitions();

            string localAdminString = (ComputerAccount == null)
                    ? "LA"
                    : new SecurityIdentifier(WellKnownSidType.AccountAdministratorSid, ComputerAccount).ToString();

            using (new NtfsTransaction())
            {
                _clusterSize     = 4096;
                _mftRecordSize   = 1024;
                _indexBufferSize = 4096;

                long totalClusters = ((SectorCount - 1) * Sizes.Sector) / _clusterSize;

                // Allocate a minimum of 8KB for the boot loader, but allow for more
                int numBootClusters = Utilities.Ceil(Math.Max((int)(8 * Sizes.OneKiB), BootCode == null ? 0 : BootCode.Length), _clusterSize);

                // Place MFT mirror in the middle of the volume
                _mftMirrorCluster = totalClusters / 2;
                uint numMftMirrorClusters = 1;

                // The bitmap is also near the middle
                _bitmapCluster = _mftMirrorCluster + 13;
                int numBitmapClusters = (int)Utilities.Ceil((totalClusters / 8), _clusterSize);

                // The MFT bitmap goes 'near' the start - approx 10% in - but ensure we avoid the bootloader
                long mftBitmapCluster     = Math.Max(3 + (totalClusters / 10), numBootClusters);
                int  numMftBitmapClusters = 1;

                // The MFT follows it's bitmap
                _mftCluster = mftBitmapCluster + numMftBitmapClusters;
                int numMftClusters = 8;

                if (_mftCluster + numMftClusters > _mftMirrorCluster ||
                    _bitmapCluster + numBitmapClusters >= totalClusters)
                {
                    throw new IOException("Unable to determine initial layout of NTFS metadata - disk may be too small");
                }

                CreateBiosParameterBlock(stream, numBootClusters * _clusterSize);

                _context.Mft = new MasterFileTable(_context);
                File mftFile = _context.Mft.InitializeNew(_context, mftBitmapCluster, (ulong)numMftBitmapClusters, (long)_mftCluster, (ulong)numMftClusters);

                File bitmapFile = CreateFixedSystemFile(MasterFileTable.BitmapIndex, _bitmapCluster, (ulong)numBitmapClusters, true);
                _context.ClusterBitmap = new ClusterBitmap(bitmapFile);
                _context.ClusterBitmap.MarkAllocated(0, numBootClusters);
                _context.ClusterBitmap.MarkAllocated(_bitmapCluster, numBitmapClusters);
                _context.ClusterBitmap.MarkAllocated(mftBitmapCluster, numMftBitmapClusters);
                _context.ClusterBitmap.MarkAllocated(_mftCluster, numMftClusters);
                _context.ClusterBitmap.SetTotalClusters(totalClusters);
                bitmapFile.UpdateRecordInMft();

                File mftMirrorFile = CreateFixedSystemFile(MasterFileTable.MftMirrorIndex, _mftMirrorCluster, numMftMirrorClusters, true);

                File logFile = CreateSystemFile(MasterFileTable.LogFileIndex);
                using (Stream s = logFile.OpenStream(AttributeType.Data, null, FileAccess.ReadWrite))
                {
                    s.SetLength(Math.Min(Math.Max(2 * Sizes.OneMiB, (totalClusters / 500) * (long)_clusterSize), 64 * Sizes.OneMiB));
                    byte[] buffer = new byte[1024 * 1024];
                    for (int i = 0; i < buffer.Length; ++i)
                    {
                        buffer[i] = 0xFF;
                    }

                    long totalWritten = 0;
                    while (totalWritten < s.Length)
                    {
                        int toWrite = (int)Math.Min(s.Length - totalWritten, buffer.Length);
                        s.Write(buffer, 0, toWrite);
                        totalWritten += toWrite;
                    }
                }

                File       volumeFile    = CreateSystemFile(MasterFileTable.VolumeIndex);
                NtfsStream volNameStream = volumeFile.CreateStream(AttributeType.VolumeName, null);
                volNameStream.SetContent(new VolumeName(Label ?? "New Volume"));
                NtfsStream volInfoStream = volumeFile.CreateStream(AttributeType.VolumeInformation, null);
                volInfoStream.SetContent(new VolumeInformation(3, 1, VolumeInformationFlags.None));
                SetSecurityAttribute(volumeFile, "O:" + localAdminString + "G:BAD:(A;;0x12019f;;;SY)(A;;0x12019f;;;BA)");
                volumeFile.UpdateRecordInMft();

                _context.GetFileByIndex = delegate(long index) { return(new File(_context, _context.Mft.GetRecord(index, false))); };
                _context.AllocateFile   = delegate(FileRecordFlags frf) { return(new File(_context, _context.Mft.AllocateRecord(frf, false))); };

                File attrDefFile = CreateSystemFile(MasterFileTable.AttrDefIndex);
                _context.AttributeDefinitions.WriteTo(attrDefFile);
                SetSecurityAttribute(attrDefFile, "O:" + localAdminString + "G:BAD:(A;;FR;;;SY)(A;;FR;;;BA)");
                attrDefFile.UpdateRecordInMft();

                File bootFile = CreateFixedSystemFile(MasterFileTable.BootIndex, 0, (uint)numBootClusters, false);
                SetSecurityAttribute(bootFile, "O:" + localAdminString + "G:BAD:(A;;FR;;;SY)(A;;FR;;;BA)");
                bootFile.UpdateRecordInMft();

                File badClusFile = CreateSystemFile(MasterFileTable.BadClusIndex);
                badClusFile.CreateStream(AttributeType.Data, "$Bad");
                badClusFile.UpdateRecordInMft();

                File secureFile = CreateSystemFile(MasterFileTable.SecureIndex, FileRecordFlags.HasViewIndex);
                secureFile.RemoveStream(secureFile.GetStream(AttributeType.Data, null));
                _context.SecurityDescriptors = SecurityDescriptors.Initialize(secureFile);
                secureFile.UpdateRecordInMft();

                File upcaseFile = CreateSystemFile(MasterFileTable.UpCaseIndex);
                _context.UpperCase = UpperCase.Initialize(upcaseFile);
                upcaseFile.UpdateRecordInMft();

                File objIdFile = File.CreateNew(_context, FileRecordFlags.IsMetaFile | FileRecordFlags.HasViewIndex, FileAttributeFlags.None);
                objIdFile.RemoveStream(objIdFile.GetStream(AttributeType.Data, null));
                objIdFile.CreateIndex("$O", (AttributeType)0, AttributeCollationRule.MultipleUnsignedLongs);
                objIdFile.UpdateRecordInMft();

                File reparseFile = File.CreateNew(_context, FileRecordFlags.IsMetaFile | FileRecordFlags.HasViewIndex, FileAttributeFlags.None);
                reparseFile.CreateIndex("$R", (AttributeType)0, AttributeCollationRule.MultipleUnsignedLongs);
                reparseFile.UpdateRecordInMft();

                File quotaFile = File.CreateNew(_context, FileRecordFlags.IsMetaFile | FileRecordFlags.HasViewIndex, FileAttributeFlags.None);
                Quotas.Initialize(quotaFile);

                Directory extendDir = CreateSystemDirectory(MasterFileTable.ExtendIndex);
                extendDir.AddEntry(objIdFile, "$ObjId", FileNameNamespace.Win32AndDos);
                extendDir.AddEntry(reparseFile, "$Reparse", FileNameNamespace.Win32AndDos);
                extendDir.AddEntry(quotaFile, "$Quota", FileNameNamespace.Win32AndDos);
                extendDir.UpdateRecordInMft();

                Directory rootDir = CreateSystemDirectory(MasterFileTable.RootDirIndex);
                rootDir.AddEntry(mftFile, "$MFT", FileNameNamespace.Win32AndDos);
                rootDir.AddEntry(mftMirrorFile, "$MFTMirr", FileNameNamespace.Win32AndDos);
                rootDir.AddEntry(logFile, "$LogFile", FileNameNamespace.Win32AndDos);
                rootDir.AddEntry(volumeFile, "$Volume", FileNameNamespace.Win32AndDos);
                rootDir.AddEntry(attrDefFile, "$AttrDef", FileNameNamespace.Win32AndDos);
                rootDir.AddEntry(rootDir, ".", FileNameNamespace.Win32AndDos);
                rootDir.AddEntry(bitmapFile, "$Bitmap", FileNameNamespace.Win32AndDos);
                rootDir.AddEntry(bootFile, "$Boot", FileNameNamespace.Win32AndDos);
                rootDir.AddEntry(badClusFile, "$BadClus", FileNameNamespace.Win32AndDos);
                rootDir.AddEntry(secureFile, "$Secure", FileNameNamespace.Win32AndDos);
                rootDir.AddEntry(upcaseFile, "$UpCase", FileNameNamespace.Win32AndDos);
                rootDir.AddEntry(extendDir, "$Extend", FileNameNamespace.Win32AndDos);
                SetSecurityAttribute(rootDir, "O:" + localAdminString + "G:BUD:(A;OICI;FA;;;BA)(A;OICI;FA;;;SY)(A;OICIIO;GA;;;CO)(A;OICI;0x1200a9;;;BU)(A;CI;LC;;;BU)(A;CIIO;DC;;;BU)(A;;0x1200a9;;;WD)");
                rootDir.UpdateRecordInMft();

                // A number of records are effectively 'reserved'
                for (long i = MasterFileTable.ExtendIndex + 1; i <= 15; i++)
                {
                    File f = CreateSystemFile(i);
                    SetSecurityAttribute(f, "O:S-1-5-21-1708537768-746137067-1060284298-1003G:BAD:(A;;0x12019f;;;SY)(A;;0x12019f;;;BA)");
                    f.UpdateRecordInMft();
                }
            }

            // XP-style security permissions setup
            NtfsFileSystem ntfs = new NtfsFileSystem(stream);

            ntfs.SetSecurity(@"$MFT", new RawSecurityDescriptor("O:" + localAdminString + "G:BAD:(A;;FR;;;SY)(A;;FR;;;BA)"));
            ntfs.SetSecurity(@"$MFTMirr", new RawSecurityDescriptor("O:" + localAdminString + "G:BAD:(A;;FR;;;SY)(A;;FR;;;BA)"));
            ntfs.SetSecurity(@"$LogFile", new RawSecurityDescriptor("O:" + localAdminString + "G:BAD:(A;;FR;;;SY)(A;;FR;;;BA)"));
            ntfs.SetSecurity(@"$Bitmap", new RawSecurityDescriptor("O:" + localAdminString + "G:BAD:(A;;FR;;;SY)(A;;FR;;;BA)"));
            ntfs.SetSecurity(@"$BadClus", new RawSecurityDescriptor("O:" + localAdminString + "G:BAD:(A;;FR;;;SY)(A;;FR;;;BA)"));
            ntfs.SetSecurity(@"$UpCase", new RawSecurityDescriptor("O:" + localAdminString + "G:BAD:(A;;FR;;;SY)(A;;FR;;;BA)"));
            ntfs.SetSecurity(@"$Secure", new RawSecurityDescriptor("O:" + localAdminString + "G:BAD:(A;;0x12019f;;;SY)(A;;0x12019f;;;BA)"));
            ntfs.SetSecurity(@"$Extend", new RawSecurityDescriptor("O:" + localAdminString + "G:BAD:(A;;0x12019f;;;SY)(A;;0x12019f;;;BA)"));
            ntfs.SetSecurity(@"$Extend\$Quota", new RawSecurityDescriptor("O:" + localAdminString + "G:BAD:(A;;0x12019f;;;SY)(A;;0x12019f;;;BA)"));
            ntfs.SetSecurity(@"$Extend\$ObjId", new RawSecurityDescriptor("O:" + localAdminString + "G:BAD:(A;;0x12019f;;;SY)(A;;0x12019f;;;BA)"));
            ntfs.SetSecurity(@"$Extend\$Reparse", new RawSecurityDescriptor("O:" + localAdminString + "G:BAD:(A;;0x12019f;;;SY)(A;;0x12019f;;;BA)"));

            ntfs.CreateDirectory("System Volume Information");
            ntfs.SetAttributes("System Volume Information", FileAttributes.Hidden | FileAttributes.System | FileAttributes.Directory);
            ntfs.SetSecurity("System Volume Information", new RawSecurityDescriptor("O:BAG:SYD:(A;OICI;FA;;;SY)"));

            using (Stream s = ntfs.OpenFile(@"System Volume Information\MountPointManagerRemoteDatabase", FileMode.Create))
            {
            }

            ntfs.SetAttributes(@"System Volume Information\MountPointManagerRemoteDatabase", FileAttributes.Hidden | FileAttributes.System | FileAttributes.Archive);
            ntfs.SetSecurity(@"System Volume Information\MountPointManagerRemoteDatabase", new RawSecurityDescriptor("O:BAG:SYD:(A;;FA;;;SY)"));
            return(ntfs);
        }
コード例 #10
0
        private void CopyFiles(NtfsFileSystem sourceNtfs, NtfsFileSystem destNtfs, string path, bool subs)
        {
            if (subs)
            {
                foreach (var dir in sourceNtfs.GetDirectories(path))
                {
                    if (!IsExcluded(dir))
                    {
                        int  hardLinksRemaining = sourceNtfs.GetHardLinkCount(dir) - 1;
                        bool newDir             = false;

                        long   sourceFileId = sourceNtfs.GetFileId(dir);
                        string refPath;
                        if (_uniqueFiles.TryGetValue(sourceFileId, out refPath))
                        {
                            // If this is another name for a known dir, recreate the hard link
                            destNtfs.CreateHardLink(refPath, dir);
                        }
                        else
                        {
                            destNtfs.CreateDirectory(dir);
                            newDir = true;

                            FileAttributes fileAttrs = sourceNtfs.GetAttributes(dir);
                            if ((fileAttrs & FileAttributes.ReparsePoint) != 0)
                            {
                                destNtfs.SetReparsePoint(dir, sourceNtfs.GetReparsePoint(dir));
                            }

                            destNtfs.SetAttributes(dir, fileAttrs);

                            destNtfs.SetSecurity(dir, sourceNtfs.GetSecurity(dir));
                        }

                        // File may have a short name
                        string shortName = sourceNtfs.GetShortName(dir);
                        if (!string.IsNullOrEmpty(shortName) && shortName != dir)
                        {
                            destNtfs.SetShortName(dir, shortName);
                            --hardLinksRemaining;
                        }


                        if (newDir)
                        {
                            if (hardLinksRemaining > 0)
                            {
                                _uniqueFiles[sourceFileId] = dir;
                            }
                            CopyFiles(sourceNtfs, destNtfs, dir, subs);
                        }

                        // Set standard information last (includes modification timestamps)
                        destNtfs.SetFileStandardInformation(dir, sourceNtfs.GetFileStandardInformation(dir));
                    }
                }
            }

            foreach (var file in sourceNtfs.GetFiles(path))
            {
                Console.WriteLine(file);

                int hardLinksRemaining = sourceNtfs.GetHardLinkCount(file) - 1;

                long sourceFileId = sourceNtfs.GetFileId(file);

                string refPath;
                if (_uniqueFiles.TryGetValue(sourceFileId, out refPath))
                {
                    // If this is another name for a known file, recreate the hard link
                    destNtfs.CreateHardLink(refPath, file);
                }
                else
                {
                    CopyFile(sourceNtfs, destNtfs, file);

                    if (hardLinksRemaining > 0)
                    {
                        _uniqueFiles[sourceFileId] = file;
                    }
                }

                // File may have a short name
                string shortName = sourceNtfs.GetShortName(file);
                if (!string.IsNullOrEmpty(shortName) && shortName != file)
                {
                    destNtfs.SetShortName(file, shortName);
                }
            }
        }