. [PintOS, Project 4] Directory.c 공부
본문 바로가기
Pintos Project/Project 4

[PintOS, Project 4] Directory.c 공부

by 불냥이_ 2021. 3. 5.

디렉토리

날새게 날려가요 햄토리

챗바퀴를 돌려봐요 햄토리

제일 좋아하는건~ 닭도리

아싸라비야 고도리

맨손으로 때려잡은 북경오리

이건 너와 나의 연결고리

 

 

 

 

 

....

 

 

 

 

디렉토리 공부를 시작하자

/* A directory. */
struct dir
{
    struct inode *inode; /* Backing store. */
    off_t pos;           /* Current position. */
};

directory 구조체이다. 이것만 봐서는 뭘 말하는건지 모르겟다.

단지 inode를 가지고 있을 뿐이다. inode_sector가 아니라 inode를 가리키는 거 보니 이것도 memory에 올리는 놈인가?

 

 

/* A single directory entry. */
struct dir_entry
{
    disk_sector_t inode_sector; /* Sector number of header. */
    char name[NAME_MAX + 1];    /* Null terminated file name. */
    bool in_use;                /* In use or free? */
};

 이것은 directory의 entry이다. 

inode_sector와 (file) name 을 가지고 있다.

얘가 file을 직접적으로 가리키고 있을 것이다. 

 

 

/* Creates a directory with space for ENTRY_CNT entries in the
 * given SECTOR.  Returns true if successful, false on failure. */
bool dir_create(disk_sector_t sector, size_t entry_cnt)
{
    return inode_create(sector, entry_cnt * sizeof(struct dir_entry));
}

 주어진 sector와 entry_cnt로 directory를 하나 만든다.

실제로 실행하는 것은 inode_create()가 실행한다. 이 말은, 주어진 sector부터 entry 개수 * entry 사이즈 만큼을 할당받는다는 것이다. 아직은 감이 잘 안온다. 이것을 어떻게 읽어오는 것일까. 

 

 

/* Opens and returns the directory for the given INODE, of which
 * it takes ownership.  Returns a null pointer on failure. */
struct dir *
dir_open(struct inode *inode)
{
    struct dir *dir = calloc(1, sizeof *dir);
    if (inode != NULL && dir != NULL)
    {
        dir->inode = inode;
        dir->pos = 0;
        return dir;
    }
    else
    {
        inode_close(inode);
        free(dir);
        return NULL;
    }
}

 인자로 받은 inode가 속해있는 directory를 연다.

 

 dir만큼의 메모리 공간을 할당받고, 성공했다면 이 dir에 inode를 기입한다.

pos는 무엇을 말하는 것일까? 

 

 

/* Opens the root directory and returns a directory for it.
 * Return true if successful, false on failure. */
struct dir *
dir_open_root(void)
{
    return dir_open(inode_open(ROOT_DIR_SECTOR));
}

루트 디렉토리를 열고, 이에 해당하는 dir을 반환한다. 즉, 이 파일을 실행하면 dir를 가지고, 이 dir은 ROOT_DIR_SECTOR를 열 수 있다.

 

 이 말인 즉슨, ROOT_DIR_SECTOR 에는 inode가 있고, dir은 이 inode를 가지게된다는 것인데, 나는 아직 ROOT_DIR_SECTOR 에 뭘 한 적이 없다. 이 친구가 어떤식으로 구성되는 것일까. 

 

 

/* Opens and returns a new directory for the same inode as DIR.
 * Returns a null pointer on failure. */
struct dir *
dir_reopen(struct dir *dir)
{
    return dir_open(inode_reopen(dir->inode));
}

 dir를 다시 연다.

inode_reopen은 인자로 받은 inode에서 inode->open_cnt만 올려준다. 이러면 다른 곳에서 inode_close()를 실행해도 별 영향 없을 것이다.

 

/* Destroys DIR and frees associated resources. */
void dir_close(struct dir *dir)
{
    if (dir != NULL)
    {
        inode_close(dir->inode);
        free(dir);
    }
}

 dir를 닫는다.

실제로는 dir이 가지고 있는 inode를 닫는 것이다.

 

 

/* Returns the inode encapsulated by DIR. */
struct inode *
dir_get_inode(struct dir *dir)
{
    return dir->inode;
}

 dir가 가지고 있는 inode를 가지고온다. 

 

 

/* Searches DIR for a file with the given NAME.
 * If successful, returns true, sets *EP to the directory entry
 * if EP is non-null, and sets *OFSP to the byte offset of the
 * directory entry if OFSP is non-null.
 * otherwise, returns false and ignores EP and OFSP. */
static bool
lookup(const struct dir *dir, const char *name,
       struct dir_entry *ep, off_t *ofsp)
{
    struct dir_entry e;
    size_t ofs;

