Refactor to support open() on directories so that we can list files.

This commit is contained in:
Alan Kligman 2012-11-29 19:46:35 -05:00
parent 9859e7988a
commit 9053d67146
3 changed files with 275 additions and 916 deletions

View File

@ -21,7 +21,7 @@ require(["src/fs"], function(IDBFS) {
if(error) { if(error) {
return console.error(error); return console.error(error);
} }
fs.mkdir("/tmp", function() { fs.mkdir("/tmp", function(error) {
if(error) { if(error) {
return console.error(error); return console.error(error);
} }

View File

@ -1,632 +0,0 @@
/*
Copyright (c) 2012, Alan Kligman
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of the Mozilla Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
define(function(require) {
// 'use strict';
var debug = require("debug");
var _ = require("lodash");
var path = require("src/path");
var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
function guid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
}).toUpperCase();
}
function makeDirectoryEntry(name, modtime) {
var parent = path.dirname(name);
return {
"parent": parent,
"name": name,
"contenttype": "application/directory",
"lastmodified": modtime || Date.now()
}
}
function makeFileEntry(name, oid, size, modtime) {
var parent = path.dirname(name);
return {
"parent": parent,
"name": name,
"contenttype": "application/file",
"lastmodified": modtime || Date.now(),
"size": size || 0,
"oid": oid || guid()
}
}
function Data(handle, bytes) {
return {
handle: handle || guid(),
bytes: bytes || undefined
}
}
function File(handle, size, atime, ctime, mtime, nlinks, type, flags, data, xattrs) {
var now = Date.now();
return {
handle: handle || guid(),
size: size || 0,
atime: atime || now,
ctime: ctime || now,
mtime: mtime || now,
type: type || "application/octet-stream",
flags: flags || "",
xattrs: xattrs || {},
data: data || guid()
}
}
function FileEntry(fullpath, file) {
return {
name: fullpath,
parent: path.dirname(fullpath),
file: file,
type: "application/file"
}
}
function DirectoryEntry(fullpath, atime, ctime, mtime, xattrs) {
var now = Date.now();
return {
name: fullpath,
parent: path.dirname(fullpath),
atime: atime || now,
ctime: ctime || now,
mtime: mtime || now,
xattrs: xattrs || {},
type: "application/directory"
}
}
var METADATA_STORE_NAME = "metadata";
var FILE_STORE_NAME = "files";
var NAME_INDEX = "name";
var NAME_INDEX_KEY_PATH = "name";
var PARENT_INDEX = "parent";
var PARENT_INDEX_KEY_PATH = "parent";
var OBJECT_ID_INDEX = "oid";
var OBJECT_ID_INDEX_KEY_PATH = "oid";
var MIME_DIRECTORY = "application/directory";
var MIME_FILE = "application/file";
var IDB_RO = "readonly";
var IDB_RW = "readwrite";
function genericErrorHandler(callback) {
return function(transaction, error) {
debug.error("error: ", error);
if(transaction && !transaction.error) {
try {
transaction.abort();
} catch(e) {
// Transaction has is already completed or aborted, this is probably a programming error
debug.warn("attempt to abort and already completed or aborted transaction");
}
}
if(callback && "function" === typeof callback) {
callback.call(undefined, error);
}
}
}
function FileSystem(db) {
var fs = this;
var fds = {}; // Open file descriptors
// Internal prototypes
function OpenFileDescription(name, entry, flags, mode) {
this.name = name;
this.entry = entry;
this.flags = flags;
this.mode = mode;
this.pointer = 0;
this.pending = 0;
this.valid = true;
this.oncomplete = undefined;
}
function FileDescriptor(ofd) {
var descriptor = this.descriptor = guid();
function start() {
if(!ofd.valid) {
return false;
}
++ ofd.pending;
return true;
}
function end() {
-- ofd.pending;
if(!ofd.valid && !ofd.pending) {
if(ofd.oncomplete && "function" === typeof ofd.oncomplete) {
debug.info("close <--");
delete fds[descriptor];
ofd.oncomplete.call();
}
}
}
function read(buffer, callback) {
debug.info("read -->");
var onerror = genericErrorHandler(callback);
if(!start()) {
onerror(null, new BadFileDescriptorError());
return;
}
transaction = db.transaction([FILE_STORE_NAME], IDB_RO);
transaction.oncomplete = function(e)
end();
}
var store = transaction.objectStore(FILE_STORE_NAME);
if(MIME_FILE === ofd.entry["contenttype"]) {
var oid = ofd.entry["oid"];
var getRequest = store.get(oid);
getRequest.onsuccess = function(e) {
var storedBuffer = e.target.result;
if(!storedBuffer) {
// There's no file data, so return zero bytes read
end();
debug.info("read <--");
if(callback && "function" === typeof callback) {
callback.call(undefined, undefined, 0, buffer);
}
} else {
// Make sure we're not going to read past the end of the file
var bytes = (ofd.pointer + buffer.length > storedBuffer.length) ? (storedBuffer.length - ofd.pointer) : buffer.length;
// Copy the desired region from the file into the buffer supplied
var storedBufferView = storedBuffer.subarray(ofd.pointer, ofd.pointer + bytes);
buffer.set(storedBufferView);
ofd.pointer += bytes;
debug.info("read <--");
if(callback && "function" === typeof callback) {
callback.call(undefined, undefined, bytes, buffer);
}
}
};
getRequest.onerror = onerror.bind(null, transaction);
} else if(MIME_DIRECTORY === ofd.entry["contenttype"]) {
// NOT IMPLEMENTED
onerror(transaction, new NotImplementedError());
}
}
function write(buffer, callback) {
debug.info("write -->");
var onerror = genericErrorHandler(callback);
if(OM_RO === ofd.mode) {
onerror(null, new BadFileDescriptorError());
return;
}
if(!start()) {
onerror(null, new BadFileDescriptorError());
return;
}
transaction = db.transaction([METADATA_STORE_NAME, FILE_STORE_NAME], IDB_RW);
transaction.oncomplete = function(e) {
end();
}
var metaStore = transaction.objectStore(METADATA_STORE_NAME);
var fileStore = transaction.objectStore(FILE_STORE_NAME);
var oid = ofd.entry["oid"];
var getRequest = fileStore.get(oid);
getRequest.onsuccess = function(e) {
var storedBuffer = e.target.result;
if(!storedBuffer) {
storedBuffer = new Uint8Array();
}
var bytes = buffer.length;
var size = (storedBuffer.length > ofd.pointer + bytes) ? storedBuffer.length : ofd.pointer + bytes;
var writeBuffer = new Uint8Array(size);
writeBuffer.set(storedBuffer);
writeBuffer.set(buffer);
ofd.pointer += bytes;
var putRequest = fileStore.put(writeBuffer, oid);
putRequest.onsuccess = function(e) {
var readMetadataRequest = metaStore.get(ofd.entry["name"]);
readMetadataRequest.onsuccess = function(e) {
var entry = e.target.result;
entry = makeFileEntry(entry["name"], entry["oid"], size);
ofd.entry = entry;
var writeMetadataRequest = metaStore.put(entry, entry["name"]);
writeMetadataRequest.onsuccess = function(e) {
debug.info("write <--");
if(callback && "function" === typeof callback) {
callback.call(undefined, undefined, size, buffer);
}
};
writeMetadataRequest.onerror = onerror.bind(null, transaction);
}
readMetadataRequest.onerror = onerror.bind(null, transaction);
};
putRequest.onerror = onerror.bind(null, transaction);
};
getRequest.onerror = onerror.bind(null, transaction);
}
var SW_SET = "SET";
var SW_CURRENT = "CURRENT";
var SW_END = "END";
function seek(offset, whence) {
whence = whence || SW_CURRENT;
if(SW_SET === whence) {
ofd.pointer = offset;
} else if(SW_CURRENT === whence) {
ofd.pointer += offset;
} else if(SW_END === whence) {
ofd.pointer = ofd.entry["size"] + offset;
}
}
this.read = read;
this.seek = seek;
this.write = write;
Object.defineProperty(this, "valid", {
get: function() {
return ofd.valid;
}
});
fds[descriptor] = ofd;
}
// API
// Flags
var OF_CREATE = "CREATE";
var OF_APPEND = "APPEND";
var OF_TRUNCATE = "TRUNCATE";
var OF_DIRECTORY = "DIRECTORY";
// Modes
var OM_RO = "RO";
var OM_RW = "RW";
function open(fullpath, flags, mode, callback) {
debug.info("open -->");
fullpath = path.normalize(fullpath);
transaction = db.transaction([METADATA_STORE_NAME], IDB_RW);
var metaStore = transaction.objectStore(METADATA_STORE_NAME);
var fileStore = transaction.objectStore(FILE_STORE_NAME);
var nameIndex = metaStore.index(NAME_INDEX);
var onerror = genericErrorHandler(callback);
if(undefined === flags) {
flags = [];
} else if("string" === typeof flags) {
flags = [flags];
}
var getRequest = nameIndex.get(fullpath);
getRequest.onsuccess = function(e) {
var entry = e.target.result;
var file;
if(!entry) {
if(!_(flags).contains(OF_CREATE)) {
onerror(transaction, new NoEntryError());
return;
} else {
file = new File();
entry = new FileEntry(fullpath, file.handle);
var createFileRequest = fileStore.put(file, file.handle);
createFileRequest.onsuccess = function(e) {
var createEntryRequest = fileStore.put(entry, fullpath);
createEntryRequest.onsuccess = complete;
createEntryRequest.onerror = onerror.bind(null, transaction);
};
createFileRequest.onerror = onerror.bind(null, transaction);
}
} else {
if(entry["type"] === MIME_DIRECTORY && mode === OM_RW) {
onerror(transaction, new IsDirectoryError());
return;
} else {
var getFileRequest = fileStore.get(entry.file);
getFileRequest.onsuccess = function(e) {
file = e.target.result;
complete();
};
getFileRequest.onerror = onerror.bind(null, transaction);
}
}
function complete() {
var ofd;
ofd = new OpenFileDescription(fullpath, file, flags, mode);
var fd = new FileDescriptor(ofd);
debug.info("open <--");
if(callback && "function" === typeof callback) {
callback.call(undefined, undefined, fd);
}
}
};
getRequest.onerror = onerror.bind(null, transaction);
}
function close(fd, callback) {
debug.info("close -->");
var onerror = genericErrorHandler(callback);
if(!fds.hasOwnProperty(fd.descriptor)) {
onerror(null, new BadFileDescriptorError());
return;
}
var ofd = fds[fd.descriptor];
ofd.valid = false;
if(!ofd.valid && !ofd.pending) {
debug.info("close <--");
callback.call()
} else {
ofd.oncomplete = callback;
}
}
function mkdir(transaction, fullpath, callback) {
debug.info("mkdir -->");
fullpath = path.normalize(fullpath);
transaction = transaction || db.transaction([METADATA_STORE_NAME], IDB_RW);
var store = transaction.objectStore(METADATA_STORE_NAME);
var nameIndex = store.index(NAME_INDEX);
var onerror = genericErrorHandler(callback);
var getRequest = nameIndex.get(fullpath);
getRequest.onsuccess = function(e) {
var result = e.target.result;
if(result) {
onerror(null, new PathExistsError());
} else {
var entry = new DirectoryEntry(fullpath);
var directoryRequest = store.put(entry, fullpath);
directoryRequest.onsuccess = function(e) {
debug.info("mkdir <--");
if(callback && "function" === typeof callback) {
callback.call(undefined, undefined);
}
};
directoryRequest.onerror = onerror.bind(null, transaction);
}
};
getRequest.onerror = onerror.bind(null, transaction);
}
function rmdir(fullpath, callback) {
debug.info("rmdir -->");
fullpath = path.normalize(fullpath);
transaction = db.transaction([METADATA_STORE_NAME, FILE_STORE_NAME], IDB_RW);
var metaStore = transaction.objectStore(METADATA_STORE_NAME);
var nameIndex = metaStore.index(NAME_INDEX);
var parentIndex = metaStore.index(PARENT_INDEX);
var onerror = genericErrorHandler(callback);
var getRequest = nameIndex.get(fullpath);
getRequest.onsuccess = function(e) {
var result = e.target.result;
if(!result) {
onerror(transaction, new NoEntryError());
return;
} else {
var contentRequest = parentIndex.get(fullpath);
contentRequest.onsuccess = function(e) {
var result = e.target.result;
if(result) {
onerror(transaction, new NotEmptyError());
return;
} else {
var removeRequest = metaStore.delete(fullpath);
removeRequest.onsuccess = function(e) {
debug.info("rmdir <--");
if(callback && "function" === typeof callback) {
callback.call(undefined, undefined);
}
};
removeRequest.onerror = onerror.bind(null, transaction);
}
};
contentRequest.onerror = onerror.bind(null, transaction);
}
};
getRequest.onerror = onerror.bind(null, transaction);
}
function stat(transaction, fullpath, callback) {
debug.info("stat -->");
fullpath = path.normalize(fullpath);
transaction = transaction || db.transaction([METADATA_STORE_NAME], IDB_RO);
var store = transaction.objectStore(METADATA_STORE_NAME);
var nameIndex = store.index(NAME_INDEX);
var onerror = genericErrorHandler(callback);
var getRequest = nameIndex.get(fullpath);
getRequest.onsuccess = function(e) {
var result = e.target.result;
if(!result) {
onerror(transaction, new NoEntryError());
return;
} else {
debug.info("stat <--");
if(callback && "function" === typeof callback) {
callback.call(undefined, undefined, result);
}
}
};
getRequest.onerror = onerror.bind(null, transaction);
}
function link(oldpath, newpath, callback) {
}
function unlink(pathname, callback) {
debug.info("unlink -->");
pathname = path.normalize(pathname);
var transaction = db.transaction([METADATA_STORE_NAME], IDB_RW);
var metaStore = transaction.objectStore(METADATA_STORE_NAME);
var nameIndex = metaStore.index(NAME_INDEX);
var onerror = genericErrorHandler(callback);
stat(transaction, pathname, function(error, entry) {
if(error) {
onerror(transaction, error);
return;
}
if(MIME_DIRECTORY === entry["contenttype"]) {
onerror(transaction, new IsDirectoryError());
return;
}
var unlinkRequest = metaStore.delete(entry["name"]);
unlinkRequest.onsuccess = function(e) {
// We don't support links, so this entry is the only entry for the file data
var transaction = db.transaction([FILE_STORE_NAME], IDB_RW);
var fileStore = transaction.objectStore(FILE_STORE_NAME);
var deleteRequest = fileStore.delete(entry["oid"]);
deleteRequest.onsuccess = function(e) {
debug.info("unlink <--");
if(callback && "function" === typeof callback) {
callback.call(undefined, undefined);
}
};
deleteRequest.onerror = onerror.bind(null, transaction);
};
unlinkRequest.onerror = onerror.bind(null, transaction);
});
}
function api() {
return {
open: open,
close: close,
mkdir: mkdir.bind(null, null),
rmdir: rmdir,
stat: stat.bind(null, null),
link: link,
unlink: unlink,
dump: dump
}
}
this.open = open;
this.close = close;
this.mkdir = mkdir;
this.rmdir = rmdir;
this.stat = stat;
// this.link = link;
this.unlink = unlink;
this.api = api;
// DEBUG
function dump(element, callback, clear) {
if(clear) {
element.innerHTML = "";
}
element.innerHTML += "Metadata://<br>";
var transaction = db.transaction([METADATA_STORE_NAME], IDB_RO);
var metaStore = transaction.objectStore(METADATA_STORE_NAME);
var metaRequest = metaStore.openCursor();
metaRequest.onsuccess = function(e) {
var metaCursor = e.target.result;
if(metaCursor) {
element.innerHTML += JSON.stringify(metaCursor.value) + "<br>";
metaCursor.continue();
} else {
element.innerHTML += "Files://<br>"
transaction = db.transaction([FILE_STORE_NAME], IDB_RO);
var fileStore = transaction.objectStore(FILE_STORE_NAME);
var fileRequest = fileStore.openCursor();
fileRequest.onsuccess = function(e) {
var fileCursor = e.target.result;
if(fileCursor) {
element.innerHTML += JSON.stringify(fileCursor.key) + "<br>";
fileCursor.continue();
} else {
element.innerHTML += "-----------------<br>";
if(callback && "function" === typeof callback) {
callback.call();
}
}
}
}
};
}
this.dump = dump;
}
function mount(name, flags, callback) {
debug.info("mount -->");
indexedDB.deleteDatabase(name);
var format = _(flags).contains("FORMAT");
var onerror = genericErrorHandler(callback);
var openRequest = indexedDB.open(name);
openRequest.onupgradeneeded = function(e) {
var db = e.target.result;
if(db.objectStoreNames.contains(FILE_STORE_NAME)) {
db.deleteObjectStore(FILE_STORE_NAME);
}
var metadata = db.createObjectStore(METADATA_STORE_NAME);
metadata.createIndex(PARENT_INDEX, PARENT_INDEX_KEY_PATH, {unique: false});
metadata.createIndex(NAME_INDEX, NAME_INDEX_KEY_PATH, {unique: true});
metadata.createIndex(OBJECT_ID_INDEX, OBJECT_ID_INDEX_KEY_PATH, {unique: false});
var files = db.createObjectStore(FILE_STORE_NAME);
format = true;
};
openRequest.onsuccess = function(e) {
var db = e.target.result;
var fs = new FileSystem(db);
if(format) {
var transaction = db.transaction([METADATA_STORE_NAME], IDB_RW);
var store = transaction.objectStore(METADATA_STORE_NAME);
debug.info("format -->");
var clearRequest = store.clear();
clearRequest.onsuccess = function() {
fs.mkdir(transaction, "/", function(error) {
if(error) {
onerror(error);
} else {
debug.info("format <--");
debug.info("mount <--");
if(callback && "function" === typeof callback) {
callback.call(undefined, undefined, fs.api());
}
}
});
};
clearRequest.onerror = onerror.bind(null, transaction);
} else {
debug.info("mount <--");
if(callback && "function" === typeof callback) {
callback.call(undefined, undefined, fs.api());
}
}
};
openRequest.onerror = onerror.bind(null, null);
}
function umount(fs, callback) {
}
var IDBFS = {
mount: mount,
umount: undefined,
path: path
};
return IDBFS;
});

521
src/fs.js
View File

@ -18,6 +18,7 @@ define(function(require) {
var _ = require("lodash"); var _ = require("lodash");
var Path = require("src/path"); var Path = require("src/path");
var guid = require("src/guid"); var guid = require("src/guid");
var error = require("src/error");
require("crypto-js/rollups/sha256"); var Crypto = CryptoJS; require("crypto-js/rollups/sha256"); var Crypto = CryptoJS;
var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB; var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
@ -43,63 +44,55 @@ define(function(require) {
} }
} }
function runCallback(callback) { function runcallback(callback) {
if("function" === typeof callback) { if("function" === typeof callback) {
callback.apply(undefined, Array.prototype.slice.call(arguments, 1)); callback.apply(undefined, Array.prototype.slice.call(arguments, 1));
} }
} }
function hash(string) {
return Crypto.SHA256(string).toString(Crypto.enc.hex);
}
function Data(bytes) { function Data(bytes) {
return { return {
bytes: bytes || undefined bytes: bytes || undefined
} }
} }
function File(size, atime, ctime, mtime, nlinks, type, flags, data, xattrs, links) { var FILE_MIME_TYPE = "application/file";
var DIRECTORY_MIME_TYPE = "application/directory";
var BINARY_MIME_TYPE = "application/octet-stream";
var JSON_MIME_TYPE = "application/json";
var DEFAULT_DATA_TYPE = BINARY_MIME_TYPE;
function File(mode, data, type, size, version, atime, ctime, mtime, flags, xattrs, links) {
var now = Date.now(); var now = Date.now();
return { return {
size: size || 0, size: size || 0,
atime: atime || now, atime: atime || now,
ctime: ctime || now, ctime: ctime || now,
mtime: mtime || now, mtime: mtime || now,
type: type || "application/octet-stream", mode: mode || FILE_MIME_TYPE,
flags: flags || "", flags: flags || "",
xattrs: xattrs || {}, xattrs: xattrs || {},
data: data || Crypto.SHA256(guid()).toString(Crypto.enc.hex), data: data || Crypto.SHA256(guid()).toString(Crypto.enc.hex),
links: links || 0 type: type || DEFAULT_DATA_TYPE,
links: links || 0,
version: version || 0,
id: Crypto.SHA256(guid()).toString(Crypto.enc.hex)
} }
} }
var FILE_ENTRY_MIME_TYPE = "application/file-entry"; function signature(file) {
function FileEntry(fullpath, file, version) { // Compute file signature based on file id and version
return {
name: fullpath,
parent: Path.dirname(fullpath),
file: file || Crypto.SHA256(guid()).toString(Crypto.enc.hex),
type: FILE_ENTRY_MIME_TYPE,
version: version || 0
}
} }
var DIRECTORY_ENTRY_MIME_TYPE = "application/directory-entry"; function Stats(size, data, atime, ctime, mtime, links) {
function DirectoryEntry(fullpath, atime, ctime, mtime, xattrs, version) {
var now = Date.now();
return {
name: fullpath,
parent: Path.dirname(fullpath),
atime: atime || now,
ctime: ctime || now,
mtime: mtime || now,
xattrs: xattrs || {},
type: DIRECTORY_ENTRY_MIME_TYPE,
version: version || 0
}
}
function Stats(size, handle, atime, ctime, mtime, links) {
return { return {
size: size, size: size,
handle: handle, data: data,
atime: atime, atime: atime,
ctime: ctime, ctime: ctime,
mtime: mtime, mtime: mtime,
@ -150,52 +143,64 @@ define(function(require) {
var fs = this; var fs = this;
fullpath = Path.normalize(fullpath); fullpath = Path.normalize(fullpath);
var transaction = optTransaction || new fs.Transaction([METADATA_STORE_NAME, FILE_STORE_NAME], IDB_RW); var transaction = optTransaction || new fs.Transaction([FILE_STORE_NAME], IDB_RW);
var metadata = transaction.objectStore(METADATA_STORE_NAME);
var files = transaction.objectStore(FILE_STORE_NAME); var files = transaction.objectStore(FILE_STORE_NAME);
flags = parseFlags(flags); flags = parseFlags(flags);
mode = mode.toUpperCase(); mode = mode.toUpperCase();
var getEntryRequest = metadata.get(fullpath); var name = Path.basename(fullpath);
getEntryRequest.onsuccess = function(e) { var parentpath = Path.dirname(fullpath);
var entry = e.target.result; var parenthandle = hash(parentpath);
var file; var getParentRequest = files.get(parenthandle);
if(!entry) { getParentRequest.onsuccess = function(e) {
var parent = e.target.result;
var data = parent.data;
var file, filehandle;
if(!_(data).has(name)) {
if(_(flags).contains(OF_CREATE)) { if(_(flags).contains(OF_CREATE)) {
entry = new FileEntry(fullpath); filehandle = data[name] = hash(guid());
file = new File(); file = new File();
++ file.links; ++ file.links;
var createFileRequest = files.put(file, entry.file); var createFileRequest = files.put(file, data[name]);
createFileRequest.onsuccess = function(e) { createFileRequest.onsuccess = function(e) {
var createEntryRequest = metadata.put(entry, entry.name); var updateParentRequest = files.put(parent, parenthandle);
createEntryRequest.onsuccess = function(e) { updateParentRequest.onsuccess = function(e) {
_createFileDescriptor(entry, flags, mode); _createFileDescriptor(filehandle, file, flags, mode);
}; };
createEntryRequest.onerror = function(e) { updateParentRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}; };
}; };
createFileRequest.onerror = function(e) { createFileRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}; };
} }
} else { } else {
if(OM_RW === mode && DIRECTORY_ENTRY_MIME_TYPE === entry.type) { filehandle = data[name];
runCallback(callback, new error.EIsDirectory()); var getFileRequest = files.get(filehandle);
getFileRequest.onsuccess = function(e) {
file = e.target.result;
if(OM_RW === mode && DIRECTORY_MIME_TYPE === file.mode) {
runcallback(callback, new error.EIsDirectory());
} }
_createFileDescriptor(entry, flags, mode); _createFileDescriptor(filehandle, file, flags, mode);
};
getFileRequest.onerror = function(e) {
runcallback(callback, e);
};
} }
}; };
getEntryRequest.onerror = function(e) { getParentRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}; };
function _createFileDescriptor(entry, flags, mode) {
var openfile = new OpenFile(fs, entry, flags, mode); function _createFileDescriptor(handle, file, flags, mode) {
var openfile = new OpenFile(fs, handle, file, flags, mode);
var descriptor = new FileDescriptor(openfile); var descriptor = new FileDescriptor(openfile);
fs._descriptors[descriptor] = openfile; fs._descriptors[descriptor] = openfile;
runCallback(callback, null, descriptor); runcallback(callback, null, descriptor);
} }
}; };
FileSystem.prototype.close = function close(descriptor, callback) { FileSystem.prototype.close = function close(descriptor, callback) {
@ -208,277 +213,268 @@ define(function(require) {
var fs = this; var fs = this;
fullpath = Path.normalize(fullpath); fullpath = Path.normalize(fullpath);
var transaction = optTransaction || new fs.Transaction([METADATA_STORE_NAME], IDB_RW); var transaction = optTransaction || new fs.Transaction([FILE_STORE_NAME], IDB_RW);
var metadata = transaction.objectStore(METADATA_STORE_NAME); var files = transaction.objectStore(FILE_STORE_NAME);
var getEntryRequest = metadata.get(fullpath); var directoryhandle = hash(fullpath);
getEntryRequest.onsuccess = function(e) { var parentpath = Path.dirname(fullpath);
var getResult = e.target.result; var parenthandle = hash(parentpath);
if(getResult) { var getDirectoryRequest = files.get(directoryhandle);
handleError(transaction, new error.EPathExists()); getDirectoryRequest.onsuccess = function(e) {
var directory = e.target.result;
if(directory) {
runcallback(callback, new error.EPathExists());
} else { } else {
var entry = new DirectoryEntry(fullpath); directory = new File(DIRECTORY_MIME_TYPE, {
var putRequest = metadata.put(entry, fullpath); ".": directoryhandle,
putRequest.onsuccess = function(e) { "..": parenthandle,
runCallback(callback); }, JSON_MIME_TYPE, 2);
++ directory.links;
var createDirectoryRequest = files.put(directory, directoryhandle);
createDirectoryRequest.onsuccess = function(e) {
var getParentRequest = files.get(parenthandle);
getParentRequest.onsuccess = function(e) {
var parent = e.target.result;
parent.data[Path.basename(fullpath)] = directoryhandle;
++ parent.version;
var updateParentRequest = files.put(parent, parenthandle);
updateParentRequest.onsuccess = function(e) {
runcallback(callback);
}; };
putRequest.onerror = function(e) { updateParentRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
};
};
getParentRequest.onerror = function(e) {
runcallback(callback, e);
};
};
createDirectoryRequest.onerror = function(e) {
runcallback(callback, e);
}; };
} }
}; };
getEntryRequest.onerror = function(e) { getDirectoryRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
} };
}; };
FileSystem.prototype.rmdir = function rmdir(fullpath, callback, optTransaction) { FileSystem.prototype.rmdir = function rmdir(fullpath, callback, optTransaction) {
var fs = this; var fs = this;
fullpath = Path.normalize(fullpath); fullpath = Path.normalize(fullpath);
var transaction = optTransaction || new fs.Transaction([METADATA_STORE_NAME], IDB_RW); var transaction = optTransaction || new fs.Transaction([FILE_STORE_NAME], IDB_RW);
var metadata = transaction.objectStore(METADATA_STORE_NAME); var files = transaction.objectStore(FILE_STORE_NAME);
var parentIndex = metadata.index(PARENT_INDEX);
var getEntryRequest = metadata.get(fullpath); var directoryhandle = hash(fullpath);
getEntryRequest.onsuccess = function(e) { var getDirectoryRequest = files.get(directoryhandle);
var entry = e.target.result; getDirectoryRequest.onsuccess = function(e) {
if(!entry) { var directory = e.target.result;
runCallback(callback, new error.ENoEntry()); if(!directory) {
runcallback(callback, new error.ENoEntry());
} else { } else {
var contentRequest = parentIndex.get(fullpath); var data = directory.data;
contentRequest.onsuccess = function(e) { if(Object.keys(data).length > 2) {
var contentResult = e.target.result; runcallback(callback, new error.ENotEmpty());
if(contentResult) {
runCallback(callback, new error.ENotEmpty());
} else { } else {
var removeRequest = metadata.delete(fullpath); var removeDirectoryRequest = files.delete(directoryhandle);
removeRequest.onsuccess = function(e) { removeDirectoryRequest.onsuccess = function(e) {
runCallback(callback); var parentpath = Path.dirname(fullpath);
var parenthandle = hash(parentpath);
var getParentRequest = files.get(parenthandle);
getParentRequest.onsuccess = function(e) {
var parent = e.target.result;
delete parent.data[directoryhandle];
++ parent.version;
var updateParentRequest = files.put(parent, parenthandle);
updateParentRequest.onsuccess = function(e) {
runcallback(callback);
};
updateParentRequest.onerror = function(e) {
runcallback(callback, e);
};
};
getParentRequest.onerror = function(e) {
runcallback(callback, e);
};
};
removeDirectoryRequest.onerror = function(e) {
runcallback(callback, e);
}; };
removeRequest.onerror = function(e) {
runCallback(callback, e);
} }
} }
}; };
contentRequest.onerror = function(e) { getDirectoryRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}
}
}; };
getEntryRequest.onerror = function(e) {
runCallback(callback, e);
}
}; };
FileSystem.prototype.stat = function stat(fullpath, callback, optTransaction) { FileSystem.prototype.stat = function stat(fullpath, callback, optTransaction) {
var fs = this; var fs = this;
fullpath = Path.normalize(fullpath); fullpath = Path.normalize(fullpath);
var transaction = optTransaction || new fs.Transaction([METADATA_STORE_NAME, FILE_STORE_NAME], IDB_RO); var transaction = optTransaction || new fs.Transaction([FILE_STORE_NAME], IDB_RO);
var metadata = transaction.objectStore(METADATA_STORE_NAME);
var files = transaction.objectStore(FILE_STORE_NAME); var files = transaction.objectStore(FILE_STORE_NAME);
var getEntryRequest = metadata.get(fullpath); var parentpath = Path.dirname(fullpath);
getEntryRequest.onsuccess = function(e) { var parenthandle = hash(parentpath);
var entry = e.target.result; var getParentRequest = files.get(parenthandle);
var stats; getParentRequest.onsuccess = function(e) {
if(!entry) { var parent = e.target.result;
runCallback(callback, new error.ENoEntry()); var data = parent.data;
var name = Path.basename(fullpath);
if(!_(data).has(name)) {
runcallback(callback, new error.ENoEntry());
} else { } else {
if(DIRECTORY_ENTRY_MIME_TYPE === entry.type) { var filehandle = data[name];
stats = new Stats(undefined, undefined, entry.atime, entry.ctime, entry.mtime, undefined); var getFileRequest = files.get(filehandle);
runCallback(callback, null, stats);
} else if(FILE_ENTRY_MIME_TYPE === entry.type) {
var getFileRequest = files.get(entry.file);
getFileRequest.onsuccess = function(e) { getFileRequest.onsuccess = function(e) {
var file = e.target.result; var file = e.target.result;
stats = new Stats(file.size, entry.file, file.atime, file.ctime, file.mtime, file.links); var stats = new Stats(file.size, file.data, file.atime, file.ctime, file.mtime, file.links);
runCallback(callback, null, stats); runcallback(callback, null, stats);
}; };
getFileRequest.onerror = function(e) { getFileRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}; };
} }
}
}; };
getEntryRequest.onerror = function(e) { getParentRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}; };
}; };
FileSystem.prototype.link = function link(oldpath, newpath, callback, optTransaction) { FileSystem.prototype.link = function link(oldpath, newpath, callback, optTransaction) {
var fs = this; var fs = this;
oldpath = Path.normalize(oldpath); oldpath = Path.normalize(oldpath);
newpath = Path.normalize(newpath); newpath = Path.normalize(newpath);
var oldparentpath = Path.dirname(oldpath);
var newparentpath = Path.dirname(newpath);
var transaction = optTransaction || new fs.Transaction([METADATA_STORE_NAME, FILE_STORE_NAME], IDB_RW); var transaction = optTransaction || new fs.Transaction([FILE_STORE_NAME], IDB_RW);
var metadata = transaction.objectStore(METADATA_STORE_NAME);
var files = transaction.objectStore(FILE_STORE_NAME); var files = transaction.objectStore(FILE_STORE_NAME);
var getOldEntryRequest = metadata.get(oldpath); var oldparenthandle = hash(oldparentpath);
getOldEntryRequest.onsuccess = function(e) { var newparenthandle = hash(newparentpath);
var oldentry = e.target.result; var getOldParentRequest = files.get(oldparenthandle);
if(!oldentry) { getOldParentRequest.onsuccess = function(e) {
runCallback(callback, new error.ENoEntry()); var oldparent = e.target.result;
if(!oldparent) {
runcallback(callback, new error.ENoEntry());
} else { } else {
var getNewEntryRequest = metadata.get(newpath); var olddata = oldparent.data;
getNewEntryRequest.onsuccess = function(e) { var filehandle = olddata[Path.basename(oldpath)];
var newentry = e.target.result; if(!filehandle) {
if(newentry) { runcallback(callback, new error.ENoEntry());
runCallback(callback, new error.EPathExists());
} else { } else {
newentry = new FileEntry(newpath, oldentry.file); var getNewParentRequest = files.get(newparenthandle);
var putNewEntryRequest = metadata.put(newentry, newentry.name); getNewParentRequest.onsuccess = function(e) {
putNewEntryRequest.onsuccess = function(e) { var newparent = e.target.result;
var getFileRequest = files.get(newentry.file); if(!newparent) {
runcallback(callback, new error.ENoEntry());
} else {
var getFileRequest = files.get(filehandle);
getFileRequest.onsuccess = function(e) { getFileRequest.onsuccess = function(e) {
var file = e.target.result; var file = e.target.result;
++ file.links; ++ file.links;
var putFileRequest = files.put(file, newentry.file); ++ file.version;
putFileRequest.onsuccess = function(e) { var updateFileRequest = files.put(file, filehandle);
runCallback(callback); updateFileRequest.onsuccess = function(e) {
var newdata = newparent.data;
var newname = Path.basename(newpath);
if(_(newdata).has(newname)) {
runcallback(callback, new error.EPathExists());
} else {
newdata[newname] = filehandle;
++ parent.version;
var updateNewParentRequest = files.put(newparent, newparenthandle);
updateNewParentRequest.onsuccess = function(e) {
runcallback(callback);
}; };
putFileRequest.onerror = function(e) { updateNewParentRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
};
}
};
updateFileRequest.onerror = function(e) {
runcallback(callback, e);
}; };
}; };
getFileRequest.onerror = function(e) { getFileRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
};
};
putNewEntryRequest.onerror = function(e) {
runCallback(callback, e);
}; };
} }
}; };
getNewEntryRequest.onerror = function(e) { getNewParentRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}; };
} }
};
getOldEntryRequest.onerror = function(e) {
runCallback(callback, e);
} }
}; };
getOldParentRequest.onerror = function(e) {
runcallback(callback, e);
};
};
FileSystem.prototype.unlink = function unlink(fullpath, callback, optTransaction) { FileSystem.prototype.unlink = function unlink(fullpath, callback, optTransaction) {
var fs = this; var fs = this;
fullpath = Path.normalize(fullpath); fullpath = Path.normalize(fullpath);
var transaction = optTransaction || new fs.Transaction([METADATA_STORE_NAME, FILE_STORE_NAME], IDB_RW); var transaction = optTransaction || new fs.Transaction([FILE_STORE_NAME], IDB_RW);
var metadata = transaction.objectStore(METADATA_STORE_NAME);
var files = transaction.objectStore(FILE_STORE_NAME); var files = transaction.objectStore(FILE_STORE_NAME);
var getEntryRequest = metadata.get(fullpath); var parentpath = Path.dirname(fullpath);
getEntryRequest.onsuccess = function(e) { var parenthandle = hash(parentpath);
var entry = e.target.result; var getParentRequest = files.get(parenthandle);
if(!entry) { getParentRequest.onsuccess = function(e) {
runCallback(callback, new error.ENoEntry()); var parent = e.target.result;
} else if(DIRECTORY_ENTRY_MIME_TYPE === entry.type) { var data = parent.data;
runCallback(callback, new error.EIsDirectory()); var name = Path.basename(fullpath);
if(!_(data).has(name)) {
runcallback(callback, new error.ENoEntry());
} else { } else {
var deleteEntryRequest = metadata.delete(entry.name); var filehandle = data[name];
deleteEntryRequest.onsuccess = function(e) { delete data[name];
var getFileRequest = files.get(entry.file); var updateParentRequest = files.put(parent, parenthandle);
updateParentRequest.onsuccess = function(e) {
var getFileRequest = files.get(filehandle);
getFileRequest.onsuccess = function(e) { getFileRequest.onsuccess = function(e) {
var file = e.target.result; var file = e.target.result;
-- file.links; -- file.links;
if(0 === files.links) { if(0 === file.links) {
var deleteFileRequest = files.delete(entry.file); var deleteFileRequest = files.delete(filehandle);
deleteFileRequest.onsuccess = complete; deleteFileRequest.onsuccess = complete;
deleteFileRequest.onerror = function(e) { deleteFileRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}; };
} else { } else {
var putFileRequest = files.put(file, entry.file); ++ file.version;
var putFileRequest = files.put(file, filehandle);
putFileRequest.onsuccess = complete; putFileRequest.onsuccess = complete;
putFileRequest.onerror = function(e) { putFileRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}; };
} }
function complete() { function complete() {
runCallback(callback); runcallback(callback);
} }
}; };
getFileRequest.onerror = function(e) { getFileRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}; };
}; };
deleteEntryRequest.onerror = function(e) { updateParentRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}; };
} }
}; };
getEntryRequest.onerror = function(e) { getParentRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}; };
}; };
FileSystem.prototype.setxattr = function setxattr(fullpath, name, value, callback, optTransaction) { FileSystem.prototype.setxattr = function setxattr(fullpath, name, value, callback, optTransaction) {
var fs = this;
fullpath = Path.normalize(fullpath);
var transaction = optTransaction || new fs.Transaction([METADATA_STORE_NAME, FILE_STORE_NAME], IDB_RW);
var metadata = transaction.objectStore(METADATA_STORE_NAME);
var files = transaction.objectStore(FILE_STORE_NAME);
var getEntryRequest = metadata.get(fullpath);
getEntryRequest.onsuccess = function(e) {
var entry = e.target.result;
if(!entry) {
runCallback(callback, new error.ENoEntry());
} else {
var getFileRequest = files.get(entry.file);
getFileRequest.onsuccess = function(e) {
var file = e.target.result;
file.xattrs[name] = value;
var putFileRequest = files.put(file, entry.file);
putFileRequest.onsuccess = function(e) {
runCallback(callback);
};
putFileRequest.onerror = function(e) {
runCallback(callback, e);
};
};
getFileRequest.onerror = function(e) {
runCallback(callback, e);
};
}
};
getEntryRequest.onerror = function(e) {
runCallback(callback, e);
};
}; };
FileSystem.prototype.getxattr = function getxattr(fullpath, name, callback, optTransaction) { FileSystem.prototype.getxattr = function getxattr(fullpath, name, callback, optTransaction) {
var fs = this;
fullpath = Path.normalize(fullpath);
var transaction = optTransaction || new fs.Transaction([METADATA_STORE_NAME, FILE_STORE_NAME], IDB_RO);
var metadata = transaction.objectStore(METADATA_STORE_NAME);
var files = transaction.objectStore(FILE_STORE_NAME);
var getEntryRequest = metadata.get(fullpath);
getEntryRequest.onsuccess = function(e) {
var entry = e.target.result;
if(!entry) {
runCallback(callback, new error.ENoEntry());
} else {
var getFileRequest = files.get(entry.file);
getFileRequest.onsuccess = function(e) {
var file = e.target.result;
runCallback(callback, null, file.xattrs[name]);
};
getFileRequest.onerror = function(e) {
runCallback(callback, e);
};
}
};
getEntryRequest.onerror = function(e) {
runCallback(callback, e);
};
}; };
function FileSystemContext(fs, optCwd) { function FileSystemContext(fs, optCwd) {
@ -523,7 +519,7 @@ define(function(require) {
this._fs.getxattr(Path.normalize(this._cwd + "/" + path), name, callback); this._fs.getxattr(Path.normalize(this._cwd + "/" + path), name, callback);
}; };
function OpenFile(fs, entry, flags, mode, size) { function OpenFile(fs, handle, file, flags, mode, size) {
this._fs = fs; this._fs = fs;
this._pending = 0; this._pending = 0;
this._valid = true; this._valid = true;
@ -532,7 +528,8 @@ define(function(require) {
this._mode = mode; this._mode = mode;
this._size = size; this._size = size;
this._entry = entry; // Cached entry, might require an update this._handle = handle;
this._file = file; // Cached file node, might require an update
this._deferred = when.defer(); this._deferred = when.defer();
this._deferred.resolve(); this._deferred.resolve();
@ -572,17 +569,17 @@ define(function(require) {
var size = file.size; var size = file.size;
offset += size; offset += size;
openfile._position = offset; openfile._position = offset;
runCallback(callback, null, offset); runcallback(callback, null, offset);
}; };
getFileRequest.onerror = function(e) { getFileRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}; };
} else if(SK_CURRENT === origin) { } else if(SK_CURRENT === origin) {
openfile._position += offset; openfile._position += offset;
runCallback(callback, null, openfile._position); runcallback(callback, null, openfile._position);
} else if(SK_SET === origin) { } else if(SK_SET === origin) {
openfile._position = offset; openfile._position = offset;
runCallback(callback, null, offset); runcallback(callback, null, offset);
} }
}; };
OpenFile.prototype.read = function read(buffer, callback, optTransaction) { OpenFile.prototype.read = function read(buffer, callback, optTransaction) {
@ -592,13 +589,13 @@ define(function(require) {
var files = transaction.objectStore(FILE_STORE_NAME); var files = transaction.objectStore(FILE_STORE_NAME);
if(FILE_ENTRY_MIME_TYPE === openfile._entry.type) { if(FILE_MIME_TYPE === openfile._file.mode) {
var getDataRequest = files.get(Crypto.SHA256(openfile._handle).toString(Crypto.enc.hex)); var getDataRequest = files.get(openfile._file.data);
getDataRequest.onsuccess = function(e) { getDataRequest.onsuccess = function(e) {
var data = e.target.result; var data = e.target.result;
if(!data) { if(!data) {
// There's not file data, so return zero bytes read // There's not file data, so return zero bytes read
runCallback(callback, null, 0, buffer); runcallback(callback, null, 0, buffer);
} else { } else {
// Make sure we won't read past the end of the file // Make sure we won't read past the end of the file
var bytes = (openfile._position + buffer.length > data.length) ? data.length - openfile._position : buffer.length; var bytes = (openfile._position + buffer.length > data.length) ? data.length - openfile._position : buffer.length;
@ -606,14 +603,14 @@ define(function(require) {
var dataView = data.subarray(openfile._position, openfile._position + bytes); var dataView = data.subarray(openfile._position, openfile._position + bytes);
buffer.set(dataView); buffer.set(dataView);
openfile._position += bytes; openfile._position += bytes;
runCallback(callback, null, bytes, buffer); runcallback(callback, null, bytes, buffer);
} }
}; };
getDataRequest.onerror = function(e) { getDataRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
} }
} else if(DIRECTORY_ENTRY_MIME_TYPE === openfile._type) { } else if(DIRECTORY_MIME_TYPE === openfile._file.mode) {
runCallback(callback, new error.ENotImplemented()); runcallback(callback, new error.ENotImplemented());
} }
}; };
OpenFile.prototype.write = function write(buffer, callback, optTransaction) { OpenFile.prototype.write = function write(buffer, callback, optTransaction) {
@ -621,17 +618,15 @@ define(function(require) {
var fs = openfile._fs; var fs = openfile._fs;
if(OM_RO === openfile._mode) { if(OM_RO === openfile._mode) {
runCallback(callback, new error.EBadFileDescriptor()); runcallback(callback, new error.EBadFileDescriptor());
return; return;
} }
var transaction = optTransaction || openfile.Transaction([METADATA_STORE_NAME, FILE_STORE_NAME], IDB_RW); var transaction = optTransaction || openfile.Transaction([FILE_STORE_NAME], IDB_RW);
var metadata = transaction.objectStore(METADATA_STORE_NAME);
var files = transaction.objectStore(FILE_STORE_NAME); var files = transaction.objectStore(FILE_STORE_NAME);
var handle = Crypto.SHA256(openfile._handle).toString(Crypto.enc.hex); var getDataRequest = files.get(openfile._file.data);
var getDataRequest = files.get(handle);
getDataRequest.onsuccess = function(e) { getDataRequest.onsuccess = function(e) {
var data = e.target.result; var data = e.target.result;
var bytes = buffer.length; var bytes = buffer.length;
@ -643,31 +638,31 @@ define(function(require) {
} }
newData.set(buffer, openfile._position); newData.set(buffer, openfile._position);
openfile._position += bytes; openfile._position += bytes;
var putDataRequest = files.put(newData, handle); var putDataRequest = files.put(newData, openfile._file.data);
putDataRequest.onsuccess = function(e) { putDataRequest.onsuccess = function(e) {
var getFileRequest = files.get(openfile._entry.file); var getFileRequest = files.get(openfile._handle);
getFileRequest.onsuccess = function(e) { getFileRequest.onsuccess = function(e) {
var file = e.target.result; var file = e.target.result;
file.size = size; file.size = size;
file.mtime = Date.now(); file.mtime = Date.now();
var putFileRequest = files.put(file, openfile._entry.file); var putFileRequest = files.put(file, openfile._handle);
putFileRequest.onsuccess = function(e) { putFileRequest.onsuccess = function(e) {
runCallback(callback, null, size); runcallback(callback, null, size);
}; };
putFileRequest.onerror = function(e) { putFileRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}; };
}; };
getFileRequest.onerror = function(e) { getFileRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}; };
}; };
putDataRequest.onerror = function(e) { putDataRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}; };
}; };
getDataRequest.onerror = function(e) { getDataRequest.onerror = function(e) {
runCallback(callback, e); runcallback(callback, e);
}; };
}; };
@ -697,15 +692,10 @@ define(function(require) {
openRequest.onupgradeneeded = function(e) { openRequest.onupgradeneeded = function(e) {
var db = e.target.result; var db = e.target.result;
if(db.objectStoreNames.contains(METADATA_STORE_NAME)) {
db.deleteObjectStore(METADATA_STORE_NAME);
}
if(db.objectStoreNames.contains(FILE_STORE_NAME)) { if(db.objectStoreNames.contains(FILE_STORE_NAME)) {
db.deleteObjectStore(FILE_STORE_NAME); db.deleteObjectStore(FILE_STORE_NAME);
} }
var metadata = db.createObjectStore(METADATA_STORE_NAME);
var files = db.createObjectStore(FILE_STORE_NAME); var files = db.createObjectStore(FILE_STORE_NAME);
metadata.createIndex(PARENT_INDEX, PARENT_INDEX_KEY_PATH, {unique: false});
format = true; format = true;
}; };
@ -715,16 +705,17 @@ define(function(require) {
var context = new fs.Context(); var context = new fs.Context();
if(format) { if(format) {
var transaction = db.transaction([METADATA_STORE_NAME], IDB_RW); var transaction = db.transaction([FILE_STORE_NAME], IDB_RW);
var metadata = transaction.objectStore(METADATA_STORE_NAME);
var clearRequest = metadata.clear(); var files = transaction.objectStore(FILE_STORE_NAME);
var clearRequest = files.clear();
clearRequest.onsuccess = function() { clearRequest.onsuccess = function() {
fs.mkdir("/", function(error) { fs.mkdir("/", function(error) {
if(error) { if(error) {
runCallback(callback, error); runcallback(callback, error);
} else { } else {
runCallback(callback, null, context); runcallback(callback, null, context);
} }
}, transaction); }, transaction);
}; };
@ -732,7 +723,7 @@ define(function(require) {
console.log(e); console.log(e);
}; };
} else { } else {
runCallback(callback, null, context) runcallback(callback, null, context)
} }
}; };
openRequest.onerror = function(e) { openRequest.onerror = function(e) {