Beispiel #1
0
/* this entry returns the inode pointed to by the given directory entry. It also
 * handles redirecting for symbolic links */
struct inode *fs_dirent_readinode(struct dirent *dir, bool perms)
{
	assert(dir);
	if(perms) {
		if(!vfs_inode_check_permissions(dir->parent, MAY_EXEC, 0))
			return 0;
	}
	return vfs_icache_get(dir->filesystem, dir->ino);
}
Beispiel #2
0
ssize_t fs_inode_read(struct inode *node, size_t off, size_t count, unsigned char *buf)
{
	if(!vfs_inode_check_permissions(node, MAY_READ, 0))
		return -EACCES;
	ssize_t ret;

	ret = fs_callback_inode_read(node, off, count, buf);
	return ret;
}
Beispiel #3
0
ssize_t fs_inode_write(struct inode *node, size_t off, size_t count, const unsigned char *buf)
{
	if(S_ISDIR(node->mode))
		return -EISDIR;
	if(!vfs_inode_check_permissions(node, MAY_WRITE, 0))
		return -EACCES;
	ssize_t ret = fs_callback_inode_write(node, off, count, buf);
	if(ret > 0) {
		node->mtime = time_get_epoch();
		vfs_inode_set_dirty(node);
	}
	return ret;
}
Beispiel #4
0
int sys_getdents(int fd, struct dirent_posix *dirs, unsigned int count)
{
	struct file *f = file_get(fd);
	if(!f) return -EBADF;

	unsigned nex;
	if(!vfs_inode_check_permissions(f->inode, MAY_READ, 0)) {
		file_put(f);
		return -EACCES;
	}
	rwlock_acquire(&f->inode->lock, RWL_READER);
	int r = fs_callback_inode_getdents(f->inode, f->pos, dirs, count, &nex);
	rwlock_release(&f->inode->lock, RWL_READER);
	f->pos = nex;

	file_put(f);
	return r;
}
Beispiel #5
0
int fs_link(struct inode *dir, struct inode *target, const char *name, size_t namelen, bool allow_incomplete_directories)
{
	if(!vfs_inode_check_permissions(dir, MAY_WRITE, 0))
		return -EACCES;
	if(!S_ISDIR(dir->mode))
		return -ENOTDIR;
	rwlock_acquire(&dir->lock, RWL_WRITER);
	if(S_ISDIR(dir->mode) && (dir->nlink == 1) && !allow_incomplete_directories) {
		rwlock_release(&dir->lock, RWL_WRITER);
		return -ENOSPC;
	}
	rwlock_acquire(&target->metalock, RWL_WRITER);
	int r = fs_callback_inode_link(dir, target, name, namelen);
	if(!r)
		atomic_fetch_add(&target->nlink, 1);
	rwlock_release(&target->metalock, RWL_WRITER);
	rwlock_release(&dir->lock, RWL_WRITER);
	return r;
}
Beispiel #6
0
/* note that this function doesn't actually delete the dirent. It just sets a flag that
 * says "hey, this was deleted". See vfs_dirent_release for more details. An important
 * aspect is that on unlinking a directory, it does unlink the . and .. entries, even
 * though the directory won't actually be deleted until vfs_dirent_release gets called
 * and the last reference is released. */
static int do_fs_unlink(struct inode *node, const char *name, size_t namelen, int rec)
{
	if(!vfs_inode_check_permissions(node, MAY_WRITE, 0))
		return -EACCES;
	struct dirent *dir = fs_dirent_lookup(node, name, namelen);
	if(!dir)
		return -ENOENT;
	struct inode *target = fs_dirent_readinode(dir, true);
	if(!target || (rec && S_ISDIR(target->mode) && !fs_inode_dirempty(target))) {
		if(target)
			vfs_icache_put(target);
		vfs_dirent_release(dir);
		return -ENOTEMPTY;
	}
	atomic_fetch_or_explicit(&dir->flags, DIRENT_UNLINK, memory_order_release);
	if(S_ISDIR(target->mode) && rec) {
		do_fs_unlink(target, "..", 2, 0);
		do_fs_unlink(target, ".", 1, 0);
	}
	vfs_icache_put(target);
	vfs_dirent_release(dir);
	return 0;
}
Beispiel #7
0
/* This function returns the directory entry associated with the name 'name' under
 * the inode 'node'. It must be careful to lookup the entry in the cache first. */