    ASSERT(dir != NULL);
    ASSERT(name != NULL);

    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))
        {
            if (ep != NULL)
                *ep = e;
            if (ofsp != NULL)
                *ofsp = ofs;
            return true;
        }
    return false;
}

 인자로 받은 name을 가지고 있는 dir를 찾는 것이다.

만약 찾는 것에 성공했다면 ep를 해당 dir_entry로 지정한다. 

 

 함수를 뜯어보자. 우선 빈 dir_entry를 하나 마들고, dir의 inode에서 (정확히는 inode_disk가 가리키는 곳) 읽는다. 

그곳에는 dir_entry가 차곡차곡 쌓여져 있을 것이기 때문에, 가장 밑에서부터 dir_entry를 읽는다. (ofs이 현재 위치이다.)

 

 그래서 한개씩 꺼내본다. 만약 사용되고 있는 dir_entry이고, strcmp()로 dir_entry의 name과 인자name이 같은지 비교해본다.

 

 그래서 같다면 ep는 현재 e를 가리키게 하고, ofsp가 현재 ofs를 가리키게 한다. 

 

 

 

/* Searches DIR for a file with the given NAME
 * and returns true if one exists, false otherwise.
 * On success, sets *INODE to an inode for the file, otherwise to
 * a null pointer.  The caller must close *INODE. */
bool dir_lookup(const struct dir *dir, const char *name,
                struct inode **inode)
{
    struct dir_entry e;

    ASSERT(dir != NULL);
    ASSERT(name != NULL);

    if (lookup(dir, name, &e, NULL))
        *inode = inode_open(e.inode_sector);
    else
        *inode = NULL;

    return *inode != NULL;
}

 이 친구도 file을 찾는 것인데 위와 뭐가 다를까. 

lookup은 현 dir에 name을 가진 file이 있는지 bool을 반환하지만,

dir_lookup은 현 dir에 해당 file이 있는지를 보고 있으면, 인자 inode에 해당 inode를 새긴다. 

 

 

/* Adds a file named NAME to DIR, which must not already contain a
 * file by that name.  The file's inode is in sector
 * INODE_SECTOR.
 * Returns true if successful, false on failure.
 * Fails if NAME is invalid (i.e. too long) or a disk or memory
 * error occurs. */
bool dir_add(struct dir *dir, const char *name, disk_sector_t inode_sector)
{
    printf("---DEBUG // dir_add // inode_sector : %d\n", inode_sector);
    struct dir_entry e;
    off_t ofs;
    bool success = false;

    ASSERT(dir != NULL);
    ASSERT(name != NULL);

    /* Check NAME for validity. */
    if (*name == '\0' || strlen(name) > NAME_MAX)
        return false;

    /* Check that NAME is not in use. */
    if (lookup(dir, name, NULL, NULL))
        goto done;

    /* Set OFS to offset of free slot.
	 * If there are no free slots, then it will be set to the
	 * current end-of-file.

	 * inode_read_at() will only return a short read at end of file.
	 * Otherwise, we'd need to verify that we didn't get a short
	 * read due to something intermittent such as low memory. */


    for (ofs = 0; inode_read_at(dir->inode, &e, sizeof e, ofs) == sizeof e;
         ofs += sizeof e)
    {

        if (!e.in_use)
        {
            break;
        }
    }

    /* Write slot. */
    e.in_use = true;
    strlcpy(e.name, name, sizeof e.name);
    e.inode_sector = inode_sector;
    success = inode_write_at(dir->inode, &e, sizeof e, ofs) == sizeof e;

done:
    return success;
}

dir에다가 dir_entry를 추가하는 함수이다. 여기서 다시 둘의 차이를 정의하고 가자.

 

 dir은 inode를 가지고 있다. 이 inode는 다시 어느 sector를 가리키고 있을 것이다.

dir_entry는 작은 구조체이다. dir_entry는 파일 하나를 가리키고 있으며 sector에는 이 파일의 inode가 있는 sector, name에는 이 file의 name, in_use는 dir_entry가 현재 사용되고 있는지 아닌지를 검사하는 bool이다. 

 

 그럼 시작해보자.

 

 우선 이름의 유효성 검사를 하고, 이 이름을 가지고 있는 dir_entry가 dir안에 있는지 본다.

없을 때, 다음으로 진행할 것이다.

 

 for문으로  dir에서 비어있는 dir_entry를 하나 가져온다.

우선 in_use를 true로 하고, name을 새겨놓는다. 

마지막으로, 작성한 entry를 원래 자리(disk 상의)에 복사한다.

 

 

 

/* Removes any entry for NAME in DIR.
 * Returns true if successful, false on failure,
 * which occurs only if there is no file with the given NAME. */
