리눅스 시스템에서 디렉터리의 파일목록을 검색하는 명령어인 ls를 구현해보자.
ls 명령어를 구현하기 위해서는 디렉터리를 open하고, read하고, close할 수 있어야 한다. 이를 위한 directory handler func들을 알아보자.
함수 원형:
DIR* opendir(char* dirname)
/*
argument: directory name(path)
work: 해당하는 디렉터리 와 connection 생성
return : DIR*
*/
함수 원형:
struct dirent *readdir(DIR*)
/*
work: 디렉터리에 존재하는 파일들을 읽어옴
return struct dirent
*/
파일 시스템에서 디렉터리 내 파일 항목을 읽기 위한 구조체이다.
struct dirent{
ino_t d_ino; /* 파일 시스템에서 각 파일에 고유하게 부여되는 번호(inode 번호) */
off_t d_off; /* 디렉터리에서의 오프셋 (디렉터리에서 현재 항목의 위치)*/
unsigned short d_reclen; /* 이 dirent 구조체의 길이 */
unsigned char d_type; /* 파일 타입 (디렉터리, 파일 등) */
char d_name[256]; /* 파일 이름 (NULL로 종료된 문자열) */
}
int closedir(DIR *)
/*
argument: opendir로 생성한 *Dir
work: 디렉터리(DIR*) close
return : 0(success), -1(error)
*/
stat 함수는 특정 파일에 대한 정보(struct stat)를 가져온다.
ls를 구현하기 위해서는 readdir의 반환값인 struct dirent만으로는 부족하다. 그래서, struct dirent에서 file name을 받아와서 stat함수로 해당 파일에 대한 구체적 정보를 받아와야 한다.
함수 원형:
int stat(const char* path, struct stat *buf)
/*
argument: path(파일의 경로), buf(stat 정보를 받을 buffer)
work: 특정 파일에 대한 정보(struct stat)를 가져옴
return : 0(success), -1(error)
*/
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* 파일의 종류 및 접근권한 */
nlink_t st_nlink; /* hardlink 된 횟수 */
uid_t st_uid; /* 파일의 owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* 파일의 크기(bytes) */
blksize_t st_blksize; /* blocksize for file system I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};
이 중 우리가 ls 명령어를 구현하는 데 필요한 정보는 st_mode(파일 권한에 관한 정보), st_nlink, st_uid, st_gid, st_size, st_mtime(마지막 접근)이다.
파일 type과 permission 정보를 bits로 가지는 멤버 변수로, 해당 bits에서 정보를 추출하기 위해서는 별도의 bit masking이 필요하다.
struct stat로 부터 받은 uid를 getpwuid에 넘겨 uid로부터 user name을 받아온다.
struct passwd *getpwuid(uid_t uid);
/*
work: 주어진 사용자 uid에 맞는 사용자 정보를 가져옴
return: struct passwd
struct passwd {
char *pw_name; // 사용자 이름 (로그인 ID)
char *pw_passwd; // 패스워드 (대부분 shadow 파일로 관리됨)
uid_t pw_uid; // 사용자 ID (UID)
gid_t pw_gid; // 그룹 ID (GID)
char *pw_gecos; // 사용자 설명 필드
char *pw_dir; // 홈 디렉토리 경로
char *pw_shell; // 기본 쉘 경로
};
*/
struct stat로 부터 받은 gid를 getgrgid func로 넘겨 gid에 해당하는 group name을 받아온다.
struct group *getgrgid(gid_t gid);
/*
work: 주어진 group gid에 맞는 그룹 정보를 가져옴
retrun: struct group
*/
struct group{
char *gr_name; // 그룹 이름
char *gr_passwd; // 그룹 패스워드 (일반적으로 사용되지 않음)
gid_t gr_gid; // 그룹 ID (GID)
char **gr_mem; // 그룹 멤버 배열 (그룹에 속한 사용자 이름 목록)
}
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/types.h>
void do_ls(char dirname[]);
void do_stat(char *);
void mode_to_letters(mode_t mode, char mode_str[]);
char *uid_to_name(uid_t);
char *gid_to_name(gid_t);
void show_file_info(struct stat *);
int main(int ac, char *av[])
{
if (ac==1)
do_ls(".");
else
{
while(--ac)
{
printf("%s: \n",*++av);
do_ls(*av);
}
}
return 0;
}
void do_ls(char dirname[])
{
DIR* dir_ptr = NULL;
struct dirent *dirent_ptr=NULL;
if((dir_ptr=opendir(dirname))!=NULL)
{
while((dirent_ptr = readdir(dir_ptr))!=NULL)
{
char filepath[200];
strcpy(filepath, dirname);
strcat(filepath,dirent_ptr->d_name);
do_stat(filepath);
printf("%s\n", dirent_ptr->d_name);
}
closedir(dir_ptr);
}
else{
fprintf(stderr, "ls: cannot open %s\n", dirname);
}
}
void do_stat(char *filepath)
{
struct stat buf;
if(stat(filepath, &buf)==-1)
perror(filepath);
else
show_file_info(&buf);
}
void show_file_info(struct stat *info_p)
{
char mode[10];
mode_to_letters(info_p->st_mode,mode);
printf("%s", mode);
printf("%4d ", (int)info_p->st_nlink);
printf("%-8s ", uid_to_name(info_p->st_uid));
printf("%-8s ", gid_to_name(info_p->st_gid));
printf("%8ld ", (long)info_p->st_size);
printf("%.12s ", 4 + ctime(&info_p->st_mtime));
}
void mode_to_letters(mode_t mode, char mode_str[])
{
strcpy(mode_str, "----------");
// about file type
if (S_ISDIR(mode)) mode_str[0] = 'd';
if (S_ISCHR(mode)) mode_str[0] = 'c';
if (S_ISBLK(mode)) mode_str[0] = 'b';
// permission
if (mode & S_IRUSR) mode_str[1] = 'r';
if (mode & S_IWUSR) mode_str[2] = 'w';
if (mode & S_IXUSR) mode_str[3] = 'x';
if (mode & S_IRGRP) mode_str[4] = 'r';
if (mode & S_IWGRP) mode_str[5] = 'w';
if (mode & S_IXGRP) mode_str[6] = 'x';
if (mode & S_IROTH) mode_str[7] = 'r';
if (mode & S_IWOTH) mode_str[8] = 'w';
if (mode & S_IXOTH) mode_str[9] = 'x';
}
char *uid_to_name(uid_t uid)
{
struct passwd *pw_ptr = NULL;
static char uid_str[10];
if ((pw_ptr = getpwuid(uid)) == NULL) // No matched uid
{
sprintf(uid_str, "%d", uid);
return uid_str;
}
else
{
return pw_ptr->pw_name;
}
}
char *gid_to_name(gid_t gid)
{
struct group *gp_ptr = NULL;
static char gid_str[10];
if ((gp_ptr = getgrgid(gid)) == NULL) // No matched gid
{
sprintf(gid_str, "%d", gid);
return gid_str;
}
else
{
return gp_ptr->gr_name;
}
}