web-dev-qa-db-fra.com

Identifiant de fichier unique dans Windows

Existe-t-il un moyen d'identifier de manière unique un fichier (et éventuellement des répertoires) pendant toute la durée de vie du fichier, quels que soient les déplacements, les renommements et les modifications de contenu? (Windows 2000 et ultérieur). Faire une copie d'un fichier devrait lui donner son propre identifiant unique.

Mon application associe diverses méta-données à des fichiers individuels. Si des fichiers sont modifiés, renommés ou déplacés, il serait utile de pouvoir détecter et mettre à jour automatiquement les associations de fichiers.

FileSystemWatcher peut fournir des événements qui informent de ce type de modifications, mais il utilise un tampon de mémoire pouvant être facilement rempli (et des événements perdus) si de nombreux événements du système de fichiers se produisent rapidement.

Un hachage ne sert à rien car le contenu du fichier peut changer, et donc le hachage changera.

J'avais envisagé d'utiliser la date de création du fichier, mais il existe quelques cas où cela ne sera pas unique (c'est-à-dire lorsque plusieurs fichiers sont copiés).

J'ai également entendu parler d'un fichier SID (ID de sécurité?) Dans NTFS, mais je ne sais pas si cela ferait ce que je recherche.

Des idées?

41
Ash

Si vous appelez GetFileInformationByHandle , vous obtiendrez un ID de fichier dans BY_HANDLE_FILE_INFORMATION.nFileIndexHigh/Low. Cet index est unique dans un volume et reste identique même si vous déplacez le fichier (dans le volume) ou le renommez.

Si vous pouvez supposer que NTFS est utilisé, vous pouvez également envisager d’utiliser d’autres flux de données pour stocker les métadonnées.

19
Mattias S

Voici un exemple de code renvoyant un index de fichier unique. 

ApproachA () est ce que j’ai trouvé après un peu de recherche. ApproachB () est grâce aux informations contenues dans les liens fournis par Mattias et Rubens. Étant donné un fichier spécifique, les deux approches renvoient le même index de fichier (lors de mes tests de base).

Quelques mises en garde de MSDN:

Le support pour les identifiants de fichier est fichier spécifique au système. Les identifiants de fichier ne sont pas garantie d'être unique dans le temps, parce que les systèmes de fichiers sont libres de réutiliser leur. Dans certains cas, l'ID de fichier pour un Le fichier peut changer avec le temps.

Dans le système de fichiers FAT, l'ID de fichier est généré à partir du premier cluster de le répertoire contenant et l'octet décalage dans le répertoire du entrée pour le fichier. Certains les produits de défragmentation changent cela décalage d'octet. (Windows dans la boîte Défragmentation pas.) Ainsi, un FAT L'identifiant de fichier peut changer avec le temps. Renommer Un fichier dans le système de fichiers FAT peut également changer l'ID du fichier, mais seulement si le le nouveau nom de fichier est plus long que l'ancien un.

Dans le système de fichiers NTFS, un fichier conserve le même ID de fichier jusqu'à ce qu'il soit supprimé . Vous pouvez remplacer un fichier par un autre fichier sans changer l'ID de fichier par en utilisant la fonction ReplaceFile . Cependant, l'ID de fichier du fichier fichier de remplacement, pas celui remplacé fichier, est retenu comme identifiant de fichier de le fichier résultant.

Le premier commentaire audacieux ci-dessus m'inquiète. Il n'est pas clair si cette déclaration s'applique uniquement à FAT, elle semble contredire le deuxième texte en gras. Je suppose que des tests supplémentaires sont le seul moyen d’en être sûr. 

[Mise à jour: lors de mes tests, l'index/id du fichier change lorsqu'un fichier est déplacé d'un disque dur NTFS interne vers un autre disque dur NTFS interne.]

    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;   
       }
  }
30
Ash

S'il vous plaît jeter un oeil ici: ID de fichier unique pour Windows . Ceci est également utile: ID unique pour les fichiers sur NTFS?

4
Rubens Farias

L'utilisateur mentionne également une identification de répertoire unique. Ce processus est un peu plus compliqué que de récupérer des informations uniques pour un fichier; Cependant, c'est possible. Vous devez appeler le CREATE_FILE _ function approprié pour un indicateur particulier. Avec cette poignée, vous pouvez appeler la fonction GetFileInformationByHandle dans le answer de Ash.

Cela nécessite également une importation 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
        );

Je vais préciser cette réponse un peu plus tard. Mais avec la réponse liée ci-dessus, cela devrait commencer à avoir un sens. Une de mes nouvelles ressources préférées est pinvoke , ce qui m’a aidé avec les possibilités de signature .Net C #. 

0
Thomas