리눅스 프로그래밍 - 5주차

Lellow_Mellow·2022년 10월 11일
1
post-thumbnail

🔔 학교 강의를 바탕으로 개인적인 공부를 위해 정리한 글입니다. 혹여나 틀린 부분이 있다면 지적해주시면 감사드리겠습니다.

#include <unistd.h>
int readlink(const char *sympath, char* buffer, size_y bufsize);

argument

  • const char *sympath : symbolic link 경로
  • char* buffer : 저장할 buffer
  • size_y bufsize : buffer size

return

  • 성공 시 byte 개수 return
  • error 발생 시 -1 return

readlinksympath를 따라가 실질적인 realname을 보여주는 것이 아닌, sympath 그 자체를 읽어오는 역할을 한다.

3.3 Obtaining File Information : stat and fstat

System Call : stat

#include <sys/stat.h>
int stat(const char *pathname, struct stat *buf);
int fstat(int filedes, struct stat *buf);
int lstat(const char *pathname, struct stat *buf);

각 함수는 모두 동일하게 파일에 대한 정보를 출력하는 역할을 한다. 하지만 전달받는 인자에 차이가 있다.

argument

  • const char *pathname : stat일 경우 파일의 경로, lstat일 경우 symbolic link의 경로
  • struct stat *buf : 저장할 buffer

return

  • 성공 시 0 return
  • error 발생 시 -1 return

struct stat은 아래와 같은 구조를 가지고 있다.

이에 대한 예시 코드는 아래와 같다.

Example : filedata

// filedata -- 한 파일에 관한 정보를 출력
#include <stdio.h>
#include <sys/stat.h>
// 허가 비트가 설정되어 있는지 결정하기 위해 octarray를 사용
static short octarray[9] = {0400, 0200, 0100,
							0040, 0020, 0010,
							0004, 0002, 0001};
                            
// 파일 허가에 대한 기호화 코드 끝부분의 null 때문에 길이가 10문자이다.
static char perms[10] = "rwxrwxrwx";

int filedata (const char *pathname) {
	struct stat statbuf;
	char descrip[10];
	int j:
	if(stat (pathname, &statbuf) == -1) {
		fprintf (stderr, "Couldn't stat %s\n", pathname);
		return (-1);
	}
    
	// 허가를 읽기 가능한 형태로 바꾼다.
	for(j=0; j<9; j++) {
    
	// 비트별 AND를 사용하여 허가가 설정되었는지 테스트
	if (statbuf.st_mode & octarray[j])
		descrip[j] = perms[j];
	else
		descrip[j] = '-';
	}
    
	descrip[9] = '\0'; // 하나의 문자열을 가지도록 확인
    
	// 파일 정보를 출력한다.
	printf ("\nFile %s :\n", pathname);
	printf ("Size %ld bytes\n", statbuf.st_size);
	printf ("User-id %d, Group-id %d\n\n", statbuf.st_uid, statbuf.st_gid);
	printf ("Permissions: %s\n", descrip);
	return (0);
}

파일 권한에 대한 정보를 stat을 이용해 statbuf에 저장한 이후, 해당 정보를 각 권한에 해당하는 bit와의 & 연산을 통해 문자열로 변경하여 출력해준다.

Directories, File Systems and Special Files

File System and Directory

File System

file system은 file과 directory의 계층적 배열이며, 모든 것은 /로 표기되는 root에서부터 시작한다.

Working directory

모든 process는 working directory가 존재하며, 상대경로를 판단하는 경로다.

Home directory

우리가 로그인을 하면, working directory가 우리의 home directory를 설정해준다.

Pathname

경로에는 2가지가 있으며, root에서부터 시작하는 절대 경로와 (/로 시작함), working directory에서 시작하는 상대 경로가(/로 시작하지 않음) 존재한다.

4.3 The Implementation of a Directory

Directory

file에 사용되는 많은 system call들은 directory에도 사용될 수 있지만, 일반적인 file과 directory간의 차이는 존재한다.

  • createopen을 사용하여 directory 생성 불가
  • O_WRONLYO_RDWR flag가 설정된 경우에는 directory를 열 수 없음 (errno = EISDIR)
  • write를 사용해서 directory를 update할 수 없음
  • kernel만 directory에 대해서 write이 가능함

directory는 그 안에 포함된 file이나 하위 directory의 항목들로 구성이 되며 아래의 요소들을 포함한다.

  • i-node number
  • character field (=string)

이전에 정리했던 link의 경우, 동일한 i-node 번호를 가지면서 새로운 이름을 가지는 directory slot을 추가해주는 것에 해당하며, 이는 아래와 같다.

unlink는 반대로 이 directory slot을 삭제해주는 역할이며, link count가 0이 되면, 해당 i-node 전체를 삭제한다.

dot and double-dot

  • . : 현 directory
  • .. : 상위 directory

이를 그림으로 나타내면 아래와 같다.

