// Write at most 'n' bytes from 'buf' to 'fd' at the current seek position. // Advance 'fd->f_offset' by the number of bytes written. // // Returns: // The number of bytes successfully written. // < 0 on error. static ssize_t devfile_write(struct Fd *fd, const void *buf, size_t n) { // Use inode_open, inode_close, maybe inode_set_size, and inode_data. // Be careful of block boundaries! // Flush any blocks you change using BCREQ_FLUSH. // // LAB 5: Your code here. int r; size_t orig_offset = fd->fd_offset; struct Inode *ino; if((r = inode_open(fd->fd_file.inum, &ino)) < 0) return r; void *data; size_t n2 = ROUNDUP(fd->fd_offset, PGSIZE) - fd->fd_offset; if (n2 > n) n2 = n; if(n2) { n -= n2; if(fd->fd_offset + n2 > (size_t)ino->i_size && inode_set_size(ino, fd->fd_offset + n2) < 0) goto wrapup; data = inode_data(ino, fd->fd_offset); memcpy(data, buf, n2); fd->fd_offset += n2; bcache_ipc(data, BCREQ_FLUSH); buf = (void *)((char *)buf + n2); } while (n / PGSIZE) { if(fd->fd_offset + PGSIZE > (size_t)ino->i_size && inode_set_size(ino, fd->fd_offset + PGSIZE) < 0) goto wrapup; data = inode_data(ino, fd->fd_offset); memcpy(data, buf, PGSIZE); bcache_ipc(data, BCREQ_FLUSH); n -= PGSIZE; buf = (void *)((char *)buf + PGSIZE); fd->fd_offset += PGSIZE; } if (n > 0) { if(fd->fd_offset + n > (size_t)ino->i_size && inode_set_size(ino, fd->fd_offset + n) < 0) goto wrapup; data = inode_data(ino, fd->fd_offset); memcpy(data, buf, n); bcache_ipc(data, BCREQ_FLUSH); fd->fd_offset += n; } wrapup: inode_close(ino); return fd->fd_offset - orig_offset; }
/*! * \ingroup lowlevel-api * * Allocates an inode. * * \param fs a pointer to the FS filesystem handle * \return the index of the allocated inode * * \throws fs_error thrown if an error occurs * \throws std::invalid_argument thrown if \a fs is null * */ uint32_t fs_alloc_inode(FS* fs) { if (!fs) throw std::invalid_argument("fs_alloc_inode invalid arg: fs"); fs_guard lock(fs->lock); debug::tracer DT; DT << "allocating inode"; if (0 == FS_FREE_ITABLE_INODES(fs)) fs_add_itable_group(fs); uint32_t ino = 0; if (!fs_find_next_free_ino(fs, ino)) throw fs_error(ERROR_NOSPACE, "fs_alloc_inode out of space"); fs_write_inode(fs, ino, inode_data()); fs_mark_inode(fs, ino, true); fs->super.f_inode_alloc += 1; mark_super_dirty(fs); DT << "allocated inode " << ino; return ino; }
// Read at most 'n' bytes from 'fd' at the current position into 'buf'. // Advance 'fd->f_offset' by the number of bytes read. // // Returns: // The number of bytes successfully read. // < 0 on error. static ssize_t devfile_read(struct Fd *fd, void *buf, size_t n) { // Use inode_open, inode_close, and inode_data. // Be careful of block boundaries! // // LAB 5: Your code here. int r; ssize_t orig_offset = fd->fd_offset; struct Inode *ino; if((r = inode_open(fd->fd_file.inum, &ino)) < 0) return r; void *data; if (n + fd->fd_offset > (size_t)ino->i_size) n = ino->i_size - fd->fd_offset; size_t n2 = ROUNDUP(fd->fd_offset, PGSIZE) - fd->fd_offset; if (n2 > n) n2 = n; n -= n2; if((data = inode_data(ino, fd->fd_offset)) == 0) goto wrapup; fd->fd_offset += n2; memcpy(buf, data, n2); buf = (void *)((char *)buf + n2); while (n / PGSIZE) { if((data = inode_data(ino, fd->fd_offset)) == 0) goto wrapup; memcpy(buf, data, PGSIZE); n -= PGSIZE; buf = (void *)((char *)buf + PGSIZE); fd->fd_offset += PGSIZE; } if (n > 0) { if((data = inode_data(ino, fd->fd_offset)) == 0) goto wrapup; memcpy(buf, data, n); fd->fd_offset += n; } wrapup: inode_close(ino); return fd->fd_offset - orig_offset; }
// Find a directory entry in the 'ino' directory. // Looks for name 'name' with length 'namelen'. // If 'create != 0', such an entry is created if none is found. // A newly created entry will have de_inum == 0. // Returns 0 on success (storing a pointer to the struct Direntry in // *de_store), and < 0 on error. // Error codes: // -E_BAD_PATH if 'ino' is not a directory. // -E_NO_DISK if out of space. // (possibly others) // static int dir_find(struct Inode *ino, const char *name, int namelen, struct Direntry **de_store, int create) { off_t off; struct Direntry *empty = 0; *de_store = 0; if (ino->i_ftype != FTYPE_DIR) return -E_BAD_PATH; for (off = 0; off < ino->i_size; off += sizeof(struct Direntry)) { struct Direntry *de = (struct Direntry *) inode_data(ino, off); if (de->de_inum != 0 && de->de_namelen == namelen && memcmp(de->de_name, name, namelen) == 0) { *de_store = de; return 0; } if (de->de_inum == 0 && !empty) empty = de; } if (!create) return -E_NOT_FOUND; if (!empty) { int r = inode_set_size(ino, ino->i_size + sizeof(struct Direntry)); if (r < 0) return r; empty = (struct Direntry *) inode_data(ino, ino->i_size - sizeof(struct Direntry)); } memset(empty, 0, sizeof(struct Direntry)); empty->de_namelen = namelen; memcpy(empty->de_name, name, namelen); *de_store = empty; return 0; }
/*! * \ingroup lowlevel-api * * Read an inode. * * \param fs a pointer to the filesystem handle * \param ino the number of the inode to read * \return the inode data * */ inode_data fs_read_inode(FS* fs, uint32_t ino) { if (!fs) throw std::invalid_argument("fs_read_inode invalid arg: fs"); fs_guard lock(fs->lock); debug::tracer DT; DT.trace() << "reading inode " << ino; if (0 == ino) return fs->super.f_itable_data; if (ino > FS_ITABLE_INODE_CAPACITY(fs)) throw std::invalid_argument("fs_read_inode invalid arg: ino"); uint32_t logical = fs_get_inode_blockno(fs, ino); uint32_t physical = translate_itable_blockno(fs->itable, logical); if (0 == physical) return inode_data(); std::vector< char > buf(fs->blocksize, 0); fs_read_block(fs, physical, buf.data(), buf.size()); // The arithmetic below requires ino to be 0-based! --ino; // Calculate the byte-offset in the block at which we can find // the requested inode. uint32_t inodes_per_block = fs->blocksize / INODE_SIZE; uint32_t offset = INODE_SIZE * (ino % inodes_per_block); inode_data data; deserialize_inode(buf.data() + offset, data); return data; }
// Checks file system structures. // Checks all file system invariants hold and prints out any errors it finds. // Does no locking, so if run in parallel with other environments, it can // get confused and report transient "errors." // Returns 0 if the file system is OK, -E_INVAL if any errors were found. // int fsck(void) { blocknum_t min_nblocks; int i, j, k; int errors = 0; add_pgfault_handler(bcache_pgfault_handler); // superblock checks if (super->s_magic != FS_MAGIC) panic("fsck: file system magic number %08x should be %08x", super->s_magic, FS_MAGIC); if (super->s_nblocks < 4) panic("fsck: file system must have at least 4 blocks"); if (super->s_ninodes < 1) panic("fsck: file system must have at least 1 inode"); min_nblocks = 2 /* boot sector, superblock */ + ROUNDUP(super->s_nblocks, BLKSIZE) / BLKSIZE /* freemap */ + (super->s_ninodes - 1); /* inodes */ if (super->s_nblocks < min_nblocks) panic("fsck: file system with %d inodes has %d blocks, needs at least %d", super->s_ninodes, super->s_nblocks, min_nblocks); // basic freemap checks: initial blocks not free, free blocks marked // with 1 (later checks will overwrite freemap) for (i = 0; i < super->s_nblocks; ++i) if (i < min_nblocks && freemap[i] != 0) { cprintf("fsck: freemap[%d]: should be 0 (allocated), is %d\n", i, freemap[i]); ++errors; } else if (freemap[i] != 0 && freemap[i] != 1) { cprintf("fsck: freemap[%d]: should be 0 or 1, is %d\n", i, freemap[i]); ++errors; freemap[i] = (freemap[i] > 0 ? 1 : 0); } // inode checks: inode 1 is referenced, unreferenced inodes // have no data pointers, ftype makes sense, no data pointer overlap for (i = 1; i < super->s_ninodes; ++i) { struct Inode *ino = get_inode(i); off_t true_size; // check for open-but-unreferenced inode; be careful of // uninitialized inodes (use bcache_ipc to check) bool active = ino->i_refcount != 0; if (!active && ino->i_opencount != 0 && bcache_ipc(ino, BCREQ_MAP) != 0) active = true; if (active && ino->i_ftype != FTYPE_REG && ino->i_ftype != FTYPE_DIR) { cprintf("fsck: inode[%d]: odd i_ftype %d\n", i, ino->i_ftype); ++errors; } if (i == 1 && ino->i_refcount == 0) { cprintf("fsck: inode[1]: root inode should be referenced\n"); ++errors; } else if (i == 1 && ino->i_ftype != FTYPE_DIR) { cprintf("fsck: inode[1]: root inode should be directory\n"); ++errors; } if (active && ino->i_size > MAXFILESIZE) { cprintf("fsck: inode[%d]: size %d too large\n", i, ino->i_size); ++errors; } if (active && ino->i_inum != i) { cprintf("fsck: inode[%d]: wrong inumber %d\n", i, ino->i_inum); ++errors; } true_size = active ? ino->i_size : 0; for (j = 0; j < NDIRECT; ++j) { blocknum_t b = ino->i_direct[j]; if (j * BLKSIZE < true_size && !b) { cprintf("fsck: inode[%d]: direct block %d is null, though file size is %d\n", i, j, true_size); ++errors; } else if (j * BLKSIZE >= true_size && b && active) { cprintf("fsck: inode[%d]: direct block %d exists, though file size is %d\n", i, j, true_size); ++errors; } if (b && active) { if (b < min_nblocks) { cprintf("fsck: inode[%d]: direct block %d == %d is in special block range\n", i, j, b); ++errors; } else if (freemap[b] == 1) { cprintf("fsck: inode[%d]: direct block %d == %d is marked as free\n", i, j, b); ++errors; } else if (freemap[b] == -1) { cprintf("fsck: inode[%d]: direct block %d == %d used more than once\n", i, j, b); ++errors; } else freemap[b] = -1; } } ino->i_fsck_refcount = (i == 1 ? 1 : 0); ino->i_fsck_checked = 0; } // directory checks while (1) { struct Inode *ino; for (i = 1; i < super->s_ninodes; ++i) { ino = get_inode(i); if (ino->i_fsck_refcount > 0 && !ino->i_fsck_checked && ino->i_ftype == FTYPE_DIR) goto check_directory_inode; } break; check_directory_inode: ino->i_fsck_checked = 1; if (ino->i_size % sizeof(struct Direntry) != 0) { cprintf("inode[%d]: directory size %d not multiple of %d\n", i, ino->i_size, sizeof(struct Direntry)); ++errors; } for (j = 0; (off_t) (j + sizeof(struct Direntry)) <= ino->i_size; j += sizeof(struct Direntry)) { struct Direntry *de = (struct Direntry *) inode_data(ino, j); char name[MAXNAMELEN]; int namelen; if (de->de_inum == 0) continue; if (de->de_namelen <= 0 || de->de_namelen >= MAXNAMELEN) { cprintf("inode[%d] de@%d: bad filename length %d\n", i, j, de->de_namelen); ++errors; } else if (de->de_name[de->de_namelen] != 0) { cprintf("inode[%d] de@%d: filename is not null terminated\n", i, j); ++errors; } memcpy(name, de->de_name, MAXNAMELEN); namelen = MAX(MIN(de->de_namelen, MAXNAMELEN - 1), 0); name[namelen] = 0; if (de->de_inum >= super->s_ninodes) { cprintf("inode[%d] de@%d (%s): inode %d out of range\n", i, j, name, de->de_inum); ++errors; } else { struct Inode *refed = get_inode(de->de_inum); ++refed->i_fsck_refcount; if (refed->i_refcount == 0) { cprintf("inode[%d] de@%d (%s): refers to free inode %d\n", i, j, name, de->de_inum); ++errors; } } for (k = 0; k < j && de->de_namelen < MAXNAMELEN; k += sizeof(struct Direntry)) { struct Direntry *xde = (struct Direntry *) inode_data(ino, k); if (xde->de_inum != 0 && xde->de_namelen == namelen && memcmp(xde->de_name, name, namelen) == 0) { cprintf("inode[%d] de@%d (%s): same filename as de@%d\n", i, j, name, k); ++errors; } } } } // refcount consistency for (i = 1; i < super->s_ninodes; ++i) { struct Inode *ino = get_inode(i); if (ino->i_refcount != ino->i_fsck_refcount) { cprintf("fsck: inode[%d]: refcount %d should be %d\n", i, ino->i_refcount, ino->i_fsck_refcount); ++errors; } } // clean up freemap for (i = 0; i < super->s_nblocks; ++i) if (freemap[i] == -1) freemap[i] = 0; else if (freemap[i] == 0 && i >= min_nblocks) { cprintf("fsck: freemap[%d]: unreferenced block is not free\n", i); ++errors; } return errors ? -E_INVAL : 0; }