[Linux] 파일/디렉터리 다루기 with C

파일 정보 다루기

inode

: 파일에 대한 정보를 저장하는 구조체

 

파일의 상태 정보

: i-node에 저장된 파일의 상태 정보 가져오기

- 심볼릭 링크의 경우 stat()링크가 가리키는 파일에 대한 정보를, lstat()링크 자체에 대한 정보를 가져옴

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int stat(const char* pathname, struct stat* buf);
int fstat(int fd, struct stat* buf);
int lstat(const char* pathname, struct stat* buf);
// 파일의 상태 정보를 가져와서 buf에 저장
// 성공하면 0, 실패하면 -1을 저장

 

stat 구조체

struct stat {
    dev_t st_dev;          // 파일이 저장된 장치의 번호
    ino_t st_ino;          // i-node 번호
    mode_t st_mode;        // 파일 종류와 접근권한
    nlink_t st_nlink;      // 하드 링크 수
    uid_t st_uid;          // 소유자의 UID
    gid_t st_gid;          // 소유 그룹의 GID
    dev_t st_rdiv;         // 특수 파일 장치 번호
    off_t st_size;         // 파일 크기
    long st_blksize;       // 입출력 시 블럭 크기
    long st_blocks;        // 파일에 할당된 블럭 수
    struct timespec st_atim;  // 최종 내용 접근 시간
    struct timespec st_mtim;  // 최종 내용 수정 시간
    struct timespec st_ctim;  // 최종 상태 변경 시간

 

st_mode의 비트 구조

 

기호 상수를 이용한 파일의 종류 검색

: sys/stat.h

 

파일의 종류 판별 매크로

 

파일 접근 권한 확인

  • 파일 접근 권한 검색 기호 상수(sys/stat.h)
    • S_IREAD : 00400
    • S_IWRITE : 00200
    • S_IEXEC : 00100
  • 기본적인 확인 방법
    • st_mode & S_IREAD : 소유자의 읽기 권한
    • st_mode & (S_IREAD >> 3) : 소유자의 읽기 권한
    • st_mode & (S_IREAD >> 6) : 기타 사용자의 읽기 권한

 

파일 접근 권한 검색 상수

 

접근권한 검색

: 파일의 존재 여부접근 권한 확인

  • mode : F_OK, R_OK, W_OK, X_OK
  • errno 코드값
    • ENOENT : 존재하지 않거나 깨진 심벌릭 링크
    • EACCESS : 접근 권한이 없는 경우
#include <unistd.h>

int access(const char* pathname, int mode);
// 권한이 있으면 0, 없으면 -1을 반환

 

파일 접근 권한의 변경

: 대상 파일의 접근 권한을 변경

  • mode: 8진수 접근권한 (e. 0644)
  • 성공하면 0, 실패하면 -1 반환
#include <sys/stat.h>
#include <sys/types.h>

int chmod(const char* pathname, mode_t mode);
int fchmod(int fd, mode_t mode);

 

파일 소유자 변경

: 대상 파일의 소유자그룹 소유자변경

   - 파일 소유자: super-user만 변경 가능

   - 소유 그룹: 소유자가 자신이 속한 그룹으로 변경 가능

#include <sys/types.h>
#include <unistd.h>

int chown(const char* path, uid_t owner, gid_t group);
int fchown(int filedes, uid_t owner, gid_t group);
int lchown(const char* path, uid_t owner, gid_t group);  // 심볼릭 링크 자체를 변경
// 성공하면 0, 실패하면 -1 반환

링크의 생성과 삭제

하드 링크

: 기존 파일에 대한 새로운 이름(링크) 생성/삭제

#include <unistd.h>

int link(char* existing, char* new);
ink unlink(char* path);

실습(하드링크): myln.c, unlink.c

myln.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[]) // ./myln src new_file
{
	if (argc < 3) {
		fprintf(stderr, "usage: %s <target> <lnk_name>\n", argv[0]);
		exit(1);
	}


	if (link(argv[1], argv[2]) == -1) {   /* 하드링크 생성 */
		perror(argv[0]);
		exit(2);
	}

	return 0;
}

