상세 컨텐츠

본문 제목

UNIX - system call [link, unlink, remove, rename, symlink, readlink, stat]

Computer Science/UNIX

by 2021. 10. 6. 23:44

본문

반응형

link(2)

#include <unistd.h>

int link(const char *orginal_path, const char *new_path);
// Returns: 0 if OK, -1 on error

orginal_path에 해당하는 파일에 new_path에 해당하는 directory entry가 추가된다. 즉 origin_path에 해당하는 파일은 new_path라는 추가적인 link를 갖게 된다. 따라서 i-node에 관한 link count가 1증가하게 된다.

 link system call은 디렉토리에 대한 hard link를 추가하는 작업이기 때문에 superuser에 의해서만 가능하다. 

unlink(2)

#include <unistd.h>

int unlink(const char *pathname);
// Returns: 0 if OK, -1 on error

link를 삭제하는 system call이다.

삭제하고자 하는 pathname만을 매개변수로 필요로 하며, 해당 파일을 unlink한다는 것은 해당 이름의 파일을 삭제하는 것과 동일한 의미이다. unlink시 파일의 link가 1만큼 감소하며, link count가 0이 되면 해당 파일의 i-node는 free가 되고 파일은 삭제된다. 

 unlink permission에는 write권한이 필요하다. 

 

example

예시 코드를 보자

/* move -- 한 화일을 하나의 경로이름으로부터 다른 경로이름으로 옮긴다. */
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

char *usage = "usage: move file1 file2\n";

/* main은 명령줄에 의해 표준적인 방법으로 전달된 인수를 사용한다. */
main (int argc, char **argv){
 if (argc != 3){
         fprintf (stderr, usage); exit (1);
 }

 if ( link (argv[1], argv[2]) == -1){
 	perror ("link failed");
 	exit (1);
 }

 if ( unlink(argv[1]) == -1){
 	perror ("unlink failed");
 	unlink (argv[2]);
 	exit (1);
 }
 printf ("Succeeded\n");
}

arg에 대해서 먼저 보자. 

$ a.out arg1 arg2 arg3

위와 같이 입력되었을 경우 문자열을 각각 분리해서 a.out / arg1 / arg2 / arg3로 총 4개 이므로 argc는 4, a.out부터 차례로 argv[0] 부터 들어간다. 즉 위의 명령어의 경우

argv[0] = "a.out"
argv[1] = "arg1"
argv[2] = "arg2"
argv[3] = "arg3"

인 것이다.

/* move -- 한 화일을 하나의 경로이름으로부터 다른 경로이름으로 옮긴다. */
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

char *usage = "usage: move file1 file2\n";

/* main은 명령줄에 의해 표준적인 방법으로 전달된 인수를 사용한다. */
main (int argc, char **argv){
 if (argc != 3){
         fprintf (stderr, usage); exit (1);
 }

 if ( link (argv[1], argv[2]) == -1){
 	perror ("link failed");
 	exit (1);
 }

 if ( unlink(argv[1]) == -1){
 	perror ("unlink failed");
 	unlink (argv[2]);
 	exit (1);
 }
 printf ("Succeeded\n");
}

다시 코드를 보면 link를 통해서 argv[1]과 argv[2]를 연결하여 hard link를 추가한다. argv[1]에 해당하는 파일은 argv[2] link까지 총 hard link가 2개인 것이다.

 그 다음 unlink를 통해서 argv[1]를 삭제한다. 즉 기존에 argv[1]과 연결되어있던 파일이 argv[2]와 link되고 argv[1]과의 연결은 끊어지는 것이다. argv[1]에 있던 "arg1"이라는 파일이름에 연결되어있던 link가 끊어지고 "arg2"라는 파일이름에 연결되는 것이므로 즉 파일 이름이 변경되는 rename의 코드인 것이다.

remove(2)

#include <stdio.h>

int remove(const char *pathname);
// Returns: 0 if OK, -1 on error

unlink와 동일하고 이름만 삭제한다는 뜻의 remove인 system call이다. 

rename(2)

#include <stdio.h>

int rename(const char *oldname, const char *newname);
// Returns: 0 if OK, -1 on error

위에서 link와 unlink를 통해 rename을 하는 코드를 예시로 보았다. 그런데 rename을 하는 동작이 많이 쓰여지다보니 아예 system call로 하나 만들어 버린 것이다.

symlink(2)

#include <unistd.h>

int symlink(const char *realname, const char *symname);
// Returns: 0 if OK, -1 on error

