내용

 

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드라이브로 복사가  것을   있다.

 

 

+ Recent posts