// ------------------------------------------------- // ----------------- FILESYSTEM--------------------- // ------------------------------------------------- // Implementation of a unix filesystem in memory. "use strict"; var S_IRWXUGO = 0x1FF; var S_IFMT = 0xF000; var S_IFSOCK = 0xC000; var S_IFLNK = 0xA000; var S_IFREG = 0x8000; var S_IFBLK = 0x6000; var S_IFDIR = 0x4000; var S_IFCHR = 0x2000; //var S_IFIFO 0010000 //var S_ISUID 0004000 //var S_ISGID 0002000 //var S_ISVTX 0001000 var O_RDONLY = 0x0000; // open for reading only var O_WRONLY = 0x0001; // open for writing only var O_RDWR = 0x0002; // open for reading and writing var O_ACCMODE = 0x0003; // mask for above modes var STATUS_INVALID = -0x1; var STATUS_OK = 0x0; var STATUS_OPEN = 0x1; var STATUS_ON_SERVER = 0x2; var STATUS_LOADING = 0x3; var STATUS_UNLINKED = 0x4; /** @const */ var JSONFS_VERSION = 2; /** @const */ var JSONFS_IDX_NAME = 0 /** @const */ var JSONFS_IDX_SIZE = 1 /** @const */ var JSONFS_IDX_MTIME = 2 /** @const */ var JSONFS_IDX_MODE = 3 /** @const */ var JSONFS_IDX_UID = 4 /** @const */ var JSONFS_IDX_GID = 5 /** @const */ var JSONFS_IDX_TARGET = 6 /** @constructor */ function FS(baseurl) { /** @type {Array.} */ this.inodes = []; this.events = []; this.baseurl = baseurl; this.qidnumber = 0x0; this.filesinloadingqueue = 0; this.OnLoaded = function() {}; //this.tar = new TAR(this); this.inodedata = {}; this.total_size = 256 * 1024 * 1024 * 1024; this.used_size = 0; //RegisterMessage("LoadFilesystem", this.LoadFilesystem.bind(this) ); //RegisterMessage("MergeFile", this.MergeFile.bind(this) ); //RegisterMessage("tar", // function(data) { // SendToMaster("tar", this.tar.Pack(data)); // }.bind(this) //); //RegisterMessage("sync", // function(data) { // SendToMaster("sync", this.tar.Pack(data)); // }.bind(this) //); // root entry this.CreateDirectory("", -1); } // ----------------------------------------------------- FS.prototype.AddEvent = function(id, OnEvent) { var inode = this.GetInode(id); if (inode.status == STATUS_OK) { OnEvent(); return; } this.events.push({id: id, OnEvent: OnEvent}); } FS.prototype.HandleEvent = function(id) { if (this.filesinloadingqueue == 0) { this.OnLoaded(); this.OnLoaded = function() {} } //message.Debug("number of events: " + this.events.length); var newevents = []; for(var i=0; i= 0) { x.uid = this.inodes[parentid].uid; x.gid = this.inodes[parentid].gid; x.mode = (this.inodes[parentid].mode & 0x1FF) | S_IFDIR; this.inodes[parentid].nlinks++; } x.qid.type = S_IFDIR >> 8; this.PushInode(x); this.NotifyListeners(this.inodes.length-1, 'newdir'); return this.inodes.length-1; } FS.prototype.CreateFile = function(filename, parentid) { var x = this.CreateInode(); x.name = filename; x.parentid = parentid; x.uid = this.inodes[parentid].uid; x.gid = this.inodes[parentid].gid; this.inodes[parentid].nlinks++; x.qid.type = S_IFREG >> 8; x.mode = (this.inodes[parentid].mode & 0x1B6) | S_IFREG; this.PushInode(x); this.NotifyListeners(this.inodes.length-1, 'newfile'); return this.inodes.length-1; } FS.prototype.CreateNode = function(filename, parentid, major, minor) { var x = this.CreateInode(); x.name = filename; x.parentid = parentid; x.major = major; x.minor = minor; x.uid = this.inodes[parentid].uid; x.gid = this.inodes[parentid].gid; this.inodes[parentid].nlinks++; x.qid.type = S_IFSOCK >> 8; x.mode = (this.inodes[parentid].mode & 0x1B6); this.PushInode(x); return this.inodes.length-1; } FS.prototype.CreateSymlink = function(filename, parentid, symlink) { var x = this.CreateInode(); x.name = filename; x.parentid = parentid; x.uid = this.inodes[parentid].uid; x.gid = this.inodes[parentid].gid; this.inodes[parentid].nlinks++; x.qid.type = S_IFLNK >> 8; x.symlink = symlink; x.mode = S_IFLNK; this.PushInode(x); return this.inodes.length-1; } FS.prototype.CreateTextFile = function(filename, parentid, str) { var id = this.CreateFile(filename, parentid); var x = this.inodes[id]; var data = this.inodedata[id] = new Uint8Array(str.length); x.size = str.length; for (var j = 0; j < str.length; j++) { data[j] = str.charCodeAt(j); } return id; } /** * @param {Uint8Array} buffer */ FS.prototype.CreateBinaryFile = function(filename, parentid, buffer) { var id = this.CreateFile(filename, parentid); var x = this.inodes[id]; var data = this.inodedata[id] = new Uint8Array(buffer.length); data.set(buffer); x.size = buffer.length; return id; } FS.prototype.OpenInode = function(id, mode) { var inode = this.GetInode(id); if ((inode.mode&S_IFMT) == S_IFDIR) { this.FillDirectory(id); } /* var type = ""; switch(inode.mode&S_IFMT) { case S_IFREG: type = "File"; break; case S_IFBLK: type = "Block Device"; break; case S_IFDIR: type = "Directory"; break; case S_IFCHR: type = "Character Device"; break; } */ //message.Debug("open:" + this.GetFullPath(id) + " type: " + inode.mode + " status:" + inode.status); if (inode.status == STATUS_ON_SERVER) { this.LoadFile(id); return false; } return true; } FS.prototype.CloseInode = function(id) { //message.Debug("close: " + this.GetFullPath(id)); var inode = this.GetInode(id); if (inode.status == STATUS_UNLINKED) { //message.Debug("Filesystem: Delete unlinked file"); inode.status = STATUS_INVALID; delete this.inodedata[id]; inode.size = 0; } } FS.prototype.Rename = function(olddirid, oldname, newdirid, newname) { // message.Debug("Rename " + oldname + " to " + newname); if ((olddirid == newdirid) && (oldname == newname)) { return true; } var oldid = this.Search(olddirid, oldname); var oldpath = this.GetFullPath(oldid); if (oldid == -1) { return false; } var newid = this.Search(newdirid, newname); if (newid != -1) { this.Unlink(newid); } var idx = oldid; // idx contains the id which we want to rename var inode = this.inodes[idx]; // remove inode ids if (this.inodes[inode.parentid].firstid == idx) { this.inodes[inode.parentid].firstid = inode.nextid; } else { var id = this.FindPreviousID(idx); if (id == -1) { message.Debug("Error in Filesystem: Cannot find previous id of inode"); message.Abort(); } this.inodes[id].nextid = inode.nextid; } inode.parentid = newdirid; inode.name = newname; inode.qid.version++; inode.nextid = this.inodes[inode.parentid].firstid; this.inodes[inode.parentid].firstid = idx; this.inodes[olddirid].updatedir = true; this.inodes[newdirid].updatedir = true; this.inodes[olddirid].nlinks--; this.inodes[newdirid].nlinks++; this.NotifyListeners(idx, "rename", {oldpath: oldpath}); return true; } FS.prototype.Write = function(id, offset, count, GetByte) { this.NotifyListeners(id, 'write'); var inode = this.inodes[id]; var data = this.inodedata[id]; if (!data || data.length < (offset+count)) { this.ChangeSize(id, Math.floor(((offset+count)*3)/2) ); inode.size = offset + count; data = this.inodedata[id]; } else if (inode.size < (offset+count)) { inode.size = offset + count; } for(var i=0; i this.inodes.length)) { message.Debug("Error in filesystem: Attempt to get inode with id " + idx); return 0; } return this.inodes[idx]; } FS.prototype.ChangeSize = function(idx, newsize) { var inode = this.GetInode(idx); var temp = this.inodedata[idx]; //message.Debug("change size to: " + newsize); if (newsize == inode.size) return; var data = this.inodedata[idx] = new Uint8Array(newsize); inode.size = newsize; if(!temp) return; var size = Math.min(temp.length, inode.size); for(var i=0; i=0; i--) this.Unlink(toDelete[i]); } FS.prototype.DeleteNode = function(path) { var ids = this.SearchPath(path); if (ids.parentid == -1 || ids.id == -1) return; if ((this.inodes[ids.id].mode&S_IFMT) == S_IFREG){ this.Unlink(ids.id); return; } if ((this.inodes[ids.id].mode&S_IFMT) == S_IFDIR){ var toDelete = [] this.GetRecursiveList(ids.id, toDelete); for(var i=toDelete.length-1; i>=0; i--) this.Unlink(toDelete[i]); this.Unlink(ids.id); return; } } /** @param {*=} info */ FS.prototype.NotifyListeners = function(id, action, info) { //if(info==undefined) // info = {}; //var path = this.GetFullPath(id); //if (this.watchFiles[path] == true && action=='write') { // message.Send("WatchFileEvent", path); //} //for (var directory in this.watchDirectories) { // if (this.watchDirectories.hasOwnProperty(directory)) { // var indexOf = path.indexOf(directory) // if(indexOf == 0 || indexOf == 1) // message.Send("WatchDirectoryEvent", {path: path, event: action, info: info}); // } //} } FS.prototype.Check = function() { for(var i=1; i> 12, "."], data, offset); offset += marshall.Marshall( ["Q", "d", "b", "s"], [this.inodes[parentid].qid, offset+13+8+1+2+2, this.inodes[parentid].mode >> 12, ".."], data, offset); id = this.inodes[dirid].firstid; while(id != -1) { offset += marshall.Marshall( ["Q", "d", "b", "s"], [ this.inodes[id].qid, offset+13+8+1+2+UTF8.UTF8Length(this.inodes[id].name), this.inodes[id].mode >> 12, this.inodes[id].name ], data, offset); id = this.inodes[id].nextid; } inode.updatedir = false; } // ----------------------------------------------------- // only support for security.capabilities // should return a "struct vfs_cap_data" defined in // linux/capability for format // check also: // sys/capability.h // http://lxr.free-electrons.com/source/security/commoncap.c#L376 // http://man7.org/linux/man-pages/man7/capabilities.7.html // http://man7.org/linux/man-pages/man8/getcap.8.html // http://man7.org/linux/man-pages/man3/libcap.3.html FS.prototype.PrepareCAPs = function(id) { var inode = this.GetInode(id); if (inode.caps) return inode.caps.length; inode.caps = new Uint8Array(12); // format is little endian // magic_etc (revision=0x01: 12 bytes) inode.caps[0] = 0x00; inode.caps[1] = 0x00; inode.caps[2] = 0x00; inode.caps[3] = 0x01; // permitted (full capabilities) inode.caps[4] = 0xFF; inode.caps[5] = 0xFF; inode.caps[6] = 0xFF; inode.caps[7] = 0xFF; // inheritable (full capabilities inode.caps[8] = 0xFF; inode.caps[9] = 0xFF; inode.caps[10] = 0xFF; inode.caps[11] = 0xFF; return inode.caps.length; }