#include <pch.cxx>

#if i386
//
// Temporarily disable optimizations until cl386 Drop 077 is fixed.
//
#pragma optimize("",off)
#endif

#define _NTAPI_ULIB_
#define _IFSUTIL_MEMBER_

#include "ulib.hxx"
#include "ifsutil.hxx"

#include "supera.hxx"
#include "message.hxx"
#include "rtmsg.h"
#include "ifssys.hxx"

#define MaxLabelLength      1024

DEFINE_EXPORTED_CONSTRUCTOR( SUPERAREA, SECRUN, IFSUTIL_EXPORT );

IFSUTIL_EXPORT
SUPERAREA::~SUPERAREA(
    )
/*++

Routine Description:

    Destructor for SUPERAREA.

Arguments:

    None.

Return Value:

    None.

--*/
{
    Destroy();
}


VOID
SUPERAREA::Construct(
        )
/*++

Routine Description:

        Constructor for SUPERAREA.

Arguments:

        None.

Return Value:

        None.

--*/
{
    _drive = NULL;
}


VOID
SUPERAREA::Destroy(
    )
/*++

Routine Description:

    This routine returns the object to its initial state freeing up
    any memory in the process.

Arguments:

    None.

Return Value:

    None.

--*/
{
    _drive = NULL;
}


IFSUTIL_EXPORT
BOOLEAN
SUPERAREA::Initialize(
    IN OUT  PMEM                Mem,
    IN OUT  PLOG_IO_DP_DRIVE    Drive,
    IN      SECTORCOUNT         NumberOfSectors,
    IN OUT  PMESSAGE            Message
    )
/*++

Routine Description:

    This routine initializes the SUPERAREA for the given drive.

Arguments:

    Mem             - Supplies necessary memory for the underlying sector run.
    Drive           - Supplies the drive where the superarea resides.
    NumberOfSectors - Supplies the number of sectors in the superarea.
    Message         - Supplies an outlet for messages.

Return Value:

    FALSE   - Failure.
    TRUE    - Success.

--*/
{
    Destroy();

    DebugAssert(Mem);
    DebugAssert(Drive);
    DebugAssert(NumberOfSectors);

    if (!SECRUN::Initialize(Mem, Drive, 0, NumberOfSectors)) {
        Message->Set(MSG_FMT_NO_MEMORY);
        Message->Display("");
        return FALSE;
    }

    _drive = Drive;

    return TRUE;
}


IFSUTIL_EXPORT
VOLID
SUPERAREA::ComputeVolId(
    IN  VOLID   Seed
    )
/*++

Routine Description:

    This routine computes a new and unique volume identifier.

Arguments:

    None.

Return Value:

    A unique volume id.

--*/
{
    VOLID           volid;
    PUCHAR          p;
    INT             i;
    LARGE_INTEGER   NtfsTime;

    if (Seed) {
        volid = Seed;
    } else {
        volid = 0;
    }

    do {

        if (!volid) {
            IFS_SYSTEM::QueryNtfsTime( &NtfsTime );
            if (NtfsTime.LowPart) {
                volid = (VOLID) NtfsTime.LowPart;
            } else {
                volid = (VOLID) NtfsTime.HighPart;
            }

            if (volid == 0) { // This should never happen.
                volid = 0x11111111;
            }
        }

        p = (PUCHAR) &volid;
        for (i = 0; i < sizeof(VOLID); i++) {
            volid += *p++;
            volid = (volid >> 2) + (volid << 30);
        }

    } while (!volid);

    return volid;
}

NTSTATUS
SUPERAREA::FormatNotification(
    IN     PWSTRING                        Label,
       OUT PFILE_FS_SIZE_INFORMATION       FsSizeInfo,
       OUT PFILE_FS_VOLUME_INFORMATION     FsVolInfo
    )
/*++

Routine Description:

    This routine sets the volume label which serves as a notification
    to the rest of the system that format has completed and the volume
    is avaiable for use again.

Arguments:

    Label        - Supplies the volume label.  If NULL, the current volume
                   label will be used.
    FsSizeInfo   - Retrieves the File System Size Information
    FsVolInfo    - Retrieves the File System Volume Information

Return Value:

    NT status

--*/
{
    //
    // Close the current drive handle first
    //
    _drive->CloseDriveHandle();

    return SUPERAREA::GenerateLabelNotification(_drive->GetNtDriveName(),
                                                Label,
                                                FsSizeInfo,
                                                FsVolInfo);
}

