//  genoid.c

#include "oidtst.h"

#define VOLUME_PATH  L"\\\\.\\H:"
#define VOLUME_DRIVE_LETTER_INDEX 4
#define FULL_PATH    L"\\??\\H:\\1234567890123456"
#define FULL_DRIVE_LETTER_INDEX 4
#define DEVICE_PREFIX_LEN 14

#define RELATIVE_OPEN


int
FsTestGenOid( 
    IN HANDLE hFile, 
    IN FILE_OBJECTID_BUFFER *ObjectIdBuffer 
    )
{
    IO_STATUS_BLOCK IoStatusBlock;
    NTSTATUS Status;

    Status = NtFsControlFile( hFile,                           // file handle
                              NULL,                            // event
                              NULL,                            // apc routine
                              NULL,                            // apc context
                              &IoStatusBlock,                  // iosb
                              FSCTL_CREATE_OR_GET_OBJECT_ID,   // FsControlCode
                              &hFile,                          // input buffer
                              sizeof(HANDLE),                  // input buffer length
                              ObjectIdBuffer,                  // OutputBuffer for data from the FS
                              sizeof(FILE_OBJECTID_BUFFER) );  // OutputBuffer Length

    if (Status == STATUS_SUCCESS) {

        printf( "\nOid for this file is:" );
        
        FsTestHexDump( ObjectIdBuffer->ObjectId, 16 );
        
        printf( "\nExtended info is:" );

        FsTestHexDump( ObjectIdBuffer->ObjectId, 64 );
    }

    return FsTestDecipherStatus( Status );
}

int
FsTestGetFileId( 
    IN HANDLE hFile, 
    IN PFILE_INTERNAL_INFORMATION FileId 
    )
{
    IO_STATUS_BLOCK IoStatusBlock;
    NTSTATUS Status;

    Status = NtQueryInformationFile( hFile,                           // file handle
                                     &IoStatusBlock,                  // iosb
                                     FileId,
                                     sizeof( FILE_INTERNAL_INFORMATION ),
                                     FileInternalInformation );
                                     
    if (Status == STATUS_SUCCESS) {

        printf( "\nFile id for this file is:" );
        
        FsTestHexDump( (PCHAR)FileId, 8 );
    }

    return FsTestDecipherStatus( Status );
}

int
FsTestGetName (
    HANDLE File
    )
{
    NTSTATUS Status;
    IO_STATUS_BLOCK IoStatusBlock;
    PFILE_NAME_INFORMATION FileName;
    WCHAR buffer[100];

    FileName = (PFILE_NAME_INFORMATION) buffer;
    
    Status = NtQueryInformationFile( File,
                                     &IoStatusBlock,
                                     FileName, 
                                     sizeof( buffer ),
                                     FileNameInformation );

    if (Status == STATUS_SUCCESS)
    {
        printf( "\nFilename is: %.*lS", 
                FileName->FileNameLength/sizeof(WCHAR), 
                FileName->FileName );
    }

    return FsTestDecipherStatus( Status );
}

DWORD
FsTestOpenVolumeHandle (
    PWCHAR DriveLetter,
    HANDLE *VolumeHandle
    )
{
    WCHAR Volume[] = VOLUME_PATH;
    DWORD Status = 0;

    //
    //  Open the volume for relative opens.
    //

    RtlCopyMemory( &Volume[VOLUME_DRIVE_LETTER_INDEX], DriveLetter, sizeof(WCHAR) );
    *VolumeHandle = CreateFileW( (PUSHORT) &Volume,
                                 GENERIC_READ | GENERIC_WRITE,
                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
                                 NULL,
                                 OPEN_EXISTING,
                                 0,
                                 NULL );

    if (*VolumeHandle == INVALID_HANDLE_VALUE) {

        Status = GetLastError();
        printf( "Unable to open %ws volume\n", &Volume );
        printf( "Error from CreateFile", Status );
        return Status;
    }

    return Status;
}

NTSTATUS
FsTestOpenFileById (
    IN HANDLE VolumeHandle,
    IN PUNICODE_STRING FileId,
    OUT HANDLE *File
    )
{
    NTSTATUS Status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    IO_STATUS_BLOCK IoStatusBlock;
    
    InitializeObjectAttributes( &ObjectAttributes,
                                FileId,
                                0, 
                                VolumeHandle,
                                NULL );

    Status = NtCreateFile( File,
                           GENERIC_READ,
                           &ObjectAttributes,
                           &IoStatusBlock,
                           NULL,
                           0,
                           FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                           FILE_OVERWRITE,
                           FILE_OPEN_BY_FILE_ID,
                           NULL,
                           0 );

    if (!NT_SUCCESS( Status )) {
        FsTestDecipherStatus( Status );
        return Status;
    }
    
    return Status;
}