앞서 다뤘던 symbol link과 관련된 system call이다. symname가 가리키는 파일 안에 realname파일의 절대경로가 문자열의 형태로 들어도록 하여 symbol link관계를 만들어준다. 

만약 open system call을 통해서 symname을 open하면 반환되는 file descriptor는 realname의 file descriptor가 반환된다. 즉 symbol link 파일을 open하면 symbol link가 가리키는 real file의 descriptor를 반환한다.

symname 즉 symbol link 파일자체의 내용을 보고 싶다면 readlink를 사용해야한다. 

readlink(2)

#include <unistd.h>

ssize_t readlink(const char* sympath, char* buffer, size_t bufsize);
// Returns: number of bytes read if OK, -1 on error

readlink는 symbol link 자체의 내용 즉 realname의 절대경로가 문자열로 표현된 것을 buffer에 담는 일을 한다. 

  1. symbol link파일인 sympath를 열고
  2. 해당 파일의 내용을 buffer로 read한 후
  3. sympath를 close하는 작업까지 진행한다.

buffer로 읽은 byte수를 반환한다.

만약 original file이 삭제되면 symbolic link file을 open 하였을 때 EEXIST 에러를 띄운다. 

 

Obtaining file information: stat and fstat

이번에는 파일의 정보를 가져오는 system call에 대해서 알아보자

stat(2)

#include <sys/stat.h>

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

int fstat(int filedes, struct stat *buf);

int lstat(const char *pathname, struct stat *buf);

// All three return: 0 if OK, -1 on error

stat은 i-node안의 정보를 가져와서 보는 system call이다. 

pathname 파일에 hard link된 i-node의 정보를 buf에 담는다. 

fstat는 이미 오픈한 파일의 file descriptor를 통해서 i-node의 정보를 buf에 담는다. 

lstat의 l은 symbolic link를 의미한다. lstat의 pathname은 symbolic link file이고 lstat는 symbolic link file 자체에 대한 정보만을 buf에 담는다.

만약 stat에 symbolic link를 넣게되면 symbolic link가 가리키는 real file의 정보를 buf 에 담는다.

 

stat의 buf에 담는 stat struct 의 구조는 다음과 같다.

struct stat {
       mode_t    st_mode;    /* file type & mode (permissions) */
       ino_t     st_ino;     /* i-node number (serial number) */
       dev_t     st_dev;     /* device number (file system) */
       dev_t     st_rdev;    /* device number for special files */
       nlink_t   st_nlink;   /* number of links */
       uid_t     st_uid;     /* user ID of owner */
       gid_t     st_gid;     /* group ID of owner */
       off_t     st_size;    /* size in bytes, for regular files */
       time_t    st_atime;   /* time of last access */
       time_t    st_mtime;   /* time of last modification */
       time_t    st_ctime;   /* time of last file status change */
       blksize_t st_blksize; /* best I/O block size */
       blkcnt_t  st_blocks;  /* number of disk blocks allocated */
     };

mode_t자료는 permission에 대한 정보를 담는데 file type 4bit와 권한 종류 mode 12bit 종 16bit로 이루어져 있다. 

 

example

예시 코드를 보자.

/* filedata -- 한 화일에 관한 정보를 출력 */

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

/* 허가 비트가 설정되어 있는지 결정하기 위해 octarray를 사용 * /
static short octarray[9] = {	0400, 0200, 0100,	
				0040, 0020, 0010,
				0004, 0002, 0001};

/* 화일 허가에 대한 기호화 코드 끝부분의 null 때문에 길이가 10문자이다. */
static char perms[10] = "rwxrwxrwx";

int filedata (const char *pathname)
{
 struct stat statbuf;
 char descrip[10];
 int j:

 if(stat (pathname, &statbuf) == -1)
 {
	 fprintf (stderr, "Couldn't stat %s\n", pathname);
	 return (-1);
 }
 /* 허가를 읽기 가능한 형태로 바꾼다. */

 for(j=0; j<9; j++)
 {
	 / * 비트별 AND를 사용하여 허가가 설정되었는지 테스트 */
	 if (statbuf.st_mode & octarray[j])
		 descrip[j] = perms[j];
	 else
		 descrip[j] = '-';
 }

 descrip[9] = '\0'; /* 하나의 문자열을 가지도록 확인 */

 /* 화일 정보를 출력한다. */

 printf ("\nFile %s :\n", pathname);
 printf ("Size %ld bytes\n", statbuf.st_size);
 printf ("User-id %d, Group-id %d\n\n", statbuf.st_uid, statbuf.st_gid);
 printf ("Permissions: %s\n", descrip);
 return (0);
}

