657 lines
16 KiB
C

#include <print.h>
#include <syscall.h>
#include <launcher.h>
#include <defs.h>
#include <bug.h>
#include <fs_defs.h>
#include <ipc.h>
#include <string.h>
#include <proc.h>
#include "malloc.h"
#define SERVER_READY_FLAG(vaddr) (*(int *)(vaddr))
#define SERVER_EXIT_FLAG(vaddr) (*(int *)((u64)vaddr+ 4))
#define remove_char() {usys_putc('\b'); usys_putc(' '); usys_putc('\b');}
extern ipc_struct_t *tmpfs_ipc_struct;
static ipc_struct_t ipc_struct;
static int tmpfs_scan_pmo_cap;
static int tmpfs_read_pmo_cap;
/* fs_server_cap in current process; can be copied to others */
int fs_server_cap;
#define BUFLEN 4096
#define MAX_FILE_COMPLEMENT_WORD 100
#define MAX_FILENAME_LEN 255
#define FTYPE_FILE 0b01
#define FTYPE_DIR 0b10
static char *builtin_complement_word[] = {"cd", "ls", "ll", "cat", "echo", "clear"};
static int n_builtin_complement_word = sizeof(builtin_complement_word) / sizeof(char *);
static char file_complement_buf[MAX_FILE_COMPLEMENT_WORD * (MAX_FILENAME_LEN + 1)];
void path_to_absolute(char *path);
void path_append(char *path, char *sub);
int fs_scan(char *path);
/**
* 生成文件补全词列表
* @param ret_words 返回列表
* @param path_prefix 文件的路径前缀,若不是文件夹返回空列表
* @param type_mask 文件类型掩码
* @return 列表长度
*/
static int make_file_complement_word(char ***ret_words, char *path_prefix, u8 type_mask) {
int n = 0;
static char *words[MAX_FILE_COMPLEMENT_WORD];
char *buf = file_complement_buf;
char pathbuf[BUFLEN];
// 当前目录
if (path_prefix == NULL) {
pathbuf[0] = '\0';
} else {
strcpy(pathbuf, path_prefix);
}
path_to_absolute(pathbuf);
// 列出目录文件
int ret = fs_scan(pathbuf);
if (ret >= 0) {
char *print_ptr;
struct dirent *dirat;
print_ptr = (char *) TMPFS_SCAN_BUF_VADDR;
for (int i = 0; i < ret; i++) {
dirat = (struct dirent *) print_ptr;
if ((dirat->d_type & type_mask) != 0) {
// 类型符合,加入结果
words[n++] = buf;
if (path_prefix == NULL) {
buf[0] = '\0';
} else {
strcpy(buf, path_prefix);
}
path_append(buf, dirat->d_name);
buf += MAX_FILENAME_LEN + 1;
if (n >= MAX_FILE_COMPLEMENT_WORD) {
break;
}
}
print_ptr += dirat->d_reclen;
}
}
*ret_words = &words[0];
return n;
}
/**
* 在传入的匹配词中匹配补全
*/
static char *match_complement_words(char **words, size_t n, char *complement, int complement_time, int *match_count) {
size_t len = strlen(complement);
for (int i = 0; i < n; i++) {
// 前缀匹配
if (strncmp(complement, words[i], len) != 0)
continue;
// 补全计数检查
if ((*match_count)++ >= complement_time) {
return words[i];
}
}
return NULL;
}
/**
* 匹配内建指令的参数
*/
static char *match_builtin_command_param(char *complement, int complement_time, int *match_count) {
u8 type_mask;
int start_pos;
char *matched = NULL;
char **words;
int n_words;
static char buf[BUFLEN];
// 指令分类
if (!strncmp(complement, "cd", 2) || !strncmp(complement, "ls", 2) || !strncmp(complement, "ll", 2)) {
type_mask = FTYPE_DIR;
start_pos = 2;
} else if (!strncmp(complement, "cat", 3)) {
type_mask = FTYPE_FILE;
start_pos = 3;
} else {
// 不支持的指令
return NULL;
}
// 跳过空格
while (complement[start_pos] == ' ')
start_pos++;
// 匹配子目录文件
n_words = make_file_complement_word(&words, complement + start_pos, type_mask);
matched = match_complement_words(words, n_words, complement + start_pos, complement_time, match_count);
// 匹配当前目录文件
if (!matched) {
n_words = make_file_complement_word(&words, NULL, type_mask);
matched = match_complement_words(words, n_words, complement + start_pos, complement_time, match_count);
}
// 匹配结果
if (matched) {
// 如果匹配到,就进行拼接
memcpy(buf, complement, start_pos);
strcpy(buf + start_pos, matched);
return buf;
}
return NULL;
}
static int do_complement(char *buf, char *complement, int complement_time)
{
int r = -1;
int match_count = 0;
char *matched = NULL;
char **words;
int n_words;
// 匹配子目录内文件,如 tar 匹配 tar/file1
n_words = make_file_complement_word(&words, complement, FTYPE_FILE);
matched = match_complement_words(words, n_words, complement, complement_time, &match_count);
// 匹配当前目录文件,如 f 匹配 file1
if (!matched) {
n_words = make_file_complement_word(&words, NULL, FTYPE_FILE);
matched = match_complement_words(words, n_words, complement, complement_time, &match_count);
}
// 匹配 Shell 内建指令
if (!matched) {
words = builtin_complement_word;
n_words = n_builtin_complement_word;
matched = match_complement_words(words, n_words, complement, complement_time, &match_count);
}
// 匹配 Shell 内建指令的参数
if (!matched) {
matched = match_builtin_command_param(complement, complement_time, &match_count);
}
// 匹配结果
if (matched) {
// 匹配到了
strcpy(buf, matched);
r = (int) strlen(buf);
} else if (match_count > 0 && complement_time >= match_count) {
// complement_time 大于一次循环
r = do_complement(buf, complement, complement_time % match_count);
} else {
// 无匹配
// printf("no match\n");
}
return r;
}
extern char getch();
// read a command from stdin leading by `prompt`
// put the commond in `buf` and return `buf`
// What you typed should be displayed on the screen
char *readline(const char *prompt)
{
static char buf[BUFLEN];
int i = 0, j = 0;
signed char c = 0;
int ret = 0;
char complement[BUFLEN];
int complement_time = 0;
if (prompt != NULL) {
printf("%s", prompt);
}
while (1) {
c = getch();
if (c < 0)
return NULL;
if (c != '\t') {
// 重置补全计数
complement_time = -1;
}
if (c == '\r' || c == '\n') {
// 回车
usys_putc('\n');
break;
} else if (c == '\b' || c == 127) {
// 退格
if (i > 0) {
i--;
remove_char();
}
continue;
} else if (c == '\t') {
// Tab
if (complement_time < 0) {
// 首次补全
memcpy(complement, buf, i);
complement[i] = '\0';
}
ret = do_complement(buf, complement, ++complement_time);
if (ret > 0) {
// 替换屏幕内容
for (j = 0; j < i; j++)
remove_char();
i = ret;
for (j = 0; j < i; j++)
usys_putc(buf[j]);
}
continue;
} else {
usys_putc(c);
}
buf[i++] = c;
}
buf[i] = '\0';
return buf;
}
int do_cd(char *cmdline)
{
cmdline += 2;
while (*cmdline == ' ')
cmdline++;
if (*cmdline == '\0')
return 0;
if (*cmdline != '/') {
}
printf("Build-in command cd %s: Not implemented!\n", cmdline);
return 0;
}
int do_top()
{
usys_top();
return 0;
}
/**
* 转为绝对路径
*/
void path_to_absolute(char *path) {
char pathbuf[BUFLEN];
strcpy(pathbuf + 1, path);
pathbuf[0] = '/';
strcpy(path, pathbuf);
}
/**
* 路径连接
*/
void path_append(char *path, char *sub) {
size_t path_len = strlen(path);
// 如果 path 不为空,则末尾应该有 '/'
if (path_len > 0) {
if (path[path_len - 1] == '/') {
path += path_len;
} else {
path[path_len] = '/';
path += path_len + 1;
}
}
// sub 之前没有 '/'
while (*sub == '/') sub++;
// 连接路径
strcpy(path, sub);
}
/**
* 扫描目录
*/
int fs_scan(char *path)
{
int count = 4096/2;
struct fs_request fr;
int ret;
// 拼接请求
fr.req = FS_REQ_SCAN;
fr.buff = (char *) TMPFS_SCAN_BUF_VADDR;
strcpy(fr.path, path);
fr.offset = 0;
fr.count = count;
// 发送
ipc_msg_t *ipc_msg = ipc_create_msg(tmpfs_ipc_struct, sizeof(fr), 1);
ipc_set_msg_data(ipc_msg, (void *)&fr, 0, sizeof(fr));
ipc_set_msg_cap(ipc_msg, 0, tmpfs_scan_pmo_cap);
ret = ipc_call(tmpfs_ipc_struct, ipc_msg);
ipc_destroy_msg(ipc_msg);
return ret;
}
/**
* 获得文件大小
*/
int fs_get_size(char *path)
{
struct fs_request fr;
int ret;
// 拼接请求
fr.req = FS_REQ_GET_SIZE;
strcpy(fr.path, path);
// 发送
ipc_msg_t *ipc_msg = ipc_create_msg(tmpfs_ipc_struct, sizeof(fr), 1);
ipc_set_msg_data(ipc_msg, (void *)&fr, 0, sizeof(fr));
ipc_set_msg_cap(ipc_msg, 0, tmpfs_scan_pmo_cap);
ret = ipc_call(tmpfs_ipc_struct, ipc_msg);
ipc_destroy_msg(ipc_msg);
return ret;
}
int fs_read(char *path, off_t offset, ssize_t count) {
struct fs_request fr;
int ret;
fail_cond(count > PAGE_SIZE,
"read size should smaller than %d, now %d", PAGE_SIZE, count);
// 拼接请求
fr.req = FS_REQ_READ;
fr.buff = (char *) TMPFS_READ_BUF_VADDR;
strcpy(fr.path, path);
fr.offset = offset;
fr.count = count;
// 发送
ipc_msg_t *ipc_msg = ipc_create_msg(tmpfs_ipc_struct, sizeof(fr), 1);
ipc_set_msg_data(ipc_msg, (void *)&fr, 0, sizeof(fr));
ipc_set_msg_cap(ipc_msg, 0, tmpfs_read_pmo_cap);
ret = ipc_call(tmpfs_ipc_struct, ipc_msg);
ipc_destroy_msg(ipc_msg);
return ret;
}
/**
* 读入文件并返回长度
*/
int fs_read_all(char *path, void **ret_buf)
{
int ret;
// 获得文件长度
int size = fs_get_size(path);
if (size < 0) {
return size;
}
// 映射读文件区
ret = usys_map_pmo(SELF_CAP,
tmpfs_read_pmo_cap,
TMPFS_READ_BUF_VADDR, VM_READ | VM_WRITE);
if (ret < 0) {
return ret;
}
// 申请缓冲
unsigned char *buf = malloc(size);
if (!buf) {
return -1;
}
*ret_buf = buf;
// 读入文件
int pos = 0;
while (size - pos > 0) {
int cur = fs_read(path, pos, PAGE_SIZE);
if (cur < 0) {
return cur;
}
// FIXME: 此处在 Release 模式下第一次读取前 16 字节为空
memcpy(buf, (const void *) TMPFS_READ_BUF_VADDR, cur);
memcpy(buf, (const void *) TMPFS_READ_BUF_VADDR, cur);
pos += cur;
buf += cur;
}
// 释放
usys_unmap_pmo(SELF_CAP, tmpfs_read_pmo_cap, TMPFS_READ_BUF_VADDR);
return size;
}
int do_ls(char *cmdline, bool detail)
{
char pathbuf[BUFLEN];
char fpathbuf[BUFLEN];
int ret;
pathbuf[0] = '\0';
cmdline += 2;
while (*cmdline == ' ')
cmdline++;
// 路径格式化
strcat(pathbuf, cmdline);
path_to_absolute(pathbuf);
// 请求 fs
ret = fs_scan(pathbuf);
if (ret >= 0) {
char *print_ptr;
struct dirent *dirat;
print_ptr = (char *) TMPFS_SCAN_BUF_VADDR;
for (int i = 0; i < ret; i++) {
dirat = (struct dirent *) print_ptr;
if (detail) {
// 文件类型
char type = 'f';
if (dirat->d_type == 2) {
type = 'd'; // 目录
}
// 文件大小
strcpy(fpathbuf, pathbuf);
path_append(fpathbuf, dirat->d_name);
int size = fs_get_size(fpathbuf);
// 输出
printf("%c %10d %s\n", type, size, dirat->d_name);
} else {
printf("%s\n", dirat->d_name);
}
print_ptr += dirat->d_reclen;
}
} else {
printf("ls returns code %d\n", ret);
}
return 0;
}
int do_cat(char *cmdline)
{
char pathbuf[BUFLEN];
pathbuf[0] = '\0';
cmdline += 3;
while (*cmdline == ' ')
cmdline++;
// 路径格式化
strcat(pathbuf, cmdline);
path_to_absolute(pathbuf);
// 读入文件
char *buf;
int ret = fs_read_all(pathbuf, (void **) &buf);
if (ret < 0) {
printf("cannot cat file, code %d\n", ret);
return ret;
}
// 打印
for (int i = 0; i < ret; i++) {
usys_putc(buf[i]);
}
free(buf);
return 0;
}
int do_echo(char *cmdline)
{
cmdline += 4;
while (*cmdline == ' ')
cmdline++;
printf("%s\n", cmdline);
return 0;
}
void do_clear(void)
{
usys_putc(12);
usys_putc(27);
usys_putc('[');
usys_putc('2');
usys_putc('J');
}
int builtin_cmd(char *cmdline)
{
int ret, i;
char cmd[BUFLEN];
for (i = 0; cmdline[i] != ' ' && cmdline[i] != '\0'; i++)
cmd[i] = cmdline[i];
cmd[i] = '\0';
if (!strcmp(cmd, "quit") || !strcmp(cmd, "exit"))
usys_exit(0);
if (!strcmp(cmd, "cd")) {
ret = do_cd(cmdline);
return !ret ? 1 : -1;
}
if (!strcmp(cmd, "ls")) {
ret = do_ls(cmdline, false);
return !ret ? 1 : -1;
}
if (!strcmp(cmd, "ll")) {
ret = do_ls(cmdline, true);
return !ret ? 1 : -1;
}
if (!strcmp(cmd, "echo")) {
ret = do_echo(cmdline);
return !ret ? 1 : -1;
}
if (!strcmp(cmd, "cat")) {
ret = do_cat(cmdline);
return !ret ? 1 : -1;
}
if (!strcmp(cmd, "clear")) {
do_clear();
return 1;
}
if (!strcmp(cmd, "top")) {
ret = do_top();
return !ret ? 1 : -1;
}
return 0;
}
int run_cmd(char *cmdline)
{
char pathbuf[BUFLEN];
struct user_elf user_elf;
int ret;
int caps[1];
pathbuf[0] = '\0';
while (*cmdline == ' ')
cmdline++;
if (*cmdline == '\0') {
return -1;
} else if (*cmdline != '/') {
strcpy(pathbuf, "/");
}
strcat(pathbuf, cmdline);
ret = readelf_from_fs(pathbuf, &user_elf);
if (ret < 0) {
printf("[Shell] No such binary\n");
return ret;
}
caps[0] = fs_server_cap;
return launch_process_with_pmos_caps(&user_elf, NULL, NULL,
NULL, 0, caps, 1, 0);
}
static int
run_cmd_from_kernel_cpio(const char *filename, int *new_thread_cap,
struct pmo_map_request *pmo_map_reqs,
int nr_pmo_map_reqs)
{
struct user_elf user_elf;
int ret;
ret = readelf_from_kernel_cpio(filename, &user_elf);
if (ret < 0) {
printf("[Shell] No such binary in kernel cpio\n");
return ret;
}
return launch_process_with_pmos_caps(&user_elf, NULL, new_thread_cap,
pmo_map_reqs, nr_pmo_map_reqs,
NULL, 0, 0);
}
void boot_fs(void)
{
int ret = 0;
int info_pmo_cap;
int tmpfs_main_thread_cap;
struct pmo_map_request pmo_map_requests[1];
/* create a new process */
printf("Booting fs...\n");
/* prepare the info_page (transfer init info) for the new process */
info_pmo_cap = usys_create_pmo(PAGE_SIZE, PMO_DATA);
fail_cond(info_pmo_cap < 0, "usys_create_ret ret %d\n", info_pmo_cap);
ret = usys_map_pmo(SELF_CAP,
info_pmo_cap, TMPFS_INFO_VADDR, VM_READ | VM_WRITE);
fail_cond(ret < 0, "usys_map_pmo ret %d\n", ret);
SERVER_READY_FLAG(TMPFS_INFO_VADDR) = 0;
SERVER_EXIT_FLAG(TMPFS_INFO_VADDR) = 0;
/* We also pass the info page to the new process */
pmo_map_requests[0].pmo_cap = info_pmo_cap;
pmo_map_requests[0].addr = TMPFS_INFO_VADDR;
pmo_map_requests[0].perm = VM_READ | VM_WRITE;
ret = run_cmd_from_kernel_cpio("/tmpfs.srv", &tmpfs_main_thread_cap,
pmo_map_requests, 1);
fail_cond(ret != 0, "create_process returns %d\n", ret);
fs_server_cap = tmpfs_main_thread_cap;
while (SERVER_READY_FLAG(TMPFS_INFO_VADDR) != 1)
usys_yield();
/* register IPC client */
tmpfs_ipc_struct = &ipc_struct;
ret = ipc_register_client(tmpfs_main_thread_cap, tmpfs_ipc_struct);
fail_cond(ret < 0, "ipc_register_client failed\n");
tmpfs_scan_pmo_cap = usys_create_pmo(PAGE_SIZE, PMO_DATA);
fail_cond(tmpfs_scan_pmo_cap < 0, "usys create_ret ret %d\n",
tmpfs_scan_pmo_cap);
ret = usys_map_pmo(SELF_CAP,
tmpfs_scan_pmo_cap,
TMPFS_SCAN_BUF_VADDR, VM_READ | VM_WRITE);
fail_cond(ret < 0, "usys_map_pmo ret %d\n", ret);
// 创建读文件 pmo
tmpfs_read_pmo_cap = usys_create_pmo(PAGE_SIZE, PMO_DATA);
fail_cond(tmpfs_read_pmo_cap < 0, "usys create_ret ret %d\n",
tmpfs_read_pmo_cap);
printf("fs is UP.\n");
}