본문 바로가기

My Study/Programming&Theory

NTFS 원하는 파일 속성 출력하기

저번 글에서는 nt 함수를 사용해 원하는 번째의 MFT Entry 를 가져오는 코드를 작성해보았다. 이번엔 NTFS 시스템에서 사용자가 찾기를 원하는 파일의 MFT Entry를 가져오는 방법을 한번 알아보겠습니다. 정말 공부하면서 느끼는거지만 NTFS.... 너무 복잡합니다. MS는 뭐이리 복잡한 파일 시스템 구조를 만들었을까요? 그만큼 효율적이겠지요..

FAT 파일 시스템에 비교하면 압축기능, 권한부여기능, 보안기능, 복구 기능 등이 가능하고 대용량 시스템을 위해 만들어진 파일 시스템인 만큼 볼륨의 크기가 클수록 높은 성능을 발휘합니다. 요즘 시대에 대부분 개인 PC도 500GB 이상의 하드디스크를 가지고 있으므로 NTFS 가 생활하기? 좋은 환경인 것 같군요. :D


=======================================================================

먼저 우리의 목표 입니다.

원하는 파일 절대주소를 입력했을 때(예-c:\aaa\bbb.txt) 해당 파일의 MFT Entry를 리턴해주는 기능의 함수가 필요합니다.



간결한 main 함수입니다. 위와 같이 파일의 경로를 find_file로 던져주고 MFTEntry로 해당 값을 받으면 됩니다.


그러면 가장 먼저 NTFS에서는 파일을 어떻게 관리하고 있는지 알아보도록 하겠습니다.


NTFS에서 각 파일의 MFT를 찾아가기 위해선 트리 구조를 따라가야합니다. NTFS는 파일들의 MFT를 관리할 때 트리 구조로 관리하고 있기 때문입니다. 트리의 종류도 여러가지가 있지만 이진트리에서 살짝 변형된 방법을 사용합니다.




이건 기본적인 이진트리입니다. 규칙은 노드를 중심으로 왼쪽엔 자기보다 작은 값 오른쪽은 자기보다 큰 값이 오는 것이지요.

그러면 NTFS에서 관리하고 있는 그림을 보시겠습니다.





NTFS에서는 각각을 노드라고는 안하고 Index Entry라고 부릅니다. 아무튼..

각 Index Entry를 보면 화살표가 2개씩 있는게 아니고 하나씩 있습니다. 그리고 Index Entry가 하나씩 있는게 아니고 2~3개씩 붙어있습니다. 알파벳을 비교해서 보면 부모 Index Entry 보다 자식 Index Entry 값이 더 작은것을 알 수 있습니다. 이제 저 규칙을 따라 파일을 찾아갈 수 있겠군요.


복잡한 과정을 보기전에 저 위에서 hhh.txt 를 찾아가는 과정을 알아보도록 하겠습니다.

먼저 루트 노드(편의상 노드라고 표현;;)에서 부터 검사를 시작하는데 첫번째 노드(ggg.txt)가 End Of Node 인지 체크합니다. 체크 결과 마지막 노드이면 해당 노드에 자식 노드가 존재하는지 체크합니다. 자식노드가 존재하면 자식노드를 검사합니다. 하지만 위 그림에선 마지막 노드가 아니므로 다음 과정으로 넘어갑니다.


ggg.txt와 hhh.txt를 비교합니다. 이 때 단순히 같다 다르다만 판별하면 안되고 알파벳이 더 작냐 크냐까지 판별해야합니다.

hhh.txt는 ggg.txt보다 크므로 자식노드로 이동하지 않고 옆 노드로 이동하게 됩니다. kkk.txt와 hhh.txt를 비교합니다.

hhh.txt가 더 작으므로 이번엔 자식노드로 이동합니다. 자식노드의 첫번째가 또 End Of Node인지 검사합니다. 아니므로 노드 내부에 있는 파일명과 비교를 합니다. 같으면.. MFT Entry를 리턴하면 됩니다.


조금 더 복잡하면 괜찮은 예가 될 것 같지만 이정도만 해도 충분히 알 수 있을거라 생각합니다.


그러면 시작은 당연히! 루트노드부터 시작을 해야겠지요. 그에 대한 정보는 어디에 있을까요??


