하위 디렉토리의 생성 및 수정과 소프트 링크를 위한 시스템 콜들을 선언해 준다. 그리고 그에 맞는 함수들을 만들어 주어야 한다.
syscall 수정
아래 시스템 콜들을 받을 수 있도록 syscall handler를 수정해 준다.
/* syscall.c */
void
syscall_handler (struct intr_frame *f UNUSED) {
// TODO: Your implementation goes here.
// printf("syscall! , %d\\n",f->R.rax);
#ifdef VM
thread_current()->rsp_stack = f->rsp;
#endif
switch (f->R.rax)
{
...
case SYS_CHDIR:
f->R.rax = chdir(f->R.rdi);
break;
case SYS_MKDIR:
f->R.rax = mkdir(f->R.rdi);
break;
case SYS_READDIR:
f->R.rax = readdir(f->R.rdi, f->R.rsi);
break;
case SYS_ISDIR:
f->R.rax = isdir(f->R.rdi);
break;
case SYS_INUMBER:
f->R.rax = inumber(f->R.rdi);
break;
case SYS_SYMLINK:
f->R.rax = symlink(f->R.rdi, f->R.rsi);
break;
default:
exit(-1);
break;
}
}
chdir
현재 디렉토리를 dir_input으로 변경한다. parse_filepath를 통해 주어진 경로를 분리한다. find_subdir을 통해 파싱한 디렉토리 이름으로 하위 디렉토리를 검색한다. 만약 하위 디렉토리가 없다면 갈 곳이 없는 것이므로 false를 반환한다. 만약 파일 이름이 루트라면 현재 디렉토리를 루트로 변경하고 path 자원 할당 해제 후 true를 반환한다. 만약 root가 아니라면 dir_lookup을 통해 inode를 검색하여 해당 inode로 디렉토리를 open한다.open한 디렉토리를 현재 디렉토리로 설정한 후 true를 반환한다.
/* syscall.c */
bool
chdir (const char *dir_input) {
struct path* path = parse_filepath(dir_input);
if(path->dircount==-1) {
return false;
}
struct dir* subdir = find_subdir(path->dirnames, path->dircount);
if(subdir == NULL) {
dir_close (subdir);
free_path(path);
return false;
}
if(subdir == NULL) return false;
if (!strcmp(path->filename, "root")){
set_current_directory(dir_open_root());
dir_close(subdir);
free_path(path);
return true;
}
struct inode *inode = NULL; // inode of subdirectory or file
dir_lookup(subdir, path->filename, &inode);
if (inode == NULL) return false;
set_current_directory(dir_open(inode));
dir_close (subdir);
free_path(path);
return true;
}
/* directory.c */
// find subdirectory that contains last file/subdirectory in the path
// ex) a/b/c/d/e -> returns inode (dir_entry table) of directory 'd'
// returns NULL if path is invalid (ex. some subdirectory missing - a/b/c/d/e 중 c가 없다거나)
struct dir *find_subdir(char ** dirnames, int dircount){
int i;
struct inode *inode_even = NULL;
struct inode *inode_odd = NULL;
struct inode *inode = NULL; // inode of subdirectory or file
struct dir *cwd = current_directory();
if (cwd == NULL){
return NULL;
}
struct dir *subdir = dir_reopen(cwd); // prevent working directory from being closed
for(i = 0; i < dircount; i++){
struct dir *olddir = subdir;
if (i == 0 && (strcmp(dirnames[i],"root") == 0)){ // path from root dir
subdir = dir_open_root();
dir_close(olddir);
continue;
}
if (i % 2 == 0)
{
dir_lookup(olddir, dirnames[i], &inode_even);
inode = inode_even;
}
else
{
dir_lookup(olddir, dirnames[i], &inode_odd);
inode = inode_odd;
}
if(inode == NULL) return NULL;
subdir = dir_open(inode);
dir_close(olddir);
}
return subdir;
}
/* filesys.c */
struct path* parse_filepath (const char *name_original){
const int MAX_PATH_CNT = 30;
struct path* path = malloc(sizeof(struct path));
char **buf = calloc(sizeof(char *), MAX_PATH_CNT); // #ifdef DBG 🚨 로컬 변수 -> 함수 끝나면 정보 날라감; 메모리 할당해주기!
int i = 0;
int pathLen = strlen(name_original)+1;
char* name = malloc(pathLen);
strlcpy(name, name_original, pathLen);
// printf("pathLen : %d // %s, %d, copied %s %d\\n", pathLen, name_original, strlen(name_original), name, strlen(name)); // #ifdef DBG
path->pathStart_forFreeing = name; // free this later
if (name[0] == '/'){ // path from root dir
buf[0] = "root";
i++;
}
char *token, *save_ptr;
token = strtok_r(name, "/", &save_ptr);
while (token != NULL)
{
// File name too long - 'test: create-long.c'
if(strlen(token) > NAME_MAX){
path->dircount = -1; // invalid path
return path;
}
buf[i] = token;
token = strtok_r(NULL, "/", &save_ptr);
i++;
}
path->dirnames = buf;
path->dircount = i-1;
path->filename = buf[i-1]; // last name in the path
return path;
}
void free_path(struct path* path){
free(path->pathStart_forFreeing);
free(path->dirnames);
free(path);
}
mkdir
dir_input의 디렉토리를 새로 생성한다. chdir과 마찬가지로 일단 인풋값을 파싱해 준다. fat_create_chain을 통해 클러스터를 새로 할당 받는다. 그리고 해당 클러스터를 섹터 값으로 변환 한뒤 변환된 섹터 값으로 디렉토리를 새로 생선한다. 모든 디렉토리에 필요한 . 과 .. 을 디렉토리에 추가하고 파일 이름의 디렉토리를 추가 한 후 true를 반환한다.
bool mkdir (const char *dir_input){
bool success = false;
if(strlen(dir_input) == 0) return false;
struct path* path = parse_filepath(dir_input);
if(path->dircount==-1) {
return false;
}
struct dir* subdir = find_subdir(path->dirnames, path->dircount);
if(subdir == NULL) {
goto done;
}
// create new directory named 'path->filename'
cluster_t clst = fat_create_chain(0);
if(clst == 0){ // FAT is full (= disk is full)
goto done;
}
disk_sector_t sect = cluster_to_sector(clst);
dir_create(sect, DISK_SECTOR_SIZE/sizeof(struct dir_entry)); //실제 directory obj 생성
struct dir *dir = dir_open(inode_open(sect));
dir_add(dir, ".", sect);
dir_add(dir, "..", inode_get_inumber(dir_get_inode(subdir)));
dir_close(dir);
success = dir_add(subdir, path->filename, cluster_to_sector(clst));
done:
dir_close (subdir);
free_path(path);
return success;
}
readdir
fd의 디렉토리를 name에 저장한다. 디렉토리도 하나의 파일이므로 find_file_by_fd로 파일을 찾는다. 그리고 만약 해당 파일이 디렉토리 파일이라면 dir_readdir 함수를 통해 name에 디렉토리를 저장한다.
bool
readdir (int fd, char* name) {
struct file *fileobj = find_file_by_fd(fd);
if (inode_isdir(fileobj->inode)){
return dir_readdir((struct dir *)fileobj, name);
}
else return false;
}
isdir
fd가 디렉토리인지 확인하는 시스템 콜이다. readdir의 앞부분과 유사하다.
bool
isdir (int fd) {
struct file *fileobj = find_file_by_fd(fd);
return inode_isdir(fileobj->inode);
}
inumber
아이노드의 섹터 번호를 반환한다. find_file_by_fd로 아이노드 파일을 검색하고 해당 아이노드 그룹 안의 값으로 반환할 값을 찾는다.
int
inumber (int fd) {
struct file *fileobj = find_file_by_fd(fd);
// return ((struct inode*)fileobj->inode)->sector;
return fileobj->inode->sector;
}
symlink
target을 포함하는 linkpath의 심볼릭 링크를 생성한다. 우선 target과 linkpath 모두 파싱해 준다. 그리고 target의 아이노드를 검색하여 linkpath에 추가해 준다.
/* syscall.c */
int
symlink (const char* target, const char* linkpath) {
bool lazy = false;
//parse link path
struct path* path_link = parse_filepath(linkpath);
if(path_link->dircount==-1) {
return -1;
}
struct dir* subdir_link = find_subdir(path_link->dirnames, path_link->dircount);
if(subdir_link == NULL) {
dir_close (subdir_link);
free_path(path_link);
return -1;
}
//parse target path
struct path* path_tar = parse_filepath(target);
if(path_tar->dircount==-1) {
return -1;
}
struct dir* subdir_tar = find_subdir(path_tar->dirnames, path_tar->dircount);
if(subdir_tar == NULL) {
dir_close (subdir_tar);
free_path(path_tar);
return -1;
}
//find target inode
struct inode* inode = NULL;
dir_lookup(subdir_tar, path_tar->filename, &inode);
if(inode == NULL) {
//lazy symlink(target not created yet)
inode = dir_get_inode(subdir_tar);
lazy = true;
}
//add to link path
dir_add(subdir_link, path_link->filename, inode_get_inumber(inode));
set_entry_symlink(subdir_link, path_link->filename, true);
if (lazy){ // create a lazy link to some file
set_entry_lazytar(subdir_link, path_link->filename, path_tar->filename);
}
else{ // if target is a lazy link to some file; propagate lazy link
struct dir_entry target_entry;
off_t ofs;
lookup(subdir_tar, path_tar->filename, &target_entry, &ofs);
if(strcmp("lazy", target_entry.lazy)){
set_entry_lazytar(subdir_link, path_link->filename, target_entry.lazy);
}
}
dir_close (subdir_link);
free_path(path_link);
dir_close (subdir_tar);
free_path(path_tar);
return 0;
}
void set_entry_symlink(struct dir* dir, const char *name, bool issym){
struct dir_entry e;
off_t ofs;
for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e; ofs += sizeof e){
if (e.in_use && !strcmp (name, e.name)){
break;
}
}
e.is_sym = issym;
inode_write_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
}
// set dir entry's lazy symlink target info
void set_entry_lazytar(struct dir* dir, const char *name, const char *tar){
struct dir_entry e;
off_t ofs;
for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e; ofs += sizeof e){
if (e.in_use && !strcmp (name, e.name)){
break;
}
}
strlcpy(e.lazy, tar, sizeof e.lazy);
inode_write_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
}