VOID
FsTestGenerateFullName (
    IN PWCHAR DriveLetter,
    IN PUNICODE_STRING FileId,
    OUT PUNICODE_STRING FullName
    )
{
    WCHAR FullPath[] = FULL_PATH;
    UNICODE_STRING DeviceName;

    ASSERT( FullName->MaximumLength >= (DEVICE_PREFIX_LEN + FileId->Length) );

    RtlCopyMemory( &FullPath[FULL_DRIVE_LETTER_INDEX], DriveLetter, sizeof(WCHAR) );

    DeviceName.Length = DeviceName.MaximumLength = DEVICE_PREFIX_LEN;
    DeviceName.Buffer = FullPath;

    RtlCopyUnicodeString( FullName, &DeviceName );
    RtlAppendUnicodeStringToString( FullName, FileId );
}

VOID
_cdecl
main(
    int argc,
    char *argv[]
    )
{
    HANDLE File;
    HANDLE VolumeHandle = NULL;
    FILE_OBJECTID_BUFFER ObjectIdBuffer;
    FILE_INTERNAL_INFORMATION FileId;
    OBJECT_ATTRIBUTES ObjectAttributes;
    IO_STATUS_BLOCK IoStatusBlock;
    UNICODE_STRING IdString;
    NTSTATUS Status;
    WCHAR DriveLetter;
    WCHAR FullNameBuffer [100];
    UNICODE_STRING FullName;

    //
    //  Get parameters 
    //

    if (argc < 3) {
        printf("This program finds the object id of a file and generates one if necessary (ntfs only), then prints out the file name once that file is opened by the ids.\n\n");
        printf("usage: %s drive filename\n", argv[0]);
        return;
    }

    File = CreateFile( argv[2],
                        0,
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                        NULL,
                        OPEN_EXISTING,
                        0,
                        NULL );

    if ( File == INVALID_HANDLE_VALUE ) {
        printf( "Error opening file %s %x\n", argv[2], GetLastError() );
        return;
    }

    FsTestGetName( File );

    RtlZeroBytes( &FileId, sizeof( FileId ) );
    FsTestGetFileId( File, &FileId );

    RtlZeroBytes( &ObjectIdBuffer, sizeof( ObjectIdBuffer ) );
    FsTestGenOid( File, &ObjectIdBuffer );

    CloseHandle( File );

    DriveLetter = *argv[1];

#ifdef RELATIVE_OPEN
    FsTestOpenVolumeHandle( &DriveLetter, &VolumeHandle );

    if (VolumeHandle == INVALID_HANDLE_VALUE) {
        goto main_exit;
    }
#endif

    RtlInitEmptyUnicodeString( &FullName, FullNameBuffer, sizeof( FullNameBuffer ) );

    printf( "\nReopening file by file id....\n" );

    IdString.Length = IdString.MaximumLength = sizeof( LARGE_INTEGER );
    IdString.Buffer = (PWSTR) &FileId;

#ifdef RELATIVE_OPEN
    Status = FsTestOpenFileById( VolumeHandle, &IdString , &File );
#else
    FsTestGenerateFullName( &DriveLetter, &IdString, &FullName );
    Status = FsTestOpenFileById( VolumeHandle, &FullName , &File );
#endif    
    
    if (!NT_SUCCESS( Status )) {
        goto main_exit;
    }
    
    FsTestGetName( File );
    
    NtClose( File );



    printf( "\nReopening file by object id....\n" );

    IdString.Length = IdString.MaximumLength = 16 * sizeof( UCHAR );
    IdString.Buffer = (PWSTR) &(ObjectIdBuffer.ObjectId);

#ifdef RELATIVE_OPEN
    Status = FsTestOpenFileById( VolumeHandle, &IdString , &File );
#else
    FsTestGenerateFullName( &DriveLetter, &IdString, &FullName );
    Status = FsTestOpenFileById( VolumeHandle, &FullName , &File );
#endif    
    
    if (!NT_SUCCESS( Status )) {
        goto main_exit;
    }
    
    FsTestGetName( File );
    
    NtClose( File );

main_exit:
    
    if (VolumeHandle != NULL) {
        CloseHandle( VolumeHandle );
    }
}