/*===========================================================================* * get_read_vp * *===========================================================================*/ static int get_read_vp(struct vfs_exec_info *execi, char *fullpath, int copyprogname, int sugid, struct lookup *resolve, struct fproc *fp) { /* Make the executable that we want to exec() into the binary pointed * to by 'fullpath.' This function fills in necessary details in the execi * structure, such as opened vnode. It unlocks and releases the vnode if * it was already there. This makes it easy to change the executable * during the exec(), which is often necessary, by calling this function * more than once. This is specifically necessary when we discover the * executable is actually a script or a dynamically linked executable. */ int r; /* Caller wants to switch vp to the file in 'fullpath.' * unlock and put it first if there is any there. */ if(execi->vp) { unlock_vnode(execi->vp); put_vnode(execi->vp); execi->vp = NULL; } /* Remember/overwrite the executable name if requested. */ if(copyprogname) { char *cp = strrchr(fullpath, '/'); if(cp) cp++; else cp = fullpath; strlcpy(execi->args.progname, cp, sizeof(execi->args.progname)); execi->args.progname[sizeof(execi->args.progname)-1] = '\0'; } /* Open executable */ if ((execi->vp = eat_path(resolve, fp)) == NULL) return err_code; unlock_vmnt(execi->vmp); if (!S_ISREG(execi->vp->v_mode)) return ENOEXEC; else if ((r = forbidden(fp, execi->vp, X_BIT)) != OK) return r; else r = req_stat(execi->vp->v_fs_e, execi->vp->v_inode_nr, VFS_PROC_NR, (vir_bytes) &(execi->sb)); if (r != OK) return r; /* If caller wants us to, honour suid/guid mode bits. */ if (sugid) { /* Deal with setuid/setgid executables */ if (execi->vp->v_mode & I_SET_UID_BIT) { execi->args.new_uid = execi->vp->v_uid; execi->args.allow_setuid = 1; } if (execi->vp->v_mode & I_SET_GID_BIT) { execi->args.new_gid = execi->vp->v_gid; execi->args.allow_setuid = 1; } } /* Read in first chunk of file. */ if((r=map_header(execi)) != OK) return r; return OK; }
/*===========================================================================* * pm_exec * *===========================================================================*/ int pm_exec(endpoint_t proc_e, vir_bytes path, size_t path_len, vir_bytes frame, size_t frame_len, vir_bytes *pc, vir_bytes *newsp, int user_exec_flags) { /* Perform the execve(name, argv, envp) call. The user library builds a * complete stack image, including pointers, args, environ, etc. The stack * is copied to a buffer inside VFS, and then to the new core image. */ int r, slot; vir_bytes vsp; struct fproc *rfp; int extrabase = 0; static char mbuf[ARG_MAX]; /* buffer for stack and zeroes */ struct vfs_exec_info execi; int i; static char fullpath[PATH_MAX], elf_interpreter[PATH_MAX], firstexec[PATH_MAX], finalexec[PATH_MAX]; struct lookup resolve; struct fproc *vmfp = &fproc[VM_PROC_NR]; stackhook_t makestack = NULL; static int n; n++; struct filp *newfilp = NULL; lock_exec(); lock_proc(vmfp, 0); /* unset execi values are 0. */ memset(&execi, 0, sizeof(execi)); execi.vmfd = -1; /* passed from exec() libc code */ execi.userflags = user_exec_flags; execi.args.stack_high = kinfo.user_sp; execi.args.stack_size = DEFAULT_STACK_LIMIT; okendpt(proc_e, &slot); rfp = fp = &fproc[slot]; rfp->text_size = 0; rfp->data_size = 0; lookup_init(&resolve, fullpath, PATH_NOFLAGS, &execi.vmp, &execi.vp); resolve.l_vmnt_lock = VMNT_READ; resolve.l_vnode_lock = VNODE_READ; /* Fetch the stack from the user before destroying the old core image. */ if (frame_len > ARG_MAX) FAILCHECK(ENOMEM); /* stack too big */ r = sys_datacopy(proc_e, (vir_bytes) frame, SELF, (vir_bytes) mbuf, (size_t) frame_len); if (r != OK) { /* can't fetch stack (e.g. bad virtual addr) */ printf("VFS: pm_exec: sys_datacopy failed\n"); FAILCHECK(r); } /* The default is to keep the original user and group IDs */ execi.args.new_uid = rfp->fp_effuid; execi.args.new_gid = rfp->fp_effgid; /* Get the exec file name. */ FAILCHECK(fetch_name(path, path_len, fullpath)); strlcpy(finalexec, fullpath, PATH_MAX); strlcpy(firstexec, fullpath, PATH_MAX); /* Get_read_vp will return an opened vn in execi. * if necessary it releases the existing vp so we can * switch after we find out what's inside the file. * It reads the start of the file. */ Get_read_vp(execi, fullpath, 1, 1, &resolve, fp); /* If this is a script (i.e. has a #!/interpreter line), * retrieve the name of the interpreter and open that * executable instead. */ if(is_script(&execi)) { /* patch_stack will add interpreter name and * args to stack and retrieve the new binary * name into fullpath. */ FAILCHECK(fetch_name(path, path_len, fullpath)); FAILCHECK(patch_stack(execi.vp, mbuf, &frame_len, fullpath)); strlcpy(finalexec, fullpath, PATH_MAX); strlcpy(firstexec, fullpath, PATH_MAX); Get_read_vp(execi, fullpath, 1, 0, &resolve, fp); } /* If this is a dynamically linked executable, retrieve * the name of that interpreter in elf_interpreter and open that * executable instead. But open the current executable in an * fd for the current process. */ if(elf_has_interpreter(execi.args.hdr, execi.args.hdr_len, elf_interpreter, sizeof(elf_interpreter))) { /* Switch the executable vnode to the interpreter */ execi.is_dyn = 1; /* The interpreter (loader) needs an fd to the main program, * which is currently in finalexec */ if((r = execi.elf_main_fd = common_open(finalexec, O_RDONLY, 0)) < 0) { printf("VFS: exec: dynamic: open main exec failed %s (%d)\n", fullpath, r); FAILCHECK(r); } /* ld.so is linked at 0, but it can relocate itself; we * want it higher to trap NULL pointer dereferences. */ execi.args.load_offset = 0x10000; /* Remember it */ strlcpy(execi.execname, finalexec, PATH_MAX); /* The executable we need to execute first (loader) * is in elf_interpreter, and has to be in fullpath to * be looked up */ strlcpy(fullpath, elf_interpreter, PATH_MAX); strlcpy(firstexec, elf_interpreter, PATH_MAX); Get_read_vp(execi, fullpath, 0, 0, &resolve, fp); } /* We also want an FD for VM to mmap() the process in if possible. */ { struct vnode *vp = execi.vp; assert(vp); if(vp->v_vmnt->m_haspeek && major(vp->v_dev) != MEMORY_MAJOR) { int newfd = -1; if(get_fd(vmfp, 0, R_BIT, &newfd, &newfilp) == OK) { assert(newfd >= 0 && newfd < OPEN_MAX); assert(!vmfp->fp_filp[newfd]); newfilp->filp_count = 1; newfilp->filp_vno = vp; newfilp->filp_flags = O_RDONLY; FD_SET(newfd, &vmfp->fp_filp_inuse); vmfp->fp_filp[newfd] = newfilp; /* dup_vnode(vp); */ execi.vmfd = newfd; execi.args.memmap = vfs_memmap; } } } /* callback functions and data */ execi.args.copymem = read_seg; execi.args.clearproc = libexec_clearproc_vm_procctl; execi.args.clearmem = libexec_clear_sys_memset; execi.args.allocmem_prealloc_cleared = libexec_alloc_mmap_prealloc_cleared; execi.args.allocmem_prealloc_junk = libexec_alloc_mmap_prealloc_junk; execi.args.allocmem_ondemand = libexec_alloc_mmap_ondemand; execi.args.opaque = &execi; execi.args.proc_e = proc_e; execi.args.frame_len = frame_len; execi.args.filesize = execi.vp->v_size; for (i = 0; exec_loaders[i].load_object != NULL; i++) { r = (*exec_loaders[i].load_object)(&execi.args); /* Loaded successfully, so no need to try other loaders */ if (r == OK) { makestack = exec_loaders[i].setup_stack; break; } } FAILCHECK(r); /* Inform PM */ FAILCHECK(libexec_pm_newexec(proc_e, &execi.args)); /* Save off PC */ *pc = execi.args.pc; /* call a stack-setup function if this executable type wants it */ vsp = execi.args.stack_high - frame_len; if(makestack) FAILCHECK(makestack(&execi, mbuf, &frame_len, &vsp, &extrabase)); /* Patch up stack and copy it from VFS to new core image. */ libexec_patch_ptr(mbuf, vsp + extrabase); FAILCHECK(sys_datacopy(SELF, (vir_bytes) mbuf, proc_e, (vir_bytes) vsp, (phys_bytes)frame_len)); /* Return new stack pointer to caller */ *newsp = vsp; clo_exec(rfp); if (execi.args.allow_setuid) { /* If after loading the image we're still allowed to run with * setuid or setgid, change credentials now */ rfp->fp_effuid = execi.args.new_uid; rfp->fp_effgid = execi.args.new_gid; } /* Remember the new name of the process */ strlcpy(rfp->fp_name, execi.args.progname, PROC_NAME_LEN); rfp->text_size = execi.args.text_size; rfp->data_size = execi.args.data_size; pm_execfinal: if(newfilp) unlock_filp(newfilp); else if (execi.vp != NULL) { unlock_vnode(execi.vp); put_vnode(execi.vp); } if(execi.vmfd >= 0 && !execi.vmfd_used) { if(OK != close_fd(vmfp, execi.vmfd)) { printf("VFS: unexpected close fail of vm fd\n"); } } unlock_proc(vmfp); unlock_exec(); return(r); }
/*===========================================================================* * new_node * *===========================================================================*/ static struct vnode *new_node(struct lookup *resolve, int oflags, mode_t bits) { /* Try to create a new inode and return a pointer to it. If the inode already exists, return a pointer to it as well, but set err_code accordingly. NULL is returned if the path cannot be resolved up to the last directory, or when the inode cannot be created due to permissions or otherwise. */ struct vnode *dirp, *vp; struct vmnt *dir_vmp, *vp_vmp; int r; struct node_details res; struct lookup findnode; char *path; path = resolve->l_path; /* For easy access */ lookup_init(&findnode, path, resolve->l_flags, &dir_vmp, &dirp); findnode.l_vmnt_lock = VMNT_WRITE; findnode.l_vnode_lock = VNODE_WRITE; /* dir node */ /* When O_CREAT and O_EXCL flags are set, the path may not be named by a * symbolic link. */ if (oflags & O_EXCL) findnode.l_flags |= PATH_RET_SYMLINK; /* See if the path can be opened down to the last directory. */ if ((dirp = last_dir(&findnode, fp)) == NULL) return(NULL); /* The final directory is accessible. Get final component of the path. */ lookup_init(&findnode, findnode.l_path, findnode.l_flags, &vp_vmp, &vp); findnode.l_vmnt_lock = VMNT_WRITE; findnode.l_vnode_lock = (oflags & O_TRUNC) ? VNODE_WRITE : VNODE_OPCL; vp = advance(dirp, &findnode, fp); assert(vp_vmp == NULL); /* Lookup to last dir should have yielded lock * on vmp or final component does not exist. * Either way, vp_vmp ought to be not set. */ /* The combination of a symlink with absolute path followed by a danglink * symlink results in a new path that needs to be re-resolved entirely. */ if (path[0] == '/') { unlock_vnode(dirp); unlock_vmnt(dir_vmp); put_vnode(dirp); if (vp != NULL) { unlock_vnode(vp); put_vnode(vp); } return new_node(resolve, oflags, bits); } if (vp == NULL && err_code == ENOENT) { /* Last path component does not exist. Make a new directory entry. */ if ((vp = get_free_vnode()) == NULL) { /* Can't create new entry: out of vnodes. */ unlock_vnode(dirp); unlock_vmnt(dir_vmp); put_vnode(dirp); return(NULL); } lock_vnode(vp, VNODE_OPCL); upgrade_vmnt_lock(dir_vmp); /* Creating file, need exclusive access */ if ((r = forbidden(fp, dirp, W_BIT|X_BIT)) != OK || (r = req_create(dirp->v_fs_e, dirp->v_inode_nr,bits, fp->fp_effuid, fp->fp_effgid, path, &res)) != OK ) { /* Can't create inode either due to permissions or some other * problem. In case r is EEXIST, we might be dealing with a * dangling symlink.*/ /* Downgrade lock to prevent deadlock during symlink resolving*/ downgrade_vmnt_lock(dir_vmp); if (r == EEXIST) { struct vnode *slp, *old_wd; /* Resolve path up to symlink */ findnode.l_flags = PATH_RET_SYMLINK; findnode.l_vnode_lock = VNODE_READ; findnode.l_vnode = &slp; slp = advance(dirp, &findnode, fp); if (slp != NULL) { if (S_ISLNK(slp->v_mode)) { /* Get contents of link */ r = req_rdlink(slp->v_fs_e, slp->v_inode_nr, VFS_PROC_NR, (vir_bytes) path, PATH_MAX - 1, 0); if (r < 0) { /* Failed to read link */ unlock_vnode(slp); unlock_vnode(dirp); unlock_vmnt(dir_vmp); put_vnode(slp); put_vnode(dirp); err_code = r; return(NULL); } path[r] = '\0'; /* Terminate path */ } unlock_vnode(slp); put_vnode(slp); } /* Try to create the inode the dangling symlink was * pointing to. We have to use dirp as starting point * as there might be multiple successive symlinks * crossing multiple mountpoints. * Unlock vnodes and vmnts as we're going to recurse. */ unlock_vnode(dirp); unlock_vnode(vp); unlock_vmnt(dir_vmp); old_wd = fp->fp_wd; /* Save orig. working dirp */ fp->fp_wd = dirp; vp = new_node(resolve, oflags, bits); fp->fp_wd = old_wd; /* Restore */ if (vp != NULL) { put_vnode(dirp); *(resolve->l_vnode) = vp; return(vp); } r = err_code; } if (r == EEXIST) err_code = EIO; /* Impossible, we have verified that * the last component doesn't exist and * is not a dangling symlink. */ else err_code = r; unlock_vmnt(dir_vmp); unlock_vnode(dirp); unlock_vnode(vp); put_vnode(dirp); return(NULL); } /* Store results and mark vnode in use */ vp->v_fs_e = res.fs_e; vp->v_inode_nr = res.inode_nr; vp->v_mode = res.fmode; vp->v_size = res.fsize; vp->v_uid = res.uid; vp->v_gid = res.gid; vp->v_sdev = res.dev; vp->v_vmnt = dirp->v_vmnt; vp->v_dev = vp->v_vmnt->m_dev; vp->v_fs_count = 1; vp->v_ref_count = 1; } else { /* Either last component exists, or there is some other problem. */ if (vp != NULL) { r = EEXIST; /* File exists or a symlink names a file while * O_EXCL is set. */ } else r = err_code; /* Other problem. */ } err_code = r; /* When dirp equals vp, we shouldn't release the lock as a vp is locked only * once. Releasing the lock would cause the resulting vp not be locked and * cause mayhem later on. */ if (dirp != vp) { unlock_vnode(dirp); } unlock_vmnt(dir_vmp); put_vnode(dirp); *(resolve->l_vnode) = vp; return(vp); }
/*===========================================================================* * free_proc * *===========================================================================*/ static void free_proc(struct fproc *exiter, int flags) { int i; register struct fproc *rfp; register struct filp *rfilp; register struct vnode *vp; dev_t dev; if (exiter->fp_endpoint == NONE) panic("free_proc: already free"); if (fp_is_blocked(exiter)) unpause(exiter->fp_endpoint); /* Loop on file descriptors, closing any that are open. */ for (i = 0; i < OPEN_MAX; i++) { (void) close_fd(exiter, i); } /* Release root and working directories. */ if (exiter->fp_rd) { put_vnode(exiter->fp_rd); exiter->fp_rd = NULL; } if (exiter->fp_wd) { put_vnode(exiter->fp_wd); exiter->fp_wd = NULL; } /* The rest of these actions is only done when processes actually exit. */ if (!(flags & FP_EXITING)) return; exiter->fp_flags |= FP_EXITING; /* Check if any process is SUSPENDed on this driver. * If a driver exits, unmap its entries in the dmap table. * (unmapping has to be done after the first step, because the * dmap table is used in the first step.) */ unsuspend_by_endpt(exiter->fp_endpoint); dmap_unmap_by_endpt(exiter->fp_endpoint); worker_stop_by_endpt(exiter->fp_endpoint); /* Unblock waiting threads */ vmnt_unmap_by_endpt(exiter->fp_endpoint); /* Invalidate open files if this * was an active FS */ /* Invalidate endpoint number for error and sanity checks. */ exiter->fp_endpoint = NONE; /* If a session leader exits and it has a controlling tty, then revoke * access to its controlling tty from all other processes using it. */ if ((exiter->fp_flags & FP_SESLDR) && exiter->fp_tty != 0) { dev = exiter->fp_tty; for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) { if(rfp->fp_pid == PID_FREE) continue; if (rfp->fp_tty == dev) rfp->fp_tty = 0; for (i = 0; i < OPEN_MAX; i++) { if ((rfilp = rfp->fp_filp[i]) == NULL) continue; if (rfilp->filp_mode == FILP_CLOSED) continue; vp = rfilp->filp_vno; if (!S_ISCHR(vp->v_mode)) continue; if ((dev_t) vp->v_sdev != dev) continue; lock_filp(rfilp, VNODE_READ); (void) dev_close(dev, rfilp-filp); /* Ignore any errors, even * SUSPEND. */ rfilp->filp_mode = FILP_CLOSED; unlock_filp(rfilp); } } } /* Exit done. Mark slot as free. */ exiter->fp_pid = PID_FREE; if (exiter->fp_flags & FP_PENDING) pending--; /* No longer pending job, not going to do it */ exiter->fp_flags = FP_NOFLAGS; }
/*===========================================================================* * common_open * *===========================================================================*/ int common_open(char path[PATH_MAX], int oflags, mode_t omode) { /* Common code from do_creat and do_open. */ int b, r, exist = TRUE, major_dev; dev_t dev; mode_t bits; struct filp *filp, *filp2; struct vnode *vp; struct vmnt *vmp; struct dmap *dp; struct lookup resolve; /* Remap the bottom two bits of oflags. */ bits = (mode_t) mode_map[oflags & O_ACCMODE]; if (!bits) return(EINVAL); /* See if file descriptor and filp slots are available. */ if ((r = get_fd(0, bits, &(scratch(fp).file.fd_nr), &filp)) != OK) return(r); lookup_init(&resolve, path, PATH_NOFLAGS, &vmp, &vp); /* If O_CREATE is set, try to make the file. */ if (oflags & O_CREAT) { omode = I_REGULAR | (omode & ALLPERMS & fp->fp_umask); vp = new_node(&resolve, oflags, omode); r = err_code; if (r == OK) exist = FALSE; /* We just created the file */ else if (r != EEXIST) { /* other error */ if (vp) unlock_vnode(vp); unlock_filp(filp); return(r); } else exist = !(oflags & O_EXCL);/* file exists, if the O_EXCL flag is set this is an error */ } else { /* Scan path name */ resolve.l_vmnt_lock = VMNT_READ; resolve.l_vnode_lock = VNODE_OPCL; if ((vp = eat_path(&resolve, fp)) == NULL) { unlock_filp(filp); return(err_code); } if (vmp != NULL) unlock_vmnt(vmp); } /* Claim the file descriptor and filp slot and fill them in. */ fp->fp_filp[scratch(fp).file.fd_nr] = filp; FD_SET(scratch(fp).file.fd_nr, &fp->fp_filp_inuse); filp->filp_count = 1; filp->filp_vno = vp; filp->filp_flags = oflags; /* Only do the normal open code if we didn't just create the file. */ if (exist) { /* Check protections. */ if ((r = forbidden(fp, vp, bits)) == OK) { /* Opening reg. files, directories, and special files differ */ switch (vp->v_mode & S_IFMT) { case S_IFREG: /* Truncate regular file if O_TRUNC. */ if (oflags & O_TRUNC) { if ((r = forbidden(fp, vp, W_BIT)) != OK) break; upgrade_vnode_lock(vp); truncate_vnode(vp, 0); } break; case S_IFDIR: /* Directories may be read but not written. */ r = (bits & W_BIT ? EISDIR : OK); break; case S_IFCHR: /* Invoke the driver for special processing. */ dev = (dev_t) vp->v_sdev; /* TTY needs to know about the O_NOCTTY flag. */ r = dev_open(dev, who_e, bits | (oflags & O_NOCTTY)); if (r == SUSPEND) suspend(FP_BLOCKED_ON_DOPEN); else vp = filp->filp_vno; /* Might be updated by * dev_open/clone_opcl */ break; case S_IFBLK: lock_bsf(); /* Invoke the driver for special processing. */ dev = (dev_t) vp->v_sdev; r = bdev_open(dev, bits); if (r != OK) { unlock_bsf(); break; } major_dev = major(vp->v_sdev); dp = &dmap[major_dev]; if (dp->dmap_driver == NONE) { printf("VFS: block driver disappeared!\n"); unlock_bsf(); r = ENXIO; break; } /* Check whether the device is mounted or not. If so, * then that FS is responsible for this device. * Otherwise we default to ROOT_FS. */ vp->v_bfs_e = ROOT_FS_E; /* By default */ for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp) if (vmp->m_dev == vp->v_sdev && !(vmp->m_flags & VMNT_FORCEROOTBSF)) { vp->v_bfs_e = vmp->m_fs_e; } /* Send the driver label to the file system that will * handle the block I/O requests (even when its label * and endpoint are known already), but only when it is * the root file system. Other file systems will * already have it anyway. */ if (vp->v_bfs_e != ROOT_FS_E) { unlock_bsf(); break; } if (req_newdriver(vp->v_bfs_e, vp->v_sdev, dp->dmap_label) != OK) { printf("VFS: error sending driver label\n"); bdev_close(dev); r = ENXIO; } unlock_bsf(); break; case S_IFIFO: /* Create a mapped inode on PFS which handles reads and writes to this named pipe. */ upgrade_vnode_lock(vp); r = map_vnode(vp, PFS_PROC_NR); if (r == OK) { if (vp->v_ref_count == 1) { if (vp->v_size != 0) r = truncate_vnode(vp, 0); } oflags |= O_APPEND; /* force append mode */ filp->filp_flags = oflags; } if (r == OK) { r = pipe_open(vp, bits, oflags); } if (r != ENXIO) { /* See if someone else is doing a rd or wt on * the FIFO. If so, use its filp entry so the * file position will be automatically shared. */ b = (bits & R_BIT ? R_BIT : W_BIT); filp->filp_count = 0; /* don't find self */ if ((filp2 = find_filp(vp, b)) != NULL) { /* Co-reader or writer found. Use it.*/ fp->fp_filp[scratch(fp).file.fd_nr] = filp2; filp2->filp_count++; filp2->filp_vno = vp; filp2->filp_flags = oflags; /* v_count was incremented after the vnode * has been found. i_count was incremented * incorrectly in FS, not knowing that we * were going to use an existing filp * entry. Correct this error. */ unlock_vnode(vp); put_vnode(vp); } else { /* Nobody else found. Restore filp. */ filp->filp_count = 1; } } break; } } } unlock_filp(filp); /* If error, release inode. */ if (r != OK) { if (r != SUSPEND) { fp->fp_filp[scratch(fp).file.fd_nr] = NULL; FD_CLR(scratch(fp).file.fd_nr, &fp->fp_filp_inuse); filp->filp_count = 0; filp->filp_vno = NULL; filp->filp_state &= ~FS_INVALIDATED; /* Prevent garbage col. */ put_vnode(vp); } } else { r = scratch(fp).file.fd_nr; } return(r); }