윈도우 커널 모드 포팅의 정석 4편 - 파일 I/O 함수 구현

저작권 안내
  • 책 또는 웹사이트의 내용을 복제하여 다른 곳에 게시하는 것을 금지합니다.
  • 책 또는 웹사이트의 내용을 발췌, 요약하여 강의 자료, 발표 자료, 블로그 포스팅 등으로 만드는 것을 금지합니다.

이제부터는 호환 레이어 위에서 각 기능들을 구현할 차례입니다. 이번에는 파일 I/O 함수 구현 방법에 대해 알아보도록 하겠습니다.

유저 모드에서 사용할 수 있는 파일 I/O 함수는, 똑같이 커널 모드에서도 사용할 수 있습니다. 당연한 말이겠지만, 커널 모드에서 파일 I/O 함수를 제공해 주기 때문에 유저 모드에서 사용할 수 있는 것입니다. 실제로 유저 모드에서 사용하는 함수들은 빈 껍데기일 뿐입니다. 실제로는 커널 모드 함수가 모든 일을 처리합니다.

따라서 유저 모드 함수에 사용되는 각종 옵션들도 커널 모드에서 그대로 사용할 수 있습니다. 단 WDK 스타일에 따라 각종 매크로 정의나 구조체 형태가 다를 수 있지만, 기능은 동일합니다.

주요 유저 모드 파일 I/O 함수에 해당하는 커널 모드 함수는 다음과 같습니다.

  • CreateFile - ZwCreateFile
  • ReadFile - ZwReadFile
  • WriteFile - ZwWriteFile
  • CloseHandle - ZwClose
  • DeleteFile - ZwDeleteFile

위 함수들 처럼 1:1 대응이 되는 함수들이 있는 경우에는 해당 함수를 그대로 사용하면 되지만 1:1 대응이 되지 않는 함수들이 있습니다. 이러한 함수들은 커널 모드 함수를 조합하여 동일한 기능을 하도록 구현해주어야 합니다.

GetFileSize
DWORD GetFileSize(
    HANDLE hFile,
    LPDWARD lpFileSizeHigh)
{
    NTSTATUS status;
    IO_STATUS_BLOCK iosb;
    FILE_STANDARD_INFORMATION fileStandardInfo;
    DWORD fileSize = 0;

    status = ZwQueryInformationFile(
        hFile,
        &iosb,
        &fileStandardInfo,
        sizeof(fileStandardInfo),
        FileStandardInformation);

    if (NT_SUCCESS(status))
    {
        *lpFileSizeHigh = fileStandardInfo.EndOfFile.HighPart;
        fileSize = fileStandardInfo.EndOfFile.LowPart;
    }

    return fileSize;
}

커널 모드에는 GetFileSize에 정확히 대응되는 함수가 없기 때문에 ZwQueryInformationFile 함수를 이용합니다. FILE_STANDARD_INFORMATION 구조체를 이용하면 파일 크기를 구할 수 있습니다.

