// Open a directory for scanning. // For simplicity, DIR is simply a filedesc like other file descriptors, // except we interpret fd->ofs as an inode number for scanning, // instead of as a byte offset as in a regular file. DIR *opendir(const char *path) { filedesc *fd = filedesc_open(NULL, path, O_RDONLY, 0); if (fd == NULL) return NULL; // Make sure it's a directory assert(fileino_exists(fd->ino)); fileinode *fi = &files->fi[fd->ino]; if (!S_ISDIR(fi->mode)) { filedesc_close(fd); errno = ENOTDIR; return NULL; } return fd; }
int closedir(DIR *dir) { filedesc_close(dir); return 0; }
int close(int fn) { filedesc_close(&files->fd[fn]); return 0; }
int fclose(FILE *fd) { filedesc_close(fd); return 0; }
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; }