unlink.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])   // ./unlink target
{
	if (argc < 2) {
		fprintf(stderr, "usage: %s <target>\n", argv[0]);
		exit(1);
	}

	if (unlink(argv[1]) == -1) {   /* 링크해제 */
		perror(argv[1]);
		exit(2);
	}

	return 0;
}

 

심볼릭 링크

- 심볼릭 링크의 생성

int symlink(const char* actualpath, const char* sympath);
// 성공하면 0, 실패하면 -1 반환

- 심볼릭 링크의 내용

#include <unistd.h>

int readlink(const char* path, char* buf, size_t bufsize);
// path 심볼릭 링크의 실제 내용을 읽어서 buf에 저장
// 성공하면 buf에 저장한 바이트 수를 반환, 실패하면 -1 반환

 

실습(심볼릭 링크) : myln_s.c

myln_s.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
	if (argc < 3) {
		fprintf(stderr, "usage: %s <target> <lnk_name>\n", argv[0]);
		exit(1);
	}

	if (symlink(argv[1], argv[2])) == -1) { /* 심볼릭링크 생성 */
		perror(argv[0]);
		exit(2);
	}

	return 0;
}

rlink.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[]) // ./rlink /bin
{
	if (argc < 2) {
		fprintf(stderr, "usage: %s <symbolic_link_name>\n", argv[0]);
		exit(1);
	}

	char buf[BUFSIZ + 1];
	int nread = readlink(argv[1], buf, BUFSIZ); /*  심벌릭링크 파일의 내용 읽어오기 */
	if (nread == -1) {
	    perror(argv[0]);
		exit(2);
	}

	buf[nread] = '\0';
	printf("%s -> %s\n", argv[1], buf);

	return 0;
}

디렉터리 다루기

디렉터리 생성

: 새 디렉터리를 생성, .과 ..은 자동으로 만들어짐

- mode: 접근 권한. 8진수나 mode_t에서 선언된 기호 상수 사용

#inlcude <sys/stat.h>
#include <sys/types.h>   // mode_t

int mkdir(const char* pathname, mode_t mode);
// 성공하면 0, 실패하면 -1을 반환

실습-디렉터리 생성: mymkdir.c

mymkdir.c

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>

#define PERMS 0755

int main(int argc, char* argv[])
{
	if (argc < 2) {
		fprintf(stderr, "usage: %s <directory>\n", argv[0]);
		exit(1);
	}

	if (mkdir(argv[1], PERMS) == -1) { /* 디렉터리 생성 */
		perror(argv[1]);
		exit(2);
	}

	exit(0);
}

 

디렉터리 삭제

: 대상 디렉터리가 비어있으면 삭제

#include <unistd.h>

int rmdir(const char* pathname);
// 성공하면 0, 실패하면 -1 반환

실습-디렉터리 삭제: myrmdir.c

myrmdir.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
	if (argc < 2) {
		fprintf(stderr, "usage: %s <directory>\n", argv[0]);
		exit(1);
	}

	if (rmdir(argv[1]) == -1) {  /* 대상경로의 빈 디렉터리 삭제 */
		perror(argv[1]);
		exit(2);
	}

	exit(0);
}

 

현재 작업 디렉토리 확인

: 현재 작업 디렉토리의 절대 경로 반환

- buf: 경로를 저장할 문자 배열의 시작 주소. null이 전달되면 동적할당된 배열 반환

- size: 경로를 저장할 배열의 크기. buf == NULL && size = 0이면 자동으로 필요 크기 결정

#include <unistd.h>

char* getcwd(char* buf, size_t size);
// 성공하면 절대경로 문자열, 실패하면 NULL 반환

실습-pwd 확인: mypwd.c

mypwd.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main(void)
{
    char* cwd;
    char buf1[BUFSIZ];  // 표준 입출력에서 사용하는 버퍼의 크기
    char buf2[10];

    printf("BUFSIZ=%d\n", BUFSIZ);  // 버퍼 크기 확인위한 출력

    cwd = getcwd(buf1, BUFSIZ);   /* buf1에 현재 작업 경로 얻기 */
    printf("#1: [%s]\n", buf1);
    printf("#1: [%s]\n", cwd);

    cwd = getcwd(NULL, BUFSIZ); /* BUFSIZ 크기의 배열 동적할당하여 현재 작업 경로 저장후 반환 */
    printf("#2: [%s]\n", cwd);
    free(cwd);

    cwd = getcwd(NULL, 0)/* 현재 작업 경로를 저장한 메모리를 동적할당하여 반환 */
    printf("#3: [%s]\n", cwd);
    free(cwd); /* free 하는 거 중요함!! */

    cwd = getcwd(buf2, 10); /* 오류 상황 발생 (Out of range)*/
    if (cwd == NULL) {
        perror("mypwd");
        exit(1);
    }
    printf("#4: [%s]\n", cwd);

    return 0;
}

 

