static int sys_ioctl(__user void * user_args) { struct _ioctl_get_args args; file_t * file; void * ioargs = NULL; int err, retval = -1; err = copyin(user_args, &args, sizeof(args)); if (err) { set_errno(EFAULT); return -1; } file = fs_fildes_ref(curproc->files, args.fd, 1); if (!file) { set_errno(EBADF); return -1; } if (args.arg) { ioargs = kmalloc(args.arg_len); if (!ioargs) { set_errno(ENOMEM); goto out; } if (args.request & 1) { /* Get request */ if (!useracc((__user void *)args.arg, args.arg_len, VM_PROT_WRITE)) { set_errno(EFAULT); goto out; } /* Get request doesn't need copyin */ } else { /* Set request */ /* Set operation needs copyin */ err = copyin((__user void *)args.arg, ioargs, args.arg_len); if (err) { set_errno(EFAULT); goto out; } } } /* Actual ioctl call */ retval = file->vnode->vnode_ops->ioctl(file, args.request, ioargs, args.arg_len); if (retval < 0) set_errno(-retval); /* Copyout if request type was get. */ if (args.request & 1) copyout(ioargs, (__user void *)args.arg, args.arg_len); out: kfree(ioargs); fs_fildes_ref(curproc->files, args.fd, -1); return retval; }
static int sys_lseek(void * user_args) { struct _fs_lseek_args args; file_t * file; int retval = 0; if (!useracc(user_args, sizeof(args), VM_PROT_WRITE)) { /* No permission to read/write */ set_errno(EFAULT); return -1; } copyin(user_args, &args, sizeof(args)); /* Increment refcount for the file pointed by fd */ file = fs_fildes_ref(curproc->files, args.fd, 1); if (!file) { set_errno(EBADF); return -1; } if (args.whence == SEEK_SET) file->seek_pos = args.offset; else if (args.whence == SEEK_CUR) file->seek_pos += args.offset; else if (args.whence == SEEK_END) { struct stat stat_buf; int err; err = file->vnode->vnode_ops->stat(file->vnode, &stat_buf); if (!err) { const off_t new_offset = stat_buf.st_size + args.offset; if (new_offset >= stat_buf.st_size) file->seek_pos = new_offset; else { set_errno(EOVERFLOW); retval = -1; } } else { set_errno(EBADF); retval = -1; } } else { set_errno(EINVAL); retval = -1; } /* Resulting offset is stored to args */ args.offset = file->seek_pos; /* Decrement refcount for the file pointed by fd */ fs_fildes_ref(curproc->files, args.fd, -1); copyout(&args, user_args, sizeof(args)); return retval; }
int mbr_register(int fd, int * part_count) { file_t * file; vnode_t * parent_vnode; struct dev_info * parent; uint8_t * block_0 = NULL; int parts = 0; int retval = 0; #ifdef configMBR_DEBUG KERROR(KERROR_DEBUG, "%s(fd: %d, part_count: %p)\n", __func__, fd, part_count); #endif file = fs_fildes_ref(curproc->files, fd, 1); parent_vnode = file->vnode; parent = (struct dev_info *)parent_vnode->vn_specinfo; if (!(S_ISBLK(parent_vnode->vn_mode) || S_ISCHR(parent_vnode->vn_mode))) { KERROR(KERROR_ERR, "MBR: not a device\n"); retval = -ENODEV; goto fail; } /* Check the validity of the parent device. */ if (!parent) { KERROR(KERROR_ERR, "MBR: invalid parent device\n"); retval = -ENODEV; goto fail; } block_0 = kmalloc(MBR_SIZE); if (!block_0) { retval = -ENOMEM; goto fail; } #ifdef configMBR_DEBUG KERROR(KERROR_DEBUG, "MBR: reading block 0 from device %s\n", parent->dev_name); #endif retval = read_block_0(block_0, file); if (retval) goto fail; retval = check_signature(block_0); if (retval) goto fail; #ifdef configMBR_DEBUG KERROR(KERROR_DEBUG, "MBR: found valid MBR on device %s\n", parent->dev_name); #endif /* * If parent block size is not MBR_SIZE, we have to coerce start_block * and blocks to fit. */ if (parent->block_size < MBR_SIZE) { /* We do not support parent device block sizes < 512 */ KERROR(KERROR_ERR, "MBR: block size of %s is too small (%i)\n", parent->dev_name, parent->block_size); retval = -ENOTSUP; goto fail; } uint32_t block_size_adjust = parent->block_size / MBR_SIZE; if (parent->block_size % MBR_SIZE) { /* * We do not support parent device block sizes that are not * multiples of 512. */ KERROR(KERROR_ERR, "MBR: block size of %s is not a multiple of 512 (%i)\n", parent->dev_name, parent->block_size); retval = -ENOTSUP; goto fail; } #ifdef configMBR_DEBUG if (block_size_adjust > 1) { KERROR(KERROR_DEBUG, "MBR: block_size_adjust: %i\n", block_size_adjust); } #endif int major_num = DEV_MAJOR(parent->dev_id) + 1; for (size_t i = 0; i < 4; i++) { size_t p_offset = 0x1be + (i * 0x10); struct mbr_dev * d; if (block_0[p_offset + 4] == 0x00) { /* Invalid partition */ continue; } d = kzalloc(sizeof(struct mbr_dev)); if (!d) { KERROR(KERROR_ERR, "MBR: Out of memory"); retval = -ENOMEM; break; } d->dev.dev_id = DEV_MMTODEV(major_num, mbr_dev_count); d->dev.drv_name = driver_name; ksprintf(d->dev.dev_name, sizeof(d->dev.dev_name), "%sp%u", parent->dev_name, i); d->dev.read = mbr_read; d->dev.write = (parent->write) ? mbr_write : NULL; d->dev.block_size = parent->block_size; d->dev.flags = parent->flags; d->part_no = i; d->part_id = block_0[p_offset + 4]; d->start_block = read_word(block_0, p_offset + 8); d->blocks = read_word(block_0, p_offset + 12); d->parent = parent; /* Adjust start_block and blocks to the parent block size */ if (d->start_block % block_size_adjust) { KERROR(KERROR_ERR, "MBR: partition number %i on %s does not start on a block " "boundary (%i).\n", d->part_no, parent->dev_name, d->start_block); retval = -EFAULT; goto fail; } d->start_block /= block_size_adjust; if (d->blocks % block_size_adjust) { KERROR(KERROR_ERR, "MBR: partition number %i on %s does not have a length " "that is an exact multiple of the block length (%i).\n", d->part_no, parent->dev_name, d->start_block); retval = -EFAULT; goto fail; } d->blocks /= block_size_adjust; d->dev.num_blocks = d->blocks; #ifdef configMBR_DEBUG KERROR(KERROR_DEBUG, "MBR: partition number %i (%s) of type %x, " "start sector %u, sector count %u, p_offset %03x\n", d->part_no, d->dev.dev_name, d->part_id, d->start_block, d->blocks, p_offset); #endif make_dev(&d->dev, 0, 0, 0666, NULL); mbr_dev_count++; parts++; /* Register the fs */ //register_fs__((struct dev_info *)d, d->part_id); /* TODO Register the fs dev with devfs */ } KERROR(KERROR_INFO, "MBR: found total of %i partition(s)\n", parts); fail: kfree(block_0); fs_fildes_ref(curproc->files, fd, -1); if (retval != 0) { KERROR(KERROR_ERR, "MBR registration failed on device: \"%s\"\n", parent->dev_name); } return retval; }
pid_t proc_fork(void) { /* * http://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html */ KERROR_DBG("%s(%u)\n", __func__, curproc->pid); struct proc_info * const old_proc = curproc; struct proc_info * new_proc; pid_t retval = 0; /* Check that the old process is in valid state. */ if (!old_proc || old_proc->state == PROC_STATE_INITIAL) return -EINVAL; new_proc = clone_proc_info(old_proc); if (!new_proc) return -ENOMEM; /* Clear some things required to be zeroed at this point */ new_proc->state = PROC_STATE_INITIAL; new_proc->files = NULL; new_proc->pgrp = NULL; /* Must be NULL so we don't free the old ref. */ memset(&new_proc->tms, 0, sizeof(new_proc->tms)); /* ..and then start to fix things. */ /* * Process group. */ PROC_LOCK(); proc_pgrp_insert(old_proc->pgrp, new_proc); PROC_UNLOCK(); /* * Initialize the mm struct. */ retval = vm_mm_init(&new_proc->mm, old_proc->mm.nr_regions); if (retval) goto out; /* * Clone the master page table. * This is probably something we would like to get rid of but we are * stuck with because it's the easiest way to keep some static kernel * mappings valid between processes. */ if (mmu_ptcpy(&new_proc->mm.mpt, &old_proc->mm.mpt)) { retval = -EAGAIN; goto out; } /* * Clone L2 page tables. */ if (vm_ptlist_clone(&new_proc->mm.ptlist_head, &new_proc->mm.mpt, &old_proc->mm.ptlist_head) < 0) { retval = -ENOMEM; goto out; } retval = clone_code_region(new_proc, old_proc); if (retval) goto out; /* * Clone stack region. */ retval = clone_stack(new_proc, old_proc); if (retval) { KERROR_DBG("Cloning stack region failed.\n"); goto out; } /* * Clone other regions. */ retval = clone_regions_from(new_proc, old_proc, MM_HEAP_REGION); if (retval) goto out; /* * Set break values. */ new_proc->brk_start = (void *)( (*new_proc->mm.regions)[MM_HEAP_REGION]->b_mmu.vaddr + (*new_proc->mm.regions)[MM_HEAP_REGION]->b_bcount); new_proc->brk_stop = (void *)( (*new_proc->mm.regions)[MM_HEAP_REGION]->b_mmu.vaddr + (*new_proc->mm.regions)[MM_HEAP_REGION]->b_bufsize); /* fork() signals */ ksignal_signals_fork_reinit(&new_proc->sigs); /* * Copy file descriptors. */ KERROR_DBG("Copy file descriptors\n"); int nofile_max = old_proc->rlim[RLIMIT_NOFILE].rlim_max; if (nofile_max < 0) { #if configRLIMIT_NOFILE < 0 #error configRLIMIT_NOFILE can't be negative. #endif nofile_max = configRLIMIT_NOFILE; } new_proc->files = fs_alloc_files(nofile_max, nofile_max); if (!new_proc->files) { KERROR_DBG( "\tENOMEM when tried to allocate memory for file descriptors\n"); retval = -ENOMEM; goto out; } /* Copy and ref old file descriptors */ for (int i = 0; i < old_proc->files->count; i++) { new_proc->files->fd[i] = old_proc->files->fd[i]; fs_fildes_ref(new_proc->files, i, 1); /* null pointer safe */ } KERROR_DBG("All file descriptors copied\n"); /* * Select PID. */ if (likely(nprocs != 1)) { /* Tecnically it would be good idea to have lock * on nprocs before reading it but I think this * should work fine... */ new_proc->pid = proc_get_random_pid(); } else { /* Proc is init */ KERROR_DBG("Assuming this process to be init\n"); new_proc->pid = 1; } if (new_proc->cwd) { KERROR_DBG("Increment refcount for the cwd\n"); vref(new_proc->cwd); /* Increment refcount for the cwd */ } /* Update inheritance attributes */ set_proc_inher(old_proc, new_proc); /* Insert the new process into the process array */ procarr_insert(new_proc); /* * A process shall be created with a single thread. If a multi-threaded * process calls fork(), the new process shall contain a replica of the * calling thread. * We left main_thread null if calling process has no main thread. */ KERROR_DBG("Handle main_thread\n"); if (old_proc->main_thread) { KERROR_DBG("Call thread_fork() to get a new main thread for the fork.\n"); if (!(new_proc->main_thread = thread_fork(new_proc->pid))) { KERROR_DBG("\tthread_fork() failed\n"); retval = -EAGAIN; goto out; } KERROR_DBG("\tthread_fork() fork OK\n"); /* * We set new proc's mpt as the current mpt because the new main thread * is going to return directly to the user space. */ new_proc->main_thread->curr_mpt = &new_proc->mm.mpt; } else { KERROR_DBG("No thread to fork.\n"); new_proc->main_thread = NULL; } retval = new_proc->pid; new_proc->state = PROC_STATE_READY; #ifdef configPROCFS procfs_mkentry(new_proc); #endif if (new_proc->main_thread) { KERROR_DBG("Set the new main_thread (%d) ready\n", new_proc->main_thread->id); thread_ready(new_proc->main_thread->id); } KERROR_DBG("Fork %d -> %d created.\n", old_proc->pid, new_proc->pid); out: if (unlikely(retval < 0)) { _proc_free(new_proc); } return retval; }
static int sys_filestat(void * user_args) { struct _fs_stat_args * args = 0; vnode_t * vnode; struct stat stat_buf; int err, retval = -1; err = copyinstruct(user_args, (void **)(&args), sizeof(struct _fs_stat_args), GET_STRUCT_OFFSETS(struct _fs_stat_args, path, path_len)); if (err) { set_errno(-err); goto out; } if (!useracc(args->buf, sizeof(struct stat), VM_PROT_WRITE)) { set_errno(EFAULT); goto out; } /* Validate path string */ if (!strvalid(args->path, args->path_len)) { set_errno(ENAMETOOLONG); goto out; } if (args->flags & AT_FDARG) { /* by fildes */ file_t * fildes; /* Note: AT_SYMLINK_NOFOLLOW == O_NOFOLLOW */ const int ofalgs = (args->flags & AT_SYMLINK_NOFOLLOW); fildes = fs_fildes_ref(curproc->files, args->fd, 1); if (!fildes) { set_errno(EBADF); goto out; } err = fildes->vnode->vnode_ops->stat(fildes->vnode, &stat_buf); if (!err) { /* Check if fildes was opened with O_SEARCH or if not then if we * have a permission to search by file permissions. */ if (fildes->oflags & O_SEARCH || chkperm_cproc(&stat_buf, O_EXEC)) err = lookup_vnode(&vnode, fildes->vnode, args->path, ofalgs); else /* No permission to search */ err = -EACCES; } fs_fildes_ref(curproc->files, args->fd, -1); if (err) { /* Handle previous error */ set_errno(-err); goto out; } } else { /* search by path */ if (fs_namei_proc(&vnode, -1, (char *)args->path, AT_FDCWD)) { set_errno(ENOENT); goto out; } } if ((args->flags & AT_FDARG) && (args->flags & O_EXEC)) { /* Get stat of given fildes, which we have * have in stat_buf. */ goto ready; } err = vnode->vnode_ops->stat(vnode, &stat_buf); if (err) { set_errno(-err); goto out; } ready: copyout(&stat_buf, args->buf, sizeof(struct stat)); retval = 0; out: freecpystruct(args); return retval; }
static int sys_fcntl(void * user_args) { struct _fs_fcntl_args args; file_t * file; int err, retval = -1; err = copyin(user_args, &args, sizeof(args)); if (err) { set_errno(EFAULT); return -1; } file = fs_fildes_ref(curproc->files, args.fd, 1); if (!file) { set_errno(EBADF); return -1; } switch (args.cmd) { case F_DUPFD_CLOEXEC: file->fdflags = FD_CLOEXEC; case F_DUPFD: { int new_fd; new_fd = fs_fildes_cproc_next(file, args.third.ival); if (new_fd < 0) { set_errno(-new_fd); goto out; } fs_fildes_ref(curproc->files, new_fd, 1); retval = new_fd; break; } case F_DUP2FD: { int new_fd = args.third.ival; if (args.fd == new_fd) { retval = new_fd; goto out; } if (curproc->files->fd[new_fd]) { if (fs_fildes_close_cproc(new_fd)) { set_errno(EIO); goto out; } } new_fd = fs_fildes_cproc_next(file, new_fd); if (new_fd < 0) { set_errno(-new_fd); goto out; } if (new_fd != args.third.ival || !fs_fildes_ref(curproc->files, new_fd, 1)) { fs_fildes_close_cproc(new_fd); set_errno(EIO); goto out; } retval = new_fd; break; } case F_GETFD: retval = file->fdflags; break; case F_SETFD: file->fdflags = args.third.ival; break; case F_GETFL: retval = file->oflags; break; case F_SETFL: /* TODO sync will need some operations to be done */ file->oflags = args.third.ival & (O_APPEND | O_SYNC | O_NONBLOCK); retval = 0; break; case F_GETOWN: /* TODO */ case F_SETOWN: /* TODO */ case F_GETLK: /* TODO */ case F_SETLK: /* TODO */ case F_SETLKW: /* TODO */ default: set_errno(EINVAL); } out: fs_fildes_ref(curproc->files, args.fd, -1); return retval; }
static int sys_getdents(void * user_args) { struct _ds_getdents_args args; struct dirent * dents; size_t bytes_left; file_t * fildes; struct dirent d; /* Temp storage */ int err, count = 0; err = copyin(user_args, &args, sizeof(args)); if (err) { set_errno(EFAULT); return -1; } /* We must have a write access to the given buffer. */ if (!useracc(args.buf, args.nbytes, VM_PROT_WRITE)) { set_errno(EFAULT); return -1; } fildes = fs_fildes_ref(curproc->files, args.fd, 1); if (!fildes) { set_errno(EBADF); return -1; } if (!S_ISDIR(fildes->vnode->vn_mode)) { count = -1; set_errno(ENOTDIR); goto out; } dents = kmalloc(args.nbytes); if (!dents) { count = -1; set_errno(ENOMEM); goto out; } /* * This is a bit tricky, if we are here for the first time there should be a * magic value 0x00000000FFFFFFFF set to seek_pos but we can't do a thing if * fildes was initialized incorrectly, so lets cross our fingers. */ d.d_off = fildes->seek_pos; bytes_left = args.nbytes; while (bytes_left >= sizeof(struct dirent)) { vnode_t * vnode = fildes->vnode; if (vnode->vnode_ops->readdir(vnode, &d)) break; dents[count++] = d; bytes_left -= (sizeof(struct dirent)); } fildes->seek_pos = d.d_off; copyout(dents, args.buf, count * sizeof(struct dirent)); kfree(dents); out: fs_fildes_ref(curproc->files, args.fd, -1); return count; }