struct dirent *fs_dirent_lookup(struct inode *node, const char *name, size_t namelen)
{
	if(!vfs_inode_check_permissions(node, MAY_READ, 0))
		return 0;
	if(!S_ISDIR(node->mode))
		return 0;
	if(node == current_process->root && !strncmp(name, "..", 2) && namelen == 2)
		return fs_dirent_lookup(node, ".", 1);
	mutex_acquire(dirent_cache_lock);
	rwlock_acquire(&node->lock, RWL_WRITER);
	struct dirent *dir = vfs_inode_get_dirent(node, name, namelen);
	if(!dir) {
		dir = vfs_dirent_create(node);
		dir->count = 1;
		strncpy(dir->name, name, namelen);
		dir->namelen = namelen;
		int r = fs_callback_inode_lookup(node, name, namelen, dir);
		if(r) {
			dir->count = 0;
			vfs_dirent_destroy(dir);
			rwlock_release(&node->lock, RWL_WRITER);
			mutex_release(dirent_cache_lock);
			return 0;
		}
		vfs_inode_get(node);
		vfs_inode_add_dirent(node, dir);
	} else {
		if(atomic_fetch_add(&dir->count, 1) == 0) {
			fs_dirent_remove_lru(dir);
			vfs_inode_get(node);
		}
	}
	rwlock_release(&node->lock, RWL_WRITER);
	mutex_release(dirent_cache_lock);
	return dir;
}
Beispiel #8
0
int do_exec(char *path, char **argv, char **env, int shebanged /* oh my */)
{
	unsigned int i=0;
	addr_t end, eip;
	unsigned int argc=0, envc=0;
	char **backup_argv=0, **backup_env=0;
	/* Sanity */
	if(!path || !*path)
		return -EINVAL;
	/* Load the file, and make sure that it is valid and accessible */
	if(EXEC_LOG == 2) 
		printk(0, "[%d]: Checking executable file (%s)\n", current_process->pid, path);
	struct file *efil;
	int err_open;
	efil = fs_file_open(path, _FREAD, 0, &err_open);
	if(!efil)
		return err_open;
	/* are we allowed to execute it? */
	if(!vfs_inode_check_permissions(efil->inode, MAY_EXEC, 0))
	{
		file_put(efil);
		return -EACCES;
	}
	/* is it a valid elf? */
	int header_size = 0;
#if CONFIG_ARCH == TYPE_ARCH_X86_64
	header_size = sizeof(elf64_header_t);
#elif CONFIG_ARCH == TYPE_ARCH_X86
	header_size = sizeof(elf32_header_t);
#endif
	/* read in the ELF header, and check if it's a shebang */
	if(header_size < 2) header_size = 2;
	unsigned char mem[header_size];
	fs_file_pread(efil, 0, mem, header_size);
	
	if(__is_shebang(mem))
		return loader_do_shebang(efil, argv, env);
	
	int other_bitsize=0;
	if(!is_valid_elf(mem, 2) && !other_bitsize) {
		file_put(efil);
		return -ENOEXEC;
	}
	
	if(EXEC_LOG == 2) 
		printk(0, "[%d]: Copy data\n", current_process->pid);
	/* okay, lets back up argv and env so that we can
	 * clear out the address space and not lose data...
	 * If this call if coming from a shebang, then we don't check the pointers,
	 * since they won't be from userspace */
	size_t total_args_len = 0;
	if((shebanged || mm_is_valid_user_pointer(SYS_EXECVE, argv, 0)) && argv) {
		while((shebanged || mm_is_valid_user_pointer(SYS_EXECVE, argv[argc], 0)) && argv[argc] && *argv[argc])
			argc++;
		backup_argv = (char **)kmalloc(sizeof(addr_t) * argc);
		for(i=0;i<argc;i++) {
			backup_argv[i] = (char *)kmalloc(strlen(argv[i]) + 1);
			_strcpy(backup_argv[i], argv[i]);
			total_args_len += strlen(argv[i])+1 + sizeof(char *);
		}
	}
	if((shebanged || mm_is_valid_user_pointer(SYS_EXECVE, env, 0)) && env) {
		while((shebanged || mm_is_valid_user_pointer(SYS_EXECVE, env[envc], 0)) && env[envc] && *env[envc]) envc++;
		backup_env = (char **)kmalloc(sizeof(addr_t) * envc);
		for(i=0;i<envc;i++) {
			backup_env[i] = (char *)kmalloc(strlen(env[i]) + 1);
			_strcpy(backup_env[i], env[i]);
			total_args_len += strlen(env[i])+1 + sizeof(char *);
		}
	}
	total_args_len += 2 * sizeof(char *);
	/* and the path too! */
	char *path_backup = (char *)kmalloc(strlen(path) + 1);
	_strcpy((char *)path_backup, path);
	path = path_backup;
	
	/* Preexec - This is the point of no return. Here we close out unneeded 
	 * file descs, free up the page directory and clear up the resources 
	 * of the task */
	if(EXEC_LOG)
		printk(0, "Executing (p%dt%d, cpu %d, tty %d): %s\n", current_process->pid, current_thread->tid, current_thread->cpu->knum, current_process->pty ? current_process->pty->num : 0, path);
	preexec();
	
	/* load in the new image */
	strncpy((char *)current_process->command, path, 128);
	if(!loader_parse_elf_executable(mem, efil, &eip, &end))
		eip=0;
	/* do setuid and setgid */
	if(efil->inode->mode & S_ISUID) {
		current_process->effective_uid = efil->inode->uid;
	}
	if(efil->inode->mode & S_ISGID) {
		current_process->effective_gid = efil->inode->gid;
	}
	/* we don't need the file anymore, close it out */
	file_put(efil);
	file_close_cloexec();
	if(!eip) {
		printk(5, "[exec]: Tried to execute an invalid ELF file!\n");
		free_dp(backup_argv, argc);
		free_dp(backup_env, envc);
		kfree(path);
		tm_thread_exit(0);
	}
	
	if(EXEC_LOG == 2) 
		printk(0, "[%d]: Updating task values\n", current_process->pid);
	/* Setup the task with the proper values (libc malloc stack) */
	addr_t end_l = end;
	end = ((end-1)&PAGE_MASK) + PAGE_SIZE;
	total_args_len += PAGE_SIZE;
	/* now we need to copy back the args and env into userspace
	 * writeable memory...yippie. */
	addr_t args_start = end + PAGE_SIZE;
	addr_t env_start = args_start;
	addr_t alen = 0;
	mm_mmap(end, total_args_len,
			PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0, 0);
	if(backup_argv) {
		memcpy((void *)args_start, backup_argv, sizeof(addr_t) * argc);
		alen += sizeof(addr_t) * argc;
		*(addr_t *)(args_start + alen) = 0; /* set last argument value to zero */
		alen += sizeof(addr_t);
		argv = (char **)args_start;
		for(i=0;i<argc;i++)
		{
			char *old = argv[i];
			char *new = (char *)(args_start+alen);
			unsigned len = strlen(old) + 4;
			argv[i] = new;
			_strcpy(new, old);
			kfree(old);
			alen += len;
		}
		kfree(backup_argv);
	}
	env_start = args_start + alen;
	alen = 0;
	if(backup_env) {
		memcpy((void *)env_start, backup_env, sizeof(addr_t) * envc);
		alen += sizeof(addr_t) * envc;
		*(addr_t *)(env_start + alen) = 0; /* set last argument value to zero */
		alen += sizeof(addr_t);
		env = (char **)env_start;
		for(i=0;i<envc;i++)
		{
			char *old = env[i];
			char *new = (char *)(env_start+alen);
			unsigned len = strlen(old) + 1;
			env[i] = new;
			_strcpy(new, old);
			kfree(old);
			alen += len;
		}
		kfree(backup_env);
	}
	end = (env_start + alen) & PAGE_MASK;
	current_process->env = env;
	current_process->argv = argv;
	kfree(path);
	
	/* set the heap locations, and map in the start */
	current_process->heap_start = current_process->heap_end = end + PAGE_SIZE*2;
	addr_t ret = mm_mmap(end + PAGE_SIZE, PAGE_SIZE,
			PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0, 0);
	/* now, we just need to deal with the syscall return stuff. When the syscall
	 * returns, it'll just jump into the entry point of the new process */
	tm_thread_lower_flag(current_thread, THREAD_SCHEDULE);
	/* the kernel cares if it has executed something or not */
	if(!(kernel_state_flags & KSF_HAVEEXECED))
		set_ksf(KSF_HAVEEXECED);
	arch_loader_exec_initializer(argc, eip);
	if(EXEC_LOG == 2) 
		printk(0, "[%d]: Performing call\n", current_process->pid);
	return 0;
}
Beispiel #9
0
/* This is possibly the lengthiest function in the VFS (possibly in the entire kernel!).
 * It resolves a path until the last name in the path string, and then tries to create
 * a new file with that name. It requires a lot of in-sequence checks for permission,
 * existance, is-a-directory, and so forth. */
