int steal_cleanup_child(struct steal_pty_state *steal) { if (ptrace_memcpy_to_child(&steal->child, steal->child_scratch, "/dev/null", sizeof("/dev/null"))) { return steal->child.error; } int nullfd = do_syscall(&steal->child, openat, -1, steal->child_scratch, O_RDWR, 0, 0, 0); if (nullfd < 0) { return steal->child.error; } int i; for (i = 0; i < steal->master_fds.n; ++i) { do_dup2(&steal->child, nullfd, steal->master_fds.fds[i]); } do_syscall(&steal->child, close, nullfd, 0, 0, 0, 0, 0); do_syscall(&steal->child, close, steal->child_fd, 0, 0, 0, 0, 0); steal->child_fd = 0; ptrace_restore_regs(&steal->child); ptrace_detach_child(&steal->child); ptrace_wait(&steal->child); return 0; }
static int sys_dup2(const dup2_args_t *arg) { dup2_args_t kern_args; int err; if ((err = copy_from_user(&kern_args, arg, sizeof(kern_args))) < 0) { curthr->kt_errno = -err; return -1; } if ((err = do_dup2(kern_args.ofd, kern_args.nfd)) < 0) { curthr->kt_errno = -err; return -1; } else return err; }
int attach_child(pid_t pid, const char *pty, int force_stdio) { struct ptrace_child child; child_addr_t scratch_page = -1; int *child_tty_fds = NULL, n_fds, child_fd, statfd = -1; int i; int err = 0; long page_size = sysconf(_SC_PAGE_SIZE); #ifdef __linux__ char stat_path[PATH_MAX]; #endif if ((err = check_pgroup(pid))) { return err; } if ((err = preflight_check(pid))) { return err; } debug("Using tty: %s", pty); if ((err = copy_tty_state(pid, pty))) { if (err == ENOTTY && !force_stdio) { error("Target is not connected to a terminal.\n" " Use -s to force attaching anyways."); return err; } } #ifdef __linux__ snprintf(stat_path, sizeof stat_path, "/proc/%d/stat", pid); statfd = open(stat_path, O_RDONLY); if (statfd < 0) { error("Unable to open %s: %s", stat_path, strerror(errno)); return -statfd; } #endif kill(pid, SIGTSTP); wait_for_stop(pid, statfd); if ((err = grab_pid(pid, &child, &scratch_page))) { goto out_cont; } if (force_stdio) { child_tty_fds = malloc(3 * sizeof(int)); if (!child_tty_fds) { err = ENOMEM; goto out_unmap; } n_fds = 3; child_tty_fds[0] = 0; child_tty_fds[1] = 1; child_tty_fds[2] = 2; } else { child_tty_fds = get_child_tty_fds(&child, statfd, &n_fds); if (!child_tty_fds) { err = child.error; goto out_unmap; } } if (ptrace_memcpy_to_child(&child, scratch_page, pty, strlen(pty) + 1)) { err = child.error; error("Unable to memcpy the pty path to child."); goto out_free_fds; } child_fd = do_syscall(&child, openat, -1, scratch_page, O_RDWR | O_NOCTTY, 0, 0, 0); if (child_fd < 0) { err = child_fd; error("Unable to open the tty in the child."); goto out_free_fds; } debug("Opened the new tty in the child: %d", child_fd); err = ignore_hup(&child, scratch_page); if (err < 0) goto out_close; err = do_syscall(&child, getsid, 0, 0, 0, 0, 0, 0); if (err != child.pid) { debug("Target is not a session leader, attempting to setsid."); err = do_setsid(&child); } else { do_syscall(&child, ioctl, child_tty_fds[0], TIOCNOTTY, 0, 0, 0, 0); } if (err < 0) goto out_close; err = do_syscall(&child, ioctl, child_fd, TIOCSCTTY, 1, 0, 0, 0); if (err != 0) { /* Seems to be returning >0 for error */ error("Unable to set controlling terminal: %s", strerror(err)); goto out_close; } debug("Set the controlling tty"); for (i = 0; i < n_fds; i++) { err = do_dup2(&child, child_fd, child_tty_fds[i]); if (err < 0) error("Problem moving child fd number %d to new tty: %s", child_tty_fds[i], strerror(errno)); } err = 0; out_close: do_syscall(&child, close, child_fd, 0, 0, 0, 0, 0); out_free_fds: free(child_tty_fds); out_unmap: do_unmap(&child, scratch_page, page_size); ptrace_restore_regs(&child); ptrace_detach_child(&child); if (err == 0) { kill(child.pid, SIGSTOP); wait_for_stop(child.pid, statfd); } kill(child.pid, SIGWINCH); out_cont: kill(child.pid, SIGCONT); #ifdef __linux__ close(statfd); #endif return err < 0 ? -err : err; }
int vfs_selftest(kshell_t *kshell, int argc, char **argv) { int fd1,fd2; char *y="/ab/fil"; char x[2]; int err; do_mkdir("/ab"); do_mknod("/ab/new", S_IFCHR,MKDEVID(1,1)); fd1=do_open("/ab/new",2); fd2=do_dup2(fd1,NFILES+1); if(fd2<0) { dbg(DBG_PRINT,"File not created\n"); } do_mknod("/ab/notmade",4096,MKDEVID(1,1)); do_mknod("/ab/new/not",S_IFCHR,MKDEVID(1,1)); do_mknod("/ab/new", S_IFCHR,MKDEVID(1,1)); do_mknod("", S_IFCHR,MKDEVID(1,1)); /*do_close(fd1);*/ for(fd2=1;fd2<35;fd2++) { sprintf(x,"%d",fd2); strcat(y,x); do_mknod(y,S_IFCHR,MKDEVID(1,0)); err=do_open(y,2); if(err<0) { break; } if(fd2<10) { y[strlen(y)-1]='\0'; } else { y[strlen(y)-2]='\0'; } } do_mknod("/ab/new1", S_IFCHR,MKDEVID(1,1)); err=do_dup(fd1); do_unlink("/ab/new/ab"); do_unlink("/ab/new"); do_close(fd1); for(fd2=NFILES-1;fd2>0;fd2--) { err=do_close(fd2); sprintf(x,"%d",fd2); strcat(y,x); do_unlink(y); if(err<0) { break; } if(fd2<10) { y[strlen(y)-1]='\0'; } else { y[strlen(y)-2]='\0'; } } do_link("/a","/dev"); do_link("/dev","/a"); do_link("/dev","/a"); do_rmdir("/a"); /* mkdir("/k"); do_link("/ab","/k");*/ do_rmdir("/ab"); /*do_rmdir("/k");*/ /*GS: SELF TESTS*/ dbg(DBG_PRINT,"\n*************************************************************\n"); dbg(DBG_PRINT,"\n\n\n\n(GRADING2C)(kmain.c)(selftest_proc_run) selftests begin\n"); int retVal = 0; int i = 0; /* 1. dbg(DBG_PRINT, "(GRADING2C) (vfs_syscall.c) (do_stat) strlen too long, return -ENAMETOOLONG\n");*/ char longPath[1024 + 1] = {0}; for(i = 0; i < 1025; i++) longPath[i] = 'a'; struct stat buf; retVal = do_stat(longPath, &buf); retVal=do_chdir(longPath); /*2. dbg(DBG_PRINT, "(GRADING2B) ENOTDIR or ENOENT\n");*/ retVal = do_stat("", &buf); /*3. dbg(DBG_PRINT, "(GRADING2C) (vfs_syscall.c) (do_getdent) Invalid file descriptor fd, return -EBADF\n");*/ struct dirent dirp; retVal = do_getdent(-1, &dirp); /*4. dbg(DBG_PRINT, "(GRADING2C) (vfs_syscall.c) (do_getdent) Invalid file descriptor fd, return -EBADF\n");*/ retVal = do_getdent(1, &dirp); /*5. dbg(DBG_PRINT, "(GRADING2C) (vfs_syscall.c) (do_getdent) File descriptor does not refer to a directory, return -ENOTDIR\n");*/ do_mknod("/./file", S_IFCHR,MKDEVID(1,1)); fd1 = do_open("/./file",2); retVal = do_getdent(fd1, &dirp); do_unlink("/./file"); do_close(fd1); /*6. dbg(DBG_PRINT, "(GRADING2C) (vfs_syscall.c) (do_rename) Both are valid names \n");*/ /* and */ /*7. dbg(DBG_PRINT, "(GRADING2C) (vfs_syscall.c) (do_rename) error do_link, return error\n"); \n");*/ retVal = do_rename("/./aaa", "/./bbb"); dbg(DBG_PRINT,"\n\nretVal=%d",retVal); dbg(DBG_PRINT,"\n*************************************************************\n"); return 0; }
void *extra_self_tests(int arg1, void *arg2) { /* creating /test1/test2/ directories */ dbg(DBG_ERROR | DBG_VFS,"TEST: Creating directories\n"); do_mkdir("dir"); do_mkdir("dir/dir1"); do_mkdir("dir/dir2"); do_mkdir("dir/dir3"); do_mkdir("dir/dir4"); dbg(DBG_ERROR | DBG_VFS,"TEST: Directories are created\n"); int fd; char *file2buf="File 2 write only test case"; char *file1buf="Testing file_1 for write operation"; char readbuf[150]; dbg(DBG_ERROR | DBG_VFS,"TEST: Change directory to dir/dir1\n"); do_chdir("dir/dir1"); /* file1.txt creation with O_CREAT|O_WRONLY flag*/ dbg(DBG_ERROR | DBG_VFS,"TEST: Create file1.txt with O_CREAT|O_WRONLY flag in directory dir/dir1\n"); fd = do_open("file1.txt", O_CREAT|O_WRONLY); do_write(fd, file1buf, strlen(file1buf)); do_close(fd); /* file2.txt creation with O_CREAT|O_RDONLY flag*/ dbg(DBG_ERROR | DBG_VFS,"TEST: Change directory to dir/dir2\n"); do_chdir("/dir/dir2"); dbg(DBG_ERROR | DBG_VFS,"TEST: Create file2.txt with O_CREAT | O_RDONLY flag in directory dir/dir2\n"); fd = do_open("file2.txt", O_CREAT | O_RDONLY); do_close(fd); /* Write into file2.txt using O_WRONLY flag*/ dbg(DBG_ERROR | DBG_VFS,"TEST: Write into file2.txt with O_WRONLY flag in directory dir/dir2\n"); fd = do_open("file2.txt", O_WRONLY); do_write(fd, file2buf, strlen(file2buf)); do_close(fd); dbg(DBG_ERROR | DBG_VFS,"TEST: written chars: \"%s\" in file2.txt\n",file2buf); char *appendbuf=" Appending for O_WRONLY|O_APPEND mode"; /* Append into file2.txt using O_WRONLY|O_APPEND flag*/ dbg(DBG_ERROR | DBG_VFS,"TEST: Append into file2.txt with O_WRONLY|O_APPEND flag in directory dir/dir2\n"); fd = do_open("file2.txt", O_WRONLY|O_APPEND); do_write(fd, appendbuf, strlen(appendbuf)); do_close(fd); dbg(DBG_ERROR | DBG_VFS,"TEST: Appending chars: \"%s\" in file2.txt\n",appendbuf); fd = do_open("file2.txt", O_RDONLY); memset(readbuf,0,sizeof(char)*150); do_read(fd,readbuf,strlen(file2buf)+strlen(appendbuf)); dbg(DBG_ERROR | DBG_VFS,"TEST: After Appending text in file2.txt is: \"%s\" \n",readbuf); char *append2buf=" Appending for O_RDWR|O_APPEND mode in file2"; /* Append into file2.txt using O_RDWR|O_APPEND flag*/ dbg(DBG_ERROR | DBG_VFS,"TEST: Append into file2.txt with O_RDWR|O_APPEND flag in directory dir/dir2\n"); fd = do_open("file2.txt", O_RDWR|O_APPEND); do_write(fd, append2buf, strlen(append2buf)); do_close(fd); dbg(DBG_ERROR | DBG_VFS,"TEST: Appending chars: \"%s\" in file2.txt\n",append2buf); fd = do_open("file2.txt", O_RDONLY); memset(readbuf,0,sizeof(char)*150); do_read(fd,readbuf,strlen(file2buf)+strlen(append2buf)+strlen(appendbuf)); dbg(DBG_ERROR | DBG_VFS,"TEST: After Appending text in file2.txt is: \"%s\" \n",readbuf); dbg(DBG_ERROR | DBG_VFS,"TEST:Linking Source directory => /dir/dir2, Destination directory => /dir/linkofdir2 \n"); do_chdir("/"); do_link("dir/dir2","dir/linkofdir2"); dbg(DBG_ERROR | DBG_VFS,"TEST:Linking Source file => /dir/dir1/file1.txt, to the Destination => /dir/linkoffile1 \n"); do_link("dir/dir1/file1.txt","dir/linkoffile1"); dbg(DBG_ERROR | DBG_VFS,"TEST: Renaming directory from dir/dir3 to dir/renamed \n"); do_rename("dir/dir3","dir/renameddir3"); dbg(DBG_ERROR | DBG_VFS,"TEST: Renaming file from dir/dir1/file1.txt to dir/dir1/renamedfile1.txt \n"); do_rename("dir/dir1/file1.txt","dir/dir1/renamedfile1.txt"); dbg(DBG_ERROR | DBG_VFS,"TEST: Removing directory dir/dir4 \n"); do_rmdir("dir/dir4"); dbg(DBG_ERROR | DBG_VFS,"TEST: reading 18 chars from file: /dir/linkoffile2 which is hard link of /dir/dir2/file2.txt \n"); fd = do_open("dir/linkoffile2", O_RDONLY); memset(readbuf,0,sizeof(char)*150); do_close(fd); dbg(DBG_ERROR | DBG_VFS,"TEST: read 18 chars: \"%s\" from file: /dir/linkoffile1\n",readbuf); dbg(DBG_ERROR | DBG_VFS,"TEST: reading file using lseek function on /dir/linkoffile2 which is hard link of /dir/dir2/file2.txt \n"); memset(readbuf,0,sizeof(char)*150); fd = do_open("dir/linkoffile2", O_RDONLY); do_lseek(fd,-19,2); do_read(fd,readbuf,19); do_close(fd); dbg(DBG_ERROR | DBG_VFS,"TEST: read chars: \"%s\" using lseek from file: /dir/linkoffile1\n",readbuf); dbg(DBG_ERROR | DBG_VFS,"TEST: creating a duplicate file descriptor of file: /dir/dir2/file2.txt using do_dup()\n"); fd = do_open("/dir/dir2/file2.txt", O_RDONLY); int fd2= do_dup(fd); dbg(DBG_ERROR | DBG_VFS,"TEST: duplicate file descriptor is :\"%d\" of file: /dir/dir2/file2.txt \n",fd2); do_close(fd); do_close(fd2); dbg(DBG_ERROR | DBG_VFS,"TEST: creating a duplicate file descriptor of file: /dir/dir2/file2.txt using do_dup2()\n"); fd = do_open("/dir/dir2/file2.txt", O_RDONLY); fd2= do_dup2(fd,20); dbg(DBG_ERROR | DBG_VFS,"TEST: custom file descriptor is :\"%d\" of file: /dir/dir2/file2.txt \n",fd2); do_close(fd); do_close(fd2); /* Testing stat struct *statbuf; dbg(DBG_ERROR | DBG_VFS,"TEST: Testing the stat system call for directory dir\n"); do_stat("dir",statbuf); dbg(DBG_ERROR | DBG_VFS,"TEST: Output of stat for directory dir is :\"%s\" \n",statbuf);*/ shellTest(); return NULL; }
void impl_exit_start(void) { if( is_exiting == TRUE ) { return; } /* We are now exiting. * After this point, all calls to various sockets, * (i.e. accept(), listen(), etc. will result in stalls. * We are just waiting until existing connections have * finished and then we will be either exec()'ing a new * version or exiting this process. */ is_exiting = TRUE; /* Get ready to restart. * We only proceed with actual restart actions * if we are the master process, otherwise we will * simply prepare to shutdown cleanly once all the * current active connections have finished. */ if( master_pid == getpid() ) { pid_t child; DEBUG("Exit started -- this is the master."); /* Unlink files (e.g. pidfile). */ if( to_unlink != NULL && strlen(to_unlink) > 0 ) { DEBUG("Unlinking '%s'...", to_unlink); unlink(to_unlink); } /* Neuter this process. */ for( int fd = 0; fd < fd_limit(); fd += 1 ) { fdinfo_t* info = fd_lookup(fd); if( exit_strategy == FORK && info != NULL && info->type == SAVED ) { /* Close initial files. Since these * are now passed on to the child, we * ensure that the parent won't mess * with them anymore. Note that we still * have a copy as all SAVED descriptors. */ if( info->saved.fd == 2 ) { /* We treat stderr special. * Assuming logging will go here, we * allow the parent process to continue * writing to this file (and hope that * it's open in APPEND mode, etc.). */ continue; } int nullfd = open("/dev/null", O_RDWR); do_dup2(nullfd, info->saved.fd); libc.close(nullfd); } if( info != NULL && info->type == BOUND && !info->bound.is_ghost ) { /* Change BOUND sockets to dummy sockets. * This will allow select() and poll() to * operate as you expect, and never give * back new clients. */ int newfd = do_dup(fd); if( newfd >= 0 ) { int dummy_server = impl_dummy_server(); if( dummy_server >= 0 ) { /* Remove the descriptor in any epoll FDs. */ for( int efd = 0; efd < fd_limit(); efd += 1 ) { fdinfo_t* einfo = fd_lookup(efd); if( einfo != NULL && einfo->type == EPOLL ) { struct epoll_event no_event; epoll_ctl(efd, EPOLL_CTL_DEL, fd, &no_event); } } info->bound.is_ghost = 1; do_dup2(dummy_server, fd); DEBUG("Replaced FD %d with dummy.", fd); } else { do_close(newfd); } } } } switch( exit_strategy ) { case FORK: /* Start the child process. * We will exit gracefully when the tracked * connection count reaches zero. */ DEBUG("Exit strategy is fork."); child = libc.fork(); if( child == 0 ) { DEBUG("I'm the child."); impl_exec(); } else { DEBUG("I'm the parent."); } break; case EXEC: /* Nothing necessary beyond the above. */ DEBUG("Exit strategy is exec."); break; } } else { /* Force our strategy to fork, though we haven't forked. * This will basically just have this process exit cleanly * once all the current active connections have finished. */ DEBUG("Exit started -- this is the child."); exit_strategy = FORK; } }
static int do_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { fdinfo_t *info = NULL; int rval = -1; if( sockfd < 0 ) { errno = EINVAL; return -1; } /* At this point, we can reasonably assume * the program has started up and has installed * whatever signal handlers it wants. We check * that our own signal handler is installed. * If the user doesn't want us to override the * built-in signal handlers, they shouldn't use * huptime. */ impl_install_sighandlers(); DEBUG("do_bind(%d, ...) ...", sockfd); L(); /* See if this socket already exists. */ for( int fd = 0; fd < fd_limit(); fd += 1 ) { fdinfo_t *info = fd_lookup(fd); if( info != NULL && info->type == BOUND && info->bound.addrlen == addrlen && !memcmp(addr, (void*)info->bound.addr, addrlen) ) { DEBUG("Found ghost %d, cloning...", fd); /* Give back a duplicate of this one. */ int rval = do_dup2(fd, sockfd); if( rval < 0 ) { /* Dup2 failed? */ DEBUG("Failed."); continue; } if( info->bound.is_ghost ) { /* Close the original (not needed). */ info->bound.is_ghost = 0; do_close(fd); } /* Success. */ U(); DEBUG("do_bind(%d, ...) => 0 (ghosted)", sockfd); return 0; } } #ifdef SO_REUSEPORT /* Multi mode? Set socket options. */ if( multi_mode == TRUE ) { int optval = 1; if( setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) < 0 ) { U(); DEBUG("do_bind(%d, ...) => -1 (no multi?)", sockfd); return -1; } DEBUG("Multi mode enabled."); } #endif /* Try a real bind. */ info = alloc_info(BOUND); if( info == NULL ) { U(); DEBUG("do_bind(%d, ...) => -1 (alloc error?)", sockfd); return -1; } rval = libc.bind(sockfd, addr, addrlen); if( rval < 0 ) { dec_ref(info); U(); DEBUG("do_bind(%d, ...) => %d (error)", sockfd, rval); return rval; } /* Ensure that this socket is non-blocking, * this is because we override the behavior * for accept() and we require non-blocking * behavior. We deal with the consequences. */ rval = fcntl(sockfd, F_SETFL, O_NONBLOCK); if( rval < 0 ) { dec_ref(info); U(); DEBUG("do_bind(%d, ...) => %d (fcntl error)", sockfd, rval); return -1; } /* Save a refresh bound socket info. */ info->bound.stub_listened = 0; info->bound.real_listened = 0; info->bound.addr = (struct sockaddr*)malloc(addrlen); info->bound.addrlen = addrlen; memcpy((void*)info->bound.addr, (void*)addr, addrlen); fd_save(sockfd, info); /* Success. */ U(); DEBUG("do_bind(%d, ...) => %d", sockfd, rval); return rval; }
int sys_dup3(struct tcb *tcp) { return do_dup2(tcp, 2); }
int sys_dup2(struct tcb *tcp) { return do_dup2(tcp, -1); }