하위 디렉토리의 생성 및 수정과 소프트 링크를 위한 시스템 콜들을 선언해 준다. 그리고 그에 맞는 함수들을 만들어 주어야 한다.

  1. 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;
    	}
    }
    
  2. 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);
    }
    
  3. 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;
    }
    
  4. 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;
    }
    
  5. isdir

    fd가 디렉토리인지 확인하는 시스템 콜이다. readdir의 앞부분과 유사하다.

    bool
    isdir (int fd) {
    	struct file *fileobj = find_file_by_fd(fd);
    	return inode_isdir(fileobj->inode);
    }
    
  6. 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;
    }
    
  7. 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;
    }