리눅스 파일에 대한 정보와 접근 권한을 검색하고 변경해보자!

funky·2023년 10월 24일
0
post-thumbnail
이 글은 <시스템 프로그래밍 리눅스 & 유닉스(이종원 지음)> 책을 공부하며 정리한 것입니다!

파일 정보 검색

파일에 대한 정보는 inode에 저장되어있는데,
inode의 정보를 검색하는 함수로는 state(), lstat(), fstat() 이 있다.

하나씩 파헤쳐보자!


stat() : 파일명으로 파일 정보 검색

  • 파일 정보를 검색할 때 가장 많이 사용하는 함수
  • pathname에 지정한 파일의 정보를 검색한다 !
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int stat(const char *pathname, struct stat *statbuf);

pathname: 정보를 알고자 하는 파일명
statbuf: 검색한 파일 정보를 저장할 구조체 주소


파일의 정보를 검색할 때, 파일에 대한 읽기/쓰기/실행 권한이 반드시 있어야 하는 것은 아니지만,
파일에 이르는 경로의 각 디렉터리에 대한 읽기 권한은 있어야한다.

  • 성공하면 0을 반환하고 inode 구조체에 파일 정보를 저장함
  • 실패하면 -1를 리턴

stat 구조체

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);
   
}
  • 예시 결과값
  • uid와 gid 값이 1000으로 출력되었다면 etc/passwd 파일을 참조해 어떤 사용자인지 알아낼 수 있다!
  • 만약 파일의 크기가 219바이트이고 블럭의 크기가 4096바이트가 나왔다면,블럭의 개수는 블럭의 크기 / 512 해서 8개이다

fstat(): 파일 디스크립터로 파일 정보 검색

  • 현재 열려있는 파일의 fd를 인자로 받아 파일 정보를 검색한 후 statbuf로 지정한 구조체에 저장
#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 구조

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 연산 어떻게 하더라? 싶은 사람들을 위해
대신 챗 지피티에게 물어보았습니다

8진수(100644와 01700000) & 연산 과정

  1. 01006440170000을 이진수로 변환

    • 0100644를 이진수로 변환하면: 000 001 000 000 110 100 100
    • 0170000를 이진수로 변환하면: 000 001 111 000 000 000 000
  2. 이제 두 이진수 값을 비트 단위로 AND 연산합니다. AND 연산은 각 비트 위치에서 비트가 둘 다 1일 경우에만 결과가 1이 되는 연산입니다.

   000001000000110100100 (0100644)
 & 000001111000000000000 (0170000)
   -------------------------
   000001000000000000000
  1. AND 연산의 결과인 이진수는 000001000000000000000

  2. 마지막으로, 이 결과를 8진수로 변환하여 표시합니다. 8진수로 변환할 때는 3비트씩 묶어서 변환하면 됩니다. 그 결과로 0100000이 됩니다.

따라서 01006440170000 간의 8진수 AND 연산 결과는 0100000입니다.



매크로를 이용해 파일 종류 검색

각 매크로는 인자로 받은 mode 값을 0xF000and 연산을 수행한다.
연산의 결과를 파일의 종류별로 정해진 값과 비교해 파일의 종류를 판단한다.
판단할 때 사용하는 값은 위 표의 상수와 일치한다 !

매크로를 이용한 파일 종류 검색 코드를 살펴보자~!

#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");
    }
}


함수를 이용한 접근 권한 검색 : access()

파일의 접근 권한 검색할 수 있는 시스템 호출이다 !
pathname에 지정된 파일이 mode로 지정한 권한을 지니고 있는지 확인하고 리턴한다.

  • 접근 권한이 있으면 0을, 오류가 있으면 -1을 리턴
- 오류 메세지에 따른 오류 내용
	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()를 호출해야 변경된 접근 권한이 적용된다!



파일 디스크립터로 접근 권한 변경 : fchmod()

접근 권한을 변경할 파일의 파일 기술자를 받아 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); 
}

  • 실행 결과
profile
living Fun & Lucky

0개의 댓글