NTFS 파일시스템 심화

NTFS MFT 구조, 속성 시스템, B-tree 인덱싱, $LogFile 저널링, ntfs3 커널 드라이버, 보안 모델, 성능 튜닝 종합 가이드.

관련 표준: NTFS(New Technology File System)는 Microsoft가 Windows NT를 위해 설계한 저널링 파일시스템입니다. Linux 커널 5.15에서 Paragon ntfs3 드라이버가 병합되어 읽기/쓰기 완전 지원이 가능해졌습니다. 종합 목록은 참고자료 — 표준 & 규격 섹션을 참고하세요.

개요 & 역사

NTFS(New Technology File System)는 1993년 Windows NT 3.1과 함께 출시된 Microsoft의 주력 파일시스템입니다. FAT/HPFS를 대체하기 위해 설계되었으며, 메타데이터 저널링, ACL 기반 보안, 압축, 암호화(EFS), Alternate Data Streams 등 엔터프라이즈 기능을 제공합니다. 현재까지 Windows 운영체제의 기본 파일시스템으로 사용되고 있습니다.

NTFS 버전 역사

버전OS주요 변경사항
1.0NT 3.1 (1993)최초 릴리스, 기본 MFT/저널링 구조
1.1NT 3.5 (1994)압축 파일 지원 (LZNT1)
1.2NT 4.0 (1996)Security Descriptor 스트림, 디스크 쿼터 기반 구축
3.0Windows 2000디스크 쿼터, EFS 암호화, Reparse Point, $UsnJrnl 변경 저널, Sparse 파일
3.1Windows XP+$MFT 미러 확장, 셀프 힐링(Self-Healing), MFT 엔트리 재사용 시퀀스 번호 강화
버전 번호: NTFS 2.x는 존재하지 않습니다. Windows 2000에서 1.2 → 3.0으로 직접 점프했으며, 현재 모든 Windows(XP~11/Server 2025)는 NTFS 3.1을 사용합니다.

NTFS 주요 스펙

항목
최대 볼륨 크기256 TiB (클러스터 64 KiB 기준), 이론적 264 클러스터
최대 파일 크기256 TiB (볼륨 크기에 의존)
최대 파일명 길이255 UTF-16 문자
최대 경로 길이32,767 UTF-16 문자 (Win32 API 제한 260자)
클러스터 크기512 B ~ 2 MiB (기본 4 KiB)
MFT 엔트리 크기1024 바이트 (고정)
저널링메타데이터 전용 (WAL 방식)
대소문자대소문자 보존, 기본적으로 대소문자 비구분
파일시스템 IDMBR: 0x07, GPT: EBD0A0A2-B9E5-4433-...

Linux에서의 NTFS 지원 개요

Linux에서 NTFS 접근은 세 가지 방식으로 발전해왔습니다:

NTFS 디스크 레이아웃

NTFS 볼륨은 크게 VBR(Volume Boot Record), MFT Zone, Data Area로 구성됩니다. 모든 것이 파일로 표현되는 것이 NTFS의 핵심 설계 원칙입니다. 디렉터리조차 특별한 속성을 가진 MFT 레코드에 불과합니다.

NTFS 볼륨 레이아웃 VBR Boot Sector + Boot Code MFT Zone $MFT (Master File Table) 볼륨의 ~12.5% 예약 Data Area 파일 데이터, 디렉터리 인덱스 Non-Resident 속성 데이터 MFT Mirror $MFTMirr 시스템 메타데이터 파일 (MFT 레코드 0~15): #0 $MFT #1 $MFTMirr #2 $LogFile #3 $Volume #4 $AttrDef #5 . (root dir) #6 $Bitmap #7 $Boot #8 $BadClus #9 $Secure #10 $UpCase #11 $Extend 레코드 #12~#15: 예약 (미사용), #16~: 일반 파일/디렉터리 $Extend 하위: $ObjId, $Quota, $Reparse, $UsnJrnl, $RmMetadata

시스템 메타데이터 파일 상세

MFT #이름역할
0$MFTMFT 자체를 기술하는 레코드 (자기 참조)
1$MFTMirr$MFT 처음 4개 레코드의 백업 (복구용)
2$LogFile트랜잭션 저널 (메타데이터 일관성 보장)
3$Volume볼륨 이름, NTFS 버전, 플래그 (dirty bit 등)
4$AttrDef속성 타입 정의 테이블
5. (root)루트 디렉터리 (\)
6$Bitmap클러스터 할당 비트맵 (1비트/클러스터)
7$BootVBR(Volume Boot Record), BPB, 부트 코드
8$BadClus불량 클러스터 목록 (Sparse 파일)
9$Secure보안 디스크립터 저장소 (ACL 중앙 관리)
10$UpCase유니코드 대문자 변환 테이블 (파일명 비교용)
11$Extend확장 메타데이터 디렉터리

VBR (Volume Boot Record) 구조

NTFS VBR은 섹터 0에 위치하며, BIOS Parameter Block(BPB)과 부트 코드를 포함합니다. 마지막 섹터에 VBR 백업이 위치합니다.