bool dir_remove(struct dir *dir, const char *name)
{
    struct dir_entry e;
    struct inode *inode = NULL;
    bool success = false;
    off_t ofs;

    ASSERT(dir != NULL);
    ASSERT(name != NULL);

    /* Find directory entry. */
    if (!lookup(dir, name, &e, &ofs))
        goto done;

    /* Open inode. */
    inode = inode_open(e.inode_sector);
    if (inode == NULL)
        goto done;

    /* Erase directory entry. */
    e.in_use = false;
    if (inode_write_at(dir->inode, &e, sizeof e, ofs) != sizeof e)
        goto done;

    /* Remove inode. */
    inode_remove(inode);
    success = true;

done:
    inode_close(inode);
    return success;
}

 dir에서 name을 가진 file을 지운다. 

 

우선 dir에 name을 가진 entry가 있는지를 봐야될 것이다.

만약 있다면 inode를 열고, entry를 지운다.

 

지우는 동작은 in_use를 false로 바꾸고, 원래대로 돌려놓는다. 

그리고 inode를 삭제한다. 

  

 

/* Reads the next directory entry in DIR and stores the name in
 * NAME.  Returns true if successful, false if the directory
 * contains no more entries. */
bool dir_readdir(struct dir *dir, char name[NAME_MAX + 1])
{
    struct dir_entry e;

    while (inode_read_at(dir->inode, &e, sizeof e, dir->pos) == sizeof e)
    {
        dir->pos += sizeof e;
        if (e.in_use)
        {
            strlcpy(name, e.name, NAME_MAX + 1);
            return true;
        }
    }
    return false;
}

 

 몰라 안해

 

 

 

+++++++++++ 3/5  ++++++++++++

 다시 한번 dir와 dir_entry에 대해 정리하고 가자. 

어제 자기전에 생각하다가 헷갈리는 부분이 있었다.

 

 

 

/* Opens and returns the directory for the given INODE, of which
 * it takes ownership.  Returns a null pointer on failure. */
struct dir *
dir_open(struct inode *inode)
{
    struct dir *dir = calloc(1, sizeof *dir);
    if (inode != NULL && dir != NULL)
    {
        dir->inode = inode;
        dir->pos = 0;
        return dir;
    }
    else
    {
        inode_close(inode);
        free(dir);
        return NULL;
    }
}

우선 dir_open() 을 보면, dir은 calloc으로 선언하고, inode를 그 안에 집어넣는다. 즉, inode는 어딘가에서 가져오다는 뜻이다.

 

 즉, file이나 directory나 데이터의 집합이고, 이 데이터의 집합은 inode가 관리한다는 것은 같다.

dir은 inode를 가리키고, 이 inode는 dir_entry가 쌓여있는 실제 데이터를 가리키는 것이다. dir_entry는 file (의 inode)을 가리키고 있다. dir_entry들을 구분하기 위해서 name을 사용하고, 우리가 file에 이름을 짓는 것은 dir_entry에 있는 name을 말하는 것이다. dir_entry는 file을 대표하고 있다고 해도 되는 것이다. 

 

 아직은 sub_directory가 구현되어있지 않으니, 현재 우리가 다루는 file은 모두 root_dir에 dir_entry의 형태로 들어가있을 것이다. 

 

 그렇다면, 여기서 드는 의문은 root_dir은 1로 지정되어있다. 이 때, root_dir inode여야하고, 실제 dir_entry가 들어있는 곳은 sector no.1 이 아닐 것인데, 어디에 들어있는 것인가?

 

 이걸 수정하거나 추가해야할 것 같다. 

생각을 해보자.

 

 일단 root_dir 자체는 확보되어있다 (1). 그렇다면 dir_entry를 넣을 공간을 마련해줘야한다. 

이걸 어느단계에서 해야될까. 

 

/* Formats the file system. */
static void
do_format (void) {
	printf ("Formatting file system...");

#ifdef EFILESYS
	/* Create FAT and save it to the disk. */
	fat_create ();
	fat_close ();
#else
	free_map_create ();
	if (!dir_create (ROOT_DIR_SECTOR, 16))
		PANIC ("root directory creation failed");
	free_map_close ();
#endif

	printf ("done.\n");
}

 찾아보니 free-map의 경우(기존 시스템의 경우), filesys.c에서 root_dir_sector를 만들어줬었다. 

이거를 똑같이 넣어주자. 

 

 

/* Formats the file system. */
static void
do_format (void) {
	printf ("Formatting file system...");

#ifdef EFILESYS
	/* Create FAT and save it to the disk. */
	fat_create ();
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
	if (!dir_create (ROOT_DIR_SECTOR, 16))
		PANIC ("---ERROR // do_format // ROOT_DIR CREATION FAILED\n");
	/* ---------------------------- << Project.4 FAT << ---------------------------- */
	fat_close ();
#else
	free_map_create ();
	if (!dir_create (ROOT_DIR_SECTOR, 16))
		PANIC ("root directory creation failed");
	free_map_close ();
#endif

	printf ("done.\n");
}

 

 

 이렇게하고 돌린 결과, project 1, 2, 3은 전부 통과하는 것 같다.  (3의 fork와 merge는 간간히 터지고 있는 상황)

 

 

이제 file growth를 해보자

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

댓글