NTSTATUS
SUPERAREA::GenerateLabelNotification(
    IN     PCWSTRING                       NtDriveName,
    IN     PWSTRING                        Label,
       OUT PFILE_FS_SIZE_INFORMATION       FsSizeInfo,
       OUT PFILE_FS_VOLUME_INFORMATION     FsVolInfo
    )
/*++

Routine Description:

    This routine sets the volume label which serves as a notification
    to the rest of the system that the volume has changed.

Arguments:

    NtDriveName  - Supplies the name of the volume.
    Label        - Supplies the volume label.  If NULL, the current volume
                   label will be used.
    FsSizeInfo   - Retrieves the File System Size Information
    FsVolInfo    - Retrieves the File System Volume Information

Return Value:

    NT status

--*/
{
    CONST                       vollen = sizeof(FILE_FS_VOLUME_INFORMATION) +
                                         MaxLabelLength;
    CONST                       lablen = sizeof(FILE_FS_LABEL_INFORMATION) +
                                         MaxLabelLength;
    PFILE_FS_VOLUME_INFORMATION volinfo;
    PFILE_FS_LABEL_INFORMATION  labinfo;
    STR                         vol_info_buf[vollen];
    STR                         lab_info_buf[lablen];

    IO_STATUS_BLOCK             status_block;
    NTSTATUS                    status;

    PCWSTRING                   ntDriveName;
    UNICODE_STRING              string;
    OBJECT_ATTRIBUTES           oa;
    HANDLE                      handle;

    ntDriveName = NtDriveName;

    string.Length = (USHORT) ntDriveName->QueryChCount() * sizeof(WCHAR);
    string.MaximumLength = string.Length;
    string.Buffer = (PWSTR)ntDriveName->GetWSTR();

    InitializeObjectAttributes( &oa,
                                &string,
                                OBJ_CASE_INSENSITIVE,
                                0,
                                0 );

    status = NtOpenFile(&handle,
                        SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
                        &oa, &status_block,
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                        FILE_SYNCHRONOUS_IO_ALERT | FILE_WRITE_THROUGH);

    if (!NT_SUCCESS(status)) {
        DebugPrintTrace(("IFSUTIL: Unable to open handle with status %x\n", status));
        return status;
    }

    // status is good at this point

    if (FsVolInfo) {

        status = NtQueryVolumeInformationFile(handle,
                                              &status_block,
                                              FsVolInfo,
                                              sizeof(*FsVolInfo),
                                              FileFsVolumeInformation);
        if (!NT_SUCCESS(status)) {
            DebugPrintTrace(("IFSUTIL: Unable to query file system volume information with status %x\n", status));
        }
    }

    if (NT_SUCCESS(status) &&
        FsSizeInfo) {
        status = NtQueryVolumeInformationFile(handle,
                                              &status_block,
                                              FsSizeInfo,
                                              sizeof(*FsSizeInfo),
                                              FileFsSizeInformation);
        if (!NT_SUCCESS(status)) {
            DebugPrintTrace(("IFSUTIL: Unable to query file system size information with status %x\n", status));
        }
    }

    if (NT_SUCCESS(status) &&
        Label == NULL) {
        volinfo = (PFILE_FS_VOLUME_INFORMATION) vol_info_buf;

        status = NtQueryVolumeInformationFile(handle,
                                              &status_block,
                                              volinfo,
                                              vollen,
                                              FileFsVolumeInformation);
        if (!NT_SUCCESS(status)) {
            DebugPrintTrace(("IFSUTIL: Unable to query volume label with status %x\n", status));
        }
    }

    if (NT_SUCCESS(status)) {

        labinfo = (PFILE_FS_LABEL_INFORMATION)lab_info_buf;

        if (Label == NULL) {
            labinfo->VolumeLabelLength = volinfo->VolumeLabelLength;
            memcpy(labinfo->VolumeLabel, volinfo->VolumeLabel, labinfo->VolumeLabelLength);
        } else {
            labinfo->VolumeLabelLength = Label->QueryChCount()*sizeof(WCHAR);
            memcpy(labinfo->VolumeLabel, Label->GetWSTR(), labinfo->VolumeLabelLength);
        }

        status = NtSetVolumeInformationFile(handle,
                                            &status_block,
                                            labinfo,
                                            lablen,
                                            FileFsLabelInformation);
        if (!NT_SUCCESS(status)) {
            DebugPrintTrace(("IFSUTIL: Unable to set volume label with status %x\n", status));
        }
    }

    NtClose(handle);
    return status;
}