struct inode *fs_path_resolve_create_get(const char *path, int flags, mode_t mode, int *result, struct dirent **dirent)
{
	// Step 1: Split the path up into directory to create in, and name of new file.
	int len = strlen(path) + 1;
	char tmp[len];
	memcpy(tmp, path, len);
	char *del = strrchr(tmp, '/');
	if(del)
		*del = 0;
	char *dirpath = del ? tmp : ".";
	char *name = del ? del + 1 : tmp;
	if(dirpath[0] == 0)
		dirpath = "/";
	if(dirent)
		*dirent = 0;
	if(result)
		*result = 0;

	// Step 2: Resolve the target directory.
	struct inode *dir = fs_path_resolve_inode(dirpath, flags, result);
	if(!dir)
		return 0;
	if(!S_ISDIR(dir->mode)) {
		if(result) *result = -ENOTDIR;
		return 0;
	}

	// Step 3: Try to look up the file that we're trying to create.
	struct dirent *test = fs_dirent_lookup(dir, name, strlen(name));
	if(test) {
		// If it was found, return it and its inode.
		if(dirent)
			*dirent = test;
		struct inode *ret = fs_dirent_readinode(test, true);
		if(ret)	
			ret = fs_resolve_mount(ret);
		if(!ret) {
			if(dirent)
				*dirent = 0;
			if(result)
				*result = -EIO;
			vfs_dirent_release(test);
		} else {
			if(!dirent)
				vfs_dirent_release(test);
			if(result)
				*result = 0;
		}
		vfs_icache_put(dir);
		return ret;
	}
	