/* NTFS BPB (BIOS Parameter Block) - 오프셋 0x00~0x53 */
struct ntfs_bpb {
    uint8_t  jump[3];           /* 0x00: JMP 명령 (EB 52 90) */
    char     oem_id[8];          /* 0x03: "NTFS    " */
    uint16_t bytes_per_sector;   /* 0x0B: 보통 512 */
    uint8_t  sectors_per_cluster; /* 0x0D: 1,2,4,8...128 */
    uint16_t reserved_sectors;   /* 0x0E: 0 (사용 안 함) */
    uint8_t  unused[5];          /* 0x10~0x14: 항상 0 */
    uint8_t  media_descriptor;   /* 0x15: 0xF8 (하드디스크) */
    uint8_t  unused2[2];         /* 0x16~0x17: 항상 0 */
    uint16_t sectors_per_track;  /* 0x18: CHS 지오메트리 */
    uint16_t number_of_heads;   /* 0x1A: CHS 지오메트리 */
    uint32_t hidden_sectors;    /* 0x1C: 파티션 시작 오프셋 */
    uint32_t unused3;           /* 0x20: 항상 0 */
    uint32_t unused4;           /* 0x24: 0x00800080 */
    uint64_t total_sectors;     /* 0x28: 볼륨 전체 섹터 수 */
    uint64_t mft_lcn;           /* 0x30: $MFT 시작 LCN */
    uint64_t mftmirr_lcn;       /* 0x38: $MFTMirr 시작 LCN */
    int8_t   clusters_per_mft;  /* 0x40: MFT 레코드 크기 */
    uint8_t  padding1[3];       /* 0x41~0x43 */
    int8_t   clusters_per_idx;  /* 0x44: 인덱스 블록 크기 */
    uint8_t  padding2[3];       /* 0x45~0x47 */
    uint64_t serial_number;     /* 0x48: 볼륨 시리얼 번호 */
    uint32_t checksum;          /* 0x50: 부트 섹터 체크섬 */
};
/* 0x54~0x1FD: 부트 코드, 0x1FE~0x1FF: 0x55AA 시그니처 */
clusters_per_mft 해석: 양수이면 MFT 레코드 = 값 × 클러스터 크기. 음수이면 MFT 레코드 = 2|값| 바이트. 일반적으로 0xF6(-10)으로 설정되어 210 = 1024바이트입니다.

MFT (Master File Table) 구조

