이 글은 <시스템 프로그래밍 리눅스 & 유닉스(이종원 지음)> 책을 공부하며 정리한 것입니다!
파일에 대한 정보는 inode에 저장되어있는데,
inode의 정보를 검색하는 함수로는 state(), lstat(), fstat()
이 있다.
하나씩 파헤쳐보자!
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
pathname
: 정보를 알고자 하는 파일명
statbuf
: 검색한 파일 정보를 저장할 구조체 주소
파일의 정보를 검색할 때, 파일에 대한 읽기/쓰기/실행 권한이 반드시 있어야 하는 것은 아니지만,
파일에 이르는 경로의 각 디렉터리에 대한 읽기 권한은 있어야한다.
man -s 2 stat
으로 세부 구조 확인 가능
struct stat {
dev_t st_dev; //파일이 저장되어 있는 장치의 번호 저장
ino_t st_ino; //파일의 inode 번호 저장
mode_t st_mode; //파일의 종류와 접근 권한 지정
nlink_t st_nlink; //하드링크 개수 저장
uid_t st_uid; //파일 소유자의 uid 저장
gid_t st_gid; //파일 소유 그룹의 GID를 저장
dev_t st_rdev; //주 장치 번호와 부 장치 번호 저장(장치 파일일 때만 유효)
off_t st_size; //파일 크기
blksize_t st_blksize; //파일 내용 입출력 시 사용하는 버퍼의 크기
blkcnt_t st_blocks; //파일에 할당된 파일 시스템의 블록 수, 블록 크기는 512byte
struct timespec st_atim; //마지막으로 파일 읽거나 실행한 시각-timespec구조체
struct timespec st_mtim; //마지막으로 파일을 변경(쓰기)한 시각
struct timespec st_ctim; //inode의 내용을 변경한 시각
// 소유자/그룹 변경, 파일 크기 변경, 링크 개수 변경
#define st_atime st_atim.tv_spec
#define st_mtime st_mtim.tv_spec
#define st_ctime st_ctim.tv_spec
}
stat() 을 통해 파일명으로 inode 정보를 검색해보자!
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
int main(){
struct stat statbuf;
stat("linux.txt", &statbuf);
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_atim);
printf("Mtime = %d\n", (int)statbuf.st_mtim);
printf("Ctime = %d\n", (int)statbuf.st_ctim);
}
etc/passwd
파일을 참조해 어떤 사용자인지 알아낼 수 있다!#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.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);
printf("inode = %d\n", (int)statbuf.st_ino);
printf("uid = %d\n", (int)statbuf.st_uid);
close(fd);
}
inode = 1048600
uid = 1000
stat 구조체의 st_mode
항목에 파일의 종류와 접근 권한 정보가 저장되며,
st_mode에 저장된 값은 100644
와 같은 숫자이다.
st_mode 항목의 데이터형인 mode_t는 unsigned int
로 정의되어 있지만 실제 16비트
로 구성
sys/stath.h
파일에 정의된 상수와 매크로는 아래의 구조로 저장된 값과 상수를 AND 연산해 추출하는 것
아래의 표에서 S_IFMT(비스마스크)인 0170000랑 st_mode 값을 and 연산 후 아래의 표에서 일치하는 상숫값을 통해 파일의 종류를 알아낼 수 있다
말이 어려운가...? 코드를 보는 게 낫다 ..
//상수를 이용해 파일 종류 검색하기
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main() {
struct stat statbuf;
int kind;
stat("linux.txt", &statbuf); // stat으로 파일의 정보를 읽어옴
printf("Mode = %o\n", (unsigned int)statbuf.st_mode); //100644
kind = statbuf.st_mode & S_IFMT; //and 연산
printf("Kind = %o\n", kind); // 100000
switch(kind){ //and 연산 결과와 상숫값 비교해 파일의 종류 출력
case S_IFLMK:
printf("linux.txt: Symbolic Link\n");
break;
case S_IFDIR:
printf("linux.txt: Directory\n");
break;
case S_IFREG:
printf("linux.txt: Regular File\n"); //여기 출력->regular file임을 알 수 있음
break;
}
}
추가로 .. 8진수의 and 연산 어떻게 하더라? 싶은 사람들을 위해
대신 챗 지피티에게 물어보았습니다
0100644
와 0170000
을 이진수로 변환
0100644
를 이진수로 변환하면: 000 001 000 000 110 100 100
0170000
를 이진수로 변환하면: 000 001 111 000 000 000 000
이제 두 이진수 값을 비트 단위로 AND 연산합니다. AND 연산은 각 비트 위치에서 비트가 둘 다 1일 경우에만 결과가 1이 되는 연산입니다.
000001000000110100100 (0100644)
& 000001111000000000000 (0170000)
-------------------------
000001000000000000000
AND 연산의 결과인 이진수는 000001000000000000000
마지막으로, 이 결과를 8진수로 변환하여 표시합니다. 8진수로 변환할 때는 3비트씩 묶어서 변환하면 됩니다. 그 결과로 0100000
이 됩니다.
따라서 0100644
와 0170000
간의 8진수 AND 연산 결과는 0100000
입니다.
각 매크로는 인자로 받은 mode
값을 0xF000
와 and 연산을 수행한다.
연산의 결과를 파일의 종류별로 정해진 값과 비교해 파일의 종류를 판단한다.
판단할 때 사용하는 값은 위 표의 상수와 일치한다 !
매크로를 이용한 파일 종류 검색 코드를 살펴보자~!
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main() {
struct stat statbuf;
int kind;
stat("linux.txt", &statbuf);
printf("Mode = %o\n", (unsigned int)statbuf.st_mode);
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"); //결과는 동일!
기존에는 특수 접근 권한과 소유자 접근 권한에 해당하는 상수만 정의되어 있었기에,
아래의 st_mode의 구조에 따라 소유자 외의 사용자에 대한 접근은
st_mode & (S_IREAD >> 3)
이런 식으로 st_mode의 값을 왼쪽으로 3비트 이동시켜 그룹의 접근 권한을, 6비트 이동시켜 기타 사용자의 접근 권한을 알아내곤 했다.
(상숫값을 오른쪽으로 3비트 이동시키거나 상숫값을 6비트 이동시키기도 함.)
그치만.. 많이 번거롭다.
따라서 POSIX에서 shift 연산 대신 직접 AND 연산이 가능한 다른 상수를 정의했다
//상수를 이용해 파일 접근 권한 검색하기
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main() {
struct stat statbuf;
stat("linux.txt", &statbuf); // stat으로 파일의 정보를 읽어옴
printf("Mode = %o\n", (unsigned int)statbuf.st_mode); //100644
if ((statbuf.st_mode & S_IREAD) != 0)
printf("linux.txt: User has a read permission\n");
if ((statbuf.st_mode & S_IREAD >> 3)) != 0) //오른쪽으로 3만큼 이동
printf("linux.txt: Group has a read permission\n");
if ((statbuf.st_mode & S_IROTH) != 0) //POSIX가 정의한 S_IROTH 이용
printf("linux.txt: Other have a read permission\n");
}
}
파일의 접근 권한 검색할 수 있는 시스템 호출이다 !
pathname에 지정된 파일이 mode로 지정한 권한을 지니고 있는지 확인하고 리턴한다.
- 오류 메세지에 따른 오류 내용
ENOENT - 해당 파일이 존재하지 않거나 심벌릭 링크의 경우 원본 파일이 없어서 발생한 오류
EACCES - 접근 권한이 없어서 발생한 오류
주의할 점: 유효 사용자 ID(EUID - Effective User Id)가 아닌
실제 사용자 ID(RUID - Real User ID)에 대한 접근 권한만 확인 가능하다!
#include <unidstd.h>
int access(const char *pathname, int mode);
두 번째 인자인 mode
에 사용할 수 있는 상수는 <unistd.h>에 정의되어있으며,
필요에 따라 OR 연산자로 연결해 사용할 수도 있다.
R_OK
: 읽기 권한 확인W_OK
: 쓰기 권한 확인X_OK
: 실행 권한 확인F_OK
: 파일이 존재하는지 확인 #include <sys/errno.h>
#include <unistd.h>
#include <stdio.h>
extern int errno;
int main(){
int perm;
if (access("linux.bak", F_OK) == -1 && errno == ENOENT) //없는 파일인지 검사
printf("linux.bak: File not exist. \n");
perm = access("linux.txt", R_OK); //읽기 권한
if(perm == 0) // 0 이면 읽기 권한이 있다는 뜻!
printf("linux.txt: Read permission is permitted. \n");
else if (perm == -1 &&errno == EACCES)
printf("linux.txt: Read permission is not permitted. \n");
}
파일의 접근 권한을 변경할 때 사용하는 명령은 chmod,
이에 해당하는 시스템 호출도 chmod() !
fchmod()함수로도 접근 권한 변경 가능!
접근 권한을 변경할 파일의 경로를 받아 mode에 지정한 상숫값으로 권한 변경
S_ISUID 0x800(상숫값) setuid 설정 확인 시 사용
S_ISGID 0x400 setgid 설정 확인 시 사용
S_ISVTX 0x200 sticky 비트 설정 확인
소유자/그룹/기타 사용자의 접근 권한을 변경할 때는 아래의 상수명 이용
기존 접근 권한과 관계없이 접근 권한을 모두 새로 지정하려면 상수를 이용해 권한을 생성한 후 인자로 지정한다!
chmod(pathname, S_IRWXU);
chmod(pathname, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH);
기존 접근 권한을 변경해 권한을 조정할 수도 있는데, 이때는 stat()
을 사용해 기존 접근 권한을 읽고 더하거나 뺄 수 있다
접근 권한 더할 때는 OR 연산자를 사용한다 !
mode |= S_IWGRP;
접근 권한 제거하려면 제거하려는 권한의 상숫값을 NOT 연산 한 후 AND 연산을 실행한다.
mode &= ~(S_IROTH);
mode 값을 변경한 후 chmod()를 호출해야 변경된 접근 권한이 적용된다!
접근 권한을 변경할 파일의 파일 기술자를 받아 mode에 미리 정의한 상숫값으로 변경할 권한을 지정한다!
chmod()
함수는 파일의 경로를 받지만, fchmod()
함수는 이미 열려 있는 파일의 fd를 받아 접근 권한을 변경한다.#include <sys/stat.h>
int fchmod(int fd, mode_t mode);
//fd: 열려 있는 파일의 파일 기술자
//mode: 접근 권한
그치만.. 보통의 경우 chmod() 함수를 쓴다 ..
// chmod() 예제
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main() {
struct stat statbuf;
chmod("linux.txt", S_IRXU|S_IRGRP|S_IXGRP|S_IROTH);
// 기존 권한에 관계없이 linux.txt 파일의 권한 변경
// 소유자: rwx, 그룹 : rx, 기타 사용자 : r (754) 권한 부여
stat("linux.txt", &statbuf);
printf("1. Mode = %o\n", (unsigned int)statbuf.st_mode);
statbuf.st_mode |= S_IWGRP; //그룹 w 권한 추가
statbuf.st_mode &= ~(S_IROTH); //기타 사용자 r 권한 제거
chmod("linux.txt", statbuf.st_mode); // 앞서 설정한 값으로 변경하겠다!
stat("linux.txt", &statbuf);
printf("2. Mode = %o\n", (unsigned int)statbuf.st_mode);
}