	// Didn't find the entry. Step 4: Create one.
	if(!vfs_inode_check_permissions(dir, MAY_WRITE, 0)) {
		if(result) *result = -EACCES;
		vfs_icache_put(dir);
		return 0;
	}
	if(dir->nlink == 1) {
		if(result) *result = -ENOSPC;
		vfs_icache_put(dir);
		return 0;
	}

	uint32_t id;
	// Step 4a: Allocate an inode.
	int r = fs_callback_fs_alloc_inode(dir->filesystem, &id);
	if(r) {
		if(result) *result = r;
		vfs_icache_put(dir);
		return 0;
	}

	// Step 4b: Read in that inode, and set some initial values (like creation time).
	struct inode *node = vfs_icache_get(dir->filesystem, id);
	if(!node) {
		vfs_icache_put(dir);
		if(result) *result = -EIO;
		return 0;
	}
	node->mode = mode;
	node->length = 0;
	node->ctime = node->mtime = time_get_epoch();
	vfs_inode_set_dirty(node);
	
	// If we're making a directory, create the . and .. entries.
	if(S_ISDIR(mode)) {
		// Create . and ..
		if(fs_link(node, node, ".", 1, true))
			r = -EPERM;
		if(fs_link(node, dir, "..", 2, true))
			r = -EMLINK;
	}

	// Step 4c: Create the link for the directory entry to the inode.
	r = fs_link(dir, node, name, strlen(name), false);

	if(result)
		*result = !r ? 1 : r;
	if(dirent && !r) {
		*dirent = fs_dirent_lookup(dir, name, strlen(name));
		if(!*dirent && node) {
			vfs_icache_put(node);
			vfs_icache_put(dir);
			if(result)
				*result = -EIO;
			return 0;
		}
	}
	vfs_icache_put(dir);
	return r ? 0 : node;
}