DIR 타입은 디렉터리를 읽어 들이기 위한 스트림을 관리하는 구조체
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *path);
디렉터리 스트림 d로부터 엔트리를 하나씩 읽어 들여 struct dirent(DIRectory ENTry)타입으로 반환
#include <sys/types.h>
#include <dirent.h>
struct dirent *readdir(DIR *d);
return 디렉터리 스트림 d로부터 엔트리를 하나씩 읽어 들여 struct dirent(DIRectory ENTry)타입으로 반환, 더 읽을 엔트리가 없거나 읽어 들이는 데 실패하면 NULL 반환
struct dirent의 내용
운영체제마다 다르나, 리눅스에는 적어도 엔트리의 이름에 해당하는 char *d_name 존재
d_name은 '\0'을 마지막에 담고 있는 문자열이라 printf()나 fputs()에서 그대로 사용 가능
readdir()이 반환한 포인터는 다시 호출했을 때 덮어 쓰이므로 주의
디렉터리 스트림 d를 닫는 함수
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *d);
return 성공하면 0, 실패하면 -1 반환
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
static void do_ls(char *path);
int
main(int argc, char *argv[]){
int i;
if (argc < 2) {
fprintf(stderr, "%s: no arguments\n", argv[0]);
exit(1);
}
for (i = 1; i < argc; i++) {
do_ls(argv[i]);
}
exit(0);
}
static void do_ls(char *path){
DIR *d;
struct dirent *ent;
//경로에 있는 디렉터리를 opendir로 열기
d = opendir(path);
//path가 존재하지 않거나 디렉터리가 아니어서 NULL 반환된 경우 exit()
if (!d) {
perror(path);
exit(1);
}
//더 읽어들일 엔트리가 없을 때까지(NULL이 반환될 때까지) readdir() 반복해 이름 출력
while (ent = readdir(d)) {
printf("%s\n", ent->d_name);
}
//opendir() 후에는 반드시 closedir()
closedir(d);
}
dirent 구조체
struct dirent{
long d_ino; // 파일이 가지고 있는 자신만의 번호 inode를 가리키는 숫자
off_t d_off; // dirent의 offset
unsigned short d_reclen; // d_name의 길이
char d_name [NAME_MAX+1]; //파일 혹은 디렉토리의 이름(없다면 NULL로 종료)
}
시스템의 ls 커맨드는 -a 옵션을 사용하지 않는 한 '.'으로 시작하는 엔트리를 표시하지 않으므로 일반적으로는 '.'와 '..'는 표시되지 않음
readdir()의 결과는 이름순으로 정렬되어 있지 않음
디렉터리 안의 또 다른 디렉터리까지 접근하고 싶은 경우(즉, 재귀적으로 접근하고 싶은 경우)의 조작을 디렉터리 트리 순회라고 함
기본적으로 순회할 때도 opendir(), readdir(), closedir() 세 가지를 사용해 꾸준히 디렉터리를 타고 들어가면 되지만, 주의사항 존재
void
traverse(path){
DIR *d = opendir(path);
struct dirent *ent;
while (ent = readdir(d)) {
if /* ent가 디렉터리이면 */ {
traverse(path/ent);
}
/* 처리 */
}
}
int mkdir(const char *dirname);
dirname : 생성할 디렉토리 경로와 이름
return : 정상 일 때 0, 에러 시 -1
Error
EEXIST: 만들려는 폴더가 이미 존재
ENOENT: 상위디렉터라가 없을경우
ENOTDIR: path로 지정한 상위디렉터라가 디렉터리가 아닐경우
EPERM: 상위 디렉터리에 대한 변경 권한이 없다
mode_t umask(mode_t mask);
mkdir 이나 open 을 사용할 때 만들어질 파일의 권한을 지정할 수 있지만, 두 경우 모두 지정한 값이 그대로 사용되는 것은 아니다. umask를 사용해서 변경된 값이 사용된다.
umask는 프로세스의 속성중 하나로, 가장 일반적인 값은 8진수 022다. open 이나 mkdir 에서 실제로 사용되는 권한은 c언어로 표현하면, 'mode & ~ umask로 계산된다. 즉, 인자로 지정한 mode 로부터 umask에 포함되는 비트를 빼는 것이다. 예를 들어 인자로 지정한 mode가 0777이고 mask가 022라면 실제 권한은 0755가 된다.
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
int main(int argc, char *argv[]){
int i;
if (argc < 2) {
fprintf(stderr, "%s: no arguments\n", argv[0]);
exit(1);
}
for (i = 1; i < argc; i++) {
if (mkdir(argv[i], 0777) < 0) {
perror(argv[i]);
exit(1);
}
}
exit(0);
}
rmdir 은 path로 지정한 디렉터리를 삭제
디렉토리 내에 파일이 존재하거나 사용중이면 삭제할 수 없다.
삭제 전에 디렉토리 존재유무 확인 후 삭제해야 한다.
int rmdir( const char *dirname );
//dirname : 삭제할 디렉토리 경로
//반환값 : 성공하면 0, 에러 시 -1/errno 설정
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main (int argc, char *argv[]){
int i;
if (argc < 2) {
fprintf(stderr, "%s : no arguments\n" , argv[0]);
exit(1);
}
for (i = 1 ; i < argc; i++) {
// -1 리턴되면 실패(에러처리)
if (rmdir(argv[i]) < 0) {
perror(argv[i]);
exit(1);
}
}
exit(0);
}
리눅스에서는 하나의 파일에 두개 이상의 이름을 지정할 수 있다.
이를 위해 링크라는 개념이 존재
rm이 삭제하는 것은 파일이 아니고 실체를 가리키고있는 파일의 이름이 모두 없어진 시점에(링크 카운트 0)이 되었을 때 비로소 실체가 삭제
링크를 작성
int link( const char *oldpath, const char *newpath)
oldpath: 이미 존재하는 파일 이름
newpath: 만들고자하는 링크 이름
return : 성공 0, 실패 -1/errno
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]){
if (argc != 3) {
fprintf(stderr, "%s: wrong arguments\n", argv[0]);
exit(1);
}
이미 존재하는 파일에 대해서 argv[2]이름으로 링크를 작성
if (link(argv[1], argv[2]) < 0) {
perror(argv[1]);
exit(1);
}
exit(0);
}
하드링크는 이름과 실체를 연결하는 구조
심볼릭 링크는 이름에 이름을 연결하는 구조
심볼릭 링크에 대응하는 실체가 존재하지 않아도 된다. 심볼릭 링크는 실제로 액세스 할때가 아니면 이름과 실체의 매핑을 하지 않기 때문에 실체가 없어도 만들 수 있다.
파일 시스템의 경계를 뛰어넘어 별명을 만들 수 있다. 하드링크는 하나의 파일 시스템 내에서만 만들 수 있는 제약이 있다. 그러나 심볼릭 링크는 파일 시스템의 경계와 관계없이 만들 수 있다.
디렉터리에도 별명을 붙일 수가 있다. 디렉터리에 대해서는 하드링크는 만들 수 없지만 심볼릭 링크는 만들 수 있다.
원본 파일이 삭제되면 심볼릭 링크로 원본파일에 접근할 수 없다.(고아링크), 하드링크는 가능하다.
int symlink(const char *oldpath, const char *newpath);
return 성공 0, 실패 -1/errno
ssize_t readlink(const char *path, char *buf, size_t bufsiz);
return 성공 buf에 포함된 바이트 수, 실패 -1/errno
$ ln -s
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]){
if (argc != 3) {
fprintf(stderr, "%s: wrong number of arguments\n", argv[0]);
exit(1);
}
if (symlink(argv[1], argv[2]) < 0) {
perror(argv[1]);
exit(1);
}
exit(0);
}
int unlink(const char *pathname);
정확하게는 unlink는 hard link의 이름을 삭제하고 hard link가 참조하는 count를 1감소시킨다.
hard link의 참조 count가 0이 되면, 실제 파일의 내용이 저장되어 있는 disk space를 free하여 OS가 다른 파일을 위해서 사용할 수 있도록 한다.
따라서 hard link를 생성하지 않은 파일은 바로 disk space를 해제하여 사실상 파일 삭제한다.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main (int argc, char *argv[]){
int i;
if (argc < 2) {
fprintf(stderr, "%s : no arguments\n" , argv[0]);
exit(1);
}
for (i = 1; i < argc; i++) {
//파일명 삭제
if (unlink(argv[i]) < 0) {
perror(argv[1]);
exit(1);
}
}
exit(0);
}
리눅스에서 파일을 이동한다는 것은 하나의 실체에 대한 이름을 변경한다는 것과 대체로 동일
$ mv a b
$ ln a b
$ rm a
는 최종적인 결과는 같지만 작업과정에는 중대한 차이가 있음
ln과 rm을 사용하는 경우에는 작업 도중에 이름 a, b가 모두 존재하는 순간이 존재하지만 mv는 그렇지않다.(하나의 명령어로 이름이 변경되기 때문에)
int rename( const char *oldname, const char *newname );
oldname: 파일 또는 디렉토리의 경로나 이름
newname: 변경할 파일 또는 디렉토리의 이름
return 성공 시 0, 실패 시 errno(EACCES, ENOENT, EINVAL)
어떤 종류의 파일이라도 이동할 수 있지만, 파일 시스템을 넘어서 이동할 수는 없다. oldname, newname이 존재하는 파일 시스템이 서로 다른 경우 rename은 실패하고, errno에 상수 EXDEV가 설정된다.
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
return 성공 0, 실패 -1/errno
int access(const char *path, int amode);
메타 정보 변경하는 시스템콜
권한: chmod(2)
오너와 그룹: chown(2)
최종 액세스 시각과 최종 갱신 시간: utime(2)
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);
return 성공 0, 실패 -1/errno
파일 path의 소유 사용자르라 owner로 소유 그룹을 group로 변경
int chown(const char *path, uid_t owner, gid_t group);
int lchown(const char *path, uid_t owner, gid_t group);
return 성공 0, 실패 -1/errno
int utime(const char *filepath, const struct utimbuf *time);
struct utimbuf {
time_t actime; // 접근 시간
time_t modtime; // 수정 시간
};
filepath: 대상 파일의 경로
time: 변경될 시간
return 성공 시 0, 에러 시 -1/errno