리눅스에서 파일과 디렉토리 시스템을 다루는 방법을 알아보자
LINUX 파일 시스템의 구조에 대해서는 다음 포스팅을 참고하면 좋다.
LINUX is not UNIX 이지만 기본적으로는 UNIX의 구조를 따르기 때문이다.
Implementing File System
운영체제에서 file system의 구현은 다음과 같이 두 가지의 형태이다. 1. On-disk structure hard-disk 라고 불리는 secondary storage에 있는데 2차 저장장치에 구현된 구조이다. 2. In-memory structure memory..
josushell.tistory.com
File Status
# include <sys/types.h>
# include <sys/stat.h>
int stat(char *pathname, struct stat *buf);
함수 명세
return | 성공 시 0, 실패 시 -1 |
description | pathname에 해당하는 파일을 읽어서, 두번째 파라미터 구조체에 상태를 넘긴다. |
파라미터
char *pathname | 파일 명 |
struct stat *buf | 상태를 저장할 구조체 (시스템에 구현되어 있음) |
stat 구조체는 이미 시스템에 구현되어 있는 구조체이다. 따라서 <sys/stat.h> 를 include함으로써 선언하고 사용할 수 있다.
이때 구조체에 저장되는 정보가 바로 i-node 정보들이다.
int fstat(int fd, struct stat *buf);
이 함수는 파라미터로 파일명 대신에 file descriptor를 전달하는 것이다. 그 외에는 stat과 다르지 않으므로 설명 생략
(따라서 open, fopen으로 파일을 먼저 열어야 한다.)
int lstat(char *pathname, struct stat *buf);
이 함수 또한 stat()과 비슷하게 동작한다. 하지만 link에 대해서는 다르다.
stat() 함수는 link를 통해 연결되는 파일의 정보를 가져오는 반면
lstat() 함수는 link 자체에 대한 정보를 가져온다.
그걸 제외하고는 똑같은 동작이기 때문에 생략한다.
그럼 도대체 이렇게 받아온 stat 구조체에는 어떤 정보들이 있을까?
struct stat
man 페이지를 통해 확인 가능한 stat 구조체는 다음과 같다.
struct stat
{
mode_t st_mode;
ino_t st_ino;
dev_t st_dev;
dev_t st_rdev;
nlink_t st_nlink;
uid_t st_uid;
gid_t st_gid;
off_t st_size;
time_t st_atime;
time_t st_mtime;
time_t st_ctime;
long st_blksize;
long st_blocks;
}
각각에 대한 설명은 다음과 같다.
st_mode
: 파일의 타입과 모드, 즉 권한이다.
st_ino
: i-node number
st_dev
: device number
st_rdev
: special file에 대한 device number. 이때 special file은 regular file이 아닌 모든 파일이다.
st_nlink
: link의 수
st_uid
: owner의 user ID
st_gid
: owner의 group ID
st_size
: regular file에 대해서 byte로 표현된 파일 크기
st_atime
: 최근 접속 시간
st_mtime
: 최근 수정 시간
st_ctime
: file status가 마지막으로 변경된 시간
st_blksize
: best I/O block size
st_blocks
: 512-bytes block이 할당된 수 (0.5K)
Example
표준 입력으로 파일명을 입력하면 이를 받아 정보를 출력하는 프로그램을 작성하자.
이때 file type macro가 사용되었다.
#include <stdio.h> // printf
#include <stdlib.h> // exit
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
struct stat statbuf;
char *mode;
if (argc != 2)
{
printf("Usage: %s, source file\n", argv[0]);
return 0;
}
if (lstat(argv[1], &statbuf) < 0)
{
perror("lstat");
exit(0);
}
if (S_ISREG(statbuf.st_mode))
mode = "regular";
else if (S_ISDIR(statbuf.st_mode))
mode = "directory";
else if (S_ISCHR(statbuf.st_mode))
mode = "character special";
else if (S_ISFIFO(statbuf.st_mode))
mode = "FIFO";
else if (S_ISLNK(statbuf.st_mode))
mode = "symbolic link";
else if (S_ISSOCK(statbuf.st_mode))
mode = "socket";
printf("===== %s: %s =====\n", argv[1], mode);
printf("st_mode = %d\n",statbuf.st_mode);
printf("st_ino = %llu\n", statbuf.st_ino);
printf("st_dev = %d\n", statbuf.st_dev);
printf("st_rdev = %d\n", statbuf.st_rdev);
printf("st_nlink = %hu\n", statbuf.st_nlink);
printf("st_uid = %u\n", statbuf.st_uid);
printf("st_gid = %u\n", statbuf.st_gid);
printf("st_size = %lld\n", statbuf.st_size);
printf("st_atime = %ld\n", statbuf.st_atime);
printf("st_mtime = %ld\n", statbuf.st_mtime);
printf("st_ctime = %ld\n", statbuf.st_ctime);
printf("st_blksize = %d\n", statbuf.st_blksize);
printf("st_blocks = %lld\n", statbuf.st_blocks);
}
File type macro
리눅스에서 ls -al 명령어를 치게 되면 맨 앞에 파일 타입과 권한 정보들이 뜨게 된다.
이때 맨 앞에 뜨는 -, r, d 등등은 파일 타입 매크로를 통해 표시되는 정보들이다.
S_ISREG() // regular file
S_ISDIR() // directory file
S_ISCHR() // character special file, 즉 1byte 단위로 통신
S_ISBLK() // block special file, 즉 block 단위로 통신
S_ISFIFO() // FIFO 또는 pipe
S_ISLNK() // symbolic link
S_ISSOCK() // socket
여기서 생소한 파일 타입은 아마 FIFO, pipe 일 것 이다. 이 형태의 파일은 데이터 저장등의 목적이 아니라 그저 프로세스 간의 통신을 위해 사용된다. 즉 IPC 용도이다. 이는 뒤에 ipc 포스팅에서 정리할 예정
표준 입력으로 파일명을 주게 되면 결과는 다음과 같다.

