|
|
- #include <defs.h>
- #include <string.h>
- #include <stdlib.h>
- #include <list.h>
- #include <stat.h>
- #include <kmalloc.h>
- #include <vfs.h>
- #include <dev.h>
- #include <sfs.h>
- #include <inode.h>
- #include <iobuf.h>
- #include <bitmap.h>
- #include <error.h>
- #include <assert.h>
-
- static const struct inode_ops sfs_node_dirops; // dir operations
- static const struct inode_ops sfs_node_fileops; // file operations
-
- /*
- * lock_sin - lock the process of inode Rd/Wr
- */
- static void
- lock_sin(struct sfs_inode *sin) {
- down(&(sin->sem));
- }
-
- /*
- * unlock_sin - unlock the process of inode Rd/Wr
- */
- static void
- unlock_sin(struct sfs_inode *sin) {
- up(&(sin->sem));
- }
-
- /*
- * sfs_get_ops - return function addr of fs_node_dirops/sfs_node_fileops
- */
- static const struct inode_ops *
- sfs_get_ops(uint16_t type) {
- switch (type) {
- case SFS_TYPE_DIR:
- return &sfs_node_dirops;
- case SFS_TYPE_FILE:
- return &sfs_node_fileops;
- }
- panic("invalid file type %d.\n", type);
- }
-
- /*
- * sfs_hash_list - return inode entry in sfs->hash_list
- */
- static list_entry_t *
- sfs_hash_list(struct sfs_fs *sfs, uint32_t ino) {
- return sfs->hash_list + sin_hashfn(ino);
- }
-
- /*
- * sfs_set_links - link inode sin in sfs->linked-list AND sfs->hash_link
- */
- static void
- sfs_set_links(struct sfs_fs *sfs, struct sfs_inode *sin) {
- list_add(&(sfs->inode_list), &(sin->inode_link));
- list_add(sfs_hash_list(sfs, sin->ino), &(sin->hash_link));
- }
-
- /*
- * sfs_remove_links - unlink inode sin in sfs->linked-list AND sfs->hash_link
- */
- static void
- sfs_remove_links(struct sfs_inode *sin) {
- list_del(&(sin->inode_link));
- list_del(&(sin->hash_link));
- }
-
- /*
- * sfs_block_inuse - check the inode with NO. ino inuse info in bitmap
- */
- static bool
- sfs_block_inuse(struct sfs_fs *sfs, uint32_t ino) {
- if (ino != 0 && ino < sfs->super.blocks) {
- return !bitmap_test(sfs->freemap, ino);
- }
- panic("sfs_block_inuse: called out of range (0, %u) %u.\n", sfs->super.blocks, ino);
- }
-
- /*
- * sfs_block_alloc - check and get a free disk block
- */
- static int
- sfs_block_alloc(struct sfs_fs *sfs, uint32_t *ino_store) {
- int ret;
- if ((ret = bitmap_alloc(sfs->freemap, ino_store)) != 0) {
- return ret;
- }
- assert(sfs->super.unused_blocks > 0);
- sfs->super.unused_blocks --, sfs->super_dirty = 1;
- assert(sfs_block_inuse(sfs, *ino_store));
- return sfs_clear_block(sfs, *ino_store, 1);
- }
-
- /*
- * sfs_block_free - set related bits for ino block to 1(means free) in bitmap, add sfs->super.unused_blocks, set superblock dirty *
- */
- static void
- sfs_block_free(struct sfs_fs *sfs, uint32_t ino) {
- assert(sfs_block_inuse(sfs, ino));
- bitmap_free(sfs->freemap, ino);
- sfs->super.unused_blocks ++, sfs->super_dirty = 1;
- }
-
- /*
- * sfs_create_inode - alloc a inode in memroy, and init din/ino/dirty/reclian_count/sem fields in sfs_inode in inode
- */
- static int
- sfs_create_inode(struct sfs_fs *sfs, struct sfs_disk_inode *din, uint32_t ino, struct inode **node_store) {
- struct inode *node;
- if ((node = alloc_inode(sfs_inode)) != NULL) {
- vop_init(node, sfs_get_ops(din->type), info2fs(sfs, sfs));
- struct sfs_inode *sin = vop_info(node, sfs_inode);
- sin->din = din, sin->ino = ino, sin->dirty = 0, sin->reclaim_count = 1;
- sem_init(&(sin->sem), 1);
- *node_store = node;
- return 0;
- }
- return -E_NO_MEM;
- }
-
- /*
- * lookup_sfs_nolock - according ino, find related inode
- *
- * NOTICE: le2sin, info2node MACRO
- */
- static struct inode *
- lookup_sfs_nolock(struct sfs_fs *sfs, uint32_t ino) {
- struct inode *node;
- list_entry_t *list = sfs_hash_list(sfs, ino), *le = list;
- while ((le = list_next(le)) != list) {
- struct sfs_inode *sin = le2sin(le, hash_link);
- if (sin->ino == ino) {
- node = info2node(sin, sfs_inode);
- if (vop_ref_inc(node) == 1) {
- sin->reclaim_count ++;
- }
- return node;
- }
- }
- return NULL;
- }
-
- /*
- * sfs_load_inode - If the inode isn't existed, load inode related ino disk block data into a new created inode.
- * If the inode is in memory alreadily, then do nothing
- */
- int
- sfs_load_inode(struct sfs_fs *sfs, struct inode **node_store, uint32_t ino) {
- lock_sfs_fs(sfs);
- struct inode *node;
- if ((node = lookup_sfs_nolock(sfs, ino)) != NULL) {
- goto out_unlock;
- }
-
- int ret = -E_NO_MEM;
- struct sfs_disk_inode *din;
- if ((din = kmalloc(sizeof(struct sfs_disk_inode))) == NULL) {
- goto failed_unlock;
- }
-
- assert(sfs_block_inuse(sfs, ino));
- if ((ret = sfs_rbuf(sfs, din, sizeof(struct sfs_disk_inode), ino, 0)) != 0) {
- goto failed_cleanup_din;
- }
-
- assert(din->nlinks != 0);
- if ((ret = sfs_create_inode(sfs, din, ino, &node)) != 0) {
- goto failed_cleanup_din;
- }
- sfs_set_links(sfs, vop_info(node, sfs_inode));
-
- out_unlock:
- unlock_sfs_fs(sfs);
- *node_store = node;
- return 0;
-
- failed_cleanup_din:
- kfree(din);
- failed_unlock:
- unlock_sfs_fs(sfs);
- return ret;
- }
-
- /*
- * sfs_bmap_get_sub_nolock - according entry pointer entp and index, find the index of indrect disk block
- * return the index of indrect disk block to ino_store. no lock protect
- * @sfs: sfs file system
- * @entp: the pointer of index of entry disk block
- * @index: the index of block in indrect block
- * @create: BOOL, if the block isn't allocated, if create = 1 the alloc a block, otherwise just do nothing
- * @ino_store: 0 OR the index of already inused block or new allocated block.
- */
- static int
- sfs_bmap_get_sub_nolock(struct sfs_fs *sfs, uint32_t *entp, uint32_t index, bool create, uint32_t *ino_store) {
- assert(index < SFS_BLK_NENTRY);
- int ret;
- uint32_t ent, ino = 0;
- off_t offset = index * sizeof(uint32_t); // the offset of entry in entry block
- // if entry block is existd, read the content of entry block into sfs->sfs_buffer
- if ((ent = *entp) != 0) {
- if ((ret = sfs_rbuf(sfs, &ino, sizeof(uint32_t), ent, offset)) != 0) {
- return ret;
- }
- if (ino != 0 || !create) {
- goto out;
- }
- }
- else {
- if (!create) {
- goto out;
- }
- //if entry block isn't existd, allocated a entry block (for indrect block)
- if ((ret = sfs_block_alloc(sfs, &ent)) != 0) {
- return ret;
- }
- }
-
- if ((ret = sfs_block_alloc(sfs, &ino)) != 0) {
- goto failed_cleanup;
- }
- if ((ret = sfs_wbuf(sfs, &ino, sizeof(uint32_t), ent, offset)) != 0) {
- sfs_block_free(sfs, ino);
- goto failed_cleanup;
- }
-
- out:
- if (ent != *entp) {
- *entp = ent;
- }
- *ino_store = ino;
- return 0;
-
- failed_cleanup:
- if (ent != *entp) {
- sfs_block_free(sfs, ent);
- }
- return ret;
- }
-
- /*
- * sfs_bmap_get_nolock - according sfs_inode and index of block, find the NO. of disk block
- * no lock protect
- * @sfs: sfs file system
- * @sin: sfs inode in memory
- * @index: the index of block in inode
- * @create: BOOL, if the block isn't allocated, if create = 1 the alloc a block, otherwise just do nothing
- * @ino_store: 0 OR the index of already inused block or new allocated block.
- */
- static int
- sfs_bmap_get_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, uint32_t index, bool create, uint32_t *ino_store) {
- struct sfs_disk_inode *din = sin->din;
- int ret;
- uint32_t ent, ino;
- // the index of disk block is in the fist SFS_NDIRECT direct blocks
- if (index < SFS_NDIRECT) {
- if ((ino = din->direct[index]) == 0 && create) {
- if ((ret = sfs_block_alloc(sfs, &ino)) != 0) {
- return ret;
- }
- din->direct[index] = ino;
- sin->dirty = 1;
- }
- goto out;
- }
- // the index of disk block is in the indirect blocks.
- index -= SFS_NDIRECT;
- if (index < SFS_BLK_NENTRY) {
- ent = din->indirect;
- if ((ret = sfs_bmap_get_sub_nolock(sfs, &ent, index, create, &ino)) != 0) {
- return ret;
- }
- if (ent != din->indirect) {
- assert(din->indirect == 0);
- din->indirect = ent;
- sin->dirty = 1;
- }
- goto out;
- } else {
- panic ("sfs_bmap_get_nolock - index out of range");
- }
- out:
- assert(ino == 0 || sfs_block_inuse(sfs, ino));
- *ino_store = ino;
- return 0;
- }
-
- /*
- * sfs_bmap_free_sub_nolock - set the entry item to 0 (free) in the indirect block
- */
- static int
- sfs_bmap_free_sub_nolock(struct sfs_fs *sfs, uint32_t ent, uint32_t index) {
- assert(sfs_block_inuse(sfs, ent) && index < SFS_BLK_NENTRY);
- int ret;
- uint32_t ino, zero = 0;
- off_t offset = index * sizeof(uint32_t);
- if ((ret = sfs_rbuf(sfs, &ino, sizeof(uint32_t), ent, offset)) != 0) {
- return ret;
- }
- if (ino != 0) {
- if ((ret = sfs_wbuf(sfs, &zero, sizeof(uint32_t), ent, offset)) != 0) {
- return ret;
- }
- sfs_block_free(sfs, ino);
- }
- return 0;
- }
-
- /*
- * sfs_bmap_free_nolock - free a block with logical index in inode and reset the inode's fields
- */
- static int
- sfs_bmap_free_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, uint32_t index) {
- struct sfs_disk_inode *din = sin->din;
- int ret;
- uint32_t ent, ino;
- if (index < SFS_NDIRECT) {
- if ((ino = din->direct[index]) != 0) {
- // free the block
- sfs_block_free(sfs, ino);
- din->direct[index] = 0;
- sin->dirty = 1;
- }
- return 0;
- }
-
- index -= SFS_NDIRECT;
- if (index < SFS_BLK_NENTRY) {
- if ((ent = din->indirect) != 0) {
- // set the entry item to 0 in the indirect block
- if ((ret = sfs_bmap_free_sub_nolock(sfs, ent, index)) != 0) {
- return ret;
- }
- }
- return 0;
- }
- return 0;
- }
-
- /*
- * sfs_bmap_load_nolock - according to the DIR's inode and the logical index of block in inode, find the NO. of disk block.
- * @sfs: sfs file system
- * @sin: sfs inode in memory
- * @index: the logical index of disk block in inode
- * @ino_store:the NO. of disk block
- */
- static int
- sfs_bmap_load_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, uint32_t index, uint32_t *ino_store) {
- struct sfs_disk_inode *din = sin->din;
- assert(index <= din->blocks);
- int ret;
- uint32_t ino;
- bool create = (index == din->blocks);
- if ((ret = sfs_bmap_get_nolock(sfs, sin, index, create, &ino)) != 0) {
- return ret;
- }
- assert(sfs_block_inuse(sfs, ino));
- if (create) {
- din->blocks ++;
- }
- if (ino_store != NULL) {
- *ino_store = ino;
- }
- return 0;
- }
-
- /*
- * sfs_bmap_truncate_nolock - free the disk block at the end of file
- */
- static int
- sfs_bmap_truncate_nolock(struct sfs_fs *sfs, struct sfs_inode *sin) {
- struct sfs_disk_inode *din = sin->din;
- assert(din->blocks != 0);
- int ret;
- if ((ret = sfs_bmap_free_nolock(sfs, sin, din->blocks - 1)) != 0) {
- return ret;
- }
- din->blocks --;
- sin->dirty = 1;
- return 0;
- }
-
- /*
- * sfs_dirent_read_nolock - read the file entry from disk block which contains this entry
- * @sfs: sfs file system
- * @sin: sfs inode in memory
- * @slot: the index of file entry
- * @entry: file entry
- */
- static int
- sfs_dirent_read_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, int slot, struct sfs_disk_entry *entry) {
- assert(sin->din->type == SFS_TYPE_DIR && (slot >= 0 && slot < sin->din->blocks));
- int ret;
- uint32_t ino;
- // according to the DIR's inode and the slot of file entry, find the index of disk block which contains this file entry
- if ((ret = sfs_bmap_load_nolock(sfs, sin, slot, &ino)) != 0) {
- return ret;
- }
- assert(sfs_block_inuse(sfs, ino));
- // read the content of file entry in the disk block
- if ((ret = sfs_rbuf(sfs, entry, sizeof(struct sfs_disk_entry), ino, 0)) != 0) {
- return ret;
- }
- entry->name[SFS_MAX_FNAME_LEN] = '\0';
- return 0;
- }
-
- #define sfs_dirent_link_nolock_check(sfs, sin, slot, lnksin, name) \
- do { \
- int err; \
- if ((err = sfs_dirent_link_nolock(sfs, sin, slot, lnksin, name)) != 0) { \
- warn("sfs_dirent_link error: %e.\n", err); \
- } \
- } while (0)
-
- #define sfs_dirent_unlink_nolock_check(sfs, sin, slot, lnksin) \
- do { \
- int err; \
- if ((err = sfs_dirent_unlink_nolock(sfs, sin, slot, lnksin)) != 0) { \
- warn("sfs_dirent_unlink error: %e.\n", err); \
- } \
- } while (0)
-
- /*
- * sfs_dirent_search_nolock - read every file entry in the DIR, compare file name with each entry->name
- * If equal, then return slot and NO. of disk of this file's inode
- * @sfs: sfs file system
- * @sin: sfs inode in memory
- * @name: the filename
- * @ino_store: NO. of disk of this file (with the filename)'s inode
- * @slot: logical index of file entry (NOTICE: each file entry ocupied one disk block)
- * @empty_slot: the empty logical index of file entry.
- */
- static int
- sfs_dirent_search_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, const char *name, uint32_t *ino_store, int *slot, int *empty_slot) {
- assert(strlen(name) <= SFS_MAX_FNAME_LEN);
- struct sfs_disk_entry *entry;
- if ((entry = kmalloc(sizeof(struct sfs_disk_entry))) == NULL) {
- return -E_NO_MEM;
- }
-
- #define set_pvalue(x, v) do { if ((x) != NULL) { *(x) = (v); } } while (0)
- int ret, i, nslots = sin->din->blocks;
- set_pvalue(empty_slot, nslots);
- for (i = 0; i < nslots; i ++) {
- if ((ret = sfs_dirent_read_nolock(sfs, sin, i, entry)) != 0) {
- goto out;
- }
- if (entry->ino == 0) {
- set_pvalue(empty_slot, i);
- continue ;
- }
- if (strcmp(name, entry->name) == 0) {
- set_pvalue(slot, i);
- set_pvalue(ino_store, entry->ino);
- goto out;
- }
- }
- #undef set_pvalue
- ret = -E_NOENT;
- out:
- kfree(entry);
- return ret;
- }
-
- /*
- * sfs_dirent_findino_nolock - read all file entries in DIR's inode and find a entry->ino == ino
- */
-
- static int
- sfs_dirent_findino_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, uint32_t ino, struct sfs_disk_entry *entry) {
- int ret, i, nslots = sin->din->blocks;
- for (i = 0; i < nslots; i ++) {
- if ((ret = sfs_dirent_read_nolock(sfs, sin, i, entry)) != 0) {
- return ret;
- }
- if (entry->ino == ino) {
- return 0;
- }
- }
- return -E_NOENT;
- }
-
- /*
- * sfs_lookup_once - find inode corresponding the file name in DIR's sin inode
- * @sfs: sfs file system
- * @sin: DIR sfs inode in memory
- * @name: the file name in DIR
- * @node_store: the inode corresponding the file name in DIR
- * @slot: the logical index of file entry
- */
- static int
- sfs_lookup_once(struct sfs_fs *sfs, struct sfs_inode *sin, const char *name, struct inode **node_store, int *slot) {
- int ret;
- uint32_t ino;
- lock_sin(sin);
- { // find the NO. of disk block and logical index of file entry
- ret = sfs_dirent_search_nolock(sfs, sin, name, &ino, slot, NULL);
- }
- unlock_sin(sin);
- if (ret == 0) {
- // load the content of inode with the the NO. of disk block
- ret = sfs_load_inode(sfs, node_store, ino);
- }
- return ret;
- }
-
- // sfs_opendir - just check the opne_flags, now support readonly
- static int
- sfs_opendir(struct inode *node, uint32_t open_flags) {
- switch (open_flags & O_ACCMODE) {
- case O_RDONLY:
- break;
- case O_WRONLY:
- case O_RDWR:
- default:
- return -E_ISDIR;
- }
- if (open_flags & O_APPEND) {
- return -E_ISDIR;
- }
- return 0;
- }
-
- // sfs_openfile - open file (no use)
- static int
- sfs_openfile(struct inode *node, uint32_t open_flags) {
- return 0;
- }
-
- // sfs_close - close file
- static int
- sfs_close(struct inode *node) {
- return vop_fsync(node);
- }
-
- /*
- * sfs_io_nolock - Rd/Wr a file contentfrom offset position to offset+ length disk blocks<-->buffer (in memroy)
- * @sfs: sfs file system
- * @sin: sfs inode in memory
- * @buf: the buffer Rd/Wr
- * @offset: the offset of file
- * @alenp: the length need to read (is a pointer). and will RETURN the really Rd/Wr lenght
- * @write: BOOL, 0 read, 1 write
- */
- static int
- sfs_io_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, void *buf, off_t offset, size_t *alenp, bool write) {
- struct sfs_disk_inode *din = sin->din;
- assert(din->type != SFS_TYPE_DIR);
- off_t endpos = offset + *alenp, blkoff;
- *alenp = 0;
- // calculate the Rd/Wr end position
- if (offset < 0 || offset >= SFS_MAX_FILE_SIZE || offset > endpos) {
- return -E_INVAL;
- }
- if (offset == endpos) {
- return 0;
- }
- if (endpos > SFS_MAX_FILE_SIZE) {
- endpos = SFS_MAX_FILE_SIZE;
- }
- if (!write) {
- if (offset >= din->size) {
- return 0;
- }
- if (endpos > din->size) {
- endpos = din->size;
- }
- }
-
- int (*sfs_buf_op)(struct sfs_fs *sfs, void *buf, size_t len, uint32_t blkno, off_t offset);
- int (*sfs_block_op)(struct sfs_fs *sfs, void *buf, uint32_t blkno, uint32_t nblks);
- if (write) {
- sfs_buf_op = sfs_wbuf, sfs_block_op = sfs_wblock;
- }
- else {
- sfs_buf_op = sfs_rbuf, sfs_block_op = sfs_rblock;
- }
-
- int ret = 0;
- size_t size, alen = 0;
- uint32_t ino;
- uint32_t blkno = offset / SFS_BLKSIZE; // The NO. of Rd/Wr begin block
- uint32_t nblks = endpos / SFS_BLKSIZE - blkno; // The size of Rd/Wr blocks
-
- //LAB8:EXERCISE1 YOUR CODE HINT: call sfs_bmap_load_nolock, sfs_rbuf, sfs_rblock,etc. read different kind of blocks in file
- /*
- * (1) If offset isn't aligned with the first block, Rd/Wr some content from offset to the end of the first block
- * NOTICE: useful function: sfs_bmap_load_nolock, sfs_buf_op
- * Rd/Wr size = (nblks != 0) ? (SFS_BLKSIZE - blkoff) : (endpos - offset)
- * (2) Rd/Wr aligned blocks
- * NOTICE: useful function: sfs_bmap_load_nolock, sfs_block_op
- * (3) If end position isn't aligned with the last block, Rd/Wr some content from begin to the (endpos % SFS_BLKSIZE) of the last block
- * NOTICE: useful function: sfs_bmap_load_nolock, sfs_buf_op
- */
- out:
- *alenp = alen;
- if (offset + alen > sin->din->size) {
- sin->din->size = offset + alen;
- sin->dirty = 1;
- }
- return ret;
- }
-
- /*
- * sfs_io - Rd/Wr file. the wrapper of sfs_io_nolock
- with lock protect
- */
- static inline int
- sfs_io(struct inode *node, struct iobuf *iob, bool write) {
- struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs);
- struct sfs_inode *sin = vop_info(node, sfs_inode);
- int ret;
- lock_sin(sin);
- {
- size_t alen = iob->io_resid;
- ret = sfs_io_nolock(sfs, sin, iob->io_base, iob->io_offset, &alen, write);
- if (alen != 0) {
- iobuf_skip(iob, alen);
- }
- }
- unlock_sin(sin);
- return ret;
- }
-
- // sfs_read - read file
- static int
- sfs_read(struct inode *node, struct iobuf *iob) {
- return sfs_io(node, iob, 0);
- }
-
- // sfs_write - write file
- static int
- sfs_write(struct inode *node, struct iobuf *iob) {
- return sfs_io(node, iob, 1);
- }
-
- /*
- * sfs_fstat - Return nlinks/block/size, etc. info about a file. The pointer is a pointer to struct stat;
- */
- static int
- sfs_fstat(struct inode *node, struct stat *stat) {
- int ret;
- memset(stat, 0, sizeof(struct stat));
- if ((ret = vop_gettype(node, &(stat->st_mode))) != 0) {
- return ret;
- }
- struct sfs_disk_inode *din = vop_info(node, sfs_inode)->din;
- stat->st_nlinks = din->nlinks;
- stat->st_blocks = din->blocks;
- stat->st_size = din->size;
- return 0;
- }
-
- /*
- * sfs_fsync - Force any dirty inode info associated with this file to stable storage.
- */
- static int
- sfs_fsync(struct inode *node) {
- struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs);
- struct sfs_inode *sin = vop_info(node, sfs_inode);
- int ret = 0;
- if (sin->dirty) {
- lock_sin(sin);
- {
- if (sin->dirty) {
- sin->dirty = 0;
- if ((ret = sfs_wbuf(sfs, sin->din, sizeof(struct sfs_disk_inode), sin->ino, 0)) != 0) {
- sin->dirty = 1;
- }
- }
- }
- unlock_sin(sin);
- }
- return ret;
- }
-
- /*
- *sfs_namefile -Compute pathname relative to filesystem root of the file and copy to the specified io buffer.
- *
- */
- static int
- sfs_namefile(struct inode *node, struct iobuf *iob) {
- struct sfs_disk_entry *entry;
- if (iob->io_resid <= 2 || (entry = kmalloc(sizeof(struct sfs_disk_entry))) == NULL) {
- return -E_NO_MEM;
- }
-
- struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs);
- struct sfs_inode *sin = vop_info(node, sfs_inode);
-
- int ret;
- char *ptr = iob->io_base + iob->io_resid;
- size_t alen, resid = iob->io_resid - 2;
- vop_ref_inc(node);
- while (1) {
- struct inode *parent;
- if ((ret = sfs_lookup_once(sfs, sin, "..", &parent, NULL)) != 0) {
- goto failed;
- }
-
- uint32_t ino = sin->ino;
- vop_ref_dec(node);
- if (node == parent) {
- vop_ref_dec(node);
- break;
- }
-
- node = parent, sin = vop_info(node, sfs_inode);
- assert(ino != sin->ino && sin->din->type == SFS_TYPE_DIR);
-
- lock_sin(sin);
- {
- ret = sfs_dirent_findino_nolock(sfs, sin, ino, entry);
- }
- unlock_sin(sin);
-
- if (ret != 0) {
- goto failed;
- }
-
- if ((alen = strlen(entry->name) + 1) > resid) {
- goto failed_nomem;
- }
- resid -= alen, ptr -= alen;
- memcpy(ptr, entry->name, alen - 1);
- ptr[alen - 1] = '/';
- }
- alen = iob->io_resid - resid - 2;
- ptr = memmove(iob->io_base + 1, ptr, alen);
- ptr[-1] = '/', ptr[alen] = '\0';
- iobuf_skip(iob, alen);
- kfree(entry);
- return 0;
-
- failed_nomem:
- ret = -E_NO_MEM;
- failed:
- vop_ref_dec(node);
- kfree(entry);
- return ret;
- }
-
- /*
- * sfs_getdirentry_sub_noblock - get the content of file entry in DIR
- */
- static int
- sfs_getdirentry_sub_nolock(struct sfs_fs *sfs, struct sfs_inode *sin, int slot, struct sfs_disk_entry *entry) {
- int ret, i, nslots = sin->din->blocks;
- for (i = 0; i < nslots; i ++) {
- if ((ret = sfs_dirent_read_nolock(sfs, sin, i, entry)) != 0) {
- return ret;
- }
- if (entry->ino != 0) {
- if (slot == 0) {
- return 0;
- }
- slot --;
- }
- }
- return -E_NOENT;
- }
-
- /*
- * sfs_getdirentry - according to the iob->io_offset, calculate the dir entry's slot in disk block,
- get dir entry content from the disk
- */
- static int
- sfs_getdirentry(struct inode *node, struct iobuf *iob) {
- struct sfs_disk_entry *entry;
- if ((entry = kmalloc(sizeof(struct sfs_disk_entry))) == NULL) {
- return -E_NO_MEM;
- }
-
- struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs);
- struct sfs_inode *sin = vop_info(node, sfs_inode);
-
- int ret, slot;
- off_t offset = iob->io_offset;
- if (offset < 0 || offset % sfs_dentry_size != 0) {
- kfree(entry);
- return -E_INVAL;
- }
- if ((slot = offset / sfs_dentry_size) > sin->din->blocks) {
- kfree(entry);
- return -E_NOENT;
- }
- lock_sin(sin);
- if ((ret = sfs_getdirentry_sub_nolock(sfs, sin, slot, entry)) != 0) {
- unlock_sin(sin);
- goto out;
- }
- unlock_sin(sin);
- ret = iobuf_move(iob, entry->name, sfs_dentry_size, 1, NULL);
- out:
- kfree(entry);
- return ret;
- }
-
- /*
- * sfs_reclaim - Free all resources inode occupied . Called when inode is no longer in use.
- */
- static int
- sfs_reclaim(struct inode *node) {
- struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs);
- struct sfs_inode *sin = vop_info(node, sfs_inode);
-
- int ret = -E_BUSY;
- uint32_t ent;
- lock_sfs_fs(sfs);
- assert(sin->reclaim_count > 0);
- if ((-- sin->reclaim_count) != 0 || inode_ref_count(node) != 0) {
- goto failed_unlock;
- }
- if (sin->din->nlinks == 0) {
- if ((ret = vop_truncate(node, 0)) != 0) {
- goto failed_unlock;
- }
- }
- if (sin->dirty) {
- if ((ret = vop_fsync(node)) != 0) {
- goto failed_unlock;
- }
- }
- sfs_remove_links(sin);
- unlock_sfs_fs(sfs);
-
- if (sin->din->nlinks == 0) {
- sfs_block_free(sfs, sin->ino);
- if ((ent = sin->din->indirect) != 0) {
- sfs_block_free(sfs, ent);
- }
- }
- kfree(sin->din);
- vop_kill(node);
- return 0;
-
- failed_unlock:
- unlock_sfs_fs(sfs);
- return ret;
- }
-
- /*
- * sfs_gettype - Return type of file. The values for file types are in sfs.h.
- */
- static int
- sfs_gettype(struct inode *node, uint32_t *type_store) {
- struct sfs_disk_inode *din = vop_info(node, sfs_inode)->din;
- switch (din->type) {
- case SFS_TYPE_DIR:
- *type_store = S_IFDIR;
- return 0;
- case SFS_TYPE_FILE:
- *type_store = S_IFREG;
- return 0;
- case SFS_TYPE_LINK:
- *type_store = S_IFLNK;
- return 0;
- }
- panic("invalid file type %d.\n", din->type);
- }
-
- /*
- * sfs_tryseek - Check if seeking to the specified position within the file is legal.
- */
- static int
- sfs_tryseek(struct inode *node, off_t pos) {
- if (pos < 0 || pos >= SFS_MAX_FILE_SIZE) {
- return -E_INVAL;
- }
- struct sfs_inode *sin = vop_info(node, sfs_inode);
- if (pos > sin->din->size) {
- return vop_truncate(node, pos);
- }
- return 0;
- }
-
- /*
- * sfs_truncfile : reszie the file with new length
- */
- static int
- sfs_truncfile(struct inode *node, off_t len) {
- if (len < 0 || len > SFS_MAX_FILE_SIZE) {
- return -E_INVAL;
- }
- struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs);
- struct sfs_inode *sin = vop_info(node, sfs_inode);
- struct sfs_disk_inode *din = sin->din;
-
- int ret = 0;
- //new number of disk blocks of file
- uint32_t nblks, tblks = ROUNDUP_DIV(len, SFS_BLKSIZE);
- if (din->size == len) {
- assert(tblks == din->blocks);
- return 0;
- }
-
- lock_sin(sin);
- // old number of disk blocks of file
- nblks = din->blocks;
- if (nblks < tblks) {
- // try to enlarge the file size by add new disk block at the end of file
- while (nblks != tblks) {
- if ((ret = sfs_bmap_load_nolock(sfs, sin, nblks, NULL)) != 0) {
- goto out_unlock;
- }
- nblks ++;
- }
- }
- else if (tblks < nblks) {
- // try to reduce the file size
- while (tblks != nblks) {
- if ((ret = sfs_bmap_truncate_nolock(sfs, sin)) != 0) {
- goto out_unlock;
- }
- nblks --;
- }
- }
- assert(din->blocks == tblks);
- din->size = len;
- sin->dirty = 1;
-
- out_unlock:
- unlock_sin(sin);
- return ret;
- }
-
- /*
- * sfs_lookup - Parse path relative to the passed directory
- * DIR, and hand back the inode for the file it
- * refers to.
- */
- static int
- sfs_lookup(struct inode *node, char *path, struct inode **node_store) {
- struct sfs_fs *sfs = fsop_info(vop_fs(node), sfs);
- assert(*path != '\0' && *path != '/');
- vop_ref_inc(node);
- struct sfs_inode *sin = vop_info(node, sfs_inode);
- if (sin->din->type != SFS_TYPE_DIR) {
- vop_ref_dec(node);
- return -E_NOTDIR;
- }
- struct inode *subnode;
- int ret = sfs_lookup_once(sfs, sin, path, &subnode, NULL);
-
- vop_ref_dec(node);
- if (ret != 0) {
- return ret;
- }
- *node_store = subnode;
- return 0;
- }
-
- // The sfs specific DIR operations correspond to the abstract operations on a inode.
- static const struct inode_ops sfs_node_dirops = {
- .vop_magic = VOP_MAGIC,
- .vop_open = sfs_opendir,
- .vop_close = sfs_close,
- .vop_fstat = sfs_fstat,
- .vop_fsync = sfs_fsync,
- .vop_namefile = sfs_namefile,
- .vop_getdirentry = sfs_getdirentry,
- .vop_reclaim = sfs_reclaim,
- .vop_gettype = sfs_gettype,
- .vop_lookup = sfs_lookup,
- };
- /// The sfs specific FILE operations correspond to the abstract operations on a inode.
- static const struct inode_ops sfs_node_fileops = {
- .vop_magic = VOP_MAGIC,
- .vop_open = sfs_openfile,
- .vop_close = sfs_close,
- .vop_read = sfs_read,
- .vop_write = sfs_write,
- .vop_fstat = sfs_fstat,
- .vop_fsync = sfs_fsync,
- .vop_reclaim = sfs_reclaim,
- .vop_gettype = sfs_gettype,
- .vop_tryseek = sfs_tryseek,
- .vop_truncate = sfs_truncfile,
- };
-
|