". 과 .. 은 모든 디렉터리에 항상 존재하는 파일 이름이며, 디렉터리가 생성될 때 자동적으로 포함된다"
ㅁ디렉터리 허가
- 읽기(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
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 |