또한 directory의 구조를 그림으로 나타내면 아래와 같다.

Directory Permissions

directory의 권한 역시 file의 권한과 동일한 방식으로 구성되어 있지만, file의 권한과 의미하는 바가 다르다.

  • Read permission : sub directory의 file name을 list화 가능
  • Write permission : directory를 지우거나 새로 생성이 가능
  • Execute permission : 해당 directory 안으로 이동하여 working directory로 지정이 가능
    -> 앞에서 directory에 대해서 write이 불가능하다는 것은 system call을 이용해 directory 자체를 여는 것이 불가능하다는 의미

따라서 어떠한 파일 경로에 접근하기 위해서는, 해당 경로에 적힌 모든 directory에 대해서 execute permission이 존재해야 한다는 의미가 된다. 또한 execute permission bitsearch bit이라고 부르기도 한다.

이전에 자세하게 다루지 않았던 permission 9bit를 제외한 상위 3bit 중에서 S_ISVTXsave-text-image(sticky bit)이라고도 부르며, directory에만 유효한 bit이다. 해당 bit가 설정되면 file 추가는 아무나 가능하나, file 삭제와 이름 변경은 아래 3개의 경우만 가능하다.

  • file의 소유자일 경우
  • directory의 소유자일 경우
  • superuser (root)

4.4 Programming with Directories

Structure : dirent

#include <dirent.h>
struct dirent {
	ino_t d_ino;
    char  d_name[NAME_MAX + 1];
}

dirent는 자료형이며, i-node numbernull로 끝나는 file 이름을 포함하고 있다. d_ino가 0이면 빈 슬롯임을 뜻하며, 이는 새로운 file을 만들 때 활용한다.

System Call : mkdir

#include <sys/stat.h>
int mkdir(const char *pathname, mode_t mode);

mkdirpathname에 해당하는 새로운 directory를 생성한다. mode는 권한을 가리키며, umask의 영향을 받는다. 또한 ., ..를 생성하고, i-node를 부여한다.

argument

  • const char *pathname : 생성할 새로운 directory
  • mode_t mode : permission

return

  • 성공 시 0 return
  • error 발생 시 -1 return

directory 내의 file 이름들에 대해 접근하기 위해서는 적어도 하나의 execute bit이 활성화되어야 한다.

System Call : rmdir

#include <unistd.h>
int rmdir(const char *pathname);

rmdir는 빈 directory를 제거하며, 여기서 빈 directory란 ., ..만 가지고 있는 directory를 뜻한다.

argument

  • const char *pathname : 삭제할 directory

return

  • 성공 시 0 return
  • error 발생 시 -1 return

Function : opendir

#include <dirent.h>
DIR *opendir(const char *dirname);

argument

  • const char *dirname : open할 directory 이름

return

  • 성공 시 해당 directory의 DIR pointer return
  • error 발생 시 NULL return

opendirdirname에 해당하는 directory를 open하는 함수이다. 여기서 return type인 DIRstandard I/O에서 FILE과 유사하게 작동한다. NULL이 반환될 경우 error checking이 필요하다.

Function : closedir

#include <dirent.h>
int closedir(DIR *dirptr);

argument

  • DIR *dirptr : close할 directory pointer

return

  • 성공 시 0 return
  • error 발생 시 -1 return

closedir은 전달받은 dirptr에 해당하는 directory를 close하는 역할을 하며, directory는 program이 종료될때까지 닫기지 않으면, 종료 시에 자동으로 close된다.

Function : readdir

#include <dirent.h>
struct dirent *readdir(DIR *dirptr);

argument

  • DIR *dirptr : 읽고 싶은 directory pointer

return

  • 성공 시 dirent의 pointer return
  • error 발생 시 NULL return

readdir을 처음 실행하면, 첫 번째 directory entry가 dirent로 읽혀지게 된다. readdir이 성공하면, dirptr은 다음 directory entry로 이동한다. 끝까지 이동한 경우에는 NULL을 return한다.

Function : rewinddir

#include <dirent.h>
void rewinddir(DIR *dirptr);

rewinddirdirptr를 directory의 가장 처음 entry로 이동시킨다. return값이 존재하지 않는다.

위에 대한 다양한 function들에 대한 예시 코드는 아래와 같다.

Example : Directory (my_double_ls)

#include <dirent.h>

int my_double_ls (const char *name) {
	struct dirent *d;
	DIR *dp;

	if ((dp=opendir(name))==NULL)
		return (-1);
        
	while (d=readdir(dp)) {
		if (d->d_ino != 0)
		printf(%s\n”, d->d_name);
	}
    
	rewinddir(dp);
    
	while (d=readdir(dp)) {
		if (d->d_ino != 0)
		printf(%s\n”, d->d_name);
    }
	closedir(dp);
	return 0;
}

