. [PintOS, Project 4] File Growth 구현
본문 바로가기
Pintos Project/Project 4

[PintOS, Project 4] File Growth 구현

by 불냥이_ 2021. 3. 5.

[PintOS, Project 4] Indexed and Extensible Files (tistory.com)

 

[PintOS, Project 4] Indexed and Extensible Files

Indexed and Extensible Files  The basic file system allocates files as a single extent, making it vulnerable to external fragmentation, that is, it is possible that an n-block file cannot be alloc..

firecatlibrary.tistory.com

 

 할 일을 정리해보자. 

원래는 inode_create()에서 인자로 length (파일의 크기) 를 받았다. 그러나 이제 length는 0으로 들어올 것이다. 

즉, inode_create()때는 inode만 가지고 있는 file하나만 생성되는 것이다. 

 

 그리고 inode_write_at()에서 데이터를 받는대로 EOF를 늘려야되는 것 같다. 

 

그렇다면 write_at()에서 인자가 어떻게 들어올 지 생각해보자. 신생 파일의 경우에는 offset은 0으로 들어오고, size는 실제 file length만큼 들어올 것이다. 

 

상상해보자. 맨 처음에는 inode_disk밖에 없을 것이다. 

 

 그럼 이제 byte_to_sector로 가겠지. 

 

여기서 잘 생각을 해야한다.  이 참에 file이 생성되는 과정을 정리해볼까?

 