MFT는 NTFS의 핵심 자료구조로, 볼륨의 모든 파일과 디렉터리를 레코드 단위로 관리합니다. 각 MFT 엔트리(레코드)는 1024바이트 고정 크기이며, 파일의 모든 메타데이터와 작은 파일의 데이터까지 포함할 수 있습니다. MFT 자체도 하나의 파일($MFT, 레코드 #0)로 볼륨에 존재합니다.

MFT 엔트리 구조 (1024 바이트) Record Header "FILE" 시그니처 USA offset/count Fixup USA (Update Sequence Array) Attribute 1 $STANDARD_INFO (0x10, Resident) Attribute 2 $FILE_NAME (0x30, Resident) Attribute 3 $DATA (0x80, Res/Non-Res) End 0xFFFFFFFF Free 오프셋: 0x00 0x30 첫 번째 속성 오프셋 헤더 주요 필드: sequence_number: 레코드 재사용 추적 | flags: IN_USE(0x01), DIRECTORY(0x02) | base_record: 확장 레코드의 기본 레코드 참조 allocated_size: 1024 | used_size: 실제 사용 바이트 | first_attr_offset: 첫 속성 시작 위치 | lsn: $LogFile 시퀀스 번호

MFT 레코드 헤더

/* MFT Record Header (48 바이트, 오프셋 0x00~0x2F) */
struct mft_record_header {
    uint32_t magic;              /* 0x00: "FILE" (0x454C4946) */
    uint16_t usa_offset;         /* 0x04: Update Sequence Array 오프셋 */
    uint16_t usa_count;          /* 0x06: USA 엔트리 수 */
    uint64_t lsn;               /* 0x08: $LogFile 시퀀스 번호 */
    uint16_t sequence_number;    /* 0x10: 재사용 카운터 */
    uint16_t hard_link_count;    /* 0x12: 하드 링크 수 */
    uint16_t first_attr_offset;  /* 0x14: 첫 속성 시작 오프셋 */
    uint16_t flags;             /* 0x16: IN_USE(0x01), DIRECTORY(0x02) */
    uint32_t used_size;         /* 0x18: 사용 바이트 수 */
    uint32_t allocated_size;    /* 0x1C: 할당 크기 (보통 1024) */
    uint64_t base_record;       /* 0x20: 기본 레코드 참조 (확장 시) */
    uint16_t next_attr_id;      /* 0x28: 다음 속성 ID */
    uint16_t padding;           /* 0x2A: 패딩 */
    uint32_t record_number;     /* 0x2C: 이 레코드의 MFT 번호 */
};

Update Sequence Array (USA)

NTFS는 다중 섹터 전송 중 데이터 손상을 감지하기 위해 USA(Update Sequence Array)를 사용합니다. 디스크에 쓸 때, 각 512바이트 섹터의 마지막 2바이트를 USA 값으로 대체하고, 원래 값은 USA 배열에 보관합니다. 읽을 때 USA 값을 검증한 후 원래 값으로 복원합니다.

/* USA 적용 과정 (1024바이트 MFT 레코드, 2개 섹터) */
/*
 * USA 배열: [usa_value, orig_sector1_last2, orig_sector2_last2]
 *
 * 쓰기 시:
 *   sector[0].bytes[510:511] → USA[1]에 백업, usa_value로 대체
 *   sector[1].bytes[510:511] → USA[2]에 백업, usa_value로 대체
 *
 * 읽기 시:
 *   sector[N].bytes[510:511] == usa_value 확인 → 불일치 시 I/O 오류
 *   USA[N+1]에서 원래 값 복원
 */

MFT 할당 전략

NTFS는 볼륨의 약 12.5%(1/8)를 MFT Zone으로 예약합니다. MFT Zone이 소진되면 Data Area에서 MFT를 확장하며, 이때 MFT 단편화가 발생할 수 있습니다. MFT는 $MFT 레코드(#0)의 Data Run을 통해 자신의 위치를 추적합니다 (자기 참조 구조).

/* Windows 레지스트리로 MFT Zone 크기 조정 */
/* HKLM\SYSTEM\CurrentControlSet\Control\FileSystem */
/* NtfsMftZoneReservation: 1(12.5%) 2(25%) 3(37.5%) 4(50%) */

/* ntfs3 드라이버에서 MFT Zone은 자동 관리 */
/* fs/ntfs3/fsntfs.c: ntfs_update_mftmirr() */

NTFS 속성 시스템

NTFS에서 파일의 모든 데이터는 속성(Attribute)으로 표현됩니다. 파일명, 타임스탬프, 보안 정보, 실제 파일 데이터까지 모두 속성입니다. 각 속성은 Resident(MFT 레코드 내부에 저장) 또는 Non-Resident(외부 클러스터에 저장)입니다.

속성 헤더 구조

/* 공통 속성 헤더 (16 바이트) */
struct attr_header {
    uint32_t type;       /* 속성 타입 코드 (0x10, 0x30, 0x80 등) */
    uint32_t length;     /* 속성 전체 길이 (헤더 포함) */
    uint8_t  non_resident; /* 0=Resident, 1=Non-Resident */
    uint8_t  name_length; /* 속성 이름 길이 (UTF-16 문자 수) */
    uint16_t name_offset; /* 속성 이름 오프셋 */
    uint16_t flags;      /* COMPRESSED(0x01), ENCRYPTED(0x4000), SPARSE(0x8000) */
    uint16_t attr_id;    /* 속성 인스턴스 ID */
};

/* Resident 속성 추가 헤더 (8 바이트) */
struct resident_attr {
    uint32_t value_length;  /* 속성 값 길이 */
    uint16_t value_offset;  /* 속성 값 오프셋 */
    uint16_t indexed_flag;  /* 인덱싱 여부 */
};

/* Non-Resident 속성 추가 헤더 (48 바이트) */
struct nonresident_attr {
    uint64_t start_vcn;       /* 시작 VCN (Virtual Cluster Number) */
    uint64_t end_vcn;         /* 마지막 VCN */
    uint16_t data_runs_offset; /* Data Run 시작 오프셋 */
    uint16_t compression_unit; /* 압축 단위 (2^N 클러스터, 보통 4) */
    uint32_t padding;
    uint64_t allocated_size;  /* 할당된 크기 (클러스터 정렬) */
    uint64_t real_size;       /* 실제 데이터 크기 */
    uint64_t initialized_size; /* 초기화된 크기 */
};

표준 속성 타입

타입 코드이름설명Resident
0x10$STANDARD_INFORMATION타임스탬프(MACB), 파일 플래그, 보안 ID, 쿼터, USN항상
0x20$ATTRIBUTE_LIST속성이 여러 MFT 레코드에 분산 시 인덱스보통
0x30$FILE_NAME파일명(UTF-16), 부모 디렉터리 참조, 타임스탬프항상
0x40$OBJECT_IDGUID 기반 파일 고유 식별자항상
0x50$SECURITY_DESCRIPTOR레거시 보안 디스크립터 (NT 4.0 이하)보통
0x60$VOLUME_NAME볼륨 이름 (NTFS 레이블)항상
0x70$VOLUME_INFORMATIONNTFS 버전, 볼륨 플래그항상
0x80$DATA파일 데이터 스트림 (기본 + ADS)가변
0x90$INDEX_ROOTB-tree 인덱스 루트 노드 (디렉터리)항상
0xA0$INDEX_ALLOCATIONB-tree 인덱스 노드 저장소Non-Res
0xB0$BITMAP인덱스/MFT 할당 비트맵가변
0xC0$REPARSE_POINTReparse Point 데이터 (심볼릭 링크, 마운트 포인트)항상
0xD0$EA_INFORMATIONExtended Attribute 정보항상
0xE0$EAExtended Attribute 데이터가변

Data Run 인코딩 (Non-Resident 속성)

Non-Resident 속성의 데이터 위치는 Data Run(Cluster Run)으로 인코딩됩니다. 각 Data Run은 가변 길이 바이트 시퀀스로, 연속 클러스터 블록을 표현합니다.

/*
 * Data Run 인코딩:
 *   Header byte: [offset_size:4][length_size:4]
 *   Length: length_size 바이트 (리틀엔디안, 클러스터 수)
 *   Offset: offset_size 바이트 (부호 있는 리틀엔디안, LCN 오프셋)
 *   종료: 0x00 바이트
 *
 * 예시: 0x31 0x05 0x30 0x02
 *   header = 0x31: offset_size=3, length_size=1
 *   length = 0x05: 5 클러스터
 *   offset = 0x000230: LCN 560 (리틀엔디안 0x30 0x02 0x00)
 *   → 클러스터 560~564에 5개 클러스터 할당
 *
 * 두 번째 run의 offset은 이전 LCN으로부터의 상대 오프셋 (부호 있음)
 * Sparse run: offset_size=0 → 비할당 영역 (Sparse 파일)
 */

Alternate Data Streams (ADS)

NTFS는 하나의 파일에 여러 $DATA 속성(스트림)을 허용합니다. 기본 스트림(이름 없음)과 하나 이상의 명명된 스트림(ADS)을 가질 수 있습니다.

# ADS 생성 및 접근 (Windows)
echo "hidden data" > file.txt:secret_stream
more < file.txt:secret_stream
dir /r file.txt    # ADS 목록 확인

# ntfs3에서 ADS 접근 (Linux)
# xattr을 통해 접근 가능
getfattr -n user.secret_stream file.txt
setfattr -n user.secret_stream -v "hidden data" file.txt
보안 주의: ADS는 보안 위협에 악용될 수 있습니다. 악성코드가 ADS에 페이로드를 숨기거나, 실행 파일을 ADS로 첨부하는 사례가 있습니다. 일부 보안 도구는 ADS를 스캔하지 않으므로 주의가 필요합니다.

NTFS 압축 (LZNT1)

NTFS는 $DATA 속성에 LZNT1 알고리즘 기반 압축을 지원합니다. 압축 단위(Compression Unit)는 기본 16개 클러스터(64 KiB)이며, 각 단위를 독립적으로 압축/해제합니다.

/*
 * LZNT1 압축 구조:
 *   - 16 클러스터 단위로 압축
 *   - 압축 후 크기가 원본보다 작으면: 압축 클러스터 + Sparse(비할당)
 *   - 압축 후 크기가 원본과 같거나 크면: 비압축으로 저장
 *
 * Data Run에서의 표현:
 *   할당된 run: 실제 압축 데이터 클러스터
 *   Sparse run (offset=0): 절약된 공간
 *
 * ntfs3 마운트 옵션:
 *   compress    - 새 파일에 LZNT1 압축 적용
 *   nocompress  - 압축 비활성화 (기본)
 */

B-tree 인덱싱

NTFS 디렉터리는 B+ tree 기반 인덱스 구조를 사용하여 파일명을 정렬하고 빠른 검색을 제공합니다. 인덱스는 $INDEX_ROOT(Resident, 루트 노드)와 $INDEX_ALLOCATION(Non-Resident, 나머지 노드)로 구성됩니다. $UpCase 테이블을 사용하여 대소문자를 구분하지 않는 유니코드 비교를 수행합니다.

NTFS B+ Tree 디렉터리 인덱스 $INDEX_ROOT (Resident) doc.txt | notes.txt | zzz.log Index Block 0 (VCN 0) a.c | backup | config Index Block 1 (VCN 1) file1 | hello | main.c Index Block 2 (VCN 2) patch | readme | test < doc.txt doc~notes notes~zzz 인덱스 엔트리 구조: MFT Reference Entry Length Content Length Flags Key ($FILE_NAME) Sub-node VCN Flags: CHILD_NODE(0x01) = 하위 노드 존재, LAST_ENTRY(0x02) = 마지막 엔트리 (키 없음, 하위 포인터만) $BITMAP 속성: Index Block 할당 상태 추적 (1비트/블록)

$INDEX_ROOT 속성

/* $INDEX_ROOT (속성 타입 0x90) */
struct index_root {
    uint32_t attr_type;          /* 인덱싱할 속성 타입 (0x30 = $FILE_NAME) */
    uint32_t collation_rule;     /* 정렬 규칙 (1 = FILENAME) */
    uint32_t index_block_size;   /* Index Block 크기 (보통 4096) */
    uint8_t  clusters_per_block; /* 블록당 클러스터 수 */
    uint8_t  padding[3];
    /* Index Node Header */
    uint32_t entries_offset;     /* 첫 엔트리 오프셋 */
    uint32_t index_size;         /* 인덱스 사용 크기 */
    uint32_t allocated_size;     /* 할당 크기 */
    uint32_t flags;              /* LARGE_INDEX(0x01) = $INDEX_ALLOCATION 사용 */
    /* ... 인덱스 엔트리 배열 ... */
};

$UpCase 정렬 규칙

NTFS 파일명 비교는 $UpCase 테이블(MFT 레코드 #10)을 사용합니다. 이 테이블은 65,536개 UTF-16 코드 포인트의 대문자 매핑을 제공하여, 대소문자를 무시한 비교를 수행합니다. Windows 버전에 따라 $UpCase 테이블이 다를 수 있으므로, ntfs3 드라이버는 볼륨의 $UpCase를 읽어 사용합니다.

저널링 ($LogFile)

NTFS 저널링은 $LogFile(MFT 레코드 #2)에 WAL(Write-Ahead Logging) 방식으로 메타데이터 변경을 기록합니다. 비정상 종료 시 $LogFile을 재생(replay)하여 파일시스템 일관성을 복구합니다. $LogFile은 파일 데이터가 아닌 메타데이터 변경만 기록합니다.

$LogFile 구조

/*
 * $LogFile 레이아웃:
 *   ┌─────────────────────────────────────────┐
 *   │  Restart Area (페이지 0)                │  ← 마지막 체크포인트 정보
 *   │  Restart Area (페이지 1, 백업)          │
 *   ├─────────────────────────────────────────┤
 *   │  Log Record Area (순환 버퍼)            │  ← Redo/Undo 로그 레코드
 *   │  ...                                    │
 *   │  (기본 크기: 볼륨의 약 2~4%, 최대 64 MiB)│
 *   └─────────────────────────────────────────┘
 */

로그 레코드 타입

작업 코드설명Undo 작업
InitializeFileRecordSegmentMFT 레코드 초기화DeallocateFileRecordSegment
CreateAttribute속성 생성DeleteAttribute
DeleteAttribute속성 삭제CreateAttribute
UpdateResidentValueResident 속성 값 변경UpdateResidentValue (이전 값)
UpdateNonResidentValueNon-Resident 데이터 변경Noop 또는 역 데이터
AddIndexEntryRoot인덱스 루트에 엔트리 추가DeleteIndexEntryRoot
AddIndexEntryAllocation인덱스 할당에 엔트리 추가DeleteIndexEntryAllocation
SetBitsInNonResidentBitMap비트맵 비트 설정ClearBitsInNonResidentBitMap
UpdateMappingPairsData Run 업데이트UpdateMappingPairs (이전 값)

복구 과정 (Analysis → Redo → Undo)

/*
 * NTFS 복구 3단계:
 *
 * 1. Analysis Pass (분석):
 *    - Restart Area에서 마지막 체크포인트 LSN 읽기
 *    - 체크포인트 이후의 모든 로그 레코드 스캔
 *    - Dirty Page Table + Transaction Table 재구성
 *
 * 2. Redo Pass (재실행):
 *    - 체크포인트 이후 커밋된 트랜잭션의 Redo 레코드 순서대로 재실행
 *    - 디스크에 반영되지 않은 메타데이터 변경 완료
 *
 * 3. Undo Pass (취소):
 *    - 미완료 트랜잭션의 Undo 레코드를 역순으로 실행
 *    - 커밋되지 않은 변경 사항 롤백
 */

$UsnJrnl (변경 저널)

$UsnJrnl($Extend\$UsnJrnl)은 $LogFile과는 별개로, 파일/디렉터리 변경 이력을 유저스페이스에 제공하는 변경 저널입니다. Windows의 검색 인덱싱, 백업, 복제 서비스가 이 저널을 사용합니다.

/* USN_RECORD_V2 구조 (축약) */
struct usn_record_v2 {
    uint32_t record_length;
    uint16_t major_version;    /* 2 */
    uint16_t minor_version;    /* 0 */
    uint64_t file_reference;   /* MFT 레코드 번호 + 시퀀스 */
    uint64_t parent_reference; /* 부모 디렉터리 MFT 참조 */
    uint64_t usn;              /* Update Sequence Number */
    uint64_t timestamp;        /* FILETIME */
    uint32_t reason;           /* USN_REASON_* 플래그 */
    uint32_t source_info;
    uint32_t security_id;
    uint32_t file_attributes;
    uint16_t file_name_length;
    uint16_t file_name_offset;
    /* wchar_t file_name[]; */
};

보안 & 권한

NTFS는 Windows NT 보안 모델에 기반한 ACL(Access Control List) 기반 권한 시스템을 구현합니다. 각 파일/디렉터리에 Security Descriptor가 연결되며, $Secure 파일에서 중앙 집중 관리합니다.

Security Descriptor

/* Security Descriptor 구조 */
struct security_descriptor {
    uint8_t  revision;         /* 항상 1 */
    uint8_t  padding;
    uint16_t control;          /* SE_DACL_PRESENT, SE_SACL_PRESENT 등 */
    uint32_t owner_offset;     /* Owner SID 오프셋 */
    uint32_t group_offset;     /* Group SID 오프셋 */
    uint32_t sacl_offset;      /* System ACL 오프셋 */
    uint32_t dacl_offset;      /* Discretionary ACL 오프셋 */
};

/* ACL → ACE(Access Control Entry) 배열 */
/* ACE 타입: ACCESS_ALLOWED, ACCESS_DENIED, SYSTEM_AUDIT 등 */
/* ACE 권한: READ_DATA, WRITE_DATA, EXECUTE, DELETE, FULL_CONTROL 등 */

$Secure 파일

NTFS 3.0+에서 Security Descriptor는 $Secure 파일(MFT #9)에서 중앙 관리됩니다. 각 고유 디스크립터에 Security ID가 할당되고, 파일의 $STANDARD_INFORMATION에 이 ID만 저장합니다.

$Secure 스트림설명
$SDS (Security Descriptor Stream)Security Descriptor 실제 데이터 (추가 전용, 순차 기록)
$SDH (Security Descriptor Hash Index)해시 → Security ID 매핑 (중복 방지)
$SII (Security ID Index)Security ID → $SDS 오프셋 매핑

Linux POSIX 권한 매핑

ntfs3 드라이버는 NTFS ACL을 Linux POSIX 권한으로 매핑합니다. 마운트 옵션을 통해 기본 uid/gid/fmask/dmask를 설정할 수 있습니다.

# ntfs3 ACL/POSIX 매핑 마운트 예시
mount -t ntfs3 -o uid=1000,gid=1000,fmask=0022,dmask=0022 /dev/sda1 /mnt

# POSIX ACL 지원 (acl 마운트 옵션 필요)
mount -t ntfs3 -o acl /dev/sda1 /mnt
getfacl /mnt/file.txt
setfacl -m u:user1:rwx /mnt/file.txt

Linux NTFS 지원

Linux에서 NTFS 접근은 세 가지 구현을 통해 이루어져 왔습니다. 레거시 ntfs.ko, FUSE 기반 ntfs-3g, 그리고 최신 커널 네이티브 ntfs3 드라이버입니다.

드라이버 비교

항목ntfs.ko (레거시)ntfs-3g (FUSE)ntfs3 (Paragon)
구현 위치커널 모듈유저스페이스 (FUSE)커널 모듈
커널 버전2.6~6.8커널 무관 (FUSE 필요)5.15+
읽기지원지원지원
쓰기제한적 (안전하지 않음)완전 지원완전 지원
NTFS 3.1부분 지원완전 지원완전 지원
압축 (LZNT1)읽기만읽기/쓰기읽기/쓰기
ACL미지원제한적POSIX ACL 매핑
ADS미지원xattr 통해 접근xattr 통해 접근
성능보통커널 전환 오버헤드네이티브 커널 성능
$LogFile 재생미지원 (dirty 시 마운트 거부)지원지원
유지보수6.9에서 삭제활발 (Tuxera)활발 (Paragon)
패키지커널 내장ntfs-3g / libntfs-3g커널 내장 (CONFIG_NTFS3_FS)
권장: Linux 5.15+ 환경에서는 ntfs3 드라이버를 사용하는 것이 성능과 기능 면에서 최적입니다. ntfs-3g는 오래된 커널이나 ntfs3에서 지원하지 않는 특수 기능이 필요할 때 대안이 됩니다.

커널 설정

# ntfs3 드라이버 커널 설정
CONFIG_NTFS3_FS=m          # ntfs3 파일시스템 드라이버
CONFIG_NTFS3_LZX_XPRESS=y  # LZX/Xpress 압축 지원 (WOF 압축 읽기)
CONFIG_NTFS3_FS_POSIX_ACL=y # POSIX ACL 지원

# FUSE (ntfs-3g 사용 시)
CONFIG_FUSE_FS=m

# 모듈 로드 확인
modprobe ntfs3
lsmod | grep ntfs3

ntfs3 드라이버 심화

ntfs3은 Paragon Software가 커널 5.15에 기여한 NTFS 읽기/쓰기 드라이버입니다. fs/ntfs3/ 디렉터리에 위치하며, 기존 ntfs.ko(fs/ntfs/)를 대체합니다.

소스 파일 구조 (fs/ntfs3/)

파일역할
super.c슈퍼블록 operations, ntfs_fill_super(), 마운트/언마운트
inode.cinode operations, ntfs_iget(), 속성 읽기/쓰기
file.cfile operations, 읽기/쓰기, fallocate, fiemap
dir.cdirectory operations, readdir, lookup
namei.cinode operations (create, link, unlink, rename)
attrib.cNTFS 속성 관리 (읽기/쓰기/삽입/삭제)
index.cB-tree 인덱스 operations (삽입/삭제/검색)
record.cMFT 레코드 I/O, USA 처리
run.cData Run 인코딩/디코딩, 클러스터 할당
bitmap.c비트맵 operations (클러스터/MFT 할당)
fsntfs.cNTFS 전용 유틸리티 (MFT Mirror, $LogFile, 볼륨 플래그)
frechet.c이름 해싱, 검색 최적화
lznt.cLZNT1 압축/해제
xattr.c확장 속성, POSIX ACL, ADS 매핑

VFS 통합 (Operations)

/* fs/ntfs3/super.c - 파일시스템 타입 등록 */
static struct file_system_type ntfs_fs_type = {
    .owner          = THIS_MODULE,
    .name           = "ntfs3",
    .init_fs_context = ntfs_init_fs_context,
    .parameters     = ntfs_fs_parameters,
    .kill_sb        = kill_block_super,
    .fs_flags       = FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
};

/* fs/ntfs3/inode.c - inode operations */
const struct inode_operations ntfs_dir_inode_operations = {
    .lookup    = ntfs_lookup,
    .create    = ntfs_create,
    .link      = ntfs_link,
    .unlink    = ntfs_unlink,
    .symlink   = ntfs_symlink,
    .mkdir     = ntfs_mkdir,
    .rmdir     = ntfs_rmdir,
    .rename    = ntfs_rename,
    .setattr   = ntfs3_setattr,
    .getattr   = ntfs_getattr,
    .listxattr = ntfs_listxattr,
    .fiemap    = ntfs_fiemap,
};

/* fs/ntfs3/file.c - file operations */
const struct file_operations ntfs_file_operations = {
    .llseek        = generic_file_llseek,
    .read_iter     = ntfs_file_read_iter,
    .write_iter    = ntfs_file_write_iter,
    .mmap          = ntfs_file_mmap,
    .open          = ntfs_file_open,
    .fsync         = generic_file_fsync,
    .splice_read   = ntfs_file_splice_read,
    .splice_write  = iter_file_splice_write,
    .fallocate     = ntfs_fallocate,
    .unlocked_ioctl = ntfs_ioctl,
};

마운트 과정 (ntfs_fill_super)

/*
 * ntfs_fill_super() 주요 단계 (fs/ntfs3/super.c):
 *
 * 1. VBR 읽기 및 검증
 *    - "NTFS    " OEM ID 확인
 *    - bytes_per_sector, sectors_per_cluster 파싱
 *    - MFT/MFTMirr LCN 계산
 *
 * 2. ntfs_sb_info 초기화
 *    - 슈퍼블록 정보 구조체 할당/초기화
 *    - 클러스터 크기, MFT 레코드 크기 설정
 *
 * 3. 시스템 메타데이터 파일 로드
 *    - $MFT(#0) → MFT 자체 Data Run 파싱
 *    - $MFTMirr(#1) → 미러 검증
 *    - $LogFile(#2) → 저널 상태 확인, 필요 시 재생
 *    - $Volume(#3) → dirty 플래그 확인
 *    - $AttrDef(#4) → 속성 정의 로드
 *    - $Bitmap(#6) → 클러스터 비트맵 로드
 *    - $Secure(#9) → 보안 디스크립터 인덱스
 *    - $UpCase(#10) → 대문자 변환 테이블 로드
 *
 * 4. 루트 디렉터리 inode 로드
 *    - MFT 레코드 #5 (루트 디렉터리)
 *    - d_make_root()로 루트 dentry 생성
 *
 * 5. 볼륨 dirty 플래그 설정 (읽기-쓰기 마운트 시)
 */

xattr 매핑

ntfs3는 NTFS의 다양한 속성을 Linux xattr(확장 속성) 인터페이스로 노출합니다:

핵심 커널 자료구조 (ntfs3)

ntfs_sb_info

/* fs/ntfs3/ntfs_fs.h - 슈퍼블록 정보 (핵심 필드 발췌) */
struct ntfs_sb_info {
    struct super_block *sb;

    u32 cluster_size;          /* 클러스터 크기 (바이트) */
    u32 cluster_mask;          /* cluster_size - 1 */
    u8  cluster_bits;          /* log2(cluster_size) */
    u32 record_size;           /* MFT 레코드 크기 (보통 1024) */
    u32 index_size;            /* 인덱스 블록 크기 (보통 4096) */

    u64 maxbytes;              /* 최대 파일 크기 */
    u64 maxbytes_sparse;       /* Sparse 파일 최대 크기 */

    struct ntfs_inode *mft_ni; /* $MFT inode */
    struct ntfs_inode *security_ni; /* $Secure inode */
    struct ntfs_inode *objid_ni;   /* $ObjId inode */
    struct ntfs_inode *reparse_ni; /* $Reparse inode */
    struct ntfs_inode *usn_jrnl_ni; /* $UsnJrnl inode */

    struct wnd_bitmap used;    /* 클러스터 비트맵 ($Bitmap) */
    struct wnd_bitmap mft_bitmap; /* MFT 비트맵 */

    u16 *upcase;               /* $UpCase 테이블 (64K 엔트리) */

    struct ntfs_mount_options options; /* 마운트 옵션 */

    struct rw_semaphore vol_sem; /* 볼륨 메타데이터 락 */
    u64 maxbytes;              /* 파일시스템 최대 파일 크기 */

    u32 zone_max;              /* MFT zone 최대 클러스터 */
    bool dirty;                /* 볼륨 dirty 플래그 */
};

ntfs_inode

/* fs/ntfs3/ntfs_fs.h - NTFS inode (핵심 필드 발췌) */
struct ntfs_inode {
    struct inode vfs_inode;    /* VFS inode (반드시 첫 필드) */

    u64 mft_no;                /* MFT 레코드 번호 */
    struct MFT_REC *mi;       /* MFT 레코드 포인터 */

    struct runs_tree run;     /* Data Run 트리 (데이터 매핑) */

    union {
        struct {
            struct runs_tree dir_run; /* 디렉터리: $INDEX_ALLOCATION run */
            struct runs_tree bmp_run; /* 디렉터리: $BITMAP run */
        } dir;
        struct {
            struct rw_semaphore run_lock; /* 파일 run 보호 */
        } file;
    };

    u32 std_fa;                /* NTFS 파일 속성 ($STANDARD_INFORMATION) */
    u32 std_security_id;       /* Security ID */

    struct rw_semaphore ni_lock; /* inode 수준 락 */
};

/* ntfs_inode ↔ vfs_inode 변환 매크로 */
#define ntfs_i(inode)  container_of(inode, struct ntfs_inode, vfs_inode)

성능 튜닝 & mount 옵션

ntfs3 마운트 옵션

옵션기본값설명
uid=마운트 프로세스 uid파일 소유자 UID
gid=마운트 프로세스 gid파일 소유 그룹 GID
fmask=0022파일 권한 마스크
dmask=0022디렉터리 권한 마스크
umask=-파일/디렉터리 공통 권한 마스크
nls=utf8파일명 인코딩 (NLS 캐릭터셋)
acl비활성POSIX ACL 지원 활성화
noatime비활성접근 시간 업데이트 비활성화
prealloc비활성사전 할당 (단편화 감소)
no_acs_rules비활성NTFS ACL 검사 비활성화 (모든 접근 허용)
discard비활성TRIM/discard 명령 활성화 (SSD)
force비활성dirty 볼륨 강제 마운트
sparse비활성Sparse 파일 생성 허용
showmeta비활성시스템 메타파일을 디렉터리에 표시
compress비활성새 파일에 LZNT1 압축 적용

성능 고려사항

마운트 예시

# 기본 ntfs3 마운트 (읽기-쓰기)
mount -t ntfs3 /dev/sda1 /mnt/windows

# 성능 최적화 마운트
mount -t ntfs3 -o noatime,prealloc,discard /dev/sda1 /mnt/windows

# 데스크탑 사용 (일반 사용자 접근)
mount -t ntfs3 -o uid=1000,gid=1000,umask=0022,noatime /dev/sda1 /mnt/windows

# POSIX ACL 활성화
mount -t ntfs3 -o acl,noatime /dev/sda1 /mnt/windows

# 읽기 전용 마운트
mount -t ntfs3 -o ro /dev/sda1 /mnt/windows

# dirty 볼륨 강제 마운트 (주의: 데이터 손실 위험)
mount -t ntfs3 -o force /dev/sda1 /mnt/windows

# fstab 설정
# /dev/sda1 /mnt/windows ntfs3 defaults,noatime,prealloc,uid=1000,gid=1000 0 0

도구 & 유틸리티

Linux에서 NTFS 볼륨 관리에 사용되는 도구는 ntfsprogs(ntfs-3g 패키지) 세트입니다. ntfs3 드라이버는 마운트 후 표준 Linux 도구(cp, mv, chmod 등)를 사용할 수 있지만, 포맷/복구/레이블 변경 등은 전용 도구가 필요합니다.

ntfsprogs 도구

도구설명
mkntfs (mkfs.ntfs)NTFS 볼륨 포맷 (mkfs.ntfs는 심볼릭 링크)
ntfsfix간단한 NTFS 복구 (dirty 플래그 클리어, $LogFile 리셋)
ntfsresizeNTFS 볼륨 크기 조정 (축소/확장)
ntfscloneNTFS 볼륨 복제 (사용 중인 클러스터만 복사)
ntfslabel볼륨 레이블 읽기/변경
ntfsinfoNTFS 볼륨/파일 메타데이터 정보 출력
ntfslsNTFS 디렉터리 목록 (마운트 없이 직접 읽기)
ntfscatNTFS 파일 내용 출력 (마운트 없이 직접 읽기)
ntfscpNTFS 볼륨에 파일 복사 (마운트 없이 직접 쓰기)
ntfscmp두 NTFS 볼륨 비교
ntfswipeNTFS 볼륨 데이터 안전 삭제
ntfsdecryptEFS 암호화 파일 복호화

mkntfs 사용 예시

# 기본 NTFS 포맷
mkfs.ntfs /dev/sdb1

# 빠른 포맷 (제로 채움 건너뛰기)
mkfs.ntfs -f /dev/sdb1

# 클러스터 크기 지정
mkfs.ntfs -c 65536 /dev/sdb1    # 64 KiB 클러스터

# 볼륨 레이블 지정
mkfs.ntfs -L "DATA" /dev/sdb1

# 볼륨 정보 확인
ntfsinfo -m /dev/sdb1

# dirty 볼륨 수정
ntfsfix /dev/sdb1

# 볼륨 레이블 변경
ntfslabel /dev/sdb1 "NEWLABEL"

# 볼륨 크기 조정 (5GiB로 축소)
ntfsresize -s 5G /dev/sdb1

NTFS vs 다른 파일시스템 비교

항목NTFSext4XFSBtrfsexFAT
저널링메타데이터메타+데이터(옵션)메타데이터(WAL)COW(저널 불필요)없음
최대 볼륨256 TiB1 EiB8 EiB16 EiB128 PiB
최대 파일256 TiB16 TiB8 EiB16 EiB128 PiB
압축LZNT1없음없음zstd/lzo/zlib없음
암호화EFSfscrypt없음없음없음
스냅샷VSS (Windows)없음없음네이티브없음
Reflink없음없음지원지원없음
데이터 체크섬없음메타만메타만데이터+메타없음
Linux 지원ntfs3(5.15+)네이티브네이티브네이티브네이티브(5.4+)
Windows 지원네이티브서드파티없음WinBtrfs네이티브
주 사용처Windows 시스템Linux 범용대용량/엔터프라이즈Linux 데스크탑/NAS이동식 미디어

Linux에서의 NTFS 한계

Linux에서 NTFS 사용 시 알려진 제한사항:
  • EFS 암호화: ntfs3/ntfs-3g 모두 투명한 EFS 복호화를 지원하지 않습니다 (ntfsdecrypt로 수동 복호화 가능).
  • $UsnJrnl: ntfs3는 USN 저널을 완전히 지원하지 않습니다.
  • VSS (Volume Shadow Copy): Windows 전용 기능으로, Linux에서 접근 불가합니다.
  • 대소문자 구분: NTFS는 기본적으로 대소문자 비구분이지만, Linux는 대소문자를 구분합니다. ntfs3는 NTFS 규칙을 따라 대소문자 비구분 동작을 합니다.
  • Reparse Point: 심볼릭 링크, Junction은 매핑되지만, 커스텀 Reparse Point는 제한적입니다.
  • WOF 압축: Windows 10+의 WOF(Windows Overlay Filter) 압축(LZX/Xpress)은 CONFIG_NTFS3_LZX_XPRESS 옵션으로 읽기만 지원됩니다.
  • Deduplication: Windows Server의 데이터 중복 제거 기능은 Linux에서 지원되지 않습니다.
듀얼 부팅 팁: Windows/Linux 듀얼 부팅 환경에서는 데이터 공유 파티션에 NTFS를 사용하는 것이 일반적입니다. Windows의 "빠른 시작"(Fast Startup)을 비활성화해야 Linux에서 NTFS를 읽기-쓰기로 마운트할 수 있습니다. 빠른 시작이 활성화된 상태에서는 Windows가 $LogFile을 dirty 상태로 유지하여, ntfs3가 읽기 전용으로만 마운트합니다.