// Synchronize the root process's console special files // with the actual console I/O device. bool cons_io(void) { int num_io = 0; // Get output file fileinode *fi = &files->fi[FILEINO_CONSOUT]; int c; // spinlock_acquire(&cons_lock); while(cons_out_pos < fi->size) { c = ((char*)FILEDATA(FILEINO_CONSOUT))[cons_out_pos]; cons_putc(c); num_io++; cons_out_pos++; } // spinlock_release(&cons_lock); // Input file fi = &files->fi[FILEINO_CONSIN]; // Read from console while(fi->size <= FILE_MAXSIZE && (c = cons_getc())) { // And appened to CONSIN ((char*)FILEDATA(FILEINO_CONSIN))[fi->size++] = c; num_io++; } return num_io; }
// Grow or shrink a file to exactly a specified size. // If growing a file, then fills the new space with zeros. // Returns 0 if successful, or returns -1 and sets errno on error. int fileino_truncate(int ino, off_t newsize) { assert(fileino_isvalid(ino)); assert(newsize >= 0 && newsize <= FILE_MAXSIZE); size_t oldsize = files->fi[ino].size; size_t oldpagelim = ROUNDUP(files->fi[ino].size, PAGESIZE); size_t newpagelim = ROUNDUP(newsize, PAGESIZE); if (newsize > oldsize) { // Grow the file and fill the new space with zeros. sys_get(SYS_PERM | SYS_READ | SYS_WRITE, 0, NULL, NULL, FILEDATA(ino) + oldpagelim, newpagelim - oldpagelim); memset(FILEDATA(ino) + oldsize, 0, newsize - oldsize); } else if (newsize > 0) { // Shrink the file, but not all the way to empty. // Would prefer to use SYS_ZERO to free the file content, // but SYS_ZERO isn't guaranteed to work at page granularity. sys_get(SYS_PERM, 0, NULL, NULL, FILEDATA(ino) + newpagelim, FILE_MAXSIZE - newpagelim); } else { // Shrink the file to empty. Use SYS_ZERO to free completely. sys_get(SYS_ZERO, 0, NULL, NULL, FILEDATA(ino), FILE_MAXSIZE); } files->fi[ino].size = newsize; files->fi[ino].ver++; // truncation is always an exclusive change return 0; }
void file_initroot(proc *root) { // Only one root process may perform external I/O directly - // all other processes do I/O indirectly via the process hierarchy. assert(root == proc_root); // Make sure the root process's page directory is loaded, // so that we can write into the root process's file area directly. cpu_cur()->proc = root; lcr3(mem_phys(root->pdir)); // Enable read/write access on the file metadata area pmap_setperm(root->pdir, FILESVA, ROUNDUP(sizeof(filestate), PAGESIZE), SYS_READ | SYS_WRITE); memset(files, 0, sizeof(*files)); // Set up the standard I/O descriptors for console I/O files->fd[0].ino = FILEINO_CONSIN; files->fd[0].flags = O_RDONLY; files->fd[1].ino = FILEINO_CONSOUT; files->fd[1].flags = O_WRONLY | O_APPEND; files->fd[2].ino = FILEINO_CONSOUT; files->fd[2].flags = O_WRONLY | O_APPEND; // Setup the inodes for the console I/O files and root directory strcpy(files->fi[FILEINO_CONSIN].de.d_name, "consin"); strcpy(files->fi[FILEINO_CONSOUT].de.d_name, "consout"); strcpy(files->fi[FILEINO_ROOTDIR].de.d_name, "/"); files->fi[FILEINO_CONSIN].dino = FILEINO_ROOTDIR; files->fi[FILEINO_CONSOUT].dino = FILEINO_ROOTDIR; files->fi[FILEINO_ROOTDIR].dino = FILEINO_ROOTDIR; files->fi[FILEINO_CONSIN].mode = S_IFREG | S_IFPART; files->fi[FILEINO_CONSOUT].mode = S_IFREG; files->fi[FILEINO_ROOTDIR].mode = S_IFDIR; // Set the whole console input area to be read/write, // so we won't have to worry about perms in cons_io(). pmap_setperm(root->pdir, (uintptr_t)FILEDATA(FILEINO_CONSIN), PTSIZE, SYS_READ | SYS_WRITE); // Set up the initial files in the root process's file system. // Some script magic in kern/Makefrag creates obj/kern/initfiles.h, // which gets included above (twice) to create the 'initfiles' array. // For each initial file numbered 0 <= i < ninitfiles, // initfiles[i][0] is a pointer to the filename string for that file, // initfiles[i][1] is a pointer to the start of the file's content, and // initfiles[i][2] is a pointer to the end of the file's content // (i.e., a pointer to the first byte after the file's last byte). int ninitfiles = sizeof(initfiles)/sizeof(initfiles[0]); // Lab 4: your file system initialization code here. warn("file_initroot: file system initialization not done\n"); // Set root process's current working directory files->cwd = FILEINO_ROOTDIR; // Child process state - reserve PID 0 as a "scratch" child process. files->child[0].state = PROC_RESERVED; }
// Write 'count' data elements each of size 'eltsize' // starting at absolute byte offset 'ofs' within the file in inode 'ino'. // Returns the number of elements actually written, // which should always be equal to the 'count' input parameter // unless an error occurs, in which case this function // returns -1 and sets errno appropriately. // Since PIOS files can be up to only FILE_MAXSIZE bytes in size (4MB), // one particular reason an error might occur is if an application // tries to grow a file beyond this maximum file size, // in which case this function generates the EFBIG error. ssize_t fileino_write(int ino, off_t ofs, const void *buf, size_t eltsize, size_t count) { assert(fileino_isreg(ino)); assert(ofs >= 0); assert(eltsize > 0); fileinode *fi = &files->fi[ino]; assert(fi->size <= FILE_MAXSIZE); #if SOL >= 4 // Return an error if we'd be growing the file too big. size_t len = eltsize * count; size_t lim = ofs + len; if (lim < ofs || lim > FILE_MAXSIZE) { errno = EFBIG; return -1; } // Grow the file as necessary. if (lim > fi->size) { size_t oldpagelim = ROUNDUP(fi->size, PAGESIZE); size_t newpagelim = ROUNDUP(lim, PAGESIZE); if (newpagelim > oldpagelim) sys_get(SYS_PERM | SYS_READ | SYS_WRITE, 0, NULL, NULL, FILEDATA(ino) + oldpagelim, newpagelim - oldpagelim); fi->size = lim; } // Write the data. memmove(FILEDATA(ino) + ofs, buf, len); return count; #else // ! SOL >= 4 // Lab 4: insert your file writing code here. warn("fileino_write() not implemented"); errno = EINVAL; return -1; #endif // ! SOL >= 4 }
// Read up to 'count' data elements each of size 'eltsize', // starting at absolute byte offset 'ofs' within the file in inode 'ino'. // Returns the number of elements (NOT the number of bytes!) actually read, // or if an error occurs, returns -1 and sets errno appropriately. // The number of elements returned is normally equal to the 'count' parameter, // but may be less (without resulting in an error) // if the file is not large enough to read that many elements. ssize_t fileino_read(int ino, off_t ofs, void *buf, size_t eltsize, size_t count) { assert(fileino_isreg(ino)); assert(ofs >= 0); assert(eltsize > 0); fileinode *fi = &files->fi[ino]; #if LAB >= 9 // XXX hack: allow reading init-files bigger than 4MB #else assert(fi->size <= FILE_MAXSIZE); #endif #if SOL >= 4 ssize_t actual = 0; while (count > 0) { // Read as many elements as we can from the file. // Note: fd->ofs could well point beyond the end of file, // which means that avail will be negative - but that's OK. ssize_t avail = MIN(count, (fi->size - ofs) / eltsize); if (ofs >= fi->size) avail = 0; if (avail > 0) { memmove(buf, FILEDATA(ino) + ofs, avail * eltsize); buf += avail * eltsize; actual += avail; count -= avail; } // If there's no more we can read, stop now. if (count == 0 || !(fi->mode & S_IFPART)) break; // Wait for our parent to extend (or close) the file. #if LAB >= 99 cprintf("fileino_read: waiting for input on file %d\n", fd - files->fd); #endif sys_ret(); } return actual; #else // ! SOL >= 4 // Lab 4: insert your file reading code here. warn("fileino_read() not implemented"); errno = EINVAL; return -1; #endif // ! SOL >= 4 }
int ungetc(int c, FILE *fd) { // We only support the most common use of ungetc(), // to back up the read position after "peeking" a character. assert(filedesc_isreadable(fd)); fileinode *fi = &files->fi[fd->ino]; if (c == EOF || fd->ofs <= 0 || fd->ofs > fi->size) { errno = EINVAL; return EOF; } if (((uint8_t*)FILEDATA(fd->ino))[--fd->ofs] != c) panic("ungetc: unsupported usage, ungetting the wrong char"); return c; }
int exec_readelf(const char *path) { // We'll load the ELF image into a scratch area in our address space. sys_get(SYS_ZERO, 0, NULL, NULL, (void*)VM_SCRATCHLO, EXEMAX); // Open the ELF image to load. filedesc *fd = filedesc_open(NULL, path, O_RDONLY, 0); if (fd == NULL) return -1; void *imgdata = FILEDATA(fd->ino); size_t imgsize = files->fi[fd->ino].size; // Make sure it looks like an ELF image. elfhdr *eh = imgdata; if (imgsize < sizeof(*eh) || eh->e_magic != ELF_MAGIC) { warn("exec_readelf: ELF header not found"); goto err; } // Load each program segment into the scratch area proghdr *ph = imgdata + eh->e_phoff; proghdr *eph = ph + eh->e_phnum; if (imgsize < (void*)eph - imgdata) { warn("exec_readelf: ELF program header truncated"); goto err; } for (; ph < eph; ph++) { if (ph->p_type != ELF_PROG_LOAD) continue; // The executable should fit in the first 4MB of user space. intptr_t valo = ph->p_va; intptr_t vahi = valo + ph->p_memsz; if (valo < VM_USERLO || valo > VM_USERLO+EXEMAX || vahi < valo || vahi > VM_USERLO+EXEMAX) { warn("exec_readelf: executable image too large " "(%d bytes > %d max)", vahi-valo, EXEMAX); goto err; } // Map all pages the segment touches in our scratch region. // They've already been zeroed by the SYS_ZERO above. intptr_t scratchofs = VM_SCRATCHLO - VM_USERLO; intptr_t pagelo = ROUNDDOWN(valo, PAGESIZE); intptr_t pagehi = ROUNDUP(vahi, PAGESIZE); sys_get(SYS_PERM | SYS_READ | SYS_WRITE, 0, NULL, NULL, (void*)pagelo + scratchofs, pagehi - pagelo); // Initialize the file-loaded part of the ELF image. // (We could use copy-on-write if SYS_COPY // supports copying at arbitrary page boundaries.) intptr_t filelo = ph->p_offset; intptr_t filehi = filelo + ph->p_filesz; if (filelo < 0 || filelo > imgsize || filehi < filelo || filehi > imgsize) { warn("exec_readelf: loaded section out of bounds"); goto err; } memcpy((void*)valo + scratchofs, imgdata + filelo, filehi - filelo); // Finally, remove write permissions on read-only segments. if (!(ph->p_flags & ELF_PROG_FLAG_WRITE)) sys_get(SYS_PERM | SYS_READ, 0, NULL, NULL, (void*)pagelo + scratchofs, pagehi - pagelo); } // Copy the ELF image into its correct position in child 0. sys_put(SYS_COPY, 0, NULL, (void*)VM_SCRATCHLO, (void*)VM_USERLO, EXEMAX); // The new program should have the same entrypoint as we do! if (eh->e_entry != (intptr_t)start) { warn("exec_readelf: executable has a different start address"); goto err; } filedesc_close(fd); // Done with the ELF file return 0; err: filedesc_close(fd); return -1; }