NTFS
NTFS(New Technology File System)는 Windows NT가 개발되면서 FAT의 구조상의 한계점을 개선하기 위해 개발되었다.
NTFS의 특징
NTFS는 처음 개발된 이후로 많은 기능들이 추가되었다. NTFS의 많은 기능들 중에 대표적인 기능만 살펴보자.
1. USN 저널 (Update Sequence Number Journal)
USN 저널은 NTFS의 메타데이터를 구성하는 파일로 $UsnJnrl 이라는 이름을 가진다. 이 파일은 파일시스템의 모든 파일 및 디렉터리의 변경 사항을 기록하는 로그이다.
USN 저널 파일은 최초에 빈 파일로 생성이 된다. 이후에 NTFS 볼륨에 변경이 생길때 마다 미리 정의된 레코드 형식으로 변경을 기록하여 저널 파일에 추가한다. 레코드에 기록되는 내용은 64 비트 USN(Update Sequence Number), 파일 이름, 변경에 관한 간략한 정보가 기록된다.
2. ADS (Alternate Data Stream)
ADS는 추가적으로 생성되는 데이터 스트림을 의미한다. 일반적으로 파일은 하나의 데이터 스트림만 가진다. 하지만 NTFS에서는 ADS라는 기능을 추가해 파일이 하나 이상의 데이터 스트림을 가지도록 지원한다.
추가적인 ADS 데이터 스트림들은 고유한 이름을 가진다. ADS는 기존 $DATA에 추가되기 때문에 고유한 이름을 통해 접근한다. 파일 이름은 “filename:streamname”과 같이 원본 파일 이름(filename)에 :(콜론)을 붙여 ADS(streamname)를 표현한다.
ADS 들의 크기는 파일 크기에 포함되지 않는다. 기존 파일이 1KB 였는데 이 파일에 1MB의 추가적인 데이터 스트림을 추가하더라도 파일 크기는 1KB이다. 뿐만아니라 탐색기를 통해 내용을 확인하는 것도 불가능하다.
3. Spare 특징
Sparse 특징은 파일의 데이터가 대부분 0으로 채워져 있는 경우, 실제 데이터를 기록하지 않고 크기만 유지하는 특징이다.
4. 파일 압축
NTFS에서는 파일시스템 수준의 압축을 지원한다. 파일 및 디렉터리 별로 압축하여 저장할 수 있는데 압축 방식은 LZ77의 변형된 방식을 사용한다.
5. VSS(Volume Shadow Copy System)
VSS는 새롭게 덮여 쓰여진 파일 및 디렉터리에 대해 백업본을 유지하는 기능이다. 이렇게 저장된 백업본은 비정상적인 종료시 부팅과정에서 시스템의 저널정보와 함께 안전한 복구를 할 수 있도록 도와준다.
6. EFS (Encrypting File System)
EFS는 NTFS 상에서 파일 및 디렉터리를 암호화 하는 기능으로 CryptoAPI와 EFS File System Run-Time Library(ESRTL)를 사용한 대칭키 방식으로 암호화 한다.
7. Quotas
Windows 서버형 제품군은 다중사용자를 위한 시스템이다. 다중 사용자를 지원할 경우 각 사용자의 디스크 사용량을 제한하기 위해 쿼터(Quotas) 기능을 사용한다.
8. 유니코드 지원
NTFS는 파일, 디렉터리, 볼륨 등의 이름을 지정할 때 모두 유니코드를 사용해 처리한다.
9. 동적 배드 클러스터 재할당
배드 섹터가 발생한 클러스터는 사용할 수 없다. 따라서 시스템 사용 중에 배드 섹터가 발생한 경우 자동으로 새로운 클러스터를 할당해 정상 데이터를 복사하는 기법이다. 이후 배드 섹터가 발생한 클러스터는 $BadClus 파일에 추가되어 더이상 사용되지 않도록 관리된다.
NTFS 구조
NTFS는 크게 VBR 영역, MFT 영역, 데이터 영역으로 나눌수 있다.
FAT 파일시스템에서 FAT 영역의 크기는 전체 볼륨의 클러스터 수에 따라 정해졌다. 따라서 파일시스템 생성시 전체 볼륨을 클러스터로 나눈 후, 클러스터의 갯수에 4 를 곱하면 FAT 영역의 크기가 나온다. 하지만 MFT 영역은 미리 계산되어 질 수 없다. MFT 에는 파일 및 디렉터리마다 하나 이상의 MFT Entry가 할당되는데 생성될 파일 및 디렉터리 수를 미리 예측할 수 없기 때문에다. 따라서 미리 할당된 MFT 영역을 모두 사용하여 MFT 영역이 더 필요하면 데이터 영역을 추가할당하여 사용한다.
구조로만 보면 FAT 파일시스템과 큰 차이가 없어 보인다. 하지만 NTFS는 앞서 언급한 다양한 특징들을 구현하기 위해 내부적으로 상당히 복잡한 구조를 가지고 있다.
NTFS - VBR
VBR(Volume Boot Record)는 NTFS 구조에서 가장 앞부분에 위치하는 영역이다. VBR은 FAT 예약된 영역과 유사하게 부트 섹터와 추가적인 부트 코드가 저장된다.
VBR의 크기는 고정된 크기를 가지지 않고 다음과 같이 클러스터 크기에 의존한다.
Cluster Size (Byte) | VBR Size (Sector) |
512 | 1 |
1K | 2 |
2K | 4 |
4K | 8 |
부트 섹터 (Boot Sector)
VBR의 첫 번째 섹터는 부트 코드를 포함한 부트 섹터가 위치한다. FAT 파일시스템 예약된 영역의 부트 섹터와 그 내용이 같다고 이해하면 된다.
클러스터 크기가 512인 경우는 VBR 자체가 부트 섹터가 된다. VBR 크기가 1섹터를 넘는 경우, 나머지 섹터들은 추가적인 부트 코드를 저장하기 위한 용도로 사용되거나 NTLDR(NT Loader)을 빠르게 로드하기 위해 NTLDR의 위치를 저장하기 위한 용도로 사용된다.
다음은 NTFS 부트 섹터를 덤프한 내용이다. 위의 데이터 구조와 함께 살펴보자.
MBR에서 부팅 가능한 파티션(볼륨)을 찾으면 해당 볼륨의 첫 섹터(부트섹터)를 메모리에 로드한 후 실행한다. 위 부트섹터 그림에서 첫 3바이트는 “EB 52 90″으로 되어 있는데 이 내용을 해석하면 다음과 같다.
EB 52 90 : JMP 52h + NOP (54h로 점프하여 BPB 이후의 부트 코드가 실행)
이후 BPB 항목을 참조하여 해당 볼륨의 운영체제를 로드하는 부트 코드가 실행된다.
NTFS - MFT
MFT(Master File Table)영역은 위 그림처럼 VBR영역 이후에 온다. 실제로는 그림과 다르게 VBR과 MFT 사이의 물리적인 공간이 존재한다. 또한 MFT의 위치는 고정적이지도 않다. 즉 VBR 이후의 모든 볼륨 영역 가운데 아무곳에나 존재 할 수 있다.
MFT의 위치는 VBR의 offset 48-55(logical cluster number for the file $MFT)를 통해 알 수 있다.
먼저 VBR의 부트 섹터에서 MFT 엔트리의 시작 위치를 얻어 이동한다. MFT영역은 MFT엔트리라는 1024 bytes로 이루어진 구조의 집합이다. MFT엔트리는 각 파일 및 디렉터리마다 하나씩 생성되어 해당 파일 및 디렉터리의 정보를 관리한다. 즉 실제 파일 및 디렉터리의 메타 정보 저장을 위해 사용된다.
MFT엔트리 0번은 $MFT파일이다. $MFT파일은 MFT 영역 자체의 정보를 담고 있다. MFT 영역 자체도 NTFS는 하나의 파일로 본다. 결국 $MFT는 전체 MFT 영역의 메타 정보를 유지하고 있는 엔트리이다.
$MFT 엔트리 정보를 읽어보면 전체 MFT가 할당하고 있는 클러스터 정보를 얻을 수 있다. 위에서는 간단한 예로 MFT가 6개의 클러스터만 할당하고 있을때는 나타낸 것이다. 위 처럼 MFT 영역은 서로 조각나 있을 수 있고 이러한 조각난 정보는 $MFT 엔트리를 통해 확인할 수 있다.
결과적으로 NTFS에 접근할 때 부트 섹터의 정보를 이용해 $MFT 엔트리 정보를 획득하면 전체 MFT 영역의 레이아웃을 알 수 있다. 전체 MFT 영역을 획득한다면 NTFS 내에 존재하는 모든 파일 및 디렉터리의 메타 정보(파일 이름, 시간 정보, 크기 등) 를 얻을 수 있을 것이다.
MFT Entry
MFT 영역은 MFT 엔트리(Entry)들의 집합이다. MFT 엔트리는 문서에 따라 MFT Record 또는 File Record 라고 부르기도 한다. MFT 엔트리는 1024 바이트의 크기로 각 파일 및 디렉터리의 위치, 시간 정보, 파일이름, 크기 등의 속성 정보를 담고 있다.
NTFS 내에 존재하는 모든 파일 및 디렉터리는 하나 이상의 MFT 엔트리가 할당된다. 이는 FAT 파일시스템의 FAT 영역과 유사할 수 있으나 FAT 영역은 전체 물리적인 볼륨을 클러스터 단위로 나누고 각 클러스터당 4바이트를 할당해 FAT 영역이 만들어 진다. 하지만 MFT 영역의 엔트리는 생성되는 파일마다 생성된다.
따라서 FAT 영역은 볼륨의 크기가 변하지 않는 이상, 항상 고정된 크기를 가지기 때문에 포맷시 일정한 영역을 할당할 수 있다. 하지만 MFT는 생성될 파일 수를 미리 예측할 수 없기 때문에 미리 고정된 크기를 생성할 수 없다. 그래서 일정 크기를 할당한 후 파일의 수가 늘어나 할당된 MFT 영역을 초과할 경우 데이터 영역의 일정부분을 MFT 영역으로 추가할당하여 사용한다.
MFT 영역의 16개 엔트리는 파일시스템 자체의 메타 역할 및 추가적인 특성을 지원하기 위해 파일시스템 포맷시 미리 할당된다. 다음은 NTFS 포맷시 생성되는 예약된 MFT 엔트리이다.
MFT Entry 구조
MFT 엔트리는 다음 그림과 같은 구조를 가진다. 맨 앞부분에 48bytes 크기의 엔트리 헤더가 오고, 이어서 Fixup 값이 온다. 그리고 해당하는 파일의 특성에 따라 여러 개의 속성들이 따라온다.
파일은 일반 파일, 심볼릭 파일, 보안 속성이 저장된 파일, 비트맵 파일 등에 따라 해당 MFT 엔드리에 존재하는 속성들이 다르다.
속성의 마지막에는 속성의 끝을 나타내는 End Marker가 온다. 이후의 값들은 MFT엔트리에서 사용되지 않는 값이다.
MFT 엔트리 헤더
MFT 엔트리 헤더는 모든 MFT 엔트리의 앞 부분에 위치하는 48 바이트 정보이다. 다음 MFT 엔트리 헤더의 데이터 구조이다.
위의 데이터 구조와 같이 MFT 엔트리는 모두 “FILE”이라는 시그니처를 가진다. 중요한 값들의 내용을 살펴보면 다음과 같다.
- Offset to fixup array : fixup배열의 시작 위치
- Sequence Number : 해당 MFT 엔트리가 할당, 해제 될 때마다 1씩 증가하는 값
- Offset to first attribute : 첫 번째 속성의 위치
- Flags : MFT 엔트리의 속성
- Used size of MFT Entry : 실제 사용되는 크기
- Allocated size of MFT Entry : MFT 엔트리의 크기(1024 bytes)
- File reference to base record : base record의 MFT 엔트리 주소 값
다음은 $MFT 파일의 MFT 엔트리 내용이다. 여기서 기억해야 할 것은 NTFS를 접하면서 다들 많이 헷갈리는 부분이다. $MFT는 MFT 엔트리를 모아둔 파일이라고 했다. 그런데 $MFT 역시 하나의 파일이므로 해당 파일에 대한 정보를 가지는 MFT 엔트리도 존재할 것이다. 여기서 말하는 MFT 엔트리는 $MFT 파일의 내용 중 하나가 아닌 $MFT 파일 자체의 MFT 엔트리이다.
$MFT 파일의 MFT 엔트리를 위의 데이터 구조와 비교해보면 다음과 같다.Signature : “FILE”
- Offset to fixup array : 0x0030
- Number of entries in fixup array : 0x0003
- $LogFile Sequence Number (LSN) : 0x00000006 3B16F7D5
- Sequence Number : 0x0001
- Link count : 0x0001
- Offset to first attribute : 0x0038
- Flags : 0x0001
- Used size of MFT entry : 0x000001A8
- Allocated size of MFT Entry : 0x00000400 (1024 바이트)
- File reference to base record : 0x00000000 00000000
- Next attribute id : 0x0006
- Align to 4B boundary : 0x0000
- Number of this MFT Entry : 0x00000000
Fixup Array Values
Fixup이라는 것에 대해 살펴보자. Fixup은 해석하면 “수리(하다), 고치다” 등의 의미를 가진다. NTFS에서 Fixup이라는 구조를 둔 이유는 신뢰성을 높이기 위한 방안이다. MFT 엔트리는 기본적으로 1,024(1K) 바이트이므로 2개의 섹터를 사용한다.
이처럼 NTFS를 구성하는 데이터가 하나 이상의 섹터를 사용할 경우 섹터의 마지막 2 바이트 값을 별도로 저장하고, 해당 위치에는 Fixup 값 2바이트가 들어간다. 이로 인해 혹시나 섹터의 내용이 비정상적으로 변경될 경우, 해당 데이터를 해석하기 전, 오류를 사전에 찾아낼 수 있다. 다음은 Fixup 값의 활용을 그림으로 나타낸 것이다.
위의 그림은 Fixup이 사용되기 전의 모습이고, 밑에 모습은 Fixup이 적용된 모습이다.
MFT 엔트리 헤더에서 Fixup과 관련된 필드는 2개가 있다. 하나는 “Offset to fixup array”이고, 다른 하나는 “Number of entries in fixup array” 이다. 위의 $MFT 파일의 MFT 엔트리 헤더에서 이 값은 다음과 같다.
- Offset to fixup array : 0x0030
- Number of entries in fixup array : 0x0003
fixup 배열이 위치한 곳이 0x30(48) 이다. 데이터 구조는 2바이트를 사용하므로 Fixup 값을 살펴보면 “0xB76F” 값을 가진다. 즉, Fixup 값이 “B76F” 이다. 그리고 Number of entries in fixup array 값이 3이므로 Fixup 값 이후로 3*2바이트 (총 6바이트)가 Fixup 값에 의해 대체된 값을 저장하는 배열이 된다. 이 값은 기본 값이 3 이다. 3 이라는 의미는 전체 MFT 엔트리는 2개의 섹터를 사용하므로, MFT 엔트리 내에 존재하는 섹터의 마지막 2바이트와 추가적으로 하나를 더 준 셈이다.
하나의 파일이 MFT 엔트리를 2개 이상 쓴다면 물론 이 값도 거기에 맞게 증가할 것이다. 하지만, MFT 엔트리를 2개 이상 쓰는 파일은 매우 드믈다. 따라서, 기본적으로 3을 쓰는 것으로 판단된다. 1이 추가된 것은 그냥 여유롭게 하나 더 준게 아닐까? 그리고 3이 되어야만 위의 그림에서처럼 이어서 나오는 속성의 위치가 안정적으로 위치한다.
위의 Fixup 배열을 보면, 첫 번째 값이 “D585”이다. 즉, 해당 MFT 엔트리의 첫 섹터의 마지막 2바이트 실제 값이 “D585”이고, 거기에는 이 값 대신 “B76F”가 쓰여져 있을 것이다. 직접 확인해 보면 다음과 같이 “B76F”가 써 있는 것을 확인할 수 있다.
MFT Entry Flags
MFT 엔트리 헤더를 보면 엔트리의 속성을 나타내는 Flags 값이 존재한다. 엔트리 속성은 사용되고 있는 엔트리인지, 해당 MFT 엔트리가 디렉터리인지 나타낸다.
- In-use : 0x01
- Directory : 0x02
주의 해야 할점은 사용되는 디렉터리는 값이 0x03을 가진다는 점이다. 사용 중이면서 디렉터리이기 때문에 두 값을 더한 값이 오게 된다.
파일 참조 주소
NTFS에는 64비트로 이루어진 파일 참조 주소(File Reference Address)가 존재한다. 특정 MFT 엔트리를 찾고자 할 경우 MFT 엔트리의 주소만 있으면 되겠지만, NTFS는 파일 참조 주소를 사용해 해당 MFT 엔트리의 위치를 표현한다. 파일 참조 주소는 각 MFT 엔트리의 주소값과 MFT 엔트리의 순서 번호(Sequence Number)를 조합해서 만들어 진다. 우선 MFT 엔트리의 주소값을 살펴보자.
MFT 엔트리 주소값은 첫 번째 엔트리부터 마지막 엔트리 까지 순차적으로 매겨진 번호이다. 따라서, MFT 엔트리 0번인 $MFT의 주소값은 0이 된다. 마지막 MFT 엔트리 주소값은 $MFT 의 크기가 파일의 수에 따라 가변적이지만, MFT 엔트리 크기 (1,024 bytes)로 $MFT 파일 크기를 나누면 구할 수 있다. 그리고 MFT 엔트리 주소는 48비트로 표현된다.
파일 참조 주소는 48비트 MFT 엔트리 주소값 앞의 16바이트로 해당 MFT 엔트리의 순서 번호를 사용한다. 순서 번호는 위에서 살펴본 바와 같이, 해당 엔트리가 할당 또는 해제될 때마다 1씩 증가하는 값이다. 아래 그림에서 살펴보는 바와 같이 파일 참조 주소를 구성하는 64비트 중 앞의 16비트는 각 MFT 엔트리의 순서 번호를 사용하고, 나머지 48비트는 MFT 엔트리의 주소값을 사용한다.
MFT 엔트리 주소만으로도 충분히 원하는 엔트리에 접근이 가능한데 왜 궂이 앞에 16바이트를 순서 번호로 사용했을까? 그 이유는 주소값만으로 해당 엔트리가 찾고자 하는 엔트리인지는 보장할 수 없기 때문이다. 순서 번호는 해당 엔트리의 고유한 값이므로, 만약 파일 참조 주소를 통해 찾은 엔트리의 순서 번호가 틀리다면, 파일시스템이 손상되었다고 판단할 수 있다. 따라서, 이 경우에는 적절한 오류를 발생시키거나 해당 파일을 참조할 수 없도록 해야 할 것이다.
그리고 할당, 해제될 때마다 1씩 증가한다는 것을 이용하면 한 가지 중요한 것을 생각해 낼 수도 있다. MFT 엔트리가 할당 되었다는 것은 특정 파일이 생성되고, 해당 MFT 엔트리를 사용한다는 것이고, 해제 되었다는 의미는 그 파일이 삭제되었다는 것을 의미한다. 그렇다면 순서 번호가 0보자 큰 짝수인 MFT 엔트리는 현재 사용되는 엔트리가 아닌 삭제된 파일로 간주 할 수 있을 것이다.