Уникальный идентификатор файла в windows

Есть ли способ однозначно идентифицировать файл (и, возможно, каталоги) на время существования файла независимо от его перемещения, переименования и изменения содержимого? (Windows 2000 и более поздние версии). Создание копии файла должно дать копии его собственный уникальный идентификатор.

Мое приложение связывает различные метаданные с отдельными файлами. Если файлы изменены, переименованы или перемещены, было бы полезно иметь возможность автоматически обнаруживать и обновлять ассоциации файлов.

FileSystemWatcher может предоставлять события, которые сообщают об изменениях такого рода, однако он использует буфер памяти, который можно легко заполнить (и потерять события), если многие события файловой системы происходят быстро.

Хеш бесполезен, потому что содержимое файла может измениться, и поэтому хеш изменится.

Я думал об использовании даты создания файла, однако есть несколько ситуаций, когда это не будет уникальным (то есть, когда копируются несколько файлов).

Я также слышал о файле SID (идентификатор безопасности?) В NTFS, но я не уверен, что это будет делать то, что я ищу.

Есть идеи?

5 ответов

Решение

Если вы вызовете GetFileInformationByHandle, вы получите идентификатор файла в BY_HANDLE_FILE_INFORMATION.nFileIndexHigh/Low. Этот индекс уникален для тома и остается неизменным даже при перемещении файла (внутри тома) или его переименовании.

Если вы можете предположить, что NTFS используется, вы также можете рассмотреть возможность использования альтернативных потоков данных для хранения метаданных.

Вот пример кода, который возвращает уникальный индекс файла.

Подход A() - это то, что я придумал после небольшого исследования. ApproachB() благодаря информации, предоставленной Маттиасом и Рубенсом. Для конкретного файла оба подхода возвращают один и тот же индекс файла (во время базового тестирования).

Некоторые предостережения от MSDN:

Поддержка идентификаторов файлов зависит от файловой системы. Не гарантируется, что идентификаторы файлов со временем будут уникальными, поскольку файловые системы могут использовать их повторно. В некоторых случаях идентификатор файла может изменяться со временем.

В файловой системе FAT идентификатор файла генерируется из первого кластера содержащей директории и байтового смещения в каталоге записи для файла. Некоторые продукты дефрагментации изменяют это смещение байта. (Операционная дефрагментация Windows не выполняется.) Таким образом, идентификатор файла FAT может изменяться со временем. Переименование файла в файловой системе FAT также может изменить идентификатор файла, но только в том случае, если имя нового файла длиннее старого.

В файловой системе NTFS файл сохраняет тот же идентификатор файла, пока не будет удален. Вы можете заменить один файл другим файлом без изменения идентификатора файла с помощью функции ReplaceFile. Однако идентификатор файла замещающего файла, а не заменяемого файла, сохраняется в качестве идентификатора файла полученного файла.

Первый смелый комментарий выше меня беспокоит. Пока не ясно, относится ли это утверждение только к FAT, оно противоречит второму тексту, выделенному жирным шрифтом. Я предполагаю, что дальнейшее тестирование - единственный способ быть уверенным.