NTFS에서 각 볼륨에는 Meta Data File 이라는 것이 존재합니다.

http://ezbeat.tistory.com/406

글을 보시면 대략 16개의 메타 데이터 파일들이 있는데 저기서 Entry Number가 5인 루트 디렉토리를 나타내는 MFT Entry 의 속성을 보면 시작 정보를 알 수 있습니다.


루트 디렉토리 MFT Entry 속성을 보면 

$INDEX_ROOT 속성과 $INDEX_ALLOCATION 속성이 존재합니다. 저 두 속성을 이잡듯 뒤지면 되는 것입니다.


파일을 찾는 순서는 다음과 같이 됩니다.


"G:\AAA\BBB\ezbeat.txt" 의 MFT Entry를 찾는 방법

1. G: 이름을 가진 볼륨 디바이스의 핸들을 얻는다. - hVolume

2. hVolume 값을 가지고 루트 디렉토리 메타 데이터 파일의 MFT Entry를 구한다. - mft

3. 얻은 mft 에서 Fixup 배열 복구를 해준다.

4. AAA 이름만 따로 자른다. ( strtok 사용 )

5. 루트 디렉토리의 mft에서 $INDEX_ROOT 속성의 위치를 구한다. - pAttrRoot

6. AAA 이름을 전부 대문자로 변경한다. ( 디렉토리 인덱스는 대문자로만 정렬하기 때문 )

7. findFile_inNode 함수로 AAA 이름과 pAttrRoot에서 $INDEX_ROOT 속성 헤더를 뺀 Index Node Header 위치를 던진다.


findFile_inNode 하는 일

해당 Index Node Header에 묶여 있는 Index Entry들을 조사하면서 찾으려는 파일명과 일치하는 Index Entry가 있는지 조사

a. 일치하는 파일명이 존재하면 디렉토리인지 아닌지 검사 후 해당 Index Entry의 File Referrence 를 리턴

b. 일치하는 파일명이 없지만 파일명 비교 결과 찾으려는 파일명이 더 작은 경우 자식노드가 있는지 확인후 있으면 자식노드의 VCN을 리턴

c. 일치하는 파일명도 없고 이동해야하는 자식노드도 없으면 끝


8. 일치하는 파일명이 존재하면 리턴된 File Referrence를 가지고 해당 파일의 MFT Entry를 얻음

9. strtok를 사용해 또 잘랐을 때 NULL이 아니면 ( 여기선 BBB가 존재 하겠죠? ) 방금 검사했던 파일이 디렉토리 인지 확인하고 둘다 만족하면 얻은 MFT Entry에서 Fixup 배열 복구해주고 5번 부터 다시 시작!

10. strtok을 사용했을 때 NULL이면 ( 찾은 파일이 경로의 마지막.. 즉, 최종적으로 찾으려는 파일임 ) 얻은 MFT Entry에서 Fixup 배열 복구해주고 해당 MFT Entry를 리턴


11. 8번에서 일치하는 파일명이 없고 자식노드가 존재한다면 해당 MFT Entry에서 $INDEX_ALLOCATION 속성의 위치를 얻고 해당 속성 내용을 분석해 RunList 값을 얻음

12. RunList 를 분석해 요청한 만큼 클러스터를 읽음( 시작위치가 Index Record Header )

13. Index Record Header에서 Fixup 배열 복구

14. findFile_inNode 호출해 또 파일 찾음


이런식으로 자식노드가 계속 발견되면 11~14를 계속 돌면서 파일 찾음

하지만 찾으려는 파일이 존재하면 8~10 번과 같은 코드를 또 수행


이런식으로 진행하면 최종적으로 우리가 원하는 파일의 MFT Entry를 찾을 수 있습니다.




프로그램을 실행시킨 결과입니다. 찾으려고 파일들 검사할 때 검사하는 파일의 이름을 출력하도록 했습니다.

저도 파일 시스템 원리와 실습이라는 책으로 기본적인 코드는 만들어 보았고 나름대로 조금 응용하여 사용하기 편리하게끔 함수를 수정해본 것입니다.


이제 원하는 방식대로 작동은 하니 프로젝트에 이러한 내용이 들어가기 위해선 최대한 다른 디바이스와의 I/O 시간을 줄이는 일과 조금 더 최적화된 코드로 탈바꿈 해야될 것 같습니다.