Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jor1k filesystem integration #131

Merged
merged 2 commits into from
Apr 1, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"jquery-ui": "~1.11.4",
"jquery-fullscreen": "~1.1.4",
"cjs": "*",
"ace": "~1.2.2"
"ace": "~1.2.2",
"browserfs": "0.5.8"
},
"devDependencies": {},
"resolutions": {
Expand Down
3 changes: 2 additions & 1 deletion src/app/require.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ require.config({
"bloodhound": "bower_modules/typeahead.js/dist/bloodhound.min", // exports window global "Bloodhound"
"FileSaver": "bower_modules/FileSaver/FileSaver.min", // exports window global "saveAs"
"Blob": "bower_modules/Blob/Blob", // exports window global "Blob"

"browserfs": "bower_modules/browserfs/dist/browserfs.min",

// Application-specific modules
"app/config": "app/config/config.dev" // overridden to 'config.dist' in build config
},
Expand Down
321 changes: 321 additions & 0 deletions src/app/sys-filesystem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,321 @@
/* global require, Buffer */
import BrowserFS from 'browserfs';

class SysFileSystem {

constructor() {
this.initialized = false;
return this;
}

initialize(jor1kFS)
{
this.initialized = true;
BrowserFS.install(window);
BrowserFS.initialize(new BrowserFS.FileSystem.LocalStorage());

this.localFS = require('fs');
this.jor1kFS = jor1kFS;
this.listeners = [];

this.syncVM();

this.jor1kFS.WatchDirectory('home/user', this.Jor1kNotifyCallBack.bind(this), this);

}
/*------------------------------------------------------------------------------------------------*/
/**
* API for interating with the joined file system
**/

writeFile(path, buf){
if(path.charAt(0)!='/')
path = '/' + path;

if (typeof buf == 'string') buf = new Buffer(buf);

this.jor1kFS.MergeBinaryFile('home/user'+path, new Uint8Array(buf.toArrayBuffer()));
this.localFS.writeFileSync(path, buf);
}

readFile(path, cb){
if(this.localFS.statSync(path).isDirectory())
return;

this.localFS.readFile(path, cb);
}

/*
* This is a blocking call, user readFile for asyc.
*/
readFileSync(path){
if(this.localFS.statSync(path).isDirectory())
return;

return this.localFS.readFileSync(path);
}

deleteFile(path){
if(this.localFS.statSync(path).isDirectory())
return;

if(path.charAt(0)!='/')
path = '/' + path;

if(this.localFS.statSync(path).isFile())
this.jor1kFS.DeleteNode('home/user'+path);
}

/*
* Creates a directory.
* Does not overwrite existing directories.
*/
makeDirectory(path){
if(path.charAt(0)!='/')
path = '/' + path;

this.jor1kFS.CreateDirectory('home/user'+path);
}

/*
* Recursively removes a directory.
*/
removeDirectory(path){
if(this.localFS.statSync(path).isFile())
return;

if(path.charAt(0)!='/')
path = '/' + path;

if(this.localFS.statSync(path).isDirectory())
this.jor1kFS.DeleteNode('home/user'+path);
}

/*
* Renames a file or directory from oldpath to newpath.
* Same functionality as mv.
*/
rename(oldpath, newpath){
if(oldpath==newpath)
return;

if(oldpath.charAt(0)!='/')
oldpath = '/' + oldpath;

if(newpath.charAt(0)!='/')
newpath = '/' + newpath;

if(this.localFS.existsSync(oldpath))
this.jor1kFS.Rename('home/user'+oldpath, 'home/user'+newpath);
}

/*
* Copies a single file from srcpath to dstpath.
* Copying of directories is not yet implemented.
*/
copyTo(srcpath, dstpath){
if(srcpath == dstpath)
return;

var stat = this.localFS.statSync(srcpath);
if(stat.isDirectory())
return;

this.localFS.readFile(srcpath, function(err, buf){
if(dstpath.charAt(0)!='/')
dstpath = '/' + dstpath;

this.jor1kFS.MergeBinaryFile('home/user'+dstpath, new Uint8Array(buf.toArrayBuffer()), stat.mode);

}.bind(this));
}

/*
* Returns an array of { isDirectory: boolean, name: string } objects
* of all nodes with in the directory specified in path.
*/
getDirectoryChildren(path){
if(path == '')
path = '/';

if((!this.localFS.existsSync(path)) || this.localFS.statSync(path).isFile())
return [];

var children = this.localFS.readdirSync(path);
var ret = [];

if(path.charAt(path.length-1)!='/')
path = path + '/';

for(var i=0; i<children.length; i++)
{
var child = {};
child.name = children[i];

if(this.localFS.statSync(path+children[i]).isDirectory())
child.isDirectory = true;
else
child.isDirectory = false;
if(!(child.name.indexOf('.') == 0))
ret.push(child);
}
return ret;
}

/*
* Returns an array of { isDirectory: boolean, path: string } objects
* of all nodes within /home/user. Partially ordered from root -> leafs
*/
getDirectoryTree(){
return this.getDirectoryTreeHelper('/');
}

/*
* Helper for getDirectoryTree
*/
getDirectoryTreeHelper(path){
var children = this.localFS.readdirSync(path);

if(path=='/')
path = '';

var ret = [];
var dirs = [];
for(var i=0; i<children.length; i++)
{
var childPath = path+'/'+children[i];
var child = {};
child.path = childPath;

if(this.localFS.statSync(childPath).isDirectory())
{
child.isDirectory = true;
dirs.push(childPath);
}
else
child.isDirectory = false;

ret.push(child);
}

for(var a=0; a<dirs.length; a++)
{
ret.push.apply(ret, this.getDirectoryTreeHelper(dirs[a]));
}

return ret;

}

addChangeListener(fn){
var ary = this.listeners;
if (ary) {
ary.push(fn);
} else {
this.listeners = [fn];
}
}

removeChangeListener(fn) {
var ary = this.listeners;
this.listeners = ary.filter(function (el) {
return el !== fn;
});
}

notifyChangeListeners() {
var ary = this.listeners;
if (!ary) {
return;
}
ary = ary.slice(); // Listeners may be added/removed during this event, so make a copy first
for (var i = 0; ary && i < ary.length; i++) {
ary[i]();
}
}

/*------------------------------------------------------------------------------------------------*/
/**
* Write all local files (those stored in local storage) to
* the Jor1k file system (i.e /home/user)
**/
syncVM() {

console.log('Starting Syncing VM File System');
this.jor1kFS.DeleteDirContents('home/user');
this.syncDirectory('/');
console.log('Done Syncing');
}

syncDirectory(directory) {
if(directory != '/')
this.jor1kFS.CreateDirectory('home/user'+directory);

var children = this.localFS.readdirSync(directory);
if(directory == '/')
directory = '';
for(var i = 0; i<children.length; i++){
var newpath = directory+'/'+children[i];
var stat = this.localFS.statSync(newpath);
if(stat.isDirectory())
this.syncDirectory(newpath);
else
{
var buf = this.localFS.readFileSync(newpath);
this.jor1kFS.MergeBinaryFile('home/user'+newpath, new Uint8Array(buf.toArrayBuffer()), 33261);
}
}
}
/*------------------------------------------------------------------------------------------------*/
/**
* Handeling callbacks from file operations done on
* the Jor1k VM
**/
Jor1kNotifyCallBack(info){
var path = info.path.substring('home/user'.length, info.path.length);

if(path=='') return;
console.log(info.event);
switch(info.event) {
case 'write':
this.jor1kFS.ReadFile(info.path, this.Jor1kReadCallBack.bind(this), this);
break;
case 'newdir':
this.localFS.mkdir(path, function(err){if(err)console.log(err);});
this.notifyChangeListeners();
break;
case 'newfile':
this.jor1kFS.ReadFile(info.path, this.Jor1kReadCallBack.bind(this), this);
this.notifyChangeListeners();
break;
case 'delete':
if(this.localFS.statSync(path).isDirectory())
this.localFS.rmdir(path, function(err){if(err)console.log(err);});
else
if(this.localFS.existsSync(path))
this.localFS.unlink(path, function(err){if(err)console.log(err);});
this.notifyChangeListeners();
break;
case 'rename':
if (info.info!={}){
var oldpath = info.info.oldpath.substring('home/user'.length, info.info.oldpath.length);
this.localFS.rename(oldpath, path);
this.notifyChangeListeners();
}
}
}

Jor1kReadCallBack(file){
var filename = file.name.substring('home/user'.length, file.name.length);

console.log('Writting to local: ' + filename);
var buf = new Buffer(file.data);
this.localFS.writeFileSync(filename, buf, {mode:file.mode});

}


}

// SysFileSystem is meant to be used as a singleton
export default (new SysFileSystem());
5 changes: 5 additions & 0 deletions src/app/sys-runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import GccOutputParser from 'app/gcc-output-parser';
import * as Jor1k from 'cjs!jor1k/master/master';
import LinuxTerm from 'cjs!jor1k/plugins/terminal-linux';
import { jor1kBaseFsUrl, jor1kWorkerUrl } from 'app/config';
import SysFileSystem from 'app/sys-filesystem';

// Encapsulates the virtual machine interface
class SysRuntime {
Expand Down Expand Up @@ -34,6 +35,10 @@ class SysRuntime {

var onBootFinished = () => {
if (this.tty0ready && this.tty1ready) {

//Attach persistent filesystem
SysFileSystem.initialize(this.jor1kgui.fs);

// LiveEdit uses the bootFinished value when sent the ready event,
// so bootFinished must be updated before broadcasting the event
this.bootFinished = true;
Expand Down