중간에 d_ino가 0이 아님을 확인하는 조건문은 유효성을 판단하는 것이며, 해당 코드는 ls를 2번 실행하는 것과 유사하다.

Example : Directory (find_entry)

#include <stdio.h>
#include <dirent.h>
#include <string.h>

int match (const char *, const char*);
char *find_entry(char *dirname, char *suffix, int cont) {
	static DIR *dp=NULL;
	struct dirent *d;
	if (dp == NULL || cont == 0) {
		if (dp != NULL)
			closedir (dp);
		if ((dp = opendir(dirname)) == NULL)
			return (NULL);
	}
	while (d = readdir(dp)) {
		if (d->d_ino == 0)
			continue;
		if (match(d->d_name, suffix))
			return (d->d_name);
	}
	closedir(dp);
	dp = NULL;
	return (NULL);
}

int match (const char *s1, const char *s2) {
	int diff = strlen(s1)strlen(s2);
	if (stlen(s1) > strlen(s2))
		return (strcmp(&s1[diff], s2) == 0);
	else
		return 0;
}

suffix로 전달받은 file 이름과 동일한 file의 file entry를 찾는 코드이다.

The Current Working Directory

각 Linux(Unix) process는 각자의 working directory를 가지고 있으며, 현재 실행되고 있는 process의 working directory는 process가 시작된 working directory인, 보통은 shell로 설정된다.

System Call : chdir

int chdir(const char *path);

chdir은 현재 process의 working directory를 변경한다.

argument

  • const char *path : working directory로 지정할 경로

return

  • 성공 시 0 return
  • error 발생 시 -1 return

error가 발생하는 경우는 해당 path가 없는 경우, 혹은 path에 대해 execute permission이 없는 경우이며, 둘 중 하나라도 없으면 안된다.

특정 directory에 있는 file 여러개를 open해야할 경우, chdir를 이용하여 해당 directory로 이동하여 작업하는 것이 더 효율적이다.

Function : getcwd

#include <unistd.h>
char *getcwd(char *name, size_t size);

getcwd는 현 directory의 pathname을 받아온다.

argument

  • char *name : 현재 working directory 이름 저장
  • size_t size : 현재 working directory를 받아오는 역할

return

  • 성공 시 현재 working directory 이름 return
  • error 발생 시 NULL return

항상 sizeworking directory보다 1 이상 더 커야하며, 같거나 더 작은 경우 errno = ERANGE와 함께 error가 발생한다.

이에 대한 예시 코드는 아래와 같다.

Example : my_pwd

#include <stdio.h>
#include <unistd.h>
#define VERYBIG 200

void my_pwd ();
int main() {
	my_pwd();
	return 0;
}

void my_pwd () {
	char dirname[VERYBIG];
	if ( getcwd(dirname, VERYBIG) == NULL)
		perror("getcwd error");
	else
		printf("%s\n", dirname);
}

위는 getcwd를 활용하여 pwd를 간단히 구현한 코드이다.

Function : ftw

#include <unistd.h>
int ftw(const char *path, int(*func)(), int nopenfd);

int func(const char *name, const struct stat *sptr, int type) {
	...
}

ftwdirectory tree walk, 즉 directory tree를 따라 반복적으로 모든 entry에 대해 func을 수행하는 함수이다. func의 형태는 정해져있다.

argument

  • const char *path : 시작할 directory pathname
  • int(*func)() : 실행할 함수
  • int nopenfd : depth와 동일한 의미로, 동시에 열 directory의 개수를 의미 (1이어도 상관 없음)

종료되는 조건

  • Bottom에 도달하거나, error이 발생한 경우
  • func이 0이 아닌 값을 return한 경우

function

  • const char *name : object 이름
  • const struct stat *sptr : 해당 object에 대한 stat
  • int type : <ftw.h>에 정의되어있음. 해당 파일의 type을 나타냄
    - FTW_F : object가 file임
    - FTW_D : object가 directory임
    - FTW_DNR : object가 읽을 수 없는 directory임
    - FTW_SL : object가 symbolic link임
    - FTW_NS : object가 symbolic link가 아니지만 stat를 성공적으로 실행할 수 없음

이에 대한 예시 코드는 아래와 같다.

Example : ftw

#include <sys/stat.h>
#include <ftw.h>

int list(const char *name, const struct stat *status, int type) {
	if (type == FTW_NS)
		return 0;
	if(type == FTW_F)
		printf("%-30s\t0%3o\n", name, status->st_mode&0777);
	else
		printf("%-30s*\t0%3o\n", name, status->st_mode&0777);
	return 0;
}

int main (int argc, char **argv) {
	int list(const char *, const struct stat *, int);
	if (argc == 1)
		ftw (".", list, 1);
	else
		ftw (argv[1], list, 1);
	return 0;
}

file이면 그냥, 아닐 경우에는 *를 붙여서 출력하며, ls -r과 유사한 역할을 한다.

profile
festina lenta

0개의 댓글