Unique File Identifier in Windows

Unique file identifier in windows

If you call GetFileInformationByHandle, you'll get a file ID in BY_HANDLE_FILE_INFORMATION.nFileIndexHigh/Low. This index is unique within a volume, and stays the same even if you move the file (within the volume) or rename it.

If you can assume that NTFS is used, you may also want to consider using Alternate Data Streams to store the metadata.

Unique File Identifier

Here's some code from Ashley Henderson I copied from this answer. It implies two approaches which both return the same unique identifier.

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

Unique file identifiers on NTFS and $Object_ID

It All Depends On What You're Trying To Do. :)

The file index number, like the documentation says, is really only a good identifier for the file in NTFS (as opposed to FAT), but even then it can change if the file is deleted and recreated (think backup/restore), though you might actually consider that a bonus if you were looking to distinguish those.

It's probably best not to worry about the link tracking service with regard to object IDs, and instead think of them as something you can create/set/read, though again only in NTFS.

Object IDs have some overhead, whereas file IDs do not. For most applications, I would probably end up picking file reference numbers (even though they are potentially fragile).

Unique file identifiers on NTFS and $Object_ID

It All Depends On What You're Trying To Do. :)

The file index number, like the documentation says, is really only a good identifier for the file in NTFS (as opposed to FAT), but even then it can change if the file is deleted and recreated (think backup/restore), though you might actually consider that a bonus if you were looking to distinguish those.

It's probably best not to worry about the link tracking service with regard to object IDs, and instead think of them as something you can create/set/read, though again only in NTFS.

Object IDs have some overhead, whereas file IDs do not. For most applications, I would probably end up picking file reference numbers (even though they are potentially fragile).

Uniquely identify file on Windows

The next best method (but one that involves reading every file completely, which I'd avoid when it can be helped) would be to compare file size and a hash (e.g. SHA-256) of the file contents. The probability that both collide is fairly slim, especially under normal circumstances.

I'd use the GetFileInformationByHandle way on NTFS and fall back to hashing on FAT volumes.

In Dropbox' case I think though, that there is a service or process running in background observing file system changes. It's the most reliable way, even if it ceases to work if you stop said service/process.

Get unique file id in Windows with Java?

Using JNA version 3.3.0:

Kernel32.INSTANCE.GetFileInformationByHandle Test case:

package win.test;

import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinBase;
import com.sun.jna.platform.win32.WinBase.FILETIME;
import com.sun.jna.platform.win32.WinNT.HANDLE;

import win.test.Kernel32.BY_HANDLE_FILE_INFORMATION;

public class FileTest {

/**
* @param args
*/
public static void main(String[] args) {
//http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858%28v=vs.85%29.aspx
final int FILE_SHARE_READ = (0x00000001);
//final int FILE_SHARE_WRITE = (0x00000002);
//final int FILE_SHARE_DELETE = (0x00000004);
final int OPEN_EXISTING = (3);
final int GENERIC_READ = (0x80000000);
//final int GENERIC_WRITE = (0x40000000);
//final int FILE_FLAG_NO_BUFFERING = (0x20000000);
//final int FILE_FLAG_WRITE_THROUGH = (0x80000000);
//final int FILE_READ_ATTRIBUTES = (0x0080);
//final int FILE_WRITE_ATTRIBUTES = (0x0100);
//final int ERROR_INSUFFICIENT_BUFFER = (122);
final int FILE_ATTRIBUTE_ARCHIVE = (0x20);

WinBase.SECURITY_ATTRIBUTES attr = null;
BY_HANDLE_FILE_INFORMATION lpFileInformation = new BY_HANDLE_FILE_INFORMATION();
HANDLE hFile = null;

hFile = Kernel32.INSTANCE.CreateFile(args[0], GENERIC_READ, FILE_SHARE_READ, attr, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, null);

System.out.println("CreateFile last error:" + Kernel32.INSTANCE.GetLastError());

//if (hFile. != -1)
{

win.test.Kernel32.INSTANCE.GetFileInformationByHandle(hFile, lpFileInformation);

System.out.println("CREATION TIME: " + FILETIME.filetimeToDate(lpFileInformation.ftCreationTime.dwHighDateTime, lpFileInformation.ftCreationTime.dwLowDateTime));

System.out.println("VOLUME SERIAL NO.: " + Integer.toHexString(lpFileInformation.dwVolumeSerialNumber.intValue()));

System.out.println("FILE INDEX HIGH: " + lpFileInformation.nFileIndexHigh);
System.out.println("FILE INDEX LOW: " + lpFileInformation.nFileIndexLow);

System.out.println("GetFileInformationByHandle last error:" + Kernel32.INSTANCE.GetLastError());
}

Kernel32.INSTANCE.CloseHandle(hFile);

System.out.println("CloseHandle last error:" + Kernel32.INSTANCE.GetLastError());

}

}

Sample output:

CreateFile last error:0
CREATION TIME: Tue Nov 29 22:24:04 SGT 2011
VOLUME SERIAL NO.: 900c0655
FILE INDEX HIGH: 1769472
FILE INDEX LOW: 286306
GetFileInformationByHandle last error:0
CloseHandle last error:0

Kernel32 JNA instance class:

package win.test;

import java.util.HashMap;
import java.util.Map;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.WinBase.FILETIME;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIFunctionMapper;
import com.sun.jna.win32.W32APITypeMapper;

public interface Kernel32 extends StdCallLibrary {
final static Map<String, Object> WIN32API_OPTIONS = new HashMap<String, Object>() {
private static final long serialVersionUID = 1L;
{
put(Library.OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
put(Library.OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
}
};

public Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("Kernel32", Kernel32.class, WIN32API_OPTIONS);

public int GetLastError();

/**
typedef struct _BY_HANDLE_FILE_INFORMATION {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD dwVolumeSerialNumber;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD nNumberOfLinks;
DWORD nFileIndexHigh;
DWORD nFileIndexLow;
} BY_HANDLE_FILE_INFORMATION, *PBY_HANDLE_FILE_INFORMATION;
*/

public class BY_HANDLE_FILE_INFORMATION extends Structure {
public DWORD dwFileAttributes;
public FILETIME ftCreationTime;
public FILETIME ftLastAccessTime;
public FILETIME ftLastWriteTime;
public DWORD dwVolumeSerialNumber;
public DWORD nFileSizeHigh;
public DWORD nFileSizeLow;
public DWORD nNumberOfLinks;
public DWORD nFileIndexHigh;
public DWORD nFileIndexLow;
public static class ByReference extends BY_HANDLE_FILE_INFORMATION implements Structure.ByReference {

};
public static class ByValue extends BY_HANDLE_FILE_INFORMATION implements Structure.ByValue {

};
};

/**
BOOL WINAPI GetFileInformationByHandle(
__in HANDLE hFile,
__out LPBY_HANDLE_FILE_INFORMATION lpFileInformation
);
*/
boolean GetFileInformationByHandle(
HANDLE hFile,
BY_HANDLE_FILE_INFORMATION lpFileInformation
);
}


Related Topics



Leave a reply



Submit