file permission
# include <sys/stat.h>
# include <sys/types.h>
mode_t umask(mode_t cmask);
함수 명세
return | 기존에 설정되있던 umask 값 |
description | 파일을 생성할 때 파일의 권한을 설정한다. |
파라미터
mode_t cmask | bit field mask |
cmask로 가능한 매크로는 다음과 같다.
S_ISUID, S_ISGID
S_IRUSR, S_IWUSR, S_IXUSR // user의 R(read), W(write), X(execute)
S_IRGRP, S_IWGRP, S_IXGRP // group의 RWX
S_IROTH, S_IWOTH, S_IWOTH // other의 RWX
이 bit field mask는 mask 라는 이름처럼 파라미터로 넘어간 권한을 갖지 않도록 한다.
즉 특정 비트를 끄고 umask를 생성할 수 있다.
예를 들면, umask(0) 이라고 하면 아무것도 씌우지 말라는 뜻이므로 0666 즉 모두에게 read, write 권한을 부여한다.
# include <sys/types.h>
# include <sys/stat.h>
int chmod(char *pathname, mode_t mode);
함수 명세
return | 성공 시 0, 실패 시 -1 |
description | pathname에 해당하는 파일의 권한을 mode로 변경한다. |
파라미터
char *pathname | 파일 명 |
mode_t mode | 변경하고자 하는 mode |
Example
test 라는 이름의 파일을 다음과 같이 user, group에게만 read, write 권한을 주고 생성한다. 그 후 chmod로 0755 권한을 주는 예시이다.
chmod 의 인자로는 2진수의 비트값을 주어도 된다. 예를 들면 RWX 권한을 2진수 비트값으로 표현하면 7이다. (4+2+1)
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
if (creat("test", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) < 0)
exit(0);
if (chmod("test", 0755) < 0)
exit(0);
}
실행결과는 다음과 같다.