SetFilePointer
DWORD SetFilePointer(
    HANDLE hFile,
    LONG lDistanceToMove,
    PLONG lpDistanceToMoveHigh,
    DWORD dwMoveMethod)
{
    NTSTATUS status;
    IO_STATUS_BLOCK iosb;
    FILE_POSITION_INFORMATION filePositionInfo;
    FILE_STANDARD_INFORMATION fileStandardInfo;
    DWORD ret = INVALID_SET_FILE_POINTER;

    switch (dwMoveMethod)
    {
    case FILE_BEGIN:

        filePositionInfo.CurrentByteOffset.HighPart = *lpDistanceToMoveHigh;
        filePositionInfo.CurrentByteOffset.LowPart = lDistanceToMove;

        status = ZwSetInformationFile(
            hFile,
            &iosb,
            &filePositionInfo, 
            sizeof(filePositionInfo),
            FilePositionInformation);

        break;

    case FILE_CURRENT:

        status = ZwQueryInformationFile(
            hFile,
            &iosb,
            &filePositionInfo, 
            sizeof(filePositionInfo),
            FilePositionInformation);

        if (NT_SUCCESS(status))
        {
            filePositionInfo.CurrentByteOffset.HighPart += *lpDistanceToMoveHigh;
            filePositionInfo.CurrentByteOffset.LowPart += lDistanceToMove;

            status = ZwSetInformationFile(
                hFile,
                &iosb,
                &filePositionInfo, 
                sizeof(filePositionInfo),
                FilePositionInformation);
        }

        break;

    case FILE_END:

        status = ZwQueryInformationFile(
            hFile,
            &iosb,
            &fileStandardInfo,
            sizeof(fileStandardInfo),
            FileStandardInformation);

        if (NT_SUCCESS(status))
        {
            filePositionInfo.CurrentByteOffset.HighPart =
                fileStandardInfo.EndOfFile.HighPart;
            filePositionInfo.CurrentByteOffset.LowPart =
                fileStandardInfo.EndOfFile.LowPart;

            filePositionInfo.CurrentByteOffset.HighPart += *lpDistanceToMoveHigh;
            filePositionInfo.CurrentByteOffset.LowPart += lDistanceToMove;

            status = ZwSetInformationFile(
                hFile,
                &iosb,
                &filePositionInfo,
                sizeof(filePositionInfo),
                FilePositionInformation);
        }

        break;
    }

    if (NT_SUCCESS(status))
    {
        status = ZwQueryInformationFile(
            hFile,
            &iosb,
            &filePositionInfo, 
            sizeof(filePositionInfo),
            FilePositionInformation);

        if (NT_SUCCESS(status))
        {
            *lpDistanceToMoveHigh = filePositionInfo.CurrentByteOffset.HighPart;
            ret = filePositionInfo.CurrentByteOffset.LowPart;
        }
    }

    return ret;
}

SetFilePointer 함수는 기본적으로 ZwQueryInformationFile, ZwSetInformationFile 함수와 FILE_POSITION_INFORMATION 구조체를 이용합니다. 그리고 FILE_END 옵션을 처리하기 위해 FILE_STANDARD_INFORMATION 구초제를 이용합니다.

GetFileAttributes
DWORD GetFileAttributes(
    LPCTSTR lpFileName)
{
    NTSTATUS status;
    HANDLE fileHandle;
    UNICODE_STRING fileName;
    OBJECT_ATTRIBUTES objectAttributes;
    IO_STATUS_BLOCK iosb;
    FILE_ATTRIBUTE_TAG_INFORMATION fileAttributeTagInfo;
    DWORD ret = INVALID_FILE_ATTRIBUTES;

    RtlInitUnicodeString(&fileName, lpFileName);

    InitializeObjectAttributes(
        &objectAttributes,
        &fileName,
        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
        NULL,
        NULL);

    status = ZwOpenFile(
        &fileHandle,
        FILE_ALL_ACCESS,
        &objectAttributes,
        &iosb,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        FILE_OPEN);

    if (NT_SUCCESS(status))
    {
        status = ZwQueryInformationFile(
            fileHandle,
            &iosb,
            &fileAttributeTagInfo,
            sizeof(fileAttributeTagInfo),
            FileAttributeTagInformation);

        ret = fileAttributeTagInfo.FileAttributes;

        ZwClose(fileHandle);
    }

    return ret;
}

GetFileAttributes는 매개변수로 핸들을 받는 것이 아니라 파일명을 받기 때문에 일단 ZwOpenFile을 이용하여 핸들을 얻습니다. 그리고 ZwQueryInformationFile 함수와 FILE_ATTRIBUTE_TAG_INFORMATION 구조체를 이용하여 구현합니다.

SetFileAttributes 및 기타 함수들도 ZwQueryInformationFile 함수를 이용하면 어렵지 않게 구현할 수 있습니다.

관련글


저작권 안내

이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.
  • 블로그, 게시판 등에 퍼가는 것을 금지합니다.
  • 비공개 포스트에 퍼가는 것을 금지합니다.
  • 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
  • 링크 및 SNS 공유는 허용합니다.

Published

2009-07-20