내용
1. 하드 링크(Hard Link)
- Ext2 파일시스템에서 특정 파일이나 디렉토리를 다른 이름으로 가지고 싶거나, 다른 위치에서 접근을 쉽게 하고 싶을 경우 링크(Link) 기능을 사용할 수 있게 된다. 링크는 하드 링크와 소프트 링크(또는 심벌릭 링크) 두 종류 있다. 하드 링크에 대해 알아보자.
- 일반적으로 Ext2 파일시스템에서 '링크' 는 하드 링크를 가르킨다.
- 링크에 대한 설명을 들을 때 Include는 추가로 만들지 않고 링크 카운트만 증가하게 된다.
- 하드 링크를 생성할 때 데이터 구조에 2가지 업데이트가 일어나는데, 첫째로 해당 파일의 Inode 구조체에서 링크 카운트를 나타내는 Link Count 항목의 값이 1이 증가한다.
- 링크를 생성한 디렉토리 내에 새로운 디렉토리 엔트리가 추가되며, 이 엔트리가 가리키는 Inode 번호가 바로 원본 파일의 Inode를 가리키게 된다.
[하드링크]
- 기존 파일에 새로운 디렉토리 엔트리가 추가되면서 링크하고자 하는 원본 파일의 Inode를 가리키고, 해당 Inode의 링크 카운트를 1 증가시킨다. 하드링크는 하나의 Inode가 여러 개의 이름을 가진 것이라고 볼 수 있다.
- 그러나 제약 사항이 발생하는데 하드링크가 모두 해제되지 않는 한 원본 파일은 삭제될 수 없다. 즉 Link Count가 0이 되어야 파일을 삭제할 수 있게 되는 것이다. 또한 파일의 경로와 이름을 이용한 것이 아니라 Inode 번호를 이용하여 특정 Inode를 가리키고 있는 것이므로 같은 파일시스템의 같은 파티션안에 존재하는 파일이여야 한다. 이는 Inode 번호를 이용한 것이므로 구조상 당연한 제약이지만, 리눅스와 같이 디렉토리의 성격에 따라 파티션을 세부적으로 나누는 상황에서 이런 제약은 비효율 적일 것이다.
- 가장 대표적인 하드 링크는 특정 디렉토리 내의 '.'과 '..' 로 같은 폴더와 상위폴더를 가리키는 이 엔트리들은 하드 링크로 되어 있다. 따라서 디렉토리의 경우 최소 2개의 하드 링크를 가지게 되고, 삭제 시 파일을 삭제하는 명령으로 디렉토리를 삭제할 수 없는 이유가 여기 있게 된다.
2. 소프트 링크(Soft Link)
- 소프트 링크는 심벌릭 링크(Symbolic Link)라고 하며, 하드 링크의 제약 사항을 개선하기 위해 만들어졌다.
- 파일시스템의 다른 파일이나 디렉토리를 가리키는 것은 같지만, 하드 링크와 달리 소프트 링크가 만들어지는 파티션 또는 파일시스템과 그 위치가 달라도 가능하다.
- 소프트 링크는 일반 파일과 같이 디렉토리 엔트리와 Inode를 각기 가지고 있지만, 파일의 유형을 나타내는 File mode 항목에 소프트 링크는 0xA000 플래그가 ON되어 있다.
- 소프트 링크가 가리키는 파일명은 Inode의 구조를 파악 하면 알 수 있을 것이다. 일반 파일과 마찬가지로 Inode 구조체의 i_block 항목이 가리키는 데이터 블록을 읽어 들이면 파일 경로가 나오게 된다. 그러나 파일명의 길이가 60 Byte 이하라면 i_block 배열 안에도 기록될 수 있기 때문에 i_block 항목을 스트링으로 읽어 들이면 된다.
[소프트 링크]
- 위 그림의 경우 소프트 링크의 2가지 방식을 표현한 것이다. 문자열의 길이 '/dir/test/txt'이기 때문에 60Byte 이하이므로 B와 같은 방식으로 데이터 블록을 읽어오는 일은 발생하지 않지만 예를 든 것이다.
- 'A' 는 원본파일의 경로가 60Byte 이하인 경우 데이터 블록을 읽지 않고 원본 파일을 가리키는 것이고, 'B'는 원본 파일 경로가 60Byte를 넘는 경우 사용된다. 원본파일의 Inode를 직접 건드리는 작업이 없기 때문에 소프트 링크는 원본파일을 지우거나 기타 변경 작업을 함에 있어서 전혀 영향이 없다.
3. Inode 생성 과정
- Ext2 파일시스템에서 파일이나 디렉토리 등을 생성하려면 비어있는 Inode와 블록이 있는 블록 그룹이 있어야 한다.
- Inode를 생성할 공간이 있다고 하더라도 여러 그룹들 중 어떤 그룹을 선택해야 할지 살펴볼 필요가 있다. 그룹의 선택에 따라 저장장치의 I/O가 더 발생할 수도 있고, 전체적으로 단편화가 더 많이 일어날 수도 있기 때문에 성능면이나 사용 효율면에서 중장기 적으로 악 영향을 끼칠 수 있기 때문이다.
- 커널에 따라 사용되는 알고리즘은 달라질 수 있으나 여기서 나오는 대부분의 알고리즘의 커다란 틀은 비슷하다고 할 수 있다.
- 파일을 생성하는 경우 Inode를 선택하는 가장 첫 번째 조건은 상위 디렉토리의 블록 그룹과 같은 그룹이냐 하는 것이다. Ext2 파일시스템에서 블록 그룹을 나누어 놓은 이유가 바로 이 개념으로 관련된 디렉토리 및 파일들의 물리적인 위치를 최대한 가깝게 하도록 저장하여 I/O 효율을 높이고자 하는 것이다. 물리적인 공간이 부족하여 다른 그룹에 위치할 수밖에 없는 상황이면 틀리겠지만, 같은 그룹에 파일을 저장할만한 공간이 존재한다면 그 어떤 조건보다 높은 우선순위를 지니게 되는 것이다.
- 디렉토리를 생성하는 경우는 약간 다른데 디렉토리 생성시 블록 그룹 선택 우선순위는 상위 디렉토리에 크게 얽매이지 않는다. 디렉토리는 파일을 담을 수 있는 일종의 그릇 역할을 하는 것이므로 디렉토리 내에 파일을 담을 수 있는 여유 공간의 크기에 따라 우선순위를 달리하기 때문이다.
- 커널의 종류에 따라 알고리즘이 달라질 수 있으니 커널의 버전을 살펴보고 실제 소스를 참고하면 좋을 것이다. Fedora Core2 커널 파일시스템을 예로 들어 설명의 파일시스템을 예로 설명한다.
[Inode 생성과정]
- 커널에서 Inode를 생성하고자 하면 ext2_new_inode( ) 함수(linux/fs/ext2/ialloc/c)가 호출된다. 이 함수는 Inode를 생성하고자 하는 그룹을 결정하고, 실질적인 할당 및 초기화를 진행한다. 이때 Inode가 위치할 블록 그룹을 결정하는 과정이 생각보다 까다롭다.
- ext2_new_inode( ) 함수 내에서 Inode가 생성되기 전까지의 호출 과정으로 중요한 부분은 할당된 Inode가 위치할 블록 그룹을 선택하는 과정이다. 여러 가지 방법이 있는데 위 그림의 함수들을 통해 3가지 정도 방법을 알아보자.
- 블록 그룹을 선택하는 알고리즘은 디렉토리인 경우와 디렉토리가 아닌 경우에 따라 차이가 나는데 디렉토리가 정규 파일이나 소프트 링크와 달리 취급되는 이유가 상위디렉토리와 하위 디렉토리가 굳이 같은 블록 그룹에 있지 않아도 크게 영향을 미치지 않기 때문이다. 각각의 파일들은 가급적 상위 디렉토리와 같은 그룹에 있어야 저장장치 안에서 파일 데이터 블록을 읽어들이기 위한 이동 경로가 짧아지며, 단편화를 줄일 수 있는 등 이점이 있지만, 디렉토리의 경우 파일들을 묶는 상위 개념으로 생각할 수 있기 때문에 블록 그룹의 위치에 상대적으로 덜 민감하다.
- 디렉토리의 경우 Super Block의 마운트 옵션에 EXT2_MOUNT_LODALLOC(0x0002) 플래그가 ON 되어 있다면 find_group_orlov( ) 함수를 사용하고 그렇지 않으면 find_group_dir( ) 함수를 이용하여 블록 그룹을 선택한다.
- find_group_dir( )함수는 그룹 내에 비어 있는 Inode 개수가 평균치 이상인 것들 중 비어있는 블록의 개수가 가장 많은 그룹을 선택하는 간단한 알고리즘이다.
- find_group_orlov( )함수는 Orlov 라는 사람이 만든 함수로, 복잡하긴 하지만 여러가지 장점이 있다. 비어 있는 Inode와 블록들이 평균을 기준으로 하여 상대적으로 수치가 높은 것들 중 디렉토리의 개수가 가장 적은 것을 선택한다. 이러한 조건에 부합하지 않으면 랜덤으로 블록을 선택하고, 이때 다음 조건과 부합하지 않으면 또 다른 방식을 사용한다.
- 좀더 구체적으로 알아보자
1_ 그룹 내에 비어 있는 Inode가 있어야 한다.
2_ 사용되는 디렉토리의 개수 inode per group보다 적어야 한다.
3_ 비어있는 Inode의 개수와 블록의 개수가 평균치 이상 이어야 한다.
- 위 조건에 맞지 않으면 조건을 낮추어 0번부터 순차적으로 검색한다.
1_ 그룹내에 비어 있는 Inode가 잇어야 한다.
2_ 디렉토리의 개수가 '그룹 평균 디렉토리 수 + inode per group /16' 보다 작아야 한다.
3_ 비어 있는 Inode의 개수와 블록의 개수가 평균치에서 'inode 또는 block per group / 4'를 뺀 값보다 적어야 한다.
- 위 조건도 맞지 않으면 그룹들은 순차적으로 검색하여 Inode의 개수가 평균치 이상이면 선택되어, 여기에도 못 미치면 하나라도 비어 있는 Inode가 존재하는 그룹을 선택한다.
- find_group_other( )함수는 디렉토리가 아닌 일반 파일이나 소프트 링크 등의 Inode를 생성할 때 블록 그룹을 선택하는 함수이다. 최우선적으로 파일이 속한 디렉토리와 같은 그룹에 빈 Inode와 블록이 개수에 관계없이 존재하기만 한다면 해당 그룹이 선택된다.
- 블록 그룹 내에 비어있는 Inode나 블록이 없을 경우 다른 블록 그룹을 찾아 할당하는데 이때 커널은 이중 해시를 사용하여 할당하고자 하는 블록 그룹을 선택한다. 이중 해시는 현재의 블록 그룹 인덱스에서 2의 제곱 값을 이용해서 블록 그룹을 선택하는 알고리즘을 사용한다.
- 이렇게 검색한 그룹에 빈 Inode와 블록이 있다면 이를 사용하지만, 그렇지 않다면 0번 그룹부터 순차적으로 검색하여 빈 Inode가 있는 그룹을 선택한다. 이때 빈 블록에 대해서는 조건에 넣지 않는다.
- 이렇게 Inode가 위치할 블록 그룹을 선택한 뒤에는 Inode를 생성하고 구조체 내의 모든 값들을 초기화한다. 이때 시간 정보들은 생성된 현재 시간으로 설정되고, 삭제시간은 0으로 초기화 한다.
(시간 정보 : 파일 변경시간 modification time, 접근 시간 access time, Inode 업데이트 시간 change time)
[이 과정을 커널 소스를 통해 참조하고자 한다면 linux/fs/ext2/ialloc.c 경로의 소스를 통해 각 함수들을 확인할 수 있다.]
4. Inode Time 업데이트
- Ext2 파일시스템에는 시간과 관련된 4가지 항목이 있다.
M-time(The last modified time)
A-time(The last access time)
C-time(The last updated time of inode)
D-time(The deleted time)
A-time |
A-time 파일에서 Inode를 읽어 들이는 시점에 업데이트 되며 시스템 내의 프로세스가 동작하면서 디렉토리나 파일에 접근해서 읽어 들일 때가 이러한 경우에 해당한다. 그러나 동일한 볼륨에 파일을 옮길 때는 A-time이 업데이트 되지 않는다. 다른 볼륨에 이동 시에는 데이터 블록을 복사하는 과정에 해당하므로 A-time이 업데이트 된다. 디렉토리의 경우 하위 파일이나 디렉토리들이 사용되는 시점에 업데이트되며, 단순히 디렉토리의 내용을 출력하는 명령 등에는 업데이트 되지 않는다. 소스 레벨에서 보면 'linux/fs/ext2/ialoc.c' 파일의 ext2_new_inode( )함수에서 Inode가 생성되는 시점에 A-time이 생성되는 시간으로 설정되며, 파일시스템 사용 시에는 'linux/fs/ext2/inode.c' 파일 내에서 Inode를 읽어 들이는 함수인 ext2_read_inode( )와 파일을 쓰는 시점에 호출되는 ext2_update_inode( )함수에서 A-time이 업데이트 된다. |
M-time |
파일이나 디렉토리의 내용이 변경되는 시점에 업데이트 된다. 파일 이동이 있는 상황에서는 파일의 내용이 변경되는 상황이 아니므로 M-time에 그 시간이 반영되지는 않는다. 물론 복사의 경우 파일을 생성하여 내용을 쓰기 때문에 Inode가 생성되면서 M-time이 현재 시간으로 설정된다. 디렉토리의 경우도 파일과 그게 다르지 않으며, 파일의 데이터 블록에 해당되는 부분이 디렉토리에서는 디렉토리 엔트리의 해당하므로 엔트리가 변경되는 시점이 바로 M-time이 업데이트되는 시점이다. 하위파일이나 링크 등이 생성, 삭제되었을 경우 업데이트 된다. |
C-time |
파일이나 디렉토리를 가리키는 Inode 정보가 변경되는 상황에 업데이트가 일어나며, 자주는 아니지만 Inode의 정보를 변경할 일이 있기 마련이다. 파일 권한의 설정으로 Inode의 File mode 항목이 변경되거나 소유자나 그룹 설정을 할 수도 있는데 이때 Inode가 변경되어 M-time이 업데이트 되는 것이다. |
D-time |
파일이 생성되는 시점과 삭제되는 시점에 업데이트 되며 생성될 때는 0으로 초기화되고, 삭제될 때는 현재 시간으로 설정된다. |
[Inode 시간 정보 업데이트 타이밍]
업데이트 시점 |
해당 항목 |
파일이 생성될 때 |
M, A, C, D |
파일 또는 디렉토리의 내용이 변경될 때 |
M, C |
파일 복사 시 |
원본 파일 및 상위 디렉토리 : A 복사 파일 : M, A, C 복사 파일의 상위 디렉토리 : M, C |
파일 이동 시 |
이동 전의 상위 디렉토리 : M, A, C 이동 후의 상위 디렉토리 : M, C 이동 파일 : C |
5. 파일 삭제
- Ext2 파일시스템에서 파일을 삭제하는 과정을 파악 해보면 삭제 과정은 크게 어렵지 않지만 커널에서 처리하는 파일시스템의 동기화 부분 처리가 손이 많이 가는 작업이다. 그러나 여기서는 파일시스템에서 삭제 과정을 살펴 보자.
- 삭제 과정을 단순히 생각하면 삭제 하고자 하는 파일이나 디렉토리에 할당된 Inode와 Block을 다시 사용 가능하도록 하면 될 것이다.
- 우선 사용자 프로세스 쪽에서 파일을 삭제하는 명령이 오게 되면 unlink 시스템 콜을 통해 VFS(가상 파일시스템)의 generic_delete_inode( ) 함수를 거쳐 Ext2 파일시스템의 ext2_delete_inode( )함수가 호출된다. 이 과정에서 하드 링크의 수를 파악하여 하드 링크 수가 0일 때 가능할 것이다.
- ext2_delete_inode( )함수에서는 Inode가 사용 가능한지(EXT2_BAD_INO 플래그가 ON되면 RETURN)확인하고 i_dtime 항목을 업데이트 한다. 정상적인 Inode라면 본격적으로 파일에 할당된 Inode와 블록을 삭제하게 된다.
[디렉토리 엔트리 삭제 과정]
- 삭제 과정을 살펴보면 삭제하고자 하는 파일을 디렉토리 엔트리에서 찾아내 삭제 작업을 진행한다. 이 과정에서 디렉토리 엔트리의 inode 항목(Inode 번호)을 0으로 초기화하여 엔트리와 Inode 사이의 연결고리를 끊는다. 그리고 디렉토리 내에서도 엔트리가 검색되지 않도록 바로 앞의 엔트리의 크기 값을 현재의 엔트리만큼 키워서 삭제하고자 하는 파일의 앞 엔트리가 삭제 파일의 뒤 엔트리를 가리키도록 한다.
- 위 그림처럼 삭제되는 엔트리의 Inode 번호와 앞 엔트리의 Record Length 항목의 값이 달라지게 되며 할당되어 있던 데이터 블록과 Inode를 해제시켜 다시 사용가능 하도록 만든다.
[파일 삭제 명령 프로세스]
- 위 그림은 실제 커널에서 사용되는 함수를 예로 들은 삭제 프로세스를 나타낸 것인데 리눅스 쉘에서는 파일 삭제 명령이 곧바로 ext2_delete_inode( ) 함수를 호출하는 것이 아니라 간단한 절차를 거치게 되지만, 실제 삭제 과정이 수행되는 시점은 ext2_delete_inode( )함수부터 이다.
- Inode와 블록을 사용 가능하도록 해제한다는 것은 해당 영역을 초기화 하는 것이 아니라 Block Bitmap과 Inode Bitmap에서 해당 Bit를 OFF 시킨다는 의미이다. 이점을 염두하여 커널 소스를 참고하도록 하면 도움이 될 것이다.
6. 파일 복구
- Ext2 파일시스템에서 파일 복구가 가능한 이유는 파일을 지울 때 Inode를 삭제하는 과정에서 실제 Inode 구조체 데이터는 삭제하지 않기 때문이다. 따라서 데이터 블록을 가리키는 직간접 포인터들이 남아있다. Inode에는 삭제시간(i_dtime)항목도 존재 하기 때문에 언제 삭제된 데이터인지도 알 수 있어서 시간 조건적으로 복구를 시도하는 것도 가능하다. 또한 용량 정보도 남아있기 때문에 복구하고자 하는 파일이 어떤 Inode인지 구분하는 것이 생각보다 어렵지 않게 된다. 물론 다른 Inode에서 할당하여 사용한다면 복구가 어렵겠지만 정상적으로 파일 데이터를 복구했다 하더라도 Inode를 이용하여 디렉토리 엔트리를 찾는 것은 사실상 어려워 파일명까지 복구하는 것은 힘들다.
- 만약 데이터 복구 시도를 할 경우 가급적 다른 파티션에 파일을 쓰는 것이 좋은데 이는 복구하는 도중에 원래 복구하고자 하던 파일에 덮어 써버릴 수 있으니 주의 하도록 하자.
- Ext3의 경우 저널링 기능이 추가되어 파일의 복구가 Ext2처럼 간단하진 않다. Ext3는 파일 삭제 시 Inode의 데이터 블록 포인터 배열(i_block 항목)을 삭제해 버리기 때문이다. 만약 데이터 복구를 해야 한다면 파일을 쓸 때 상위 디렉토리와 같은 그룹을 사용하고자 하는 Ext2 파일시스템의 특성을 이용하여 기존의 파일이 위치했던 디렉토리의 블록 그룹번호를 알아낸다. 그 뒤 해당 그룹 내에서 블록 그룹을 참고하여 비어있는 블록을 읽어 들여 원하던 파일 내용인지를 확인해 보는 방법을 써 볼 수 있다.
6. 실습
아래 소스는 최대한 간단한 방법으로 구조를 이해하는데 초점이 맞춰 진 것이라 실제 사용하는데 문제가 많다.
시작섹터와, 디바이스 번호를 정확히 지정하기 바란다.
HDD_read, HDD_write, GetLocalPartition 함수에서 자신의 PC의 연결된 디바이스 번호를 정확히 지정해야한다.
이전 소스들 참고하길 바란다.
[Partition.h]
#include <Windows.h>
#include <stdio.h>
#include <string.h>
#include <io.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
[ReadExt2.cpp]
#include <time.h>
#include "Ext2_fs.h"
int nPstart = 0;
SUPERBLOCK* ReadSuperBlock();
void InodeIO(SBINFO * pSbi, int nIno, INODE *pInode, BOOL bWrite);
BOOL BlockIO(SBINFO * pSbi, U32 block, U8 *pBlkBuf, BOOL bWrite);
char * ReadExtFile(SBINFO *pSbi, INODE *pInode);
int ReadBlocks(SBINFO * pSbi, char *pData, int * pnPtr, int nLimitBlock, int nLimitSize);
void WriteLocalFile(char *pszFile, char *pBuf, U32 nSize){
FILE *pFile ;
fopen_s(&pFile,pszFile, "wb+");
fwrite(pBuf, 1, nSize, pFile);
fclose(pFile);
}
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;
}
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;
}
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;
}
//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);
}
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);
}
//딜렉토리 엔트리 삭제
BOOL DeleteDirEntry(SBINFO *pSbi, int idir, U32 inum){
INODE inode;
DIRENTRY *pPrevDir = NULL, *pCurDir = NULL;
U8 *pBuf;
int i = 0;
pBuf = (U8*)malloc(pSbi->blocksize);
InodeIO(pSbi, idir, &inode, FALSE);
BlockIO(pSbi, inode.i_block[0], pBuf, FALSE);
pPrevDir = pCurDir = (DIRENTRY *)pBuf;
//블록 내를 돌며 같은 디렉토리를 찾는다.
while( i < pSbi->inode_size){
if(pCurDir->inode ==inum) {
//삭제하고자 하는 inode 바로 앞의 inode 크기를 늘려서 다음 inode를 가리키도록 한다.
pPrevDir->rec_len += pCurDir->rec_len;
BlockIO(pSbi, inode.i_block[0], pBuf, TRUE);
free(pBuf);
return TRUE;
}
i += pCurDir->rec_len;
pPrevDir = pCurDir;
pCurDir = (DIRENTRY *)((char*)pCurDir + pCurDir->rec_len);
}
free(pBuf);
return FALSE;
}
//Inode와 종속된 블록을 해제한다.
void DeleteInodeNBlocks(SBINFO *pSbi, int inum){
SUPERBLOCK *sb = pSbi->sb;
GROUP_DESC * gdp;
INODE inode;
U8 *pBitmap; //비트맵 버퍼
U32 block_group ;
int ipos, bpos;
int bytes, bits, i;
//Inode를 읽고 삭제 시간을 표시한 뒤 파일시스템에 씀
InodeIO(pSbi, inum, &inode, FALSE);
inode.i_dtime = time(NULL);
InodeIO(pSbi, inum, &inode, TRUE);
block_group = (inum -1) /sb->inodes_per_group;
gdp = pSbi->group_desc + block_group ;
pBitmap = (U8 *)malloc(pSbi->blocksize);
//Inode 비트맵에서 위치를 계산하여 Inode를 해제한다.
BlockIO(pSbi, gdp->inode_bitmap, pBitmap, FALSE);
ipos = inum - (block_group * sb->inodes_per_group);
bytes = ipos / 8;
bits = ipos %8;
pBitmap[bytes] &= ~(1<< bits);
//수정한 Inode 비트맵을 파일에 씀
BlockIO(pSbi, gdp->inode_bitmap, pBitmap, TRUE);
gdp->free_inode_count++;
sb->free_inodes_count++;
//파일의 데이터를 담고 있는 블록 삭제
//참고로, 간접 블록까지 삭제하는 로직은 더 구현해야 한다.
BlockIO(pSbi, gdp->block_bitmap, pBitmap, FALSE);
for(i = 0; i<EXT2_NDIR_BLOCKS && inode.i_block[i]; i++){
bpos = inode.i_block[i] -(block_group * sb->blocks_per_group);
bytes = bpos / 8;
bits = bpos % 8 ;
pBitmap[bytes] &= ~(1 <<bits);
gdp->free_blocks_count++;
sb->free_blocks_count++;
}
BlockIO(pSbi, gdp->block_bitmap, pBitmap, TRUE);
free(pBitmap);
//수정한 내용 파일시스템에 업데이트
SyncGroupDesc(pSbi);
SyncSuperBlock(pSbi);
}
//파일 데이터를 저장하는데 적학합 블록 그룹을 찾음
static int FindGroupForFile(SBINFO *pSbi, int parent_inum){
SUPERBLOCK *sb = pSbi->sb;
GROUP_DESC *desc;
//Inode가 속한 Block Group을 알아냄
int parent_group = (parent_inum -1)/sb->inodes_per_group;
int ngroups = pSbi->group_count;
int group, i;
//상위 디렉토리가 속한 블록 그룹에 공간이있다면 우선적으로 선택
group = parent_group;
desc = pSbi->group_desc + group;
if(desc && desc->free_inode_count && desc->free_blocks_count)
goto found;
//2중 해시를 이용한 블록 그룹 선택
group = (group + parent_inum) % ngroups;
for( i = 1 ; i < ngroups ; i <<= 1){
group += i;
if(group >=ngroups)
group -= ngroups;
desc = pSbi->group_desc + group;
if(desc && desc->free_inode_count && desc->free_blocks_count)
goto found;
}
//빈 Inode 공간이 있는 블록 그룹을 순차적으로 검색하여 선택
group = parent_group;
for( i = 0 ; i<ngroups ; i++){
if(++group >= ngroups)
group = 0;
desc = pSbi->group_desc +group;
if(desc && desc->free_inode_count)
goto found;
}
return -1;
found:
return group;
}
//Inode bitmap에 할당 후 번호 반환
int AllocInode(SBINFO *pSbi, int group){
int *pBitmap; //Inode Bitmap
int i, j;
int int_per_block = pSbi->blocksize/4;
pBitmap = (int*)malloc(pSbi->blocksize);
BlockIO(pSbi, pSbi->group_desc[group].inode_bitmap, (U8*)pBitmap, FALSE);
if(group ==0)
j = EXT2_GOOD_OLD_FIRST_INO + 1;
else
j = 0;
for(i = 0; i<int_per_block ; i++){
if(pBitmap[i] != 0xFFFFFFFF){
int value = pBitmap[i];
for( ; j<32 ; j++){
if( ((value >> j) & 0x01) ==0){
value |= (1 << j);
pBitmap[i] = value;
BlockIO(pSbi, pSbi->group_desc[group].inode_bitmap,(U8*) pBitmap, TRUE);
free(pBitmap);
//Inode 번호 반환
return (pSbi->inodes_per_group * group) + (i * 32 + j);
}
}
j = 0;
}
}
free(pBitmap);
return 0;
}
//데이터를 저장할 블록을 할당하고, Bitmap을 업데이트
int AllocBlock(SBINFO *pSbi, int group, int alloc_cnt, int *pBlock){
int *pBBitmap; //Inode Bitmap
int i, j;
int int_per_block = pSbi->blocksize/4;
int cnt = 0;
int offset = pSbi->blocks_per_group * group;
pBBitmap = (int*) malloc(pSbi->blocksize);
BlockIO(pSbi, pSbi->group_desc[group].block_bitmap, (U8*)pBBitmap, FALSE);
for(i = 0 ; i < int_per_block ; i++){
block_alloc_start:
if(pBBitmap[i] != 0xFFFFFFFF) {
int value = pBBitmap[i];
for(j = 0 ; j<32 ; j++) {
if( ((value >> j ) & 0x01) == 0){
pBlock[cnt++] = offset + ( i * 32 + j);
value |= (1 << j);
pBBitmap[i] = value;
//필요한 블록을 모두 업데이트 했는지 체크
if(cnt == alloc_cnt)
goto block_alloc_end;
else
goto block_alloc_start;
}
}
}
}
return 0;
block_alloc_end:
BlockIO(pSbi, pSbi->group_desc[group].block_bitmap , (U8*)pBBitmap, TRUE);
free(pBBitmap);
pSbi->sb->free_blocks_count = cnt;
pSbi->group_desc[group].free_blocks_count -=cnt;
return cnt;
}
//Inode를 생성하는데 필요한 영역을 할당하고, 기본 항목 설정
int NewInode(SBINFO *pSbi, int dir_inum, int *pgroup, INODE *inode){
int group = FindGroupForFile(pSbi, dir_inum);
int ino = AllocInode(pSbi, group);
*pgroup = group;
memset(inode , 0, sizeof(INODE));
inode->i_atime = inode->i_ctime = inode->i_mode = time(NULL);
inode->i_mode = 0x81a4; //Inode 모드는 편의상 루트 권한으로 지정함
inode->i_links_count = 1;
pSbi->sb->free_inodes_count--;
pSbi->group_desc[group].free_inode_count--;
return ino;
}
//주어진 경로에 파일이 위치하도록 디렉토리 엔트리 추가
//편의상 디렉토리 내의 전체 엔트리 크기는 1Block 이하라고 가정
void AllocDirentry(SBINFO *pSbi, char *pszDest, int dir_inum , int inum, INODE *pInode){
DIRENTRY direntry;
DIRENTRY *pCurDir, *pPrevDir;
U8 *pBuf;
int slashmark = 0, new_rec_len, i;
int pathlen = strlen(pszDest);
//파일명을 가져오기 위해 파일 경로의 마지막 '/' 위치를 알아냄
for( i = 0 ; i<pathlen ; i++){
if(pszDest[i] == '/' )
slashmark = i +1;
}
//디렉토리 엔트리 생성
memset(&direntry , 0 , sizeof(DIRENTRY));
direntry.inode = inum;
direntry.file_type = 1;
direntry.name_len = pathlen - slashmark;
direntry.rec_len = 8 + ((direntry.name_len /4 ) +1 ) *4;
strcpy_s(direntry.name,sizeof(direntry.name), pszDest + slashmark);
//디렉토리 아이노드와 해당 엔트리 블록을 읽어 들인다.
InodeIO(pSbi, dir_inum, pInode , FALSE);
pBuf = (U8 *) malloc(pSbi->blocksize);
BlockIO(pSbi, pInode->i_block[0], pBuf, FALSE);
pCurDir = (DIRENTRY *)pBuf;
i = 0;
while( i < pSbi->blocksize){
i += pCurDir->rec_len;
pPrevDir = pCurDir;
pCurDir = (DIRENTRY *)((U8*)pCurDir + pCurDir->rec_len);
}
new_rec_len = 8 + ((pPrevDir->name_len/4)+1)*4;
//마지막 엔트리의 크기를 수정하고, 추가되는 엔트리 블록의 가장 끝을 가리킴
direntry.rec_len = pPrevDir->rec_len = new_rec_len;
pPrevDir->rec_len = new_rec_len;
memcpy(((U8*)pPrevDir + pPrevDir->rec_len), &direntry, direntry.rec_len);
BlockIO(pSbi, pInode->i_block[0], pBuf, TRUE);
free(pBuf);
}
//로컬 영역의 파일을 Ext2 파일시스템으로 복사
void CopyFileLocalToExt(SBINFO * pSbi, char *pszSrcPath, char *pszDest){
INODE inode;
int block[EXT2_NDIR_BLOCKS] = {0,};
int dir_inum = 0, inum = 0, group, file_size, blks;
int write_size = 0, towrite, i;
U8 *pFileData;
printf("%s\n%s\n",pszSrcPath, pszDest);
//로컬 파일을 읽어 들임
FILE *pFile ;
fopen_s(&pFile,pszSrcPath,"rb+");
file_size = filelength(fileno(pFile));
blks = (file_size / pSbi->blocksize) + ((file_size % pSbi->blocksize) > 0 ? 1 : 0);
pFileData = (U8*)malloc(blks * pSbi->blocksize);
fread(pFileData, 1, file_size, pFile);
fclose(pFile);
//간단한 테스트를 위해 간접 블록까지 사용하지 않는 파일 사용
if(file_size > pSbi->blocksize * EXT2_NDIR_BLOCKS)
return ;
//디렉토리를 구하기위한 것이므로 반환 값은 필요 없다.
SearchFile(pSbi, pszDest, &dir_inum);
inum = NewInode(pSbi, dir_inum, &group, &inode);
inode.i_size = file_size;
AllocBlock(pSbi,group,blks,block);
memcpy(inode.i_block, block, EXT2_NDIR_BLOCKS);
InodeIO(pSbi, inum, &inode, TRUE);
SyncSuperBlock(pSbi);
SyncGroupDesc(pSbi);
//읽어온 파일을 Ext2 파일시스템의 해당 블록에 씀
for(i = 0 ; write_size < file_size ; i++){
towrite = min(file_size - write_size, pSbi->blocksize);
BlockIO(pSbi, block[i], pFileData + write_size, TRUE);
write_size += towrite;
}
AllocDirentry(pSbi, pszDest, dir_inum, inum, &inode);
}
U32 main(int argc, char* argv[]){
PARTITION PartitionArr[50];
U32 nPartitionCnt = 0;
SUPERBLOCK *sb;
SBINFO sbi;
int inum = 0, idir;
U8 *pBuf = NULL;
if(argc ==1)
return 0;
//파티션 정보 읽어오기
memset(&PartitionArr, 0, sizeof(PARTITION) *50);
nPartitionCnt = GetLocalPartition(1, PartitionArr);
nPstart = PartitionArr[0].start;
//슈퍼블록 얻기
sb = ReadSuperBlock();
InitSbInfo(sb, &sbi);
if(strcmp(argv[1], "c" ) == 0){
CopyFileLocalToExt(&sbi, argv[2], argv[3]);
}
else if(strcmp(argv[1], "d") == 0){
inum = SearchFile(&sbi, argv[2], &idir);
if(!inum)
return 0;
DeleteDirEntry(&sbi, idir, inum);
DeleteInodeNBlocks(&sbi, inum);
}
if(pBuf)
free(pBuf);
free(sbi.group_desc);
free(sb);
return 0;
}
d드라이브에 test.txt라는 파일을 생성한 것을
Ext2 파일시스템으로 포맷한 USB에 /test로 저장한 것이다.
복사한 파일을 삭제한 것이다.
막 포맷을 한 상태에서 실행을 해야 수월하게 실행될 것이다.
파일시스템에서 버퍼 캐시를 이용하지 않고 있고, 모든 Inode나 데이터 블록을 읽어 들이고자 할 때 메모리릉 할당하고, 저장장치의 I/O를 통해 데이터를 읽어 들여야 한다.
그리고 동기화 처리가 되지 않아 멀티태스킹 시에 문제가 발생할 것이다. 또한 메타 데이터와 파일 데이터의 저장순서도 실제 커널 소스처럼 구현이 되지 않아있다.
위의 소스를 수정하여 탐색기나 응용프로그램을 제작하거나 파일시스템을 읽고 쓰는 디바이스 드라이버를 제작해보면 좋을 것이다.
'Operating System' 카테고리의 다른 글
[리눅스] 파일 디스크립터 (0) | 2020.12.04 |
---|---|
[커널] Block I/O Layer (0) | 2020.05.27 |
[파일시스템] EXT3 파일시스템 (0) | 2020.05.26 |
[파일시스템] EXT2 파일시스템 (2/2) (0) | 2020.05.26 |
[파일시스템] EXT2 파일시스템 (1/2) (0) | 2020.05.26 |