본문 바로가기

STUDY/linux

Files and Directories in LINUX

리눅스에서 파일과 디렉토리 시스템을 다루는 방법을 알아보자

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