내용
1. Inode
1) Inode
- 파일시스템의 모든 파일이나 디렉토리는 각기 하나의 Inode가 할당되어 있으며, 각 블록 그룹을 위한 Ext2Inode는 어떤 Inode가 할당되었는지 아닌지를 확인하기 위한 Inode Bitmap과 함께 Inode Table에 저장되게 됩니다.
2) Inode 구조 분석
[Inode 영역 출력한 값]
- 위 그림은 Inode Table에서 첫 번째 블록 상위 256Byte만 덤프한 내용 입니다.
- Inode의 크기는 Super Block의 inode structure size 항목의 기록된 값으로 파악하며, 일반적으로 Inode 구조체 크기는 128Byte이게 됩니다.
- 0x00x70 아래 부분이 Inode사이의 경계가 되며 Super Block의 inode per group 항목의 개수만큼 연속적으로 기록되어 있기 "때문에 Inode Table의 전체 크기를 쉽게 계산하여 블록 데이터 영역을 찾을 수 있게 됩니다.
[Inode 항목]
3) Inode의 각 항목
이름 |
File Mode |
||||
위치 (Offset) |
0 |
크기 (Size) |
2 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
Inode의 타입으로 파일 타입과 접근 권한을 나타냅니다. 유닉스 시스템에서는 모든 것을 파일로 표현하는 의미를 두고 File Mode에서 확인 가능 합니다. |
[Inode의 접근 권한]
Inode Owner |
Flag |
설명 |
소유자 |
0x100 |
읽기 권한 |
0x080 |
쓰기 권한 |
|
0x040 |
실행 권한 |
|
동일 그룹 |
0x020 |
읽기 권한 |
0x010 |
쓰기 권한 |
|
0x008 |
실행 권한 |
|
기타(그 외) |
0x004 |
읽기 권한 |
0x002 |
쓰기 권한 |
|
0x001 |
실행 권한 |
- File Mode의 0~8비트까지를 나타내는 곳입니다.(하위 9비트이용)
- 리눅스에서 소유자 권한을 바꾸는 명령어인 chmod를 생각 한다면 어렵지 않게 이해가능 할 것입니다.
- 만약 File Mode의 하위 플래그가 0x1FF(1 11[소유자] 11 1[그룹]111[기타])라면 모든 소유자가 읽기,쓰기, 실행이 가능할 것입니다.
- 0x1C0(1 11[소유자]00 0[그룹] 000[기타]) 비트를 통해 확인하면 소유자만 읽기,쓰기, 실행이 가능하다는 것을 알게 됩니다.
[실행 파일 및 디렉토리 속성]
속성 타입 |
Flag |
설명 |
STICKY BIT |
0x200 |
Sticky bit |
SGID |
0x400 |
Set Group ID |
SUID |
0x800 |
Set User ID |
- File Mode의 9~11 비트 플래그로 3개의 비트를 이용합니다. (하위 10~12번째 비트)
- 실행 파일과 디렉토리에만 사용되는 필드이며, 이 비트가 ON이라면 실행 시 다르게 동작하거나 디렉토리 아의 파일들이 다른 속성을 지니게 됩니다.
[메타 데이터 타입값]
메타 데이터 타입 |
값 |
설명 |
FIFO |
0x1000 |
명명된 파이프(named pipe) |
Character device |
0x2000 |
문자 장치 |
Directory |
0x4000 |
디렉토리 |
Block device |
0x6000 |
블록 장치 |
Regular file |
0x8000 |
정규 파일 |
Symbolic link |
0xA000 |
소프트(심벌릭)링크 |
Unix Socket |
0xC000 |
소켓 |
- File Mode의 13~16번째 비트에 저장되는 값(최상위 4bit)
- 이 값들은 플래그처럼 여러 타입이 정의 될 수 없고 하나의 값만 저장됩니다.
- 이를 확인 하면 Inode 타입이 일반 파일 뿐 아니라 디렉토리, 소켓 등 다양한 것을 알 수 있습니다.
- 소켓의 경우 데이터 블록이 필요 없으며, Inode 내에 모두 기록되는 타입입니다.
이름 |
uid |
||||
위치 (Offset) |
2 |
크기 (Size) |
2 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
소유자 식별자(Owner ID) 32byte중 하위 16bit를 저장. |
이름 |
size in bytes |
||||
위치 (Offset) |
4 |
크기 (Size) |
4 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
파일 크기를 Byte 단위로 나타냄 |
이름 |
access time |
||||
위치 (Offset) |
8 |
크기 (Size) |
4 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
파일의 마지막 접근 시간(last access time) |
이름 |
change time |
||||
위치 (Offset) |
12 |
크기 (Size) |
4 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
Inode를 마지막으로 변경한 시간(last corresponds time) |
이름 |
modification time |
||||
위치 (Offset) |
16 |
크기 (Size) |
4 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
파일 내용을 마지막으로 변경한 시간(last Modification time) |
이름 |
deletion time |
||||
위치 (Offset) |
20 |
크기 (Size) |
4 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
파일이 삭제된 시간(delete time) |
이름 |
Group ID |
||||
위치 (Offset) |
24 |
크기 (Size) |
2 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
그룹 식별자(Group ID) 32Bit 중 하위 16bit |
이름 |
link count |
||||
위치 (Offset) |
26 |
크기 (Size) |
2 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
하드 링크 수 |
이름 |
block count |
||||
위치 (Offset) |
28 |
크기 (Size) |
4 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
파일 데이터를 저장하는 데 필요한 블록 개수로 size in bytes의 경우 파일의 실제 크기를 나타내고, block count는 파일의 데이터가 저장되는 블록의 개수이다. |
이름 |
flags |
||||
위치 (Offset) |
32 |
크기 (Size) |
4 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
파일 플래그 |
[Inode 파일 플래그 항목]
Inode Flags |
Flag |
설명 |
EXT2_SECRM_FL |
0x00000001 |
Secure Deletion(사용되지 않음) |
EXT2_UNRM_FL |
0x00000002 |
Undelete : 파일 삭제 시 데이터 영역을 초기화 하지 않는다.(사용되지 않음) |
EXT2_COMPR_FL |
0x00000004 |
파일 압축(사용되지 않음) |
EXT2_SYNC_FL |
0x00000008 |
Syncronous updates : 데이터가 쓰여질 때 저장장치에 바로 쓴다. |
EXT2_IMMUTABLE_FL |
0x00000010 |
파일의 내용을 변경할 수 없다. |
EXT2_APPEND_FL |
0x00000020 |
파일에 덮어 쓰기는 불가능하고, 파일의 뒤에 이어 쓰는 것만 가능 |
EXT2_NODUMP_FL |
0x00000040 |
파일 덤프 기능을 사용하지 않음 |
EXT2_NOATIME_FL |
0x00000080 |
A-time(파일 접근 시간)을 업데이트 하지 않음. |
- 이 외에도 압축 기능 등 추가로 여러 가지 플래그가 정의되어 있다.
- 이를 지속적으로 확인해야 할 것이다. (공식 루트가 아니더라도 최근 릴리즈된 커널 소스의 ext2_fs.h를 참고하여도 된다.)
이름 |
OS description 1 |
||||
위치 (Offset) |
36 |
크기 (Size) |
4 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
특정 운영체제 정보로 Inode 구조체에는 특정 OS에 대한 예약 영역을 가지고 있으나 실제 사용되진 않는다. |
이름 |
block pointers(i_block 변수) |
||||
위치 (Offset) |
40 |
크기 (Size) |
60 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
데이터 블록을 가리키는 포인터 배열, int 형(4byte) 배열 15개를 가진다. 따라서 60byte이다. - i_block[0]~iblock[11] : 각 배열 값들이 블록을 가르킴 - i_block[12] 에서 가리키는 블록 내의 각 4Byte들이 데이터 블록을 가르킴(간접 포인터) - i_block[13] : 이중 간접 포인터 - i_block[14] : 3중 간접 포인터 |
- i_block 배열은 파일 데이터가 있는 실제 블록을 가리키기도 하며, 파일 데이터를 가리키는 위치 블록을 가리키기도 하여 직, 간접적으로 파일 블록을 가리킨다.
- 커널이 파일을 저장장치에 기록할 때 i_block 배열에는 파일의 데이터가 기록되어 있는 블록의 번호가 저장되는 것이다.
[i_block 직접 포인터]
- i_block 배열이 파일 블록을 직접 가르키고 있는데 이렇게 되면 15개의 블록밖에 가리킬 수 없다.
[i_block 간접 포인터]
- 만약 파일 크기가 'Block Size X 15' 보다 크다면 i_block[0]~i_block[11]까지는 실제 데이터가 있는 블록을 가리키고 i_block[12]가 넘어가는 데이터부터는 실제 데이터를 가르키는 것이 아니라 i_block[12]가 가리키는 블록 데이터가 바로 실제 데이터를 가리키는 포인터 배열이 된다.
- 다시 정리하면 만약 Block size가 1KB이고, 20KB의 용량을 가진 파일을 가리킨다 했을 때 i_block[0~11]까지는 직접 파일의 12개의 블록을 가리키고, 13번째부터는 i_block[12]가 가리키는 위치 블록에 4Byte씩 이용하여 가리키게 된다. 즉 i_block[12]가 가리키는 블록에는 총 256개의 파일 블록을 가리킬 수 있는데(Block Size : 1KB, 가리키는 블록 주소:4Byte) 이곳에서 나머지 블록들이 차례대로 가리키게 되는 것이다. 이렇게 되면 i_block[12]를 통해 기록할 수 있는 데이터의 크기는 256KB가 되는 것이다.
- 따라서 i_block[0~11]까지의 12KB는 직접, 나머지 8KB는 i_block[12]가 가리키는 블록의 사우이 32Byte(8개)의 블록 번호가 가리키는 파일 블록을 찾아가게 된다.
- i_block의 상위 13개 배열을 통해 '12+256'KB를 가리킬 수 있다는 것을 확인 했는데 그 보다 큰 파일의 경우는 i_block[13], i_block[14]를 이용하여 가리킬수 있게 된다.
[i_block 이중 간접 포인터]
[i_block 삼중 간접 포인터]
- 위치 블록이 하나씩 늘어 남에 따라 2562의 크기로 파일 블록을 가리킬 수 있게 되어 기록할 수 있는 파일의 크기가 커지게 된다.
- 이중의 경우 256 X 256KB = 64MB, 삼중인 경우 64MB X 256 = 16384(16GB)가 거장이 된다. 따라서 블록 크기가 1KB인 경우 하나의 Inode에 저장할 수 있는 파일의 크기는 '12KB + 256KB + 64MB + 16GB'가 되는 것이다.
- 물론 이론상이고, 실질적으로 파일시스템을 직접 접근해서 사용하는 커널의 역량에 따라 크기가 달라진다. 만약 32bit 변수를 이용한 주소 지정의 경우 최대 4GB까지만 사용 가능 할 것이며, 이도 부호 지정 가능한 변수(signed)라면 반이 줄어 4GB까지 사용 가능 할 것이다.
- 최신 커널의 경우 이런 제한이 줄었겠지만 파일 시스템 및 사용되는 시스템을 구현해야 하는 상황이라면 이런 부분을 고려하여야 할 것이다.
이름 |
generation number |
||||
위치 (Offset) |
100 |
크기 (Size) |
4 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
NFS(Network File System)을 위한 파일 버전 |
이름 |
file acl |
||||
위치 (Offset) |
104 |
크기 (Size) |
4 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
파일 확장 속성(extended Attribute) |
이름 |
directory acl |
||||
위치 (Offset) |
108 |
크기 (Size) |
4 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
단편 주소 |
이름 |
Os description 2 |
||||
위치 (Offset) |
116 |
크기 (Size) |
12 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
특정 운영체제 정보 2 |
4) 예약된 Inode
- Inode 중에는 여러 용도로 미리 예약된 부분이 많다.
- 루트 디렉토리의 경우 미리 예약된 Inode 번호가 있다. 이는 파일이나 디렉토리를 찾아가기 위해 파일시스템을 읽을 경우 해당 파일의 Inode를 알아야 하고 그 상위 디렉토리를 알고 있어야 할 것이다 따라서 루트 디렉토리에는 예약된 번호를 쓰게된다.
[예약된 Inode 리스트]
예약된 Inode |
번호 |
설명 |
EXT2_BAD_INO |
1 |
사용 불가능 데이터 블록 Inode |
EXT2_ROOT_INO |
2 |
루트 디렉토리 Inode |
EXT2_BOOT_LOAER_INO |
5 |
Boot Loader의 Inode |
EXT2_UNDEL_DIR_INO |
6 |
삭제 불가 디렉토리의 Inode |
EXT2_GOOD_OLD_FIRST_INO |
11 |
예약되지 않은 첫 번째 Inode |
- EXT2_BAD_INO이 경우 배드 섹터의 이유로 문제 발생한 블록을 표시한다. e2fsck 같은 파일시스템 체크 프로그램에 의해 관리되며 배드 블록 발견시 i_block[]에 추가 되며 이렇게 추가될 수 있는 배드 블록은 최대로 만들 수 있는 파일 크기와 같다. 또한 블록 비트맵(Block Bitmap)에서도 해당 블록의 Bit를 On으로 바꾸어 사용 중인 블록으로 표시해야 한다.
- EXT2_ROOT_INO의 경우 루트 디렉토리에만 해당되는 Inode 이며, 시스템 부팅 시 커널이 루트 디렉토리에 파일 시스템을 마운트 한다.
- EXT2_BOOT_LOAER_INO 와 EXT2_UNDEL_DIR_INO는 최근 사용되지 않는 Inode로, 해당 Inode는 초기화 되거나 쓰레기 값으로 채워져있다.
- EXT2_GOOD_OLD_FIRST_INO는 예약되지 않은 첫 Inode번호를 나타내며 파일시스템이 생성된 이후 할당되는 Inode는 모두 11번 이후 번호를 가진다. 일반적으로 11번 Inode는 '/lost+found' 디렉토리가 할당되며, 이 디렉토리는 부모 디렉토리와 비정상적으로 연결이 끊어진 디렉토리나 파일을 하위로 연결시키기 위한 디렉토리이다. 구 커널 에서 사용하던 Inode 번호이기 때문에 최근에도 일반적으로 11번을 할당한다.
2. 데이터 영역
1) 디렉토리 엔트리
- 일반적으로 Ext2 파일시스템의 디렉토리와 파일은 동일하다고 하는데 실제로도 거의 동일하다. 다만 다른 점은 Inode의 파일모드(File Mode항목) 플래그가 틀리다는 것이다.
- 디렉토리는 파일시스템이 파일 데이터 저장하는 방식과 같이 디렉토리 엔트리 구조를 Block에 쓴 것을 표현한다. 디렉토리 엔트리의 경우 파일이나 데이터의 블록을 알 수 있도록 Inode의 주소를 담는 구조체 이다.
- 모든 디렉토리의 경우 현재 디렉토리와, 상위 디렉토리를 의미하는 '.', '..' 엔트리로 시작하며, 그 아래로 하위 디렉토리나 여러 파일들이 위치한다.
- 루트 디렉토리의 Inode 번호는 2번으로 디렉토리를 찾아갈 때는 2번 Inode 부터 검색하면 될 것이다.
- 파일 길이가 1~255까지 가변적이 듯이 디렉토리 엔트리 크기도 고정되지 않지만 구조체에서 파일명을 저장하는 버퍼 크기는 최대 사이즈(255Byte)가 고정되어 있지만, 블록에 저장될 때는 가변적이다.
- 엔트리 구조체에는 파일명의 길이를 저장하는 필드와 현재의 엔트리 크기를 저장하는 필드 및 데이터 블록을 알아낼 수 있는 필드 등을 가지게 된다.
[디렉토리 엔트리 Trace]
- 위 그림을 보면 Group Descriptor에서부터 디렉토리 엔트리를 찾아가는 경로를 표현한 것으로 Group Descriptor는 Inode 테이블이 시작되는 블록을 가리키고, Inode 2번은 루트 디렉토리라는 것을 이미 알고 있고, 2번 Inode의 데이터 블록 번호를 i_block[0]에서 알아낸 뒤 해당 블록으로 이동하여 디렉토리 엔트리를 읽어오면 된다.
- 모든 엔트리에는 현재 디렉토리와 상위 디렉토리를 가리키는 '.' 과 '..' 엔트리가 있는데, 루트 디렉토리도 이걸 가지고 있지만 '..' 역시 '.' 엔트리와 동일한 Inode를 가리키고 있다.
- 추가로 루트 디렉토리 하위의 디렉토리 엔트리들이 계속 기록된다.
- 디렉토리 엔트리는 파일명과 디렉토리명을 저장하는 영역으로 이 영역은 데이터 블록 영역의 사이사이에 기록되며, 파일이나 디렉토리와 관련된 Inode 주소를 가지며, 디렉토리 엔트리 구조체는 2가지 종류가 있다.
- 모두 구조체의 크기는 같지만, 나중에 만들어진 구조체가 공간 활용에 있어 이점이 있다.
- 슈퍼블록(Super Block)의 compatible feature flags항목에서 EXT2_FEATURE_INCOMPAT_FILETYPE을 보면 "디렉토리 엔트리가 파일 타입 속성을 포함하는지의 유무"가 있는데 이 내용이 바로 디렉토리 엔트리 구조체 버전과 관련된 항목이다. 현재 리눅스에 탑재된 Ext3 파일 시스템에서는 두 번째 구조체를 기본적으로 사용하고 있다.
2) 디렉토리 엔트리 항목 1번째
이름 |
Inode |
||||
위치 (Offset) |
0 |
크기 (Size) |
4 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
디렉토리 엔트리의 하위 디렉토리 또는 파일 등을 가리키는 Inode |
이름 |
Record Length |
||||
위치 (Offset) |
4 |
크기 (Size) |
2 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
현재 디렉토리 엔트리의 크기 |
- Record Length 항목은 파일명의 길이 외에도 영향을 받는 몇 가지가 있는데 엔트리가 생서되는 시점에 엔트리의 크기를 4의 배수로 맞추기 위해 패딩을 넣거나, 나중에 파일 삭제하는 경우 엔트리에도 제거하는데 이때 실제 해당 엔트리가 지워지는 것이 아니라 그 앞의 엔트리 크기를 지우려는 파일(or 디렉토리)의 엔트리 크기만큼 늘려서 그 다음 엔트리를 가리키도록 수정한다.
- 파일이나 하위 디렉토리 개수가 적어 블록 내에 엔트리들이 별로 없다면 마지막에 추가된 엔트리 크기는 '블록크기 – 엔트리들의 크기'가 될 것이다.
- 예로 빈 디렉토리라면 '..' 엔트리의 크기는 최대 4096-12가 될 것이다. 이런 요인으로 인해 디렉토리 엔트리의 크기는 자주 바뀐다.
이름 |
Name Length |
||||
위치 (Offset) |
6 |
크기 (Size) |
2 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
파일명의 길이 |
이름 |
Name |
||||
위치 (Offset) |
8 |
크기 (Size) |
255 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
파일명.
|
- Name Length의 크기가 2Byte인 것으로 보아 Ext2_NAME_LEN 매크로 값을 최대값만 변경하면 최대 길이를 65535까지 확장할 수 있을 듯 하지만 새로운 엔트리 구조체가 발표되면서 이는 현실적으로 불가능해졌다.
3) 디렉토리 엔트리 항목2번째
이름 |
Inode |
||||
위치 (Offset) |
0 |
크기 (Size) |
4 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
디렉토리 엔트리의 하위 디렉토리 또는 파일 등을 가리키는 Inode |
이름 |
Record Length |
||||
위치 (Offset) |
4 |
크기 (Size) |
2 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
현재 디렉토리 엔트리의 크기 |
이름 |
Name Length |
||||
위치 (Offset) |
6 |
크기 (Size) |
1 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
파일명의 길이 |
이름 |
File Type |
||||
위치 (Offset) |
7 |
크기 (Size) |
1 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
파일 타입. 아래 표 참고 |
[파일 타입]
타입 |
값 |
설명 |
EXT2_FT_UNKNOWN |
0 |
알려지지 않은 타입 |
EXT2_FT_REG_FILE |
1 |
정규 파일 |
EXT2_FT_DIR |
2 |
디렉토리 |
EXT2_FT_CHRDEV |
3 |
문자 장치 |
EXT2_FT_BLKDEV |
4 |
블록 장치 |
EXT2_FT_FIFO |
5 |
명명된 파이프(named pipe) |
EXT2_FT_SOCK |
6 |
소켓 |
EXT2_FT_SYMLINK |
7 |
심벌릭 링크 |
이름 |
Name |
||||
위치 (Offset) |
8 |
크기 (Size) |
255 Byte |
일반적인 값 (Value) |
가변적 |
설명 |
파일명 |
- 디렉토리 엔트리 항목 2번째는 파일명의 길이를 반드시 255Byte 내로 맞추어야 한다. 그렇지 않으면 File Type 영역을 침범하게 되어 문제가 발생되는 것이다.
4. 실습
Visual Studio 2012를 관리자 권한으로 실행한다.
관리자권한으로 실행
프로젝트 속성 – 구성 속성 – 일반 -> 프로젝트 기본값 – 문자집합 : 멀티 바이트 문자 집합 사용
1) 실행파일로 만들 것이기 때문에 Realse로 컴파일 하자.
[Partition.h] 이전에 만들었던 파티션과 관련된 소스의 모음 이다.
시작섹터와, 디바이스 번호를 정확히 지정하기 바란다.
HDD_read, HDD_write, GetLocalPartition 함수에서 자신의 PC의 연결된 디바이스 번호를 정확히 지정해야한다.
이전 소스들 참고
무조건 복사 붙여넣기 하면 제대로 동작하지 않을 것이다.
#include <Windows.h>
#include <stdio.h>
typedef unsigned char U8;
typedef unsigned short U16;
typedef unsigned int U32;
//섹터 내 파티션 테이블 번지
#define PARTITION_TBL_POS 0x1BE
//확장 파티션 타입
#define PARTITION_TYPE_EXT 0x0F
typedef struct _PARTITION
{
U8 active;
U8 begin[3];
U8 type;
U8 end[3];
U32 start;
U32 length;
}PARTITION, *PPARTITION;
U32 HDD_read (U8 drv, U32 SecAddr, U32 blocks, U8* buf);
//디스크 내의 모든 파티션 검색
U32 GetLocalPartition(U8 Drv, PPARTITION pPartition);
//확장 파티션 검색(재귀 호출)
void SearchExtPartition(U8 Drv, PPARTITION pPartition, U32 *pnPartitionCnt);
//디스크 내의 부트 레코드에서 파티션 테이블 저장
void GetPartitionTbl(U8 Drv, U32 nSecPos, PPARTITION pPartition, int nReadCnt);
//파티션 테이블의 속성 출력
void PrintPartitionInfo(PPARTITION pPartition, U32 nIdx);
void HexDump (U8 *addr, U32 len) ; //헥사 덤프
void GetPartitionTbl(U8 Drv,U32 nSecPos, PPARTITION pPartition, int nReadCnt)
{
U8 pSecBuf[512];
//물리적 디스크 Drv의 nSecPos번 섹터에서 1개의 블록을 읽어온다.
if(HDD_read(Drv, nSecPos, 1, pSecBuf) ==0) {
printf("boot sector read failed \n");
return ;
}
memcpy(pPartition, (pSecBuf + PARTITION_TBL_POS), sizeof(PARTITION) * nReadCnt);
}
U32 GetLocalPartition(U8 Drv, PPARTITION pPartition)
{
U32 i;
U32 pPartitionCnt=0;
PPARTITION pPriExtPartition = NULL;
//주 파티션이므로 4개의 파티션 읽어옴
GetPartitionTbl(Drv, 0, pPartition, 4);
for(i=0;i<4 && pPartition->length !=0; i++){
if(pPartition->type ==PARTITION_TYPE_EXT) {
pPriExtPartition = pPartition;
}
pPartition++; //다음 파티션으로 이동
pPartitionCnt++; //파티션 카운트 UP
}
if(!pPriExtPartition)
return pPartitionCnt;
SearchExtPartition(Drv, pPriExtPartition, &pPartitionCnt);
return pPartitionCnt;
}
void SearchExtPartition(U8 Drv, PPARTITION pPartition, U32 *pnPartitionCnt)
{
int nExtStart = pPartition->start;
//데이터를 읽어오기 위해 포인터를 다음 파티션 번지로 이동
pPartition++;
//부 파티션과 확장 파티션이 있을 수 있으므로 2개의 파티션을 읽어옴
GetPartitionTbl(Drv, nExtStart, pPartition, 2);
while(pPartition->length !=0) {
(*pnPartitionCnt)++;
if(pPartition->type == PARTITION_TYPE_EXT) {
SearchExtPartition(Drv, pPartition, pnPartitionCnt);
}
else
pPartition++;
}
}
U32 HDD_read (U8 drv, U32 SecAddr, U32 blocks, U8* buf){
U32 ret;
U32 ldistanceLow, ldistanceHigh, dwpointer, bytestoread, numread;
char cur_drv[100];
HANDLE g_hDevice;
sprintf_s(cur_drv,sizeof(cur_drv),"\\\\.\\PhysicalDrive%d",(U32)drv);
g_hDevice=CreateFile(cur_drv, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(g_hDevice==INVALID_HANDLE_VALUE) return 0;
ldistanceLow = SecAddr << 9;
ldistanceHigh = SecAddr >> (32 - 9);
dwpointer = SetFilePointer(g_hDevice, ldistanceLow, (long *)&ldistanceHigh, FILE_BEGIN);
if(dwpointer != 0xFFFFFFFF) {
bytestoread = blocks * 512;
ret = ReadFile(g_hDevice, buf, bytestoread, (unsigned long*)&numread, NULL);
if(ret) ret = 1;
else ret = 0;
}
CloseHandle(g_hDevice);
return ret;
}
U32 HDD_write(U8 drv, U32 SecAddr, U32 blocks, U8*buf)
{
U32 ret = 0;
U32 ldistanceLow, ldistanceHigh, dwpointer, bytestoread, numread;
char cur_drv[100];
HANDLE g_hDevice;
sprintf_s(cur_drv,sizeof(cur_drv),"\\\\.\\PhysicalDrive%d",(U32)drv);
g_hDevice = CreateFile(cur_drv, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(g_hDevice == INVALID_HANDLE_VALUE) return 0;
ldistanceLow = SecAddr << 9;
ldistanceHigh = SecAddr >> (32-9);
dwpointer = SetFilePointer(g_hDevice , ldistanceLow, (long*)&ldistanceHigh, FILE_BEGIN);
if(dwpointer != 0xFFFFFFFF){
bytestoread = blocks * 512;
ret = WriteFile(g_hDevice , buf, bytestoread, (unsigned long*)&numread, NULL);
if(ret) ret = 1;
else ret = 0;
}
CloseHandle(g_hDevice);
return ret;
}
void HexDump (U8 *addr, U32 len){
U8 *s=addr, *endPtr=(U8*)((U32)addr+len);
U32 i, remainder=len%16;
printf("\n Offset Hex Value /t/t/t/t/t Ascii value\n");
while (s+16<=endPtr){ // print out 16 byte blocks.
printf("0x%08lx ", (long)(s-addr));// offset 출력.
for (i=0; i<16; i++){ // 16 bytes 단위로 내용 출력.
printf("%02x ", s[i]);
}
printf(" ");
for (i=0; i<16; i++){
if (s[i]>=32 && s[i]<=125)printf("%c", s[i]);
else printf(".");
}
s += 16;
printf("\n");
}
if (remainder){// Print out remainder.
printf("0x%08lx ", (long)(s-addr)); // offset 출력.
for (i=0; i<remainder; i++){ // 16 bytes 단위로 출력하고 남은 것 출력.
printf("%02x ", s[i]);
}
for (i=0; i<(16-remainder); i++){
printf(" ");
}
printf(" ");
for (i=0; i<remainder; i++){
if (s[i]>=32 && s[i]<=125) printf("%c", s[i]);
else printf(".");
}
for (i=0; i<(16-remainder); i++){
printf(" ");
}
printf("\n");
}
return;
} // HexDump.
[ext2_fs.h]
#ifndef _LINUX_EXT2_FS_H
#define _LINUX_EXT2_FS_h
#include "Partition.h"
//#define min(a,b) ( ( (a)<(b) ) ? (a) : (b) )
#define EXT2_BAD_INO 1
#define EXT2_ROOT_INO 2
#define EXT2_GOOD_OLD_FIRST_INO 11
typedef struct ext2_group_desc{
U32 block_bitmap; //blocks bitmap block
U32 inode_bitmap;
U32 inode_table;
U16 free_blocks_count;
U16 free_inode_count;
U16 used_dirs_count;
U16 pad;
U32 reserved[3];
}GROUP_DESC;
#define EXT2_NDIR_BLOCKS 12
#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK +1)
#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK +1)
#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK +1)
//Revision levels
#define EXT2_GOOD_OLD_REV 0
#define EXT2_GOOD_OLD_INODE_SIZE 128
//Inode 구조체
typedef struct ext2_inode{
U16 i_mode;
U16 i_uid;
U32 i_size;
U32 i_atime;
U32 i_ctime;
U32 i_mtime;
U32 i_dtime;
U16 i_gid;
U16 i_links_count;
U32 i_blocks;
U32 i_flags;
U32 l_i_reserved1;
U32 i_block[EXT2_N_BLOCKS];
U32 i_generation;
U32 i_file_acl;
U32 i_dir_acl;
U32 i_faddr;
U32 l_i_reserved2[3];
}INODE;
//Super Blocks 구조체
typedef struct ext2_super_block{
U32 inodes_count;
U32 blocks_count;
U32 r_blocks_count;
U32 free_blocks_count;
U32 free_inodes_count;
U32 first_data_block;
U32 log_block_size;
U32 log_frag_size;
U32 blocks_per_group;
U32 frags_per_group;
U32 inodes_per_group;
U32 mtime;
U32 wtime;
U16 mnt_count;
U16 max_mnt_count;
U16 magic;
U16 state;
U16 errors;
U16 minor_rev_level;
U32 lastcheck;
U32 checkinterval;
U32 creator_os;
U32 rev_level;
U16 def_resuid;
U16 def_resgid;
U32 first_ino;
U16 inode_size;
U16 block_group_nr;
U32 feature_compat;
U32 feature_incompat;
U32 feature_ro_compat;
U8 uuid[16];
char volume_name[16];
char last_mounted[64];
U32 algorithm_usage_bitmap;
U8 prealloc_blocks;
U8 prealloc_dir_blocks;
U16 padding1;
//저널링 항목
U8 journal_uuid[16];
U32 journal_inum;
U32 journal_dev;
U32 last_orphan;
U32 hash_seed[4];
U8 def_hash_version;
U8 reserved_char_pad;
U16 reserved_word_pad;
U32 default_mount_opts;
U32 first_meta_bg;
U32 reserved[190];
}SUPERBLOCK;
#define EXT2_NAME_LEN 255
//디렉토리 엔트리 1 구조체
typedef struct ext2_dir_entry{
U32 inode;
U16 rec_len;
U16 name_len;
char name[EXT2_NAME_LEN];
} DIRENTRY_1;
//디렉토리 엔트리 2 구조체
typedef struct ext2_dir_entry_2{
U32 inode;
U16 rec_len;
U8 name_len;
U8 file_type;
char name[EXT2_NAME_LEN];
} DIRENTRY;
// 연산 결과를 미리 메모리에 저장하기 위한 구조체
typedef struct ext2_sb_info{
U32 inodes_per_block; //블록당 inode 수
U32 blocks_per_group; //Group당 block 수
U32 inodes_per_group; //Group당 inode 수
U32 itb_per_group; // group 당 inode table block 수
U32 gdb_count; // group당 inode table block 수
U32 desc_per_block; //block 당 group descriptor 수
U32 group_count; //총 group 수
SUPERBLOCK *sb; //super block 포인터
GROUP_DESC * group_desc; //group descriptor table 포인터
int addr_per_block_bits;
int desc_per_block_bits;
int inode_size; //Inode 크기(128 또는 sizeof(ext2_inode)
int first_ino; //예약되지 않은 최초 Inode 번호
int blocksize; //block 크기(1024, 2048, 4096)
int lba_per_block; //1Block당 lba 개수
int ext2_block_size_bits;
}SBINFO;
#endif
[ReadExt.cpp]
#include "ext2_fs.h"
#include <io.h>
int nPstart = 0;
SUPERBLOCK* ReadSuperBlock(){
int offset =2;
SUPERBLOCK *sb = (SUPERBLOCK *) malloc(sizeof(*sb));
//물리적 디스크 drv의 nSecPos번 섹터에서 1개 블록을 읽어옴
//sector * 2 = 1024(ext default block size)
if(HDD_read(1, nPstart + offset, 2,(U8 *)sb) == 0) {
printf("Boot sector read failed\n");
free(sb);
return NULL;
}
return sb;
}
GROUP_DESC* ReadGroupDesc(SBINFO* pSbi){ //Group Descriptor Table 을 읽어들임
GROUP_DESC *pBlkGrps;
int nBlkGrpNum = pSbi->group_count;
int nBlks=0;
while(1){
nBlks++;
nBlkGrpNum -= pSbi->desc_per_block_bits;
if(nBlkGrpNum <=0)
break;
}
pBlkGrps = (GROUP_DESC *)malloc(pSbi->blocksize * nBlks);
if(HDD_read(1, nPstart+pSbi->lba_per_block, nBlks * pSbi->lba_per_block, (U8 *)pBlkGrps) == 0){
return NULL;
}
pSbi->gdb_count = nBlks;
return pBlkGrps;
}
//수정한 Super Blck 파일시스템에 업데이트
BOOL SyncSuperBlock(SBINFO *pSbi){
int offset = 2;
if(HDD_write(1, nPstart + offset, 2, (U8 *)pSbi->sb) == 0) {
return FALSE;
}
return TRUE;
}
//수정한 Group Descriptor Table 파일시스템에 업데이트
BOOL SyncGroupDesc(SBINFO *pSbi){
if(HDD_read(1,nPstart + pSbi->lba_per_block, pSbi->gdb_count * pSbi->lba_per_block, (U8 *)pSbi->group_desc) ==0){
return FALSE;
}
return TRUE;
}
//Block Input/Output
BOOL BlockIO(SBINFO * pSbi, U32 block, U8 *pBlkBuf, BOOL bWrite){
if(bWrite){
if(HDD_write(1, nPstart+ pSbi->lba_per_block * block, pSbi->lba_per_block, pBlkBuf) ==0){
printf("Block Wirte Failed \n");
return FALSE;
}
}else{
if(HDD_read(1,nPstart + pSbi->lba_per_block * block, pSbi->lba_per_block, pBlkBuf) == 0){
printf("Block Read Failed \n");
return FALSE;
}
}
return TRUE;
}
//Inode Input/Output(Read or Wirte)
void InodeIO(SBINFO * pSbi, int nIno, INODE *pInode, BOOL bWrite){
SUPERBLOCK *sb=pSbi->sb;
GROUP_DESC * gdp;
U32 nBlockGroup, nBlock, nOffset;
U8 *pBlkBuf;
//Inode가 속한 Block Group을 알아냄
nBlockGroup = (nIno - 1) / sb->inodes_per_group;
gdp = pSbi->group_desc+nBlockGroup;
//Inode가 저장된 Block과 offset을 구함
nOffset = ((nIno - 1) % sb->inodes_per_group) * pSbi->inode_size;
nBlock = gdp->inode_table + (nOffset >> pSbi->ext2_block_size_bits);
nOffset &= (pSbi->blocksize - 1);
pBlkBuf=(U8*) malloc(pSbi->blocksize);
BlockIO(pSbi, nBlock, pBlkBuf, FALSE);
if(bWrite){ //inode 쓰기
memcpy(pBlkBuf + nOffset, pInode, sizeof(INODE));
BlockIO(pSbi, nBlock, pBlkBuf, TRUE); //읽거나 쓰기
}else { //inode 읽기
memcpy(pInode, pBlkBuf + nOffset, sizeof(INODE));
}
free(pBlkBuf);
}
//Super Block의 보조 구조체로 Supber Block을 이용하여 자주 쓰이는 값 저장
void InitSbInfo(SUPERBLOCK * sb, SBINFO * pSbi){
memset(pSbi, 0, sizeof(SBINFO));
pSbi->sb = sb;
pSbi->inode_size = sb->rev_level == EXT2_GOOD_OLD_REV ? EXT2_GOOD_OLD_INODE_SIZE : sb->inode_size;
pSbi->first_ino = EXT2_GOOD_OLD_FIRST_INO;
pSbi->blocksize = 1024 << sb->log_block_size;
pSbi->blocks_per_group = sb->blocks_per_group;
pSbi->inodes_per_group = sb->inodes_per_group;
pSbi->inodes_per_block = pSbi->blocksize / pSbi->inode_size;
pSbi->itb_per_group = pSbi->inodes_per_group/pSbi->inodes_per_block;
pSbi->desc_per_block = pSbi->blocksize/sizeof(GROUP_DESC);
pSbi->addr_per_block_bits = pSbi->blocksize / sizeof(U32);
pSbi->desc_per_block_bits = pSbi->blocksize/ sizeof(GROUP_DESC);
pSbi->group_count= (sb->blocks_count - sb->first_data_block + sb->blocks_per_group -1) /sb->blocks_per_group;
pSbi->lba_per_block = pSbi->blocksize/512; //블록 사이즈를 섹터 단위 (4K/512 = 8) 8개섹터가 1블럭
pSbi->ext2_block_size_bits = sb->log_block_size+10;
pSbi->group_desc = ReadGroupDesc(pSbi);
}
int SearchFile(SBINFO * pSbi, char *pszPath, int *pidir){ //주어진 경로의 파일을 Ext2(또는 Ext3) 파일시스템에서 찾는다.
char aszTmp[256];
char *pszStr = NULL;
U8 *pBuf = NULL;
INODE TmpInode;
DIRENTRY *pTmpDir = NULL;
int i, inum;
char* type;
strcpy_s(aszTmp,sizeof(aszTmp), pszPath);
pszStr = strtok_s(aszTmp, "/",&type);
//ROOT DIR
if(pszStr == NULL)
return EXT2_ROOT_INO;
inum = EXT2_ROOT_INO;
pBuf = (U8 *)malloc(pSbi->blocksize);
while(pszStr)
{
InodeIO(pSbi, inum, &TmpInode, FALSE);
if(TmpInode.i_mode & 0x4000 == 0){ //디렉토리인지 검사
free(pBuf);
return 0;
}
*pidir = inum;
BlockIO(pSbi, TmpInode.i_block[0], pBuf, FALSE);
pTmpDir = (DIRENTRY *)pBuf;
i = inum = 0;
while( i < pSbi->blocksize){ //블록내를 돌며 같은 디렉토리 찾기
if(strncmp(pszStr, pTmpDir->name, pTmpDir->name_len) == 0){
inum = pTmpDir->inode;
break;
}
i += pTmpDir->rec_len;
pTmpDir = (DIRENTRY *)((char*) pTmpDir + pTmpDir->rec_len);
}
if(!inum){ //해당 디렉토리를 찾지 못했다면 리턴
free(pBuf);
return 0;
}
pszStr = strtok_s(NULL, "/",&type);
}
free(pBuf);
return inum;
}
//파일 데이터를 읽어들이기 위해 pnPtr i_block 배열(pnPtr)의 Block 데이터를 읽어 드림
int ReadBlocks(SBINFO * pSbi, char *pData, int * pnPtr, int nLimitBlock, int nLimitSize){
int nReadSize = 0, nToRead, i;
U8 *pBuf = (U8*) malloc(pSbi->blocksize);
for(i = 0 ; i<nLimitBlock && nReadSize < nLimitSize ; i++)
{
BlockIO(pSbi, pnPtr[i], pBuf, FALSE);
nToRead = min(nLimitSize - nReadSize, pSbi->blocksize);
memcpy(pData+nReadSize, pBuf, nToRead);
nReadSize += nToRead;
}
free(pBuf);
return nReadSize;
}
//Ext2 파일시스템의 파일을 읽어들임
char * ReadExtFile(SBINFO *pSbi, INODE *pInode){
char *pData = (char *)malloc(pInode->i_size);
U8 *pBuf, *pBuf2;
U32 nReadSize = 0, int_per_block, i;
int_per_block = pSbi->blocksize/4;
nReadSize = ReadBlocks(pSbi, pData, (int*)(pInode->i_block), EXT2_NDIR_BLOCKS,pInode->i_size );
if(nReadSize == pInode->i_size)
return pData;
//간접 블록
pBuf = (U8*) malloc(pSbi->blocksize);
BlockIO(pSbi, pInode->i_block[EXT2_IND_BLOCK], pBuf, FALSE);
nReadSize += ReadBlocks(pSbi, pData+nReadSize, (int*)pBuf, int_per_block, pInode->i_size-nReadSize);
if(nReadSize == pInode->i_size){
free(pBuf);
return pData;
}
//이중 간접 블록
BlockIO(pSbi, pInode->i_block[EXT2_DIND_BLOCK], pBuf, FALSE);
pBuf2 = (U8*)malloc(pSbi->blocksize);
for(i=0; i<int_per_block ; i++){
BlockIO(pSbi, pBuf[i], pBuf2, FALSE);
nReadSize += ReadBlocks(pSbi, pData+nReadSize, (int*)pBuf, int_per_block, pInode->i_size - nReadSize);
if(nReadSize == pInode->i_size){
free(pBuf);
free(pBuf2);
return pData;
}
}
free(pBuf);
free(pBuf2);
return NULL;
}
void WriteLocalFile(char *pszFile, char *pBuf, U32 nSize){
FILE *pFile ;
fopen_s(&pFile,pszFile, "wb+");
fwrite(pBuf, 1, nSize, pFile);
fclose(pFile);
}
U32 main(int argc, char *argv[]){
PARTITION PartitionArr[50];
U32 nPartitionCnt = 0;
SUPERBLOCK *sb;
SBINFO sbi;
INODE Inode;
int inum = 0, idir;
char *pBuf = NULL;
if(argc == 1 || argc== 2){
printf("using : \"파일명\" \"대상 경로\" \n");
return 0;
}
//파티션 정보 읽어오기
memset(&PartitionArr, 0, sizeof(PARTITION) * 50);
nPartitionCnt = GetLocalPartition(1, PartitionArr);
nPstart = PartitionArr[0].start;
//super Block 얻기
sb = ReadSuperBlock();
InitSbInfo(sb, &sbi);
inum = SearchFile(&sbi, argv[1], &idir);
if(!inum) return 0;
InodeIO(&sbi, inum,&Inode, FALSE);
pBuf = ReadExtFile(&sbi, &Inode);
if(pBuf)
WriteLocalFile(argv[2], pBuf, Inode.i_size);
//자원 해제
if(pBuf)
free(pBuf);
free(sbi.group_desc);
free(sb);
return 0;
}
2) Ext2로 포맷 한 뒤 하나의 파일을 생성 하도록 하자. (USB를 이용한 경우)
포맷 하는 방법은 http://blog.naver.com/bitnang/70186337222
에서 Ext2로 포맷을 하고 파일을 생성하는 방법을 이용하거나 각자의 방법으로 포맷을 하도록 하면 된다.
아래 그림 처럼 USB를 ext2로 포맷을 하였다.
그리고 그림에는 없지만 마운트를 한 뒤 hello.txt를 리눅스 환경에서 작성하였다.
윈도우에서 ExtFS for Windows를 이용하여 열어보았다.
CMD를 실행할 때 관리자 권한으로 실행하여야 한다.
컴파일한 파일 [ext2에 저장된 파일명] [저장할 위치(디렉토리는 만들어 두어야 함)]
하게 되면 정상적이라면 아래와 같이 Ext2의 파일을 NTFS파일 시스템인 D드라이브로 복사가 된 것을 알 수 있다.
'Operating System' 카테고리의 다른 글
[파일시스템] EXT파일시스템 (0) | 2020.05.26 |
---|---|
[파일시스템] EXT3 파일시스템 (0) | 2020.05.26 |
[파일시스템] EXT2 파일시스템 (1/2) (0) | 2020.05.26 |
[파일시스템] FAT16 파일시스템 (3/3) (0) | 2020.05.26 |
[파일시스템] FAT16 파일시스템 (2/3) (0) | 2020.05.26 |