Partially fixed logic in the file system class with changes to support fs table structure instead of a tree
This commit is contained in:
@@ -15,6 +15,12 @@ export type BashInitArgs = {
|
|||||||
fs: any;
|
fs: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TimeStamps = {
|
||||||
|
mTime: Date;
|
||||||
|
cTime: Date;
|
||||||
|
aTime: Date;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Finish this
|
// TODO: Finish this
|
||||||
// TODO: Change into a type instead of an enum for performance (low priority)
|
// TODO: Change into a type instead of an enum for performance (low priority)
|
||||||
export enum ExitCode {
|
export enum ExitCode {
|
||||||
@@ -29,8 +35,8 @@ export type User = {
|
|||||||
readonly gid: number; // Primary group | 'Users' 1000 - Others - 1000+ root - 0 //TODO: Make a formated type
|
readonly gid: number; // Primary group | 'Users' 1000 - Others - 1000+ root - 0 //TODO: Make a formated type
|
||||||
home: string; //TODO: Make a formated type
|
home: string; //TODO: Make a formated type
|
||||||
history: string[];
|
history: string[];
|
||||||
cwd?: string[]; //TODO: Make a formated type
|
cwd?: number; //TODO: Make a formated type
|
||||||
pwd?: string[]; //TODO: Make a formated type
|
pwd?: number; //TODO: Make a formated type
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Group = {
|
export type Group = {
|
||||||
@@ -69,11 +75,11 @@ export class Bash {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getCwd(): string[] {
|
getCwd(): number {
|
||||||
return this.vfs.cwd;
|
return this.vfs.cwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPwd(): string[] {
|
getPwd(): number {
|
||||||
return this.vfs.pwd;
|
return this.vfs.pwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,13 +95,6 @@ export class Bash {
|
|||||||
return this._group[1].members.includes(uid);
|
return this._group[1].members.includes(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
changeUser(user: User) {
|
|
||||||
this.user = user;
|
|
||||||
this.vfs.home = this.vfs._splitPathString(user.home);
|
|
||||||
this.vfs.cwd = user.cwd ? user.cwd : this.vfs._splitPathString(user.home);
|
|
||||||
this.vfs.pwd = user.pwd ? user.pwd : this.vfs._splitPathString(user.home);
|
|
||||||
}
|
|
||||||
|
|
||||||
executeCommand(commandName: string, args: CommandArgs): void {
|
executeCommand(commandName: string, args: CommandArgs): void {
|
||||||
let result: Result = { exitCode: ExitCode.ERROR };
|
let result: Result = { exitCode: ExitCode.ERROR };
|
||||||
const command = this._commands[commandName];
|
const command = this._commands[commandName];
|
||||||
@@ -121,23 +120,12 @@ export class Bash {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
userLogin(username: string, passwd: string): ExitCode {
|
|
||||||
const user: User | undefined = this._passwd.find((u) => u.username === username);
|
|
||||||
if (user === undefined) return ExitCode.ERROR;
|
|
||||||
|
|
||||||
if (user.passwd === passwd) {
|
|
||||||
this._instances.push(user);
|
|
||||||
this.changeUser(user);
|
|
||||||
return ExitCode.ERROR; //TODO: Make it return the exitcode of changeUser() if needed
|
|
||||||
} else return ExitCode.ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
userLogout() {
|
userLogout() {
|
||||||
this._instances.pop();
|
this._instances.pop();
|
||||||
if (this._instances.size() === 0) {
|
if (this._instances.size() === 0) {
|
||||||
//TODO: Implement system logout
|
//TODO: Implement system logout
|
||||||
} else {
|
} else {
|
||||||
this.changeUser(this._instances.peek()!);
|
//this.changeUser(this._instances.peek()!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import type { readonly } from 'svelte/store';
|
import type { readonly } from 'svelte/store';
|
||||||
import type { Permission, User } from './bash';
|
import type { Permission, TimeStamps, User } from './bash';
|
||||||
|
import { Stack } from '../stack';
|
||||||
|
import { Pause } from '@lucide/svelte';
|
||||||
|
|
||||||
export enum Type {
|
export enum Type {
|
||||||
Directory = 16384,
|
Directory = 16384,
|
||||||
File = 32768
|
File = 32768,
|
||||||
|
SymblicLink = 40960
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NodePerms = {
|
export type NodePerms = {
|
||||||
@@ -18,118 +21,132 @@ export type FsInitArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TreeNode = {
|
export type TreeNode = {
|
||||||
|
inode: number;
|
||||||
|
parent?: number;
|
||||||
name: string;
|
name: string;
|
||||||
type: Type;
|
type: Type;
|
||||||
readonly: boolean;
|
children: number[];
|
||||||
|
content: string; // GUID of the cache file that contains the file contents.
|
||||||
|
link: number; // Links
|
||||||
|
permission: NodePerms;
|
||||||
|
owner: number;
|
||||||
|
group: number;
|
||||||
|
timestamps: TimeStamps;
|
||||||
interactible: boolean;
|
interactible: boolean;
|
||||||
func: any;
|
func: any;
|
||||||
children: TreeNode[];
|
|
||||||
content: string; // Path to the content of the file
|
|
||||||
link: string[]; // Symlink
|
|
||||||
permission: NodePerms;
|
|
||||||
owner: string;
|
|
||||||
group: string;
|
|
||||||
modtime: Date;
|
|
||||||
parent?: TreeNode;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export class VirtualFS {
|
export class VirtualFS {
|
||||||
private root: TreeNode; // TODO make this the correct type
|
private FsTable: Map<number, TreeNode>;
|
||||||
|
private rootINode: number;
|
||||||
|
|
||||||
home: string[];
|
home: number;
|
||||||
cwd: string[];
|
cwd: number;
|
||||||
pwd: string[];
|
pwd: number;
|
||||||
|
|
||||||
constructor(args: FsInitArgs) {
|
constructor(args: FsInitArgs) {
|
||||||
this.root = args.fs;
|
this.FsTable = args.fs;
|
||||||
this.home = this._splitPathString(args.user.home);
|
this.rootINode = 1;
|
||||||
|
this.home = this._pathStringToINode(args.user.home);
|
||||||
this.cwd = args.user.cwd ? args.user.cwd : this.home;
|
this.cwd = args.user.cwd ? args.user.cwd : this.home;
|
||||||
this.pwd = args.user.pwd ? args.user.pwd : this.cwd;
|
this.pwd = args.user.pwd ? args.user.pwd : this.cwd;
|
||||||
|
|
||||||
console.log(this.home);
|
console.log(this.home);
|
||||||
console.log(this.cwd);
|
console.log(this.cwd);
|
||||||
console.log(this.pwd);
|
console.log(this.pwd);
|
||||||
|
|
||||||
console.log('VFS INIT ', this._getNodeByPathArray(['/', 'home', 'kamil']));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _iNodeToPathString(inode: number): string {
|
||||||
|
let components: Stack<string> = new Stack<string>();
|
||||||
|
let currentNode = this.FsTable.get(inode);
|
||||||
|
let path: string = '';
|
||||||
|
if(!currentNode) throw new Error('iNode does not exist,');
|
||||||
|
|
||||||
_splitPathString(path: string): string[] {
|
components.push(currentNode.name);
|
||||||
if (path === '/') return ['/'];
|
|
||||||
|
|
||||||
const raw: string[] = path.split('/');
|
if(!currentNode.parent) {
|
||||||
const parts: string[] = [];
|
for(let i = 0; i < components.size(); i++) {
|
||||||
|
path += components.pop() + '/';
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < raw.length; i++) {
|
} else {
|
||||||
if (raw[i].length > 0) parts.push(raw[i]);
|
this._iNodeToPathString(currentNode.parent);
|
||||||
}
|
}
|
||||||
return parts;
|
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
_isAbsolutePath = (path: string): boolean => {
|
private _pathStringToINode(path: string): number {
|
||||||
|
const normalizedPath = path.replace(/^\/+|\/+$/g, '');
|
||||||
|
const pathComponents = normalizedPath.split('/').filter(component => component.length > 0);
|
||||||
|
|
||||||
|
if(pathComponents.length === 0) return this.rootINode;
|
||||||
|
|
||||||
|
let currentNode = this.FsTable.get(this.rootINode);
|
||||||
|
if(!currentNode) throw new Error('iNode does not exist,');
|
||||||
|
|
||||||
|
for(const component of pathComponents) {
|
||||||
|
const childINode = this._findChildNodeByName(currentNode, component);
|
||||||
|
if(childINode === null) throw new Error('this child iNode does not exist,');
|
||||||
|
|
||||||
|
const nextNode = this.FsTable.get(childINode);
|
||||||
|
if(!nextNode) throw new Error('iNode child does not exist,');
|
||||||
|
|
||||||
|
currentNode = nextNode;
|
||||||
|
}
|
||||||
|
return currentNode.inode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _findChildNodeByName(node: TreeNode, name: string): number {
|
||||||
|
for(const childINode of node.children) {
|
||||||
|
const child = this.FsTable.get(childINode);
|
||||||
|
if(child && child.name === name) {
|
||||||
|
return childINode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error('could not find the specified child node');
|
||||||
|
}
|
||||||
|
|
||||||
|
private _isAbsolutePath = (path: string): boolean => {
|
||||||
return typeof path === 'string' && path.startsWith('/');
|
return typeof path === 'string' && path.startsWith('/');
|
||||||
};
|
};
|
||||||
|
|
||||||
pathArrayToString(path: string[]): string {
|
|
||||||
if (path.length === 1 && path[0] === '/') return '/';
|
|
||||||
return '/' + path.join('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
formatPath(path: string): string {
|
formatPath(path: string): string {
|
||||||
const prefix = this.pathArrayToString(this.home);
|
const prefix = this._iNodeToPathString(this.home);
|
||||||
if (path.startsWith(prefix)) {
|
if (path.startsWith(prefix)) {
|
||||||
return path.replace(prefix, '~');
|
return path.replace(prefix, '~');
|
||||||
} else return path;
|
} else return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvePath(path: string): string[] {
|
resolvePath(path: string): TreeNode{
|
||||||
if (path === '' || path === undefined || path === null) return this.cwd.slice();
|
if(path === '/') return this._getNodeByINode(this.rootINode);
|
||||||
if (path.startsWith('/') && path.length === 1) return [];
|
|
||||||
|
|
||||||
if (path.startsWith('~')) {
|
if (!this._isAbsolutePath(path)) {
|
||||||
const trail: string = path === '~' ? '' : path.slice(1);
|
const trail: string = this._iNodeToPathString(this.cwd);
|
||||||
const home: string = this.pathArrayToString(this.home);
|
path = trail + path;
|
||||||
path = home + (trail ? (trail.startsWith('/') ? '' : '/') + trail : '');
|
|
||||||
|
}
|
||||||
|
else if (path.startsWith('~')) {
|
||||||
|
const trail: string = this._iNodeToPathString(this.home);
|
||||||
|
path = trail + path
|
||||||
}
|
}
|
||||||
|
|
||||||
const start = this._isAbsolutePath(path) ? [] : this.cwd.slice();
|
console.log(path);
|
||||||
const parts = this._splitPathString(path);
|
|
||||||
|
|
||||||
for (let i = 0; i < parts.length; i++) {
|
const INode: number = this._pathStringToINode(path);
|
||||||
const seg = parts[i];
|
const Node: TreeNode = this._getNodeByINode(INode);
|
||||||
|
|
||||||
if (seg === '.' || seg === '') continue;
|
return Node;
|
||||||
if (seg === '..') {
|
|
||||||
if (start.length > 1) start.pop();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
start.push(seg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (start.length === 0) return [];
|
|
||||||
console.log('OUTPUT', start);
|
|
||||||
return start;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_getNodeByPathArray(path: string[]): TreeNode | null {
|
private _getNodeByINode(inode: number): TreeNode {
|
||||||
if (path.length === 1 && path[0] === '/') return this.root;
|
const node: TreeNode | undefined = this.FsTable.get(inode);
|
||||||
|
if(!node) throw new Error('Could not get the node, no such i node exists');
|
||||||
let node: TreeNode = this.root;
|
|
||||||
const parts: string[] = path.slice(path[0] === '/' ? 1 : 0);
|
|
||||||
|
|
||||||
for (let i = 0; i < parts.length; i++) {
|
|
||||||
const seg: string = parts[i];
|
|
||||||
|
|
||||||
if (node.type === Type.File) return node;
|
|
||||||
const newNode = node.children.find((child) => child.name === seg);
|
|
||||||
console.log(newNode);
|
|
||||||
if (newNode !== undefined) node = newNode;
|
|
||||||
else return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getPathToNode(node: TreeNode): string[] {
|
private _getPathToNode(node: TreeNode): string[] {
|
||||||
const path: string[] = [];
|
const path: string[] = [];
|
||||||
let current = node;
|
let current = node;
|
||||||
path.push(node.name);
|
path.push(node.name);
|
||||||
|
|||||||
@@ -8,44 +8,52 @@ export function isInitializing(): boolean {
|
|||||||
return initializing;
|
return initializing;
|
||||||
}
|
}
|
||||||
|
|
||||||
function jsonToTreeNode(data: any, parent?: TreeNode): TreeNode {
|
function jsonToNodeTable(data: any, parent?: number): Map<number, TreeNode> {
|
||||||
const node: TreeNode = {
|
const FsTable: Map<number, TreeNode> = new Map<number, TreeNode>;
|
||||||
name: data.Name,
|
const keyList = Object.keys(data);
|
||||||
type: data.Type,
|
|
||||||
readonly: data.ReadOnly,
|
|
||||||
interactible: data.Interactible,
|
|
||||||
func: data.Func,
|
|
||||||
children: [],
|
|
||||||
content: data.Content,
|
|
||||||
link: data.Link || [],
|
|
||||||
permission: {
|
|
||||||
user: {
|
|
||||||
r: data.Permission[0]?.Read,
|
|
||||||
w: data.Permission[0]?.Write,
|
|
||||||
x: data.Permission[0]?.Exec
|
|
||||||
},
|
|
||||||
group: {
|
|
||||||
r: data.Permission[1]?.Read,
|
|
||||||
w: data.Permission[1]?.Write,
|
|
||||||
x: data.Permission[1]?.Exec
|
|
||||||
},
|
|
||||||
other: {
|
|
||||||
r: data.Permission[2]?.Read,
|
|
||||||
w: data.Permission[2]?.Write,
|
|
||||||
x: data.Permission[2]?.Exec
|
|
||||||
}
|
|
||||||
},
|
|
||||||
owner: data.Owner,
|
|
||||||
group: data.Group,
|
|
||||||
modtime: new Date(data.Mtime),
|
|
||||||
parent: parent
|
|
||||||
};
|
|
||||||
|
|
||||||
node.children = data.Children
|
for(const key in keyList) {
|
||||||
? data.Children.map((child: any) => jsonToTreeNode(child, node))
|
const object = data[key];
|
||||||
: [];
|
const node: TreeNode = {
|
||||||
|
inode: object.Inode,
|
||||||
|
name: object.Name,
|
||||||
|
type: object.Type,
|
||||||
|
interactible: object.Interactible,
|
||||||
|
func: object.Func,
|
||||||
|
children: [],
|
||||||
|
content: object.Content,
|
||||||
|
link: object.Link || [],
|
||||||
|
permission: {
|
||||||
|
user: {
|
||||||
|
r: object.Permission[0]?.Read,
|
||||||
|
w: object.Permission[0]?.Write,
|
||||||
|
x: object.Permission[0]?.Exec
|
||||||
|
},
|
||||||
|
group: {
|
||||||
|
r: object.Permission[1]?.Read,
|
||||||
|
w: object.Permission[1]?.Write,
|
||||||
|
x: object.Permission[1]?.Exec
|
||||||
|
},
|
||||||
|
other: {
|
||||||
|
r: object.Permission[2]?.Read,
|
||||||
|
w: object.Permission[2]?.Write,
|
||||||
|
x: object.Permission[2]?.Exec
|
||||||
|
}
|
||||||
|
},
|
||||||
|
owner: object.Owner,
|
||||||
|
group: object.Group,
|
||||||
|
timestamps: {
|
||||||
|
mTime: new Date(object.TimeStamps.MTime),
|
||||||
|
cTime: new Date(object.TimeStamps.CTime),
|
||||||
|
aTime: new Date(object.TimeStamps.ATime)
|
||||||
|
},
|
||||||
|
parent: object.parent
|
||||||
|
};
|
||||||
|
|
||||||
return node;
|
FsTable.set(object.Inode, node);
|
||||||
|
}
|
||||||
|
console.log(FsTable);
|
||||||
|
return FsTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchFsJson(sig: string): Promise<any> {
|
async function fetchFsJson(sig: string): Promise<any> {
|
||||||
@@ -95,7 +103,7 @@ export async function initTerminal(user: User, callbackInit: any): Promise<Termi
|
|||||||
try {
|
try {
|
||||||
const sig = await fetchFsSignature('/src/lib/assets/fs/signature');
|
const sig = await fetchFsSignature('/src/lib/assets/fs/signature');
|
||||||
const fsJson = await fetchFsJson(sig);
|
const fsJson = await fetchFsJson(sig);
|
||||||
const fs: TreeNode = jsonToTreeNode(fsJson);
|
const fs: Map<number, TreeNode> = jsonToNodeTable(fsJson);
|
||||||
|
|
||||||
const args: TermInitArgs = {
|
const args: TermInitArgs = {
|
||||||
bash: {
|
bash: {
|
||||||
|
|||||||
Reference in New Issue
Block a user