[Обновление: в моем тестировании файл index/id изменялся при перемещении файла с одного внутреннего жесткого диска NTFS на другой внутренний жесткий диск NTFS.]

    public class WinAPI
    {
        [DllImport("ntdll.dll", SetLastError = true)]
        public static extern IntPtr NtQueryInformationFile(IntPtr fileHandle, ref IO_STATUS_BLOCK IoStatusBlock, IntPtr pInfoBlock, uint length, FILE_INFORMATION_CLASS fileInformation);

        public struct IO_STATUS_BLOCK
        {
            uint status;
            ulong information;
        }
        public struct _FILE_INTERNAL_INFORMATION {
          public ulong  IndexNumber;
        } 

        // Abbreviated, there are more values than shown
        public enum FILE_INFORMATION_CLASS
        {
            FileDirectoryInformation = 1,     // 1
            FileFullDirectoryInformation,     // 2
            FileBothDirectoryInformation,     // 3
            FileBasicInformation,         // 4
            FileStandardInformation,      // 5
            FileInternalInformation      // 6
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool GetFileInformationByHandle(IntPtr hFile,out BY_HANDLE_FILE_INFORMATION lpFileInformation);

        public struct BY_HANDLE_FILE_INFORMATION
        {
            public uint FileAttributes;
            public FILETIME CreationTime;
            public FILETIME LastAccessTime;
            public FILETIME LastWriteTime;
            public uint VolumeSerialNumber;
            public uint FileSizeHigh;
            public uint FileSizeLow;
            public uint NumberOfLinks;
            public uint FileIndexHigh;
            public uint FileIndexLow;
        }
  }

  public class Test
  {
       public ulong ApproachA()
       {
                WinAPI.IO_STATUS_BLOCK iostatus=new WinAPI.IO_STATUS_BLOCK();

                WinAPI._FILE_INTERNAL_INFORMATION objectIDInfo = new WinAPI._FILE_INTERNAL_INFORMATION();

                int structSize = Marshal.SizeOf(objectIDInfo);

                FileInfo fi=new FileInfo(@"C:\Temp\testfile.txt");
                FileStream fs=fi.Open(FileMode.Open,FileAccess.Read,FileShare.ReadWrite);

                IntPtr res=WinAPI.NtQueryInformationFile(fs.Handle, ref iostatus, memPtr, (uint)structSize, WinAPI.FILE_INFORMATION_CLASS.FileInternalInformation);

                objectIDInfo = (WinAPI._FILE_INTERNAL_INFORMATION)Marshal.PtrToStructure(memPtr, typeof(WinAPI._FILE_INTERNAL_INFORMATION));

                fs.Close();

                Marshal.FreeHGlobal(memPtr);   

                return objectIDInfo.IndexNumber;

       }

       public ulong ApproachB()
       {
               WinAPI.BY_HANDLE_FILE_INFORMATION objectFileInfo=new WinAPI.BY_HANDLE_FILE_INFORMATION();

                FileInfo fi=new FileInfo(@"C:\Temp\testfile.txt");
                FileStream fs=fi.Open(FileMode.Open,FileAccess.Read,FileShare.ReadWrite);

                WinAPI.GetFileInformationByHandle(fs.Handle, out objectFileInfo);

                fs.Close();

                ulong fileIndex = ((ulong)objectFileInfo.FileIndexHigh << 32) + (ulong)objectFileInfo.FileIndexLow;

                return fileIndex;   
       }
  }

Одна вещь, которую вы можете использовать для файловых uid, - это временная метка создания, просто убедитесь, что при сканировании файлов в вашу программу вы настраиваете любое время создания, которое совпадает с уже встреченным, поэтому они немного отличаются, так как, очевидно, несколько файлов даже в NTFS могут в противном случае имейте тот же TS, если они были созданы в одно и то же время путем массовой копии файлов, но при нормальном ходе вещей вы не получите дубликатов, и тонкая настройка будет небольшой, если вообще будет. d

Пользователь также упоминает уникальную идентификацию каталога. Этот процесс немного сложнее, чем получение уникальной информации для файла; Однако это возможно. Требуется позвонить в соответствующий CREATE_FILE Функция, которая конкретный флаг. С этой ручкой вы можете вызвать GetFileInformationByHandle функция в ответе Эша.

Это также требует kernel32.dll Импортировать:

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern SafeFileHandle CreateFile(
            string lpFileName,
            [MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
            [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
            IntPtr securityAttributes,
            [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
            uint dwFlagsAndAttributes,
            IntPtr hTemplateFile
        );

Я уточню этот ответ чуть позже, позже. Но с вышеупомянутым связанным ответом это должно начать иметь смысл. Мой новый любимый ресурс - pinvoke, который помог мне с возможностями подписи.Net C#.

Другие вопросы по тегам