// 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 open(const char *path, int flags, ...) { // Get the optional mode argument, which applies only with O_CREAT. mode_t mode = 0; if (flags & O_CREAT) { va_list ap; va_start(ap, flags); mode = va_arg(ap, mode_t); va_end(ap); } filedesc *fd = filedesc_open(NULL, path, flags, mode); if (fd == NULL) return -1; return fd - files->fd; }
FILE * freopen(const char *path, const char *mode, FILE *fd) { assert(filedesc_isvalid(fd)); if (filedesc_isopen(fd)) fclose(fd); // Parse the open mode string int flags; switch (*mode++) { case 'r': flags = O_RDONLY; break; case 'w': flags = O_WRONLY | O_CREAT | O_TRUNC; break; case 'a': flags = O_WRONLY | O_CREAT | O_APPEND; break; default: panic("freopen: unknown file mode '%c'\n", *--mode); } if (*mode == 'b') // binary flag - compatibility only mode++; if (*mode == '+') flags |= O_RDWR; if (filedesc_open(fd, path, flags, 0666) != fd) return NULL; return fd; }
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; }