". 과 .. 은 모든 디렉터리에 항상 존재하는 파일 이름이며, 디렉터리가 생성될 때 자동적으로 포함된다"
ㅁ디렉터리 허가
- 읽기(r): 디렉터리 내의 파일이나 부디렉터리의 이름을 리스트
- 쓰기(w): 디렉터리 내의 파일을 제거하거나 새로운 파일을 생성
- 실행(x): cd 혹은 chdir 로 디렉터리 내부로 들어갈 수 있음
(리눅스 파일 함수)
[파일 정보 검색 함수] - inode 정보 검색
stat() 함수로 검색한 inode 정보는 stat 구조체에 저장되어 리턴된다
stat 구조체의 세부 구조는 man -s 2 stat 으로 확인할 수 있다 (stat은 status의 약자이다)
struct stat {
dev_t st_dev; // 파일이 저장되어 있는 장치의 번호를 저장
ino_t st_ino; // 파일의 inode 번호를 저장
mode_t st_mode; // 파일의 종류와 접근권한을 저장
nlink_t st_nlink; // 하드 링크의 개수를 저장
uid_t st_uid; // 파일 소유자의 UID를 저장 (User ID)
gid_t st_gid; // 파일 소유 그룹의 GID를 저장 (Group ID)
dev_t st_rdev; // 장치 파일이면 주 장치 번호와 부 장치 번호를 저장 (장치 파일이 아니면 의미 X)
off_t st_size;
blksize_t st_blksize; // 파일 내용을 입출력할 때 사용하는 버퍼의 크기를 저장
blkcnt_t st_blocks; // 파일에 할당된 파일 시스템의 블록 수 (블록 크기: 512byte)
// timespec: 초와 나노초를 저장하기 위한 구조체
// 리눅스 커널 2.6부터 나노초 수준의 정밀도를 지원
struct timespec st_atim; // 마지막으로 파일을 읽거나 실행한 시각
struct timespec st_mtim; // 마지막으로 파일의 내용을 변경(쓰기)한 시각
struct timespec st_ctim; // 마지막으로 inode의 내용을 변경한 시각
// 리눅스 커널 2.6 이전 버전과의 호환성을 위해 #define 으로 타임스탬프 값을 맵핑
#define st_atime st_atim.tv_sec
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};
ㅇint stat(const char* pathname, struct stat* statbuf): 파일명으로 파일 정보 검색
- pathname : 파일명
- statbuf : 검색한 파일 정보를 저장할 구조체 주소
성공하면 0을 반환하고 stat 구조체에 파일 정보를 저장, 실패하면 -1을 반환
pathname에 지정한 파일의 정보를 검색한다
※ stat() 함수로 파일의 정보를 검색할 때 파일에 대한 읽기/쓰기/실행 권한이 반드시 있어야 하는 것은 아니지만, 파일에 이르는 경로의 각 디렉터리에 대한 읽기 권한은 있어야 한다
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main() {
struct stat statbuf;
stat("linux.txt", &statbuf); // linux.txt 파일의 정보를 읽어옴
printf("inode = %d\n", (int)statbuf.st_ino);
printf("mode = %o\n", (unsigned int)statbuf.st_mode);
printf("Nlink = %o\n", (unsigned int)statbuf.st_nlink);
printf("UID = %d\n", (int)statbuf.st_uid);
printf("GID = %d\n", (int)statbuf.st_gid);
printf("SIZE = %d\n", (int)statbuf.st_size);
printf("blksize = %d\n", (int)statbuf.st_blksize);
printf("blocks = %d\n", (int)statbuf.st_blocks);
printf("** timespec style\n");
printf("Atime = %d\n", (int)statbuf.st_atim.tv_sec);
printf("Mtime = %d\n", (int)statbuf.st_mtim.tv_sec);
printf("Ctime = %d\n", (int)statbuf.st_ctim.tv_sec);
printf("** old style\n");
printf("Atime = %d\n", (int)statbuf.st_atime);
printf("Mtime = %d\n", (int)statbuf.st_mtime);
printf("Ctime = %d\n", (int)statbuf.st_ctime);
return 0;
}
ㅇint fstat(int fd, struct stat* statbuf): 파일 기술자(fd)로 파일 정보 검색
- fd : 열려있는 파일의 파일 기술자
- statbuf : 검색한 파일 정보를 저장할 구조체 주소
성공하면 0을 반환하고 stat 구조체에 파일 정보를 저장, 실패하면 -1을 반환
파일경로 대신 현재 열려있는 파일의 파일 기술자를 인자로 받아 파일 정보를 검색한 후 statbuf로 지정한 구조체에 저장
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main() {
int fd;
struct stat statbuf;
fd = open("linux.txt", O_RDONLY);
if (fd == -1) {
perror("open: linux.txt");
exit(1);
}
fstat(fd, &statbuf); // linux.txt 파일의 정보를 읽어옴
printf("inode = %d\n", (int)statbuf.st_ino);
printf("UID = %d\n", (int)statbuf.st_uid);
clsoe(fd);
return 0;
}
ㅁst_mode의 구조
st_mode 항목의 데이터형인 mode_t 는 unsigned int 로 정의되어 있다
실제로는 16비트를 사용한다
sys/stat.h 파일에 정의된 상수와 매크로는 구조로 저장된 값과 상수를 AND 연산한 값을 추출하는 것
파일의 소유자는 사용자 식별 번호로 구별한다
유효 사용자 식별 번호(EUID, Effective User-ID): 파일에 대해 실제 소유권을 갖는 사용자의 식별 번호
진짜 사용자 식별 번호(RUID, Real User-ID): 실제로 프로세스를 갖는 사용자의 식별 번호
※ 유효 그룹 식별 번호(EGID, Effective Group-ID)와 진짜 그룹 식별 번호(RGID, Real Group-ID)는 대부분 동일하다
04000(SUID, Set User-ID): 생성된 프로세스에게 그 프로세스를 시작시킨 사용자의 UID 대신 파일 소유자의 유효 사용자 식별 번호를 부여
02000(SGID, Set Group-ID): 생성된 프로세스에게 그 프로세스를 시작시킨 그룹의 GID 대신 파일 그룹의 유효 사용자 식별 번호를 부여
01000(Sticky Bit): 공유 디렉터리(/tmp)에 대한 접근 권한 or 텍스트-이미지를 swap 영역에 남겨둠(과거 리눅스)
[상수를 이용한 파일 종류 검색]
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
int main() {
struct stat statbuf;
stat("linux.txt", &statbuf);
printf("mode = %o\n", (unsigned int)statbuf.st_mode); // st_mode 값을 8진수로 출력
int kind = statbuf.st_mode & S_IFMT; // AND(&) 연산을 kind 변수에 저장
printf("kind = %o\n", kind);
switch (kind) {
case S_IFLNK:
printf("linux.txt: symbolic link\n");
break;
case S_IFDIR:
printf("linux.txt: directory\n");
break;
case S_IFREG: // linux.txt 파일은 일반 파일임을 알 수 있음
printf("linux.txt: regular file\n");
break;
}
return 0;
}
[매크로를 이용한 파일 종류 검색]
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
int main() {
struct stat statbuf;
stat("linux.txt", &statbuf);
printf("mode = %o\n", (unsigned int)statbuf.st_mode); // st_mode 값을 8진수로 출력
if (S_ISLNK(statbuf.st_mode)) {
printf("linux.txt: symbolic link\n");
}
if (S_ISDIR(statbuf.st_mode)) {
printf("linux.txt: directory\n");
}
if (S_ISREG(statbuf.st_mode)) {
printf("linux.txt: regular file\n");
}
return 0;
}
[상수를 이용한 파일 접근 권한 검색]
st_mode의 값을 왼쪽으로 3비트 이동시키거나 상수값을 오른쪽으로 3비트 이동시킨 후 AND(&) 연산을 수행하면
"그룹의 접근 권한을 알 수 있음"
st_mode & (S_IREAD >> 3)
POSIX에서는 이와 같이 번거롭게 시프트 연산을 하는 대신 직접 AND(&) 연산이 가능한 다른 상수를 정의한다
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
int main() {
struct stat statbuf;
stat("linux.txt", &statbuf);
printf("mode = %o\n", (unsigned int)statbuf.st_mode); // st_mode 값을 8진수로 출력
if ((statbuf.st_mode & S_IREAD) != 0) { // 소유자의 읽기 권한을 확인
printf("linux.txt: user has a read permission\n");
}
if (statbuf.st_mode & (S_IREAD >> 3) != 0) { // 그룹의 읽기 권한을 확인
printf("linux.txt: group has a read permission\n");
}
if ((statbuf.st_mode & S_IROTH) != 0) { // 기타 사용자의 읽기 권한을 확인
printf("linux.txt: other have a read permission\n");
}
return 0;
}
[파일 접근 권한 함수] - inode 접근 권한 정보 확인
ㅇint access(const char* pathname, int mode): 함수를 이용한 접근권한 검색
- pathname : 접근권한을 알고자 하는 파일의 경로
- mode : 접근권한
접근권한이 있으면 0을 반환하고 오류가 있으면 -1을 반환
pathname에 지정된 파일이 mode로 지정한 권한을 지니고 있는지 확인한다
유효 사용자 ID가 아닌 실제 사용자 ID에 대한 접근권한만 확인할 수 있다
※ ENOENT: 해당 파일이 존재하지 않거나 심볼릭 링크의 경우 원본 파일이 없음
※ EACCES: 접근권한이 없음
(두 번째 인자 mode 에 사용할 수 있는 상수)
R_OK : 읽기 권한 확인
W_OK : 쓰기 권한 확인
X_OK : 실행 권한 확인
F_OK : 파일이 존재하는지 확인
#include <stdio.h>
#include <sys/errno.h>
#include <unistd.h>
extern int errno;
int main() {
int perm; // permission
// F_OK를 지정해 linux.bak 파일이 존재하는지 확인
// errno 변수에 저장된 메시지가 ENOENT 라면 파일이 없다는 의미
if (access("linux.bak", F_OK) == -1 && errno == ENOENT) {
printf("linux.bak: file not exist\n");
}
perm = access("linux.txt", R_OK); // R_OK로 읽기 권한 여부 검색
if (perm == 0) {
printf("linux.txt: read permission is permitted\n");
}
else if (perm == -1 && errno == EACCES) {
printf("linux.txt: read permission is not permitted\n");
}
return 0;
}
ㅇint chmod(const char* pathname, mode_t mode): 파일명으로 접근권한 변경
- pathname : 접근 권한을 변경하려는 파일의 경로
- mode : 접근 권한 파일의 접근 권한을 검색할 수 있는 시스템 호출
접근 권한을 변경할 파일의 경로를 받아 mode에 지정한 상수값으로 권한을 변경한다
소유자/그룹/기타 사용자의 접근권한을 변경할 때는 상수를 이용
특수 접근권한을 변경할 때는 S_ISUID, S_ISGID, S_ISVTX를 이용
기존 접근권한과 관계없이 접근권한을 모두 새로 지정하려면 상수를 이용해 권한을 생성한 후 인자로 지정
chmod(pathname, S_IRWXU);
chmod(pathname, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH);
기존 접근 권한을 변경해 권한을 조정할 수도 있음
mode |= S_IWGRP;
접근 권한을 제거하려면 제거하려는 권한의 상수값을 NOT 연산한 후 AND 연산을 실행
mode &= ~(S_IROTH);
mode 값을 변경한 후 chmod() 함수를 호출해야 변경된 접근 권한이 적용
chmod(pathname, mode);
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
int main() {
struct stat statbuf;
// 기존 권한에 관계없이 linux.txt 파일의 권한을 변경
// 소유자는 읽기/쓰기/실행 권한, 그룹은 읽기/실행 권한, 기타 사용자는 읽기 권한(754)으로 변경
chmod("linux.txt", S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH);
stat("linux.txt", &statbuf);
printf("1. mode = %o\n", (unsigned int)statbuf.st_mode);
statbuf.st_mode |= S_IWGRP; // st_mode에 그룹 쓰기 권한 추가
statbuf.st_mode &= ~(S_IROTH); // st_mode에 기타 사용자 읽기 권한 제거
chmod("linux.txt", statbuf.st_mode); // 앞서 설정한 값으로 chmod() 실행
stat("linux.txt", &statbuf);
printf("2. mode = %o\n", (unsigned int)statbuf.st_mode);
}
// 실행 결과: linux.txt 파일의 접근 권한이 변경
ㅇint fchmod(int fd, mode_t mode): 파일 기술자(fd)로 접근권한 변경
- fd : 열려있는 파일의 파일 기술자
- mode : 접근권한
접근권한을 변경할 파일의 파일 기술자(fd)를 받아 mode에 미리 정의한 상수값으로 변경할 권한을 지정
이미 열려있는 파일의 파일 기술자를 받아 접근권한을 변경시킨다
(접근 권한 지정 방법은 chmod() 함수와 같음)
[링크 함수]
ㅁ링크(link): 기존 파일이나 기존 디렉터리에 접근할 수 있는 새로운 이름을 의미(여러 이름으로 접근할 수 있게 하는 것)
링크의 방법에는 하드 링크와 심볼릭 링크(소프트 링크)가 있다
링크 기능의 장점: 이전 시스템과의 호환성 유지, 경로가 복잡한 파일에 간단한 경로를 제공
1. 하드 링크: 파일에 접근할 수 있는 파일명을 새로 생성하는 것
그냥 쉽게 생각하면, 똑같은 파일을 하나 더 만드는 것이다
기존 파일과 동일한 inode를 사용하며, 하드 링크를 생성하면 inode에 저장된 링크 개수가 증가한다
※ 터미널 명령어: ln testdata.txt testdata.ln
그렇다면 cp와 하드 링크의 차이점은 뭘까?
cp는 원본을 수정해도 cp 파일이 아무 영향을 받지 않지만, 하드 링크는 원본 파일이 바뀌면 하드 링크 파일도 같이 바뀐다
2. 심볼릭 링크(소프트 링크): 기존 파일에 접근할 수 있는 다른 파일을 만드는 것
쉽게 생각해서 "바로가기 링크"와 똑같다
기존 파일과 다른 inode를 사용하며, 기존 파일의 경로를 저장한다
※ 터미널 명령어: ln -s testdata.txt testdata.sym
ㅇint link(const char* oldpath, const char* newpath): 하드 링크를 생성할 때 사용
- oldpath : 기존 파일의 경로
- newpath : 새로 생성할 링크의 경로
성공하면 0, 실패하면 -1을 반환
(하드 링크는 같은 파일 시스템에 있어야 하므로 두 경로를 반드시 같은 파일 시스템으로 지정해야 한다)
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
struct stat statbuf;
// 하드 링크를 생성하기 이전의 링크 개수를 확인
stat("linux.txt", &statbuf);
printf("before link count = %d\n", (int)statbuf.st_nlink);
link("linux.txt", "linux.ln");
// 하드 링크를 생성한 이후의 링크 개수를 확인
stat("linux.txt", &statbuf);
printf("after link count = %d\n", (int)statbuf.st_nlink);
return 0;
}
// 실행결과: linux.ln 파일이 생성되었고, 링크 개수가 변경됨
// linux.txt와 linux.ln 파일의 inode가 1048600으로 동일하다는 것도 알 수 있음
ㅇint symlink(const char* target, const char* linkpath): 심볼릭 링크를 생성할 때 사용
- target : 기존 파일의 경로
- linkpath : 새로 생성할 심볼릭 링크의 경로
성공하면 0, 실패하면 -1을 반환
(심볼릭 링크는 기존 파일과 다른 파일 시스템에도 생성할 수 있다)
#include <unistd.h>
int main() {
// linux.txt 파일의 심볼릭 링크인 linux.sym 생성
symlink("linux.txt", "linux.sym");
return 0;
}
// 실행결과: linux.sym 파일이 생성
// 심볼릭 링크를 생성했지만 기존 파일에는 아무 변화가 없음을 알 수 있음
ㅇint lstat(const char* pathname, struct stat* statbuf): 심볼릭 링크 자체의 파일 정보를 검색
- pathname : 심볼릭 링크의 경로
- statbuf : 새로 생성할 링크의 경로
※ 심볼릭 링크를 stat() 함수로 검색하면 원본 파일에 대한 정보가 검색된다는 점에 주의
(첫 번째 인자로 심볼릭 링크가 온다는 것을 제외하면 stat() 함수와 동일한 방식으로 사용된다)
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main() {
struct stat statbuf;
// 기존 linux.txt 파일의 정보를 stat() 함수로 검색해 링크 개수와 inode 번호를 출력
printf("1. stat : linux.txt ---\n");
stat("linux.txt", &statbuf);
printf("linux.txt : Link Count = %d\n", (int)statbuf.st_nlink);
printf("linux.txt : Inode = %d\n", (int)statbuf.st_ino);
// stat() 함수를 사용해 심볼릭 링크 파일인 linux.sym의 정보를 검색하고 링크 개수와 inode 번호를 출력
printf("2. stat : linux.sym ---\n");
stat("linux.sym", &statbuf);
printf("linux.sym : Link Count = %d\n", (int)statbuf.st_nlink);
printf("linux.sym : Inode = %d\n", (int)statbuf.st_ino);
// lstat() 함수를 사용해 심볼릭 링크 파일인 linux.sym의 정보를 검색하고 링크 개수와 inode 번호를 출력
printf("3. lstat : linux.sym ---\n");
lstat("linux.sym", &statbuf);
printf("linux.sym : Link Count = %d\n", (int)statbuf.st_nlink);
printf("linux.sym : Inode = %d\n", (int)statbuf.st_ino);
return 0;
}
// 실행결과: 첫번째와 두번째의 검색 정보가 동일함
// 즉, 심볼릭 링크 정보를 stat() 함수로 검색하면 원본 파일의 정보가 검색되는 것을 알 수 있음
// 세번째 lstat() 함수로 검색한 심볼릭 링크 정보는 링크 파일 자체에 대한 정보로 다른 값이 출력됨
ㅇssize_t readlink(const char* pathname, char* buf, size_t bufsiz): 심볼릭 링크의 내용 읽기
- pathname : 심볼릭 링크의 경로
- buf : 읽어온 내용을 저장할 버퍼
- bufsiz : 버퍼의 크기
성공하면 읽어 온 데이터의 크기(바이트 수)를 반환, 실패하면 –1을 반환
심볼릭 링크의 경로를 받아 해당 파일의 내용을 읽는다
(호출 시 읽은 내용을 저장할 버퍼와 해당 버퍼의 크기를 인자로 지정)
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
int main() {
char buf[BUFSIZ];
int n;
// linux.sym 파일의 내용을 readlink() 함수로 읽어 buf에 저장
n = readlink("linux.sym", buf, BUFSIZ);
if (n == -1) {
perror("readlink");
exit(1);
}
buf[n] = '\0'; // buf의 끝에 NULL 문자를 추가
printf("linux.sym : READLINK = %s\n", buf); // 저장된 문자를 출력
return 0;
}
// 심볼릭 링크를 ls -l 명령으로 확인해보자
// -> 다음에 오는 원본 파일의 경로가 심볼릭 링크 데이터 블록에 저장된 내용이다
// 심볼릭 링크의 크기는 'linux.txt'의 바이트 수인 9임을 알 수 있음
ㅇchar* realpath(const char* path, char* resolved_path): 심볼릭 링크 원본 파일의 경로 읽기
- path : 심볼릭 링크의 경로명
- resolved _path : 경로명을 저장할 버퍼 주소
성공하면 실제 경로명이 저장된 곳의 주소를, 실패하면 NULL을 리턴
심볼릭 링크명을 받아 실제 경로명을 resolved_name에 저장
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
int main() {
char buf[BUFSIZ];
// 심볼릭 링크인 linux.sym의 원본 파일 경로를 얻고 출력
realpath("linux.sym", buf);
printf("linux.sym : REALPATH = %s\n", buf);
return 0;
}
ㅇint unlink(const char* pathname): 링크 끊기
- pathname : 삭제할 링크의 경로
unlink() 함수에서 연결을 끊은 경로명이 그 파일을 참조하는 마지막 링크라면 파일을 삭제
만약 인자로 지정한 경로명이 심볼릭 링크면 링크가 가리키는 원본 파일이 아니라 심볼릭 링크 파일이 삭제
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
int main() {
struct stat statbuf;
// linux.ln 파일의 정보를 검색해 링크 개수를 출력
stat("linux.ln", &statbuf);
printf("1.linux.ln : Link Count = %d\n", (int)statbuf.st_nlink);
unlink("linux.ln"); // 하드 링크 파일인 linux.ln의 링크를 끊어 삭제
// 원본 파일인 linux.txt 파일의 정보를 검색해 링크 개수를 출력
stat("linux.txt", &statbuf);
printf("2.linux.txt: Link Count = %d\n", (int)statbuf.st_nlink);
unlink("linux.sym"); // 심볼릭 링크 파일인 linux.sym의 링크를 끊어 삭제
return 0;
}
// 첫번째에서 검색한 하드 링크 파일의 링크 개수는 2
// 첫번째에서 unlink()로 하드 링크를 삭제한 뒤 원본 파일의 링크 개수를 확인한 결과 1로 변경됨
// 심볼릭 링크 파일이 삭제된 것을 알 수 있음
참고 및 출처: 시스템 프로그래밍 리눅스&유닉스(이종원)
'Computer Science > UNIX & Linux' 카테고리의 다른 글
[UNIX/Linux] ep3) 저수준 파일 입출력 (5) | 2024.09.30 |
---|---|
[UNIX/Linux] ep2+) 파일 함수 실습 (1) | 2024.09.26 |
[UNIX/Linux] ep1+) 디렉터리 함수 실습 (1) | 2024.09.20 |
[UNIX/Linux] ep1) 디렉터리 다루기 (2) | 2024.09.11 |
오픈소스 터미널 에뮬레이터(PuTTY, Termius, MobaXterm) (3) | 2024.09.04 |