맨 마지막에 chmod() 를 통해서 755 권한을 주었기 때문에 755 권한을 가진 test 파일이 생겼다.
link file
# include <unistd.h>
int link(char *existingpath, char *newpath);
함수 명세
return | 성공 시 0, 실패 시 -1 |
description | 기존 파일(existingpath)과 새로운 파일(newpath)을 hard link로 연결하여 생성한다. |
파라미터
char *existingpath | 기존 파일 명 |
char *newpath | 새로 만들 파일 명 |
# include <unistd.h>
int symlink(char *actualpath, char *sympath);
함수 명세
return | 성공 시 0, 실패 시 -1 |
description | 기존 파일(actualpath)과 새로운 파일(sympath)을 symbolic link로 연결하여 생성한다. |
파라미터
char *actualpath | 기존 파일 명 |
char *sympath | 새로 만들 파일 명 |
hard link vs symbolic link
hard link는 link 파일을 만들게 되면 원본 파일과 동일한 i-node를 가진다.
반면에 symbolic link는 원본파일의 이름을 가리키는 링크일 뿐이므로 i-node 정보는 다르다.
shell command로는 ln / ln -s 에 해당한다.
Reading Directory
# include <sys/types.h>
# include <dirent.h>
DIR *opendir(char *pathname);
함수 명세
return | 성공 시 DIR 포인터, 실패 시 NULL |
description | directory를 열어서 하위 파일 정보를 읽고 구조체에 저장한다. |
파라미터
char *pathname | 디렉토리 명 |
struct dirent *readdir(DIR *dp);
함수 명세
return | 성공 시 dirent 구조체 포인터, 실패 또는 directory 끝에서 NULL |
description | DIR 포인터에 있는 하나의 file entry에 대한 정보를 구조체에 담는다. |
파라미터
DIR *dp | 읽고자 하는 디렉토리 포인터 |
dirent 구조체 또한 stat과 마찬가지로 시스템에 정의되어 있다.
struct dirent
{
ino_t d_ino; // i-node number
char d_name[MAX + 1] // file name (Null terminated)
}
void rewinddir(DIR *dp)
함수 명세
return | nothing |
description | DIR 포인터를 directory의 맨 처음으로 옮겨준다. |
파라미터
DIR *dp | 읽고자 하는 디렉토리 포인터 |
이 system call은 rewind()와 같은 역할이다.
int closedir(DIR *dp)
함수 명세
return | 성공 시 0, 실패 시 -1 |
description | directory를 닫는다. |
파라미터
DIR *dp | 닫고자 하는 디렉토리 포인터 |
Example
이 directory를 다루는 system call들을 사용하여 ls -al command를 구현해보자.
이때 userid, gid 를 통해서 user name, group name을 가져오는 함수는 getpwuid, getgrgid 이다.
mtime을 보기 좋게 표현하기 위해 formatter를 사용했다. gmtime, strftime으로 변환 가능하다.
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <grp.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <dirent.h>
#define FILE_MAX 256
void bubble_sort(char **arr, int size)
{
char temp[FILE_MAX];
for(int i = size - 1;i > 0;i--)
{
for(int j = 0;j < i;j++)
{
if (strcmp(arr[j], arr[j + 1]) > 0)
{
strcpy(temp, arr[j]);
strcpy(arr[j], arr[j + 1]);
strcpy(arr[j + 1], temp);
}
}
}
}
void file_mode(mode_t mode)
{
if (S_ISREG(mode))
printf("-");
else if(S_ISDIR(mode))
printf("d");
else if(S_ISCHR(mode))
printf("c");
else if(S_ISBLK(mode))
printf("b");
else if(S_ISFIFO(mode))
printf("p");
else if(S_ISLNK(mode))
printf("l");
else if(S_ISSOCK(mode))
printf("s");
// user permission
if (mode & S_IRUSR)
printf("r"); // read
else
printf("-");
if(mode&S_IWUSR) // write
printf("w");
else
printf("-");
if(mode&S_IXUSR) // execute
printf("x");
else
printf("-");
// group permission
if(mode&S_IRGRP)
printf("r");
else
printf("-");
if(mode&S_IWGRP)
printf("w");
else
printf("-");
if(mode&S_IXGRP)
printf("x");
else
printf("-");
// other permission
if(mode&S_IROTH)
printf("r");
else
printf("-");
if(mode&S_IWOTH)
printf("w");
else
printf("-");
if(mode&S_IXOTH)
printf("x");
else
printf("-");
printf("\t");
}
void user_name(uid_t uid)
{
struct passwd *pw;
pw = getpwuid(uid);
printf("%s\t", pw->pw_name);
}
void group_name(gid_t gid)
{
struct group *grp;
grp = getgrgid(gid);
printf("%s\t", grp->gr_name);
}
void modify_time(time_t time)
{
struct tm *gt;
char buf[21] = {0};
if ((gt = gmtime(&time)) != NULL)
{
strftime(buf, 20," %b %d %H:%M\t", gt);
fputs(buf, stdout);
}
}
int main(int argc, char *argv[])
{
DIR *dp;
struct dirent *dir;
struct stat buf;
char *sort[FILE_MAX] = {0};
char *name;
dp = opendir(".");
// 파일명을 저장해서 정렬
int i = 0;
while ((dir = readdir(dp)))
sort[i++] = dir->d_name;
bubble_sort(sort,i);
for(int j = 0;j < i;j++)
{
name = sort[j];
stat(name, &buf);
file_mode(buf.st_mode);
printf("%hu\t", buf.st_nlink);
user_name(buf.st_uid);
group_name(buf.st_gid);
printf("%lld\t", buf.st_size);
modify_time(buf.st_mtime);
printf("%s\n", name);
}
closedir(dp);
return 0;
}
결과는 다음과 같다.

'STUDY > linux' 카테고리의 다른 글
IPC in LINUX - Shared Memory (0) | 2022.04.04 |
---|---|
IPC in LINUX - Message Passing (0) | 2022.04.02 |
SIGNAL in LINUX (0) | 2022.03.29 |
Process and Threads in LINUX (0) | 2022.03.28 |
File I/O functions in C Library (0) | 2022.03.23 |