파일/디렉터리 이름 변경

: 대상 파일이나 디렉터리의 이름/위치를 변경. oldpath의 파일의 경로명을 newpath로 변경

#include <stdio.h>

int rename(const char* oldpath, const char* newpath);
// 성공하면 0, 실패하면 -1 반환

실습-파일/디렉토리 이름 변경: mymv.c

mymv.c

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    if (argc != 3) {
        fprintf(stderr, "usage: %s <oldpath> <newpath>\n", argv[0]);
        exit(1);
    }

    int rval = rename(argv[1], argv[2]); /* 이름 변경하기 */
    if (rval == -1) {
        perror(argv[0]);
        exit(2);
    }

    return 0;
}

 

작업 디렉토리의 이동

: 현재 프로세스의 작업 디렉토리를 변경. 절대경로와 상대경로 모두 가능

- fd: 변경할 디렉터리에 대한 파일 디스크립터. open()으로부터 얻은 값

#include <unistd.h>

int chdir(const char* path);

실습-작업디렉토리 이동: mycd.c

mycd.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    if (argc != 2) {
        fprintf(stderr, "usage: %s <pathname>\n", argv[0]);
        exit(1);
    }

    char* cwd;
    cwd = getcwd(NULL, 0);
    printf("[%s]\n", cwd);
    free(cwd);

    /* 대상 디렉터리로 작업경로 변경 */
    chdir(argv[1]);

    cwd = getcwd(NULL, 0); /* 작업 경로 잘 바뀌었는지 확인*/
    printf("[%s]\n", cwd);
    free(cwd);

    return 0;
}

mycd2.c

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    if (argc != 2) {
        fprintf(stderr, "usage: %s <pathname>\n", argv[0]);
        exit(1);
    }

    char* cwd;
    cwd = getcwd(NULL, 0);
    printf("[%s]\n", cwd);
    free(cwd);

    int fd = open(argv[1], O_RDONLY); 
    fchdir(fd);  /* 대상 디렉터리로 작업경로 변경 */

    cwd = getcwd(NULL, 0);
    printf("[%s]\n", cwd);
    free(cwd);

    return 0;
}

 

디렉터리의 내용 접근

: 디렉터리 접근을 위해 편리한 함수 별도 제공

#include <sys/types.h>
#include <dirent.h>

struct DIR* opendir(const char* path);
// path 디렉터리를 열고 성공하면 DIR 구조체 포인터 반환
// 실패하면 NULL 반환

struct dirent* readdir(DIR* dp);
// 한 번에 디렉터리 엔트리를 하나씩 읽어서 반환

int closedir(DIR* dp);
// 성공하면 0, 실패하면 -1 반환

 

디렉터리 엔트리

: 디렉터리 내에 포함된 하나의 파일에 대한 정보 (node 번호 (e. d_ino), 파일 이름 (e. d_name))

#include <dirent.h>

struct dirent {
    ino_t d_ino;
    // ...
    unsigned char d_type;
    char d_name[NAME_MAX + 1];
};

 

실습-디렉토리 엔트리: myls_i.c

myls_i.c

#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])   // ./myls_i /home
{
    char* dir;
    if (argc == 1)
        dir = ".";
    else
        dir = argv[1];

    DIR* dp = opendir(dir); /* 디렉터리 열기  */
    if (dp == NULL) {
        perror(dir);
        exit(1);
    }

    struct dirent* dent;
    while ((dent  = readdir(dp)) != NULL) {   /* 디렉터리 엔트리 읽어들이기  */
        printf("%d %s\n", (int) dent->d_ino, dent->d_name);
    }

    /* 디렉터리 닫기 */
    closedir(dp);

    return 0;
}