stat를 통해 i-node에서 permission정보를 가져온 후 출력하는 프로그램이다.

octarray는 0~8까지 각 permission bit자리를 의미한다. 각 자리에 대한 r/w/x 값을 8진수 수로 표현한 것이다.

for문에서 각 permission bit 자리에 해당하는 bit가 설정되었는지를 확인한다.

 

stat를 이용해서 statbuf에 정보를 담았는데, statbuf.st_mode의 형태는 아래와 같다.

type special Permission
4 bit u g s r w x r w x r w x

st_mode와 octarray값을 각각 bit wise 연산하여 perms에 각각의 permission bit 결과를 담아 출력한다. 

 

example

다음 예제 코드를 보자

/* lookout -- 화일이 변경될 때 메시지를 프린트 */
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>

#define MFILE    10

void cmp(const char *, time_t);
struct stat sb;

main (int argc, char **argv)
{
   int j;
   time_t last_time[MFILE+1];

   if(argc < 2){
      fprintf (stderr, "usage: lookout filename ...\n");
      exit (1);
   }

   if(――argc > MFILE){
      fprintf (stderr, "lookout: too many filenames\n");
      exit (1);
   }
   for (j=1; j<=argc; j++){	/* 초기화 */
      if (stat (argv[j], &sb) == -1){
         fprintf (stderr, "lookout: couldn't stat %s\n", argv[j]);
         exit (1);
      }
      last_time[j] = sb.st_mtime;
   }

   for (;;){			/* 화일이 변경될 때까지 루프 */
      for (j=1; j<=argc; j++)
         cmp (argv[j], last_time[j]);

        /* 60초간 쉰다. “sleep"는 표준 UNIX 라이브러리 루틴이다. */
      sleep (60);
   }
}

void cmp(const char *name, time_t last){
   /* 화일에 관한 통계를 읽을 수 있는 한 변경시간을 검사한다. */
   if (stat(name, &sb) == ―1 || sb.st_mtime ! = last){
      fprintf (stderr, "lookout: %s changed\n", name);
      exit (0);
   }
}

위 코드는 파일의 변경사항을 출력하는 프로그램이다. 

MFILE은 command line argument의 최대 갯수이다.

argc가 2보다 작을 경우 argument가 없으므로 exit하고, MFILE보다 커도 exit 한다. 

 

그 다음 for문을 통해 각 argv가 수정된 마지막 시간 st_mtime을 last_time 배열에 담고, 무한 루프를 시작한다. 

루프마다 변경사항을 체크하고, 60초 sleep함으로써 60마다 계속해서 변경사항을 검사하고, 변경되었을 경우 변경된 argv 이름과 수정시간을 출력한다.

 

cmp 함수의 조건문을 보자.

 마지막 수정 시간이 변경되었다면 sb.st_mtime != last는 true일 것이고, stat system call가 정상적으로 i-node의 정보를 읽으면 -1이 아니므로 false가 나와 둘의 ||연산 값은 true가 될 것이다.

stat system call이 -1값이 나와 true가 되는 경우는 name에 해당 파일이 삭제되는 경우이다. 

 

example

다음 예제 코드를 보자

/* addx -- 화일에 수행허가를 추가 */
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>

#define XPERM  0100       / *  소유자에 대한 수행 허가 */

main(int argc, char **argv){
   int k;
   struct stat statbuf;

   for (k=1; k<argc; k++){/* 인수 리스트의 모든 화일에 대해 루프 */
      /* 현행 화일 모드를 얻음 */
      if (stat (argv[k], &statbuf) == -1){
         fprintf (stderr, "addx: couldn't stat %s\n", argv[k]);
         continue;
      }

      /* 비트별 OR 연산을 사용하여 수행허가의 추가를 시도 */
      statbuf.st_mode |= XPERM; //statbuf.st_mode = statbuf.st_mode + XPERM; 
      if (chmod (argv[k], statbuf.st_mode) == -1)
         fprintf (stderr, "addx: couldn't change mode for %s\n", argv[k]);

   } /* 루프의 끝 */

위 코드는 파일 permission에 'x' 권한 즉 executable 권한을 추가하는 코드이다. 

argv들을 순회하다가 stat이 성공할 경우 statbuf.st_mode에 XPERM 즉 'x' 권한을 bit wise or 연산으로 추가시킨다.

XPERM 을 추가한 statbuf.st_mode를 chmod를 통해 argv에 적용시켜 권한을 변경한다. 

반응형

관련글 더보기