bool
filesys_create (const char *name, off_t initial_size) {
#ifdef EFILESYS

	disk_sector_t inode_sector = fat_create_chain(0);
	struct dir *dir = dir_open_root ();
 
	bool success = (dir != NULL
			&& inode_create (inode_sector, initial_size)
			&& dir_add (dir, name, inode_sector));
	if (!success && inode_sector != 0)
		fat_remove_chain(inode_sector, 0);
	dir_close (dir);
 
	return success;

filesys_crate()에서는 inode_create()를 하고, directory에 추가해준다. (현재는 root_dir에만 들어가게 되어있다.)

initial_size는 0으로 바뀔 것인데, 그러면 

bool inode_create(disk_sector_t sector, off_t length)
{
#ifdef EFILESYS
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
    struct inode_disk *disk_inode = NULL;
    bool success = false;

    ASSERT(length >= 0);

    /* If this assertion fails, the inode structure is not exactly
	 * one sector in size, and you should fix that. */
    ASSERT(sizeof *disk_inode == DISK_SECTOR_SIZE);
    disk_inode = calloc(1, sizeof *disk_inode);
    if (disk_inode != NULL)
    {
        size_t sectors = bytes_to_sectors(length);
        disk_inode->length = length;
        disk_inode->magic = INODE_MAGIC;

        // 남은 자유 블륵이 sectors보다 많은가 
        if (ctrl_free_blocks_EA(0) >= sectors)
        {
            disk_write(filesys_disk, sector, disk_inode);
            if (sectors > 0)
            {
                static char zeros[DISK_SECTOR_SIZE];
                size_t i;

                // chain을 만든다.
                disk_inode->start = fat_create_chain(0);
                disk_write(filesys_disk, sector, disk_inode);
                disk_sector_t prev = disk_inode->start;
                for (i = 1; i < sectors; i++)
                {
                    // 섹터가 2개 이상이면 chain으로 이어준다. 
                    disk_sector_t sector_idx = fat_create_chain(prev);
                    disk_sector_t tmp = fat_get(prev);
                    disk_write(filesys_disk, sector_idx, zeros);
                    prev = sector_idx;
                }
            }
            success = true;
        }
        free(disk_inode);
    }
    return success;

그리고 여기는 받은 size을 sector의 수로 환산해서 그만큼 빈 공간을 만든다.

그런데 이제부터는 length가 0으로 들어올 것이다. 그렇기 때문에 "if (ctrl_free_blocks_EA(0) >= sectors)" 밑으로는 들어가지 않을 것이다. 

 

disk_inode->start = fat_create_chain(0);
disk_write(filesys_disk, sector, disk_inode);

 

 이 두 문구가 문제인데, 어떻게 할 지 고민중이다. disk_inode->start = EOC로 만들고, byte_to_sector()에서 EOC를 만나면 fat_create_chain()을 호출하도록 하는 것이 나아보이긴 한다. 인자pos자체는 그대로일 것이니깐.

 

 이렇게하면 inode_create()는 문제없을 것 같다. 다시 inode_write_at()으로 가보자.

 

ilfe_growth의 기준이라면, offset이 inode에 적혀있는 length보다 크게 들어올 수 있는데 예외처리 등에 걸리는지 보자.

 

 여기서 걸리는 것은 

off_t inode_left = inode_length(inode) - offset;

 이 부분이다. inode_length는 inode에 적혀있는 length를 가져오는건데, file growth에서는 length가 0으로 되어있을 거다. 그래서 inode_left가 0으로 나온다. 그렇다면 왜 inode_left를 계산하는지 보자.

 

int min_left = inode_left < sector_left ? inode_left : sector_left;

바로 찐 남은 byte를 계산하기 위해서인데, 이걸로 실제로 sector에 얼마만큼 쓰는가를 알기 위해서이다. 이 둘을 정리해보자.

 

sector_ofs : offset % disk_sector_size 로 offset 기준으로 sector에서 이 offset이 어디서부터 시작하는지 계싼

 

sector_left = sector내에서 남은 byte

inode_left : length - offset으로 남은 byte

그런데 뭐가 다른거지? 일단 offst이 0부터 시작하면 다를게 없어 보인다. 그렇다면 중간에서 들어왔을 때 달라지나?

 

일단 min_left는 '현재 sector내에서' 얼마만큼 더 써야하는 가를 나타내는 것이다. 섹터 중간에서 시작해서 진행하는 경우면 sector_left를 쓸 것이고, 마지막  sector에서 쓰는 경우면 inode_left를 쓰는 것 같다. 

 

 그렇다면 inode_length가 0으로 들어오면 어떻게 되는걸까. inode_left 는 음수가 되겠지. 

그런데 생각해보면 inode_length는 고정 파일 크기인데, 이제 할 것은 유동파일 크기 시스템이다. 아예 얘를 없애버려야하나? 그래도되나?

 

 만약 file을 새로 쓰는 경우면은 inode_length가 아니라 size로 들어와야할 것 같은데. (기존 시스템에서 size == length라고 생각되었으니깐) 

 

 아니면은 시작할 때, inode_length와 size 중 큰 것을 기준으로 하면되지 않을까? 

근데 중간부터 시작한다면? 그러면 inode_length랑 ofs+size를 비교?

 

 괜찮아보이는데 문제될 것은 없나? 의도에는 맞나?

일단 file 크기가 줄어드는 것이 아니면, 상관없을 거 같다. 수정해보자. 

 

 

 

 

만약 추잡하게 한다면, inode_length를 바로 inode->length 에 박아도 되겠지만, 최소한의 양심은 있기에 (중간에 bounce의 malloc에 실패할 가능성이 있기에) write가 끝나면 갱신하도록 하자. 

 

그런데 생각해보니, write_at을 시작할 때, offset을 inode->length로 잡고, write할 때마다, bytes_written을 잘 되지 않을까?

 

근데 이러면 중간만 수정하는 경우는 이상하게 되겠네. (시무룩)

 

write가 끝나고 inode_length를 집어넣는 식으로 하면, 크기가 안 바뀔 때, 크기가 커질 때, 중간에 write가 멈출 때 대응이 되지 않을까. 그런데 생각해보니, 중간에 수정하다가 터졌다고 해도 수정만 실패하는거지, file 크기는 변하지 않지 않나. 

 

 

/* Writes SIZE bytes from BUFFER into INODE, starting at OFFSET.
 * Returns the number of bytes actually written, which may be
 * less than SIZE if end of file is reached or an error occurs.
 * (Normally a write at end of file would extend the inode, but
 * growth is not yet implemented.) */
off_t inode_write_at(struct inode *inode, const void *buffer_, off_t size,
                     off_t offset)
{
/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
#ifdef EFILESYS
    const uint8_t *buffer = buffer_;
    off_t bytes_written = 0;
    uint8_t *bounce = NULL;

    if (inode->deny_write_cnt){
        return 0;
    }

    

    /* ---------------------------- >> Project.4 File Growth >> ---------------------------- */
    int inode_length = inode_length(inode) > offset + size ? inode_length(inode) : offset + size;
    tmp_length = offset;
    /* ---------------------------- << Project.4 File Growth << ---------------------------- */

    while (size > 0)
    {
        /* Sector to write, starting byte offset within sector. */
        disk_sector_t sector_idx = byte_to_sector(inode, offset);
        int sector_ofs = offset % DISK_SECTOR_SIZE;

        /* Bytes left in inode, bytes left in sector, lesser of the two. */
        off_t inode_left = inode_length - offset;
        int sector_left = DISK_SECTOR_SIZE - sector_ofs;
        int min_left = inode_left < sector_left ? inode_left : sector_left;

        /* Number of bytes to actually write into this sector. */
        int chunk_size = size < min_left ? size : min_left;
        if (chunk_size <= 0)
            break;

        if (sector_ofs == 0 && chunk_size == DISK_SECTOR_SIZE)
        {
            /* Write full sector directly to disk. */
            disk_write(filesys_disk, sector_idx, buffer + bytes_written);
            /* ---------------------------- >> Project.4 File Growth >> ---------------------------- */
            tmp_length += bytes_written;
            inode->length = inode->length > tmp_length ? inode->length : tmp_length; 
            /* ---------------------------- << Project.4 File Growth << ---------------------------- */
        }
        else
        {
            /* We need a bounce buffer. */
            if (bounce == NULL)
            {
                bounce = malloc(DISK_SECTOR_SIZE);
                if (bounce == NULL)
                    break;
            }

            /* If the sector contains data before or after the chunk
			   we're writing, then we need to read in the sector
			   first.  Otherwise we start with a sector of all zeros. */
            if (sector_ofs > 0 || chunk_size < sector_left)
                disk_read(filesys_disk, sector_idx, bounce);
            else
                memset(bounce, 0, DISK_SECTOR_SIZE);
            memcpy(bounce + sector_ofs, buffer + bytes_written, chunk_size);
            disk_write(filesys_disk, sector_idx, bounce);
        }

        /* Advance. */
        size -= chunk_size;
        offset += chunk_size;
        bytes_written += chunk_size;
    }
    free(bounce);

    /* ---------------------------- >> Project.4 File Growth >> ---------------------------- */
    inode->length = inode_length;
    /* ---------------------------- << Project.4 File Growth << ---------------------------- */

    return bytes_written;
	/* ---------------------------- << Project.4 FAT << ---------------------------- */

 여기서 드는 의문은 size가 과연 실 data크기인지, 아니면 page단위라던가 무튼 실 data 보다 클수도 있는지가 걱정되는데, 일단 실 data라는 전제하에 적고, 아니라면 나중에 수정한다. 

 

주석을 읽어보면, written bytes가 size보다 작아질 수 있는데, 이 경우는 EOF를 만난 경우이다. 아마 EOC를 말하는 거겠지. 그런데 file_growth를 구현한다면, EOC를 만나면 끝나는 것이 아니라 chain을 연장시켜야한다고 한다. 

 

이를 구현하기 위해 byte_to_sector로 가자.

 

 

/* Returns the disk sector that contains byte offset POS within
 * INODE.
 * Returns -1 if INODE does not contain data for a byte at offset
 * POS. */
static disk_sector_t
byte_to_sector(const struct inode *inode, off_t pos)
{
#ifdef EFILESYS
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
    ASSERT(inode != NULL);

    if (pos < inode->data.length) 
    {
        disk_sector_t cur = inode->data.start;
        uint32_t cnt = pos / DISK_SECTOR_SIZE;
        int i = 0;
        for (i ; i < cnt ; i++)
        {
            cur = fat_get(cur);
        }
        return cur;
    }
    else
        return -1;
	/* ---------------------------- << Project.4 FAT << ---------------------------- */

  

 음... 

 

 if문만 지우면되나?  괜찮나? 이런 안일한 mind

 

 응 안돼

 

 이것은 이미 inode_create()에서 free_block을 할당받았다는 기준으로 생각한 것이다.

그러면 fat_get()이 아니라 fat_create_chain()으로 바꿔야하지 않을까. 

 

그런데 생각해보니, 기존에 있는 것을 찾을 수도 있잖아. 

그러면 if문으로 분기를 해야겠는걸

 

 다행히 if문을 그대로 쓰면 될 것 같다.  기존에 있는 sector를 찾을 때는 if문 안을 쓰고

아닐 경우에는 else로 가서 할당받으면 될 거 같다. 

 

 

 그러고보니 여기서 inode->data.length 를 사용한다. 그러면 어떻게 해야할까. 걱정되는 부분은 inode_read_at에서 잘못된 offset으로 이 함수를 호출하는 경우이다. 이 경우는 inode_read_at에서 offset이 이상하면 못 들어오도록 해야겠다. 

 

 이제 생각해야할 것은 wriet_at()에서 while문을 돌 때마다 이 함수를 호출할 것인데, 맨 처음에 필요한 block을 모두 할당하고, 다음 while문에서는 if문으로 들어오도록 할 것인지, 아니면 while문에서 여기를 들어올 때마다 할당을 한번씩 해줄 것인지. 

 

 아무래도 while문 들어올 때마다 받는 것이 낫겠지?  그리고 블록을 할당받았다는 것은, file의 실제 크기가 늘었다는 것이니깐 length도 늘려주자.

 

/* Returns the disk sector that contains byte offset POS within
 * INODE.
 * Returns -1 if INODE does not contain data for a byte at offset
 * POS. */
static disk_sector_t
byte_to_sector(const struct inode *inode, off_t pos)
{
#ifdef EFILESYS
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
    ASSERT(inode != NULL);

    if (pos < inode->data.length) 
    {
        disk_sector_t cur = inode->data.start;
        uint32_t cnt = pos / DISK_SECTOR_SIZE;
        int i = 0;
        for (i ; i < cnt ; i++)
        {
            cur = fat_get(cur);
        }
        return cur;
    }
    else
        // return -1;
        /* ---------------------------- >> Project.4 File Growth >> ---------------------------- */
        disk_sector_t cur = inode->data.start;
        disk_sector_t new = fat_create_chain(cur);

        inode->data.length += DISK_SECTOR_SIZE;
        return new;
        /* ---------------------------- << Project.4 File Growth << ---------------------------- */
	/* ---------------------------- << Project.4 FAT << ---------------------------- */

 

그리고 read_at()에서 예외처리를 해주자. 

 

/* Reads SIZE bytes from INODE into BUFFER, starting at position OFFSET.
 * Returns the number of bytes actually read, which may be less
 * than SIZE if an error occurs or end of file is reached. */
off_t inode_read_at(struct inode *inode, void *buffer_, off_t size, off_t offset)
{
#ifdef EFILESYS
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
    uint8_t *buffer = buffer_;
    off_t bytes_read = 0;
    uint8_t *bounce = NULL;
    // printf("---DEBUG // i_read // inode->sector : %d\n", inode->sector);

    /* ---------------------------- >> Project.4 File Growth >> ---------------------------- */
    disk_sector_t cur = inode->data.start;
    if (offset > inode->data.length) {
        printf("---ERROR // i_read // offset > length : %d\n");
        return 0;
    }
    /* ---------------------------- << Project.4 File Growth << ---------------------------- */

 

 

 그리고 나중에 혹시 이쪽이 문제가 될 수 있으니 ERROR print를 걸어놓자.

 

 

 이제 생각해야할 것은 offset이 EOF를 넘어서 들어오는 경우이다.

생각해보니 offset이 EOF보다 훨씬 뒤에 있는 경우에도 블록은 byte_to_sector()에서 한번만 받을건데, 문제가 있어보이네.

 

 그냥 offset까지 while문을 돌려서 할당하자. 생각해보니 offset (=pos) 는 현재 위치이니 여기까지 할당받는 것이 맞다.

그리고 아예 코드가 개판이네. data.start부터 시작하는 것이 아니라 끝부터 시작해야한다. 멍충이

 

 그럼 우선 while문으로 현 file의 끝 블록을 찾아오고, length가 offset보다 길어질 때까지 블록을 할당한다.

그런다음 할당받은 sector에 0으로 채워주자.

/* Returns the disk sector that contains byte offset POS within
 * INODE.
 * Returns -1 if INODE does not contain data for a byte at offset
 * POS. */
static disk_sector_t
byte_to_sector(const struct inode *inode, off_t pos)
{
#ifdef EFILESYS
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
    ASSERT(inode != NULL);

    if (pos < inode->data.length) 
    {
        disk_sector_t cur = inode->data.start;
        uint32_t cnt = pos / DISK_SECTOR_SIZE;
        int i = 0;
        for (i ; i < cnt ; i++)
        {
            cur = fat_get(cur);
        }
        return cur;
    }
    else
        // return -1;
        /* ---------------------------- >> Project.4 File Growth >> ---------------------------- */
        disk_sector_t cur = inode->data.start;
        while (fat_get(cur) != EOChain)
        {
            cur = fat_get(cur);
        }

        disk_sector_t new;
        uint8_t bounce;
        memset(bounce, 0, DISK_SECTOR_SIZE);

        while (pos < inode->data.length)
        {
            new = fat_create_chain(cur);
            uint8_t bounce = malloc(DISK_SECTOR_SIZE);
            disk_write(filesys_disk, new, bounce);
            cur = new;
            inode->data.length += DISK_SECTOR_SIZE;
        }

        free(bounce);

        return new;
        /* ---------------------------- << Project.4 File Growth << ---------------------------- */
	/* ---------------------------- << Project.4 FAT << ---------------------------- */

 

이렇게 하면, file을 생성하고 첫 블록받을 때 문제가 없나? 

일단 inode_create()를 바꿔야할 것 같다.

 

 

 

 

bool inode_create(disk_sector_t sector, off_t length)
{
#ifdef EFILESYS
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
    struct inode_disk *disk_inode = NULL;
    bool success = false;

    ASSERT(length >= 0);
\
    /* If this assertion fails, the inode structure is not exactly
	 * one sector in size, and you should fix that. */
    ASSERT(sizeof *disk_inode == DISK_SECTOR_SIZE);
\    disk_inode = calloc(1, sizeof *disk_inode);
    if (disk_inode != NULL)
    {
        size_t sectors = bytes_to_sectors(length);
        disk_inode->length = length;
        disk_inode->magic = INODE_MAGIC;

 만약 disk_inode->length = 0;

disk_inode->start = 0;으로 한다면 어떤 일이 일어날까.

 

우선 write_at()에서는 byte_to_sector()로 들어올 것이고...

pos = 0, length = 0이니깐 할당을 받으러 else문으로 들어올 것이다. 

그러면 fat_get(0) = EOC이므로, cur = 0이 되고, fat_create_chain(0)이니 new부터 새 chain을 만들 것이다. 

아 그런데, 이 경우에는 inode_disk->start 에 기록이 안되어있을 테니 구현이 복잡하겠네.

 

 

inode_create()에서 그냥 블록 하나 받고 시작하도록 하고, length는 512byte부터 하도록하자. 이게 맘 편할 것 같다. 

bool inode_create(disk_sector_t sector, off_t length)
{
#ifdef EFILESYS
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
    struct inode_disk *disk_inode = NULL;
    bool success = false;

    ASSERT(length >= 0);
    // printf("---DEBUG // i_create // sector : %d\n", sector);
    // printf("---DEBUG // i_create // length : %d\n", length);
    /* If this assertion fails, the inode structure is not exactly
	 * one sector in size, and you should fix that. */
    ASSERT(sizeof *disk_inode == DISK_SECTOR_SIZE);
    // printf("---DEBUG // i_create // START\n");
    disk_inode = calloc(1, sizeof *disk_inode);
    if (disk_inode != NULL)
    {
        size_t sectors = bytes_to_sectors(length);
        // disk_inode->length = length;
        disk_inode->magic = INODE_MAGIC;
        /* ---------------------------- >> Project.4 File Growth >> ---------------------------- */
        disk_inode->length = DISK_SECTOR_SIZE;
        disk_inode->start = fat_create_chain(0);
        disk_write(filesys_disk, sector, disk_inode);
        static char zeros[DISK_SECTOR_SIZE];
        disk_write(filesys_disk, disk_inode->start, zeros);
        /* ---------------------------- << Project.4 File Growth << ---------------------------- */




    //     // 남은 자유 블륵이 sectors보다 많은가 
    //     if (ctrl_free_blocks_EA(0) >= sectors)
    //     {
    //         disk_write(filesys_disk, sector, disk_inode);
    //         if (sectors > 0)
    //         {
    //             static char zeros[DISK_SECTOR_SIZE];
    //             size_t i;

    //             // chain을 만든다.
    //             disk_inode->start = fat_create_chain(0);
    //             disk_write(filesys_disk, sector, disk_inode);
    //             disk_sector_t prev = disk_inode->start;
    //             for (i = 1; i < sectors; i++)
    //             {
    //                 // 섹터가 2개 이상이면 chain으로 이어준다. 
    //                 disk_sector_t sector_idx = fat_create_chain(prev);
    //                 disk_sector_t tmp = fat_get(prev);
    //                 disk_write(filesys_disk, sector_idx, zeros);
    //                 prev = sector_idx;
    //             }
    //         }
    //         success = true;
    //     }
    //     free(disk_inode);
    // }
    // printf("---DEBUG // i_create // suc : %d\n", success);
    return success;
/* ---------------------------- << Project.4 FAT << ---------------------------- */

 이런식으로 기존에 할당받던 코드를 날리고, 블록 하나를 받아서 start에 새기고, length는 512byte를 넣어줬다.

 

문제 없겠지?

 

 

 

 

 

응 문제있어

/* Returns the disk sector that contains byte offset POS within
 * INODE.
 * Returns -1 if INODE does not contain data for a byte at offset
 * POS. */
static disk_sector_t
byte_to_sector(const struct inode *inode, off_t pos)
{
#ifdef EFILESYS
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
    ASSERT(inode != NULL);
    disk_sector_t cur = inode->data.start;

    if (pos < inode->data.length) 
    {
        uint32_t cnt = pos / DISK_SECTOR_SIZE;
        int i = 0;
        for (i ; i < cnt ; i++)
        {
            cur = fat_get(cur);
        }
        return cur;
    }
    else
        // return -1;
        /* ---------------------------- >> Project.4 File Growth >> ---------------------------- */
        
        while (fat_get(cur) != EOChain)
        {
            cur = fat_get(cur);
        }

        disk_sector_t new;
        uint8_t bounce = malloc(DISK_SECTOR_SIZE);
        memset(bounce, 0, DISK_SECTOR_SIZE);
        off_t tmp_length = inode->data.length;
        
        while (pos < tmp_length)
        {
            new = fat_create_chain(cur);
            disk_write(filesys_disk, new, bounce);
            cur = new;
            tmp_length += DISK_SECTOR_SIZE;
        }

        inode->data.length = tmp_length;
        free(bounce);

        return new;
        /* ---------------------------- << Project.4 File Growth << ---------------------------- */
	/* ---------------------------- << Project.4 FAT << ---------------------------- */

 

중간중간에 컴파일을 하지 않아 곳곳에 틀린 곳이 많습니다. malloc()위치가 멍청해다. 그리고 data.length는 read_only란다. ㅟㅏ const였네.

 

고심 끝에 const를 해체하겠습니다.

문제없겟지?

 

아몰랑

 

 

 

 

 

 

 

 

 

 

 

 

결과 :

시작부터 망ㅇ했다...

To be Continue....

 

 

 

 

 

++++ 1차 crystal ++++

byte_to_sector()에서 모든것을 하려고 했던것이 북경원인이었다. 

만약 지금 할당받은 byte가 딱 경계선이라면 (즉, 1sector만 가지고 있는데 512byte를 읽으려고 한다면), inode_read_at()에서 새로운 블록을 할당해버리기때문에 문제가되었다.

 

sector가 부족할 때, sector를 늘리는 것을 함수로 분리해주자.

 

/* ---------------------------- >> Project.4 File Growth >> ---------------------------- */
static disk_sector_t
extend_sector(struct inode *inode, off_t pos)
{
    disk_sector_t cur = inode->data.start;
    while (fat_get(cur) != EOChain)
    {
        cur = fat_get(cur);
    }

    disk_sector_t new;
    uint8_t bounce = malloc(DISK_SECTOR_SIZE);
    memset(bounce, 0, DISK_SECTOR_SIZE);
    off_t tmp_length = inode->data.length;

    while (pos < tmp_length)
    {
        new = fat_create_chain(cur);
        disk_write(filesys_disk, new, bounce);
        cur = new;
        tmp_length += DISK_SECTOR_SIZE;
    }

    inode->data.length = pos;
    free(bounce);

    return new;
}
/* ---------------------------- << Project.4 File Growth << ---------------------------- */

 

 

문제는 length를 어떻게 하느냐인데... 일단 pos으로 해주고, 나중에 쓰고나서 쓴만큼 한번더 갱신해주는 걸로 충분하지 않을까 싶다.

 

 

inode_write_at() 을 수정하자.

off_t inode_write_at(struct inode *inode, const void *buffer_, off_t size,
                     off_t offset)
{
/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
#ifdef EFILESYS
    printf("\n---DEBUG // i_write_at // START \n");
    const uint8_t *buffer = buffer_;
    off_t bytes_written = 0;
    uint8_t *bounce = NULL;

    printf("---DEBUG // i_write_at // size : %llu\n", size);
    if (inode->deny_write_cnt){
        return 0;
    }

    printf("---DEBUG // i_write_at // ofs : %llu\n", offset);
    /* ---------------------------- >> Project.4 File Growth >> ---------------------------- */
    int length = inode_length(inode) > offset + size ? inode_length(inode) : offset + size;
    int tmp_length = offset;
    /* ---------------------------- << Project.4 File Growth << ---------------------------- */

    printf("---DEBUG // i_write_at // inode_sector : %llu\n", inode->sector);
    while (size > 0)
    {
        /* Sector to write, starting byte offset within sector. */
        disk_sector_t sector_idx = byte_to_sector(inode, offset);
        if (sector_idx = -1)
        {
            sector_idx = extend_sector(inode, offset);
        }

그리고 byte-to_sector는 원래대로 돌려놨다.

 

 

static disk_sector_t
// byte_to_sector(struct inode *inode, off_t pos)
byte_to_sector(const struct inode *inode, off_t pos)
{
#ifdef EFILESYS
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
    ASSERT(inode != NULL);
    disk_sector_t cur = inode->data.start;

    if (pos < inode->data.length) 
    {
        uint32_t cnt = pos / DISK_SECTOR_SIZE;
        int i = 0;
        for (i ; i < cnt ; i++)
        {
            cur = fat_get(cur);
        }
        return cur;
    }
    else
        return -1;
	/* ---------------------------- << Project.4 FAT << ---------------------------- */

 

 

그래도 안된다.

생각해보니 root_dir 만들때 할당이 안될거같은 느낌이다. 살펴보자

 

 

(2hours later)

 

 

나는 천하의 멍텅구리임을 엄숙하게 선언합니다.

 

 

 

+++++++++ 수정사항  +++++++++

 

 조금 이거저거 많이 고쳤다. 

많이 고쳣나? 

 

무튼 

 

문제가 있었던 곳은 inode_read_at (정확히는 byte_to_sector) 랑 inode_write_at이었다. 

 

우선 

 

 

static disk_sector_t
byte_to_sector(const struct inode *inode, off_t pos)
{
#ifdef EFILESYS
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
    ASSERT(inode != NULL);
    disk_sector_t cur = inode->data.start;

    if (pos < inode->data.length) 
    {
        uint32_t cnt = pos / DISK_SECTOR_SIZE;
        int i = 0;
        for (i ; i < cnt ; i++)
        {
            cur = fat_get(cur);
        }
        return cur;
    }
    else
        return -1;
	/* ---------------------------- << Project.4 FAT << ---------------------------- */

byte_to_sector 를 원래대로 돌려놨다.

/* Reads SIZE bytes from INODE into BUFFER, starting at position OFFSET.
 * Returns the number of bytes actually read, which may be less
 * than SIZE if an error occurs or end of file is reached. */
off_t inode_read_at(struct inode *inode, void *buffer_, off_t size, off_t offset)
{
#ifdef EFILESYS
	/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
    uint8_t *buffer = buffer_;
    off_t bytes_read = 0;
    uint8_t *bounce = NULL;


    /* ---------------------------- >> Project.4 File Growth >> ---------------------------- */
    if (offset > inode->data.length) {
        return 0;
    }
    /* ---------------------------- << Project.4 File Growth << ---------------------------- */


    while (size > 0)
    {
        /* Disk sector to read, starting byte offset within sector. */
        disk_sector_t sector_idx = byte_to_sector(inode, offset);
        if (sector_idx == -1)
        {
            printf("\n---ERROR // i_read // sector_idx : -1\n\n");
            break;
        }


        int sector_ofs = offset % DISK_SECTOR_SIZE;

        /* Bytes left in inode, bytes left in sector, lesser of the two. */
        off_t inode_left = inode_length(inode) - offset;
        int sector_left = DISK_SECTOR_SIZE - sector_ofs;
        int min_left = inode_left < sector_left ? inode_left : sector_left;

        /* Number of bytes to actually copy out of this sector. */
        int chunk_size = size < min_left ? size : min_left;
        if (chunk_size <= 0)
            break;

        if (sector_ofs == 0 && chunk_size == DISK_SECTOR_SIZE)
        {
            /* Read full sector directly into caller's buffer. */
            disk_read(filesys_disk, sector_idx, buffer + bytes_read);
        }
        else
        {
            /* Read sector into bounce buffer, then partially copy
			 * into caller's buffer. */
            if (bounce == NULL)
            {
                bounce = malloc(DISK_SECTOR_SIZE);
                if (bounce == NULL)
                    break;
            }
            disk_read(filesys_disk, sector_idx, bounce);
            memcpy(buffer + bytes_read, bounce + sector_ofs, chunk_size);
        }

        /* Advance. */
        size -= chunk_size;
        offset += chunk_size;
        bytes_read += chunk_size;
    }
    free(bounce);
    return bytes_read;

 

그리고 inode_read_at은 offset이 EOF 보다 멀리 있으면 return 0을 하게 했다.

 

/* Writes SIZE bytes from BUFFER into INODE, starting at OFFSET.
 * Returns the number of bytes actually written, which may be
 * less than SIZE if end of file is reached or an error occurs.
 * (Normally a write at end of file would extend the inode, but
 * growth is not yet implemented.) */
off_t inode_write_at(struct inode *inode, const void *buffer_, off_t size,
                     off_t offset)
{
/* ---------------------------- >> Project.4 FAT >> ---------------------------- */
#ifdef EFILESYS
    // printf("\n---DEBUG // i_write_at // START \n");
    const uint8_t *buffer = buffer_;
    off_t bytes_written = 0;
    uint8_t *bounce = NULL;
    

    if (inode->deny_write_cnt){
        return 0;
    }

    /* ---------------------------- >> Project.4 File Growth >> ---------------------------- */
    int cnt = 1;
    disk_sector_t cur = inode->data.start;


    while (fat_get(cur) != EOChain)
    {
        cur = fat_get(cur);
        cnt ++;
    }


    if (inode->data.length < offset + size)
    {
        int sectors_to_make = bytes_to_sectors(offset + size) - cnt;
        for (int i = 0 ; i < sectors_to_make ; i++)
        {
            cur = fat_create_chain(cur);
        }
        inode->data.length = offset + size;
        disk_write(filesys_disk, inode->sector, &inode->data);
    }
    
    
    /* ---------------------------- << Project.4 File Growth << ---------------------------- */

    while (size > 0)
    {
        /* Sector to write, starting byte offset within sector. */
        disk_sector_t sector_idx = byte_to_sector(inode, offset);
        int sector_ofs = offset % DISK_SECTOR_SIZE;

        /* Bytes left in inode, bytes left in sector, lesser of the two. */
        off_t inode_left = inode_length(inode) - offset;
        int sector_left = DISK_SECTOR_SIZE - sector_ofs;
        int min_left = inode_left < sector_left ? inode_left : sector_left;

        /* Number of bytes to actually write into this sector. */
        int chunk_size = size < min_left ? size : min_left;
        if (chunk_size <= 0)
            break;

        if (sector_ofs == 0 && chunk_size == DISK_SECTOR_SIZE)
        {
            /* Write full sector directly to disk. */
            disk_write(filesys_disk, sector_idx, buffer + bytes_written);
        }
        else
        {
            /* We need a bounce buffer. */
            if (bounce == NULL)
            {
                bounce = malloc(DISK_SECTOR_SIZE);
                if (bounce == NULL)
                    break;
            }

            /* If the sector contains data before or after the chunk
			   we're writing, then we need to read in the sector
			   first.  Otherwise we start with a sector of all zeros. */
            if (sector_ofs > 0 || chunk_size < sector_left)
                disk_read(filesys_disk, sector_idx, bounce);
            else
                memset(bounce, 0, DISK_SECTOR_SIZE);
            memcpy(bounce + sector_ofs, buffer + bytes_written, chunk_size);
            disk_write(filesys_disk, sector_idx, bounce);
        }
        // printf("---DEBUG // i_write_at // chunk_size : %d\n", chunk_size);
        /* Advance. */
        size -= chunk_size;
        offset += chunk_size;
        bytes_written += chunk_size;
    }
    free(bounce);

    return bytes_written;

 

inode_write_at()에는 깝죽거리지않고, 처음에 file이 가져야할 sector 수 - file이 가지고 있는 sector 수로 현재 할당해야할 sector수를 구하고 그만큼 할당받는다.

 

그리고 length랑 offset+size 중에 큰 수를 length로 정한다.

 

 

이러니 깔끔하게 통과했다.

이제 깝치지않고 내 방식대로 추잡하게 나아가야겠다. 괜히 원칙지켰다가 피봤따

흑흑

 

 

 

 

 

 

 

 

 

+++++++++++++++++++++++ 다음화 예고 ++++++++++++++++++++++++++++++

 

 

시발ㄹ연아

 

 

 

 

// 중간중간에 컴파일을 하지 않아 곳곳에 틀린 곳이 많습니다.

// 알아서 수정하세요 ^오^

 

 

 

댓글