590 lines
14 KiB
Plaintext
590 lines
14 KiB
Plaintext
var cellfs = {}
|
|
|
|
var fd = use('fd')
|
|
var miniz = use('miniz')
|
|
var qop = use('internal/qop')
|
|
var wildstar = use('internal/wildstar')
|
|
var blib = use('blob')
|
|
|
|
var mounts = []
|
|
|
|
var write_mount = null
|
|
|
|
function normalize_path(path) {
|
|
if (!path) return ""
|
|
return replace(path, /^\/+|\/+$/, "")
|
|
}
|
|
|
|
function mount_exists(mount, path) {
|
|
var result = false
|
|
var full_path = null
|
|
var st = null
|
|
var _check = null
|
|
if (mount.type == 'zip') {
|
|
_check = function() {
|
|
mount.handle.mod(path)
|
|
result = true
|
|
} disruption {}
|
|
_check()
|
|
} else if (mount.type == 'qop') {
|
|
_check = function() {
|
|
result = mount.handle.stat(path) != null
|
|
} disruption {}
|
|
_check()
|
|
} else if (mount.type == 'fs') {
|
|
full_path = fd.join_paths(mount.source, path)
|
|
_check = function() {
|
|
st = fd.stat(full_path)
|
|
result = st.isFile || st.isDirectory
|
|
} disruption {}
|
|
_check()
|
|
}
|
|
return result
|
|
}
|
|
|
|
function is_directory(path) {
|
|
var res = resolve(path)
|
|
var mount = res.mount
|
|
var result = false
|
|
var full_path = null
|
|
var st = null
|
|
var _check = null
|
|
if (mount.type == 'zip') {
|
|
_check = function() {
|
|
result = mount.handle.is_directory(path)
|
|
} disruption {}
|
|
_check()
|
|
} else if (mount.type == 'qop') {
|
|
_check = function() {
|
|
result = mount.handle.is_directory(path)
|
|
} disruption {}
|
|
_check()
|
|
} else {
|
|
full_path = fd.join_paths(mount.source, path)
|
|
_check = function() {
|
|
st = fd.stat(full_path)
|
|
result = st.isDirectory
|
|
} disruption {}
|
|
_check()
|
|
}
|
|
return result
|
|
}
|
|
|
|
function resolve(path, must_exist) {
|
|
var idx = null
|
|
var mount_name = ""
|
|
var rel_path = ""
|
|
var mount = null
|
|
var found_mount = null
|
|
var npath = normalize_path(path)
|
|
|
|
if (starts_with(npath, "@")) {
|
|
idx = search(npath, "/")
|
|
|
|
if (idx == null) {
|
|
mount_name = text(npath, 1)
|
|
rel_path = ""
|
|
} else {
|
|
mount_name = text(npath, 1, idx)
|
|
rel_path = text(npath, idx + 1)
|
|
}
|
|
|
|
arrfor(mounts, function(m) {
|
|
if (m.name == mount_name) {
|
|
mount = m
|
|
return true
|
|
}
|
|
}, false, true)
|
|
|
|
if (!mount) {
|
|
log.error("Unknown mount point: @" + mount_name); disrupt
|
|
}
|
|
|
|
return { mount: mount, path: rel_path }
|
|
}
|
|
|
|
arrfor(mounts, function(m) {
|
|
if (mount_exists(m, npath)) {
|
|
found_mount = { mount: m, path: npath }
|
|
return true
|
|
}
|
|
}, false, true)
|
|
|
|
if (found_mount) {
|
|
return found_mount
|
|
}
|
|
|
|
if (must_exist) {
|
|
log.error("File not found in any mount: " + npath); disrupt
|
|
}
|
|
}
|
|
|
|
function mount(source, name) {
|
|
var st = null
|
|
var blob = null
|
|
var qop_archive = null
|
|
var zip = null
|
|
var _try_qop = null
|
|
var http = null
|
|
|
|
var mount_info = {
|
|
source: source,
|
|
name: name || null,
|
|
type: 'fs',
|
|
handle: null,
|
|
zip_blob: null
|
|
}
|
|
|
|
if (starts_with(source, 'http://') || starts_with(source, 'https://')) {
|
|
http = use('http')
|
|
mount_info.type = 'http'
|
|
mount_info.handle = {
|
|
base_url: source,
|
|
get: function(path, callback) {
|
|
var url = source + '/' + path
|
|
$clock(function() {
|
|
var resp = http.request('GET', url, null, null)
|
|
if (resp && resp.status == 200) {
|
|
callback(resp.body)
|
|
} else {
|
|
callback(null, "HTTP " + text(resp ? resp.status : 0) + ": " + url)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
push(mounts, mount_info)
|
|
return
|
|
}
|
|
|
|
st = fd.stat(source)
|
|
|
|
if (st.isDirectory) {
|
|
mount_info.type = 'fs'
|
|
} else if (st.isFile) {
|
|
blob = fd.slurp(source)
|
|
|
|
qop_archive = null
|
|
_try_qop = function() {
|
|
qop_archive = qop.open(blob)
|
|
} disruption {}
|
|
_try_qop()
|
|
|
|
if (qop_archive) {
|
|
mount_info.type = 'qop'
|
|
mount_info.handle = qop_archive
|
|
mount_info.zip_blob = blob
|
|
} else {
|
|
zip = miniz.read(blob)
|
|
if (!is_object(zip) || !is_function(zip.count)) {
|
|
log.error("Invalid archive file (not zip or qop): " + source); disrupt
|
|
}
|
|
|
|
mount_info.type = 'zip'
|
|
mount_info.handle = zip
|
|
mount_info.zip_blob = blob
|
|
}
|
|
} else {
|
|
log.error("Unsupported mount source type: " + source); disrupt
|
|
}
|
|
|
|
push(mounts, mount_info)
|
|
}
|
|
|
|
function unmount(name_or_source) {
|
|
mounts = filter(mounts, function(m) {
|
|
return m.name != name_or_source && m.source != name_or_source
|
|
})
|
|
}
|
|
|
|
function slurp(path) {
|
|
var res = resolve(path, true)
|
|
var data = null
|
|
var full_path = null
|
|
if (!res) { log.error("File not found: " + path); disrupt }
|
|
|
|
if (res.mount.type == 'zip') {
|
|
return res.mount.handle.slurp(res.path)
|
|
} else if (res.mount.type == 'qop') {
|
|
data = res.mount.handle.read(res.path)
|
|
if (!data) { log.error("File not found in qop: " + path); disrupt }
|
|
return data
|
|
} else {
|
|
full_path = fd.join_paths(res.mount.source, res.path)
|
|
return fd.slurp(full_path)
|
|
}
|
|
}
|
|
|
|
function slurpwrite(path, data) {
|
|
var full_path = null
|
|
if (write_mount) {
|
|
full_path = fd.join_paths(write_mount.source, path)
|
|
} else {
|
|
full_path = fd.join_paths(".", path)
|
|
}
|
|
fd.slurpwrite(full_path, data)
|
|
}
|
|
|
|
function exists(path) {
|
|
var res = resolve(path, false)
|
|
if (starts_with(path, "@")) {
|
|
return mount_exists(res.mount, res.path)
|
|
}
|
|
return res != null
|
|
}
|
|
|
|
function stat(path) {
|
|
var res = resolve(path, true)
|
|
var mod = null
|
|
var s = null
|
|
var full_path = null
|
|
if (!res) { log.error("File not found: " + path); disrupt }
|
|
|
|
if (res.mount.type == 'zip') {
|
|
mod = res.mount.handle.mod(res.path)
|
|
return {
|
|
filesize: 0,
|
|
modtime: mod * 1000,
|
|
isDirectory: false
|
|
}
|
|
} else if (res.mount.type == 'qop') {
|
|
s = res.mount.handle.stat(res.path)
|
|
if (!s) { log.error("File not found in qop: " + path); disrupt }
|
|
return {
|
|
filesize: s.size,
|
|
modtime: s.modtime,
|
|
isDirectory: s.isDirectory
|
|
}
|
|
} else {
|
|
full_path = fd.join_paths(res.mount.source, res.path)
|
|
s = fd.stat(full_path)
|
|
return {
|
|
filesize: s.size,
|
|
modtime: s.mtime,
|
|
isDirectory: s.isDirectory
|
|
}
|
|
}
|
|
}
|
|
|
|
function searchpath() {
|
|
return array(mounts)
|
|
}
|
|
|
|
function mount_package(name) {
|
|
if (name == null) {
|
|
mount('.', null)
|
|
return
|
|
}
|
|
|
|
var shop = use('internal/shop')
|
|
var dir = shop.get_package_dir(name)
|
|
|
|
if (!dir) {
|
|
log.error("Package not found: " + name); disrupt
|
|
}
|
|
|
|
mount(dir, name)
|
|
}
|
|
|
|
function match(str, pattern) {
|
|
return wildstar.match(pattern, str, wildstar.WM_PATHNAME | wildstar.WM_PERIOD | wildstar.WM_WILDSTAR)
|
|
}
|
|
|
|
function rm(path) {
|
|
var res = resolve(path, true)
|
|
var full_path = null
|
|
var st = null
|
|
if (res.mount.type != 'fs') { log.error("Cannot delete from non-fs mount"); disrupt }
|
|
|
|
full_path = fd.join_paths(res.mount.source, res.path)
|
|
st = fd.stat(full_path)
|
|
if (st.isDirectory) fd.rmdir(full_path)
|
|
else fd.unlink(full_path)
|
|
}
|
|
|
|
function mkdir(path) {
|
|
var full = null
|
|
if (write_mount) {
|
|
full = fd.join_paths(write_mount.source, path)
|
|
} else {
|
|
full = fd.join_paths(".", path)
|
|
}
|
|
fd.mkdir(full)
|
|
}
|
|
|
|
function set_writepath(mount_name) {
|
|
var found = null
|
|
if (mount_name == null) { write_mount = null; return }
|
|
arrfor(mounts, function(m) {
|
|
if (m.name == mount_name) { found = m; return true }
|
|
}, false, true)
|
|
if (!found || found.type != 'fs') {
|
|
log.error("writepath: must be an fs mount"); disrupt
|
|
}
|
|
write_mount = found
|
|
}
|
|
|
|
function basedir() {
|
|
return fd.getcwd()
|
|
}
|
|
|
|
function prefdir(org, app) {
|
|
return "./"
|
|
}
|
|
|
|
function realdir(path) {
|
|
var res = resolve(path, false)
|
|
if (!res) return null
|
|
return fd.join_paths(res.mount.source, res.path)
|
|
}
|
|
|
|
function enumerate(_path, recurse) {
|
|
var path = _path == null ? "" : _path
|
|
|
|
var res = resolve(path, true)
|
|
var results = []
|
|
var full = null
|
|
var st = null
|
|
var all = null
|
|
var prefix = null
|
|
var prefix_len = null
|
|
var seen = null
|
|
|
|
function visit(curr_full, rel_prefix) {
|
|
var list = fd.readdir(curr_full)
|
|
if (!list) return
|
|
|
|
arrfor(list, function(item) {
|
|
var item_rel = rel_prefix ? rel_prefix + "/" + item : item
|
|
var child_st = null
|
|
push(results, item_rel)
|
|
|
|
if (recurse) {
|
|
child_st = fd.stat(fd.join_paths(curr_full, item))
|
|
if (child_st.isDirectory) {
|
|
visit(fd.join_paths(curr_full, item), item_rel)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
if (res.mount.type == 'fs') {
|
|
full = fd.join_paths(res.mount.source, res.path)
|
|
st = fd.stat(full)
|
|
if (st && st.isDirectory) {
|
|
visit(full, "")
|
|
}
|
|
} else if (res.mount.type == 'qop') {
|
|
all = res.mount.handle.list()
|
|
prefix = res.path ? res.path + "/" : ""
|
|
prefix_len = length(prefix)
|
|
|
|
seen = {}
|
|
|
|
arrfor(all, function(p) {
|
|
var rel = null
|
|
var slash = null
|
|
if (starts_with(p, prefix)) {
|
|
rel = text(p, prefix_len)
|
|
if (length(rel) == 0) return
|
|
|
|
if (!recurse) {
|
|
slash = search(rel, '/')
|
|
if (slash != null) {
|
|
rel = text(rel, 0, slash)
|
|
}
|
|
}
|
|
|
|
if (!seen[rel]) {
|
|
seen[rel] = true
|
|
push(results, rel)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
return results
|
|
}
|
|
|
|
function globfs(globs, _dir) {
|
|
var dir = _dir == null ? "" : _dir
|
|
var res = resolve(dir, true)
|
|
var results = []
|
|
var full = null
|
|
var st = null
|
|
var all = null
|
|
var prefix = null
|
|
var prefix_len = null
|
|
|
|
function check_neg(path) {
|
|
var result = false
|
|
arrfor(globs, function(g) {
|
|
if (starts_with(g, "!") && wildstar.match(text(g, 1), path, wildstar.WM_WILDSTAR)) {
|
|
result = true
|
|
return true
|
|
}
|
|
}, false, true)
|
|
return result
|
|
}
|
|
|
|
function check_pos(path) {
|
|
var result = false
|
|
arrfor(globs, function(g) {
|
|
if (!starts_with(g, "!") && wildstar.match(g, path, wildstar.WM_WILDSTAR)) {
|
|
result = true
|
|
return true
|
|
}
|
|
}, false, true)
|
|
return result
|
|
}
|
|
|
|
function visit(curr_full, rel_prefix) {
|
|
if (rel_prefix && check_neg(rel_prefix)) return
|
|
|
|
var list = fd.readdir(curr_full)
|
|
if (!list) return
|
|
|
|
arrfor(list, function(item) {
|
|
var item_rel = rel_prefix ? rel_prefix + "/" + item : item
|
|
|
|
var child_full = fd.join_paths(curr_full, item)
|
|
var child_st = fd.stat(child_full)
|
|
|
|
if (child_st.isDirectory) {
|
|
if (!check_neg(item_rel)) {
|
|
visit(child_full, item_rel)
|
|
}
|
|
} else {
|
|
if (!check_neg(item_rel) && check_pos(item_rel)) {
|
|
push(results, item_rel)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
if (res.mount.type == 'fs') {
|
|
full = fd.join_paths(res.mount.source, res.path)
|
|
st = fd.stat(full)
|
|
if (st && st.isDirectory) {
|
|
visit(full, "")
|
|
}
|
|
} else if (res.mount.type == 'qop') {
|
|
all = res.mount.handle.list()
|
|
prefix = res.path ? res.path + "/" : ""
|
|
prefix_len = length(prefix)
|
|
|
|
arrfor(all, function(p) {
|
|
var rel = null
|
|
if (starts_with(p, prefix)) {
|
|
rel = text(p, prefix_len)
|
|
if (length(rel) == 0) return
|
|
|
|
if (!check_neg(rel) && check_pos(rel)) {
|
|
push(results, rel)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
return results
|
|
}
|
|
|
|
// Requestor factory: returns a requestor for reading a file at path
|
|
function get(path) {
|
|
return function get_requestor(callback, value) {
|
|
var res = resolve(path, false)
|
|
var full = null
|
|
var f = null
|
|
var acc = null
|
|
var cancelled = false
|
|
var data = null
|
|
var _close = null
|
|
|
|
if (!res) { callback(null, "not found: " + path); return }
|
|
|
|
if (res.mount.type == 'zip') {
|
|
callback(res.mount.handle.slurp(res.path))
|
|
return
|
|
}
|
|
if (res.mount.type == 'qop') {
|
|
data = res.mount.handle.read(res.path)
|
|
if (data) {
|
|
callback(data)
|
|
} else {
|
|
callback(null, "not found in qop: " + path)
|
|
}
|
|
return
|
|
}
|
|
if (res.mount.type == 'http') {
|
|
res.mount.handle.get(res.path, callback)
|
|
return
|
|
}
|
|
|
|
full = fd.join_paths(res.mount.source, res.path)
|
|
f = fd.open(full, 'r')
|
|
acc = blob()
|
|
|
|
function next() {
|
|
var chunk = null
|
|
if (cancelled) return
|
|
chunk = fd.read(f, 65536)
|
|
if (length(chunk) == 0) {
|
|
fd.close(f)
|
|
stone(acc)
|
|
callback(acc)
|
|
return
|
|
}
|
|
acc.write_blob(chunk)
|
|
$clock(next)
|
|
}
|
|
|
|
next()
|
|
return function cancel() {
|
|
cancelled = true
|
|
_close = function() { fd.close(f) } disruption {}
|
|
_close()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Requestor factory: returns a requestor for writing data to path
|
|
function put(path, data) {
|
|
return function put_requestor(callback, value) {
|
|
var _data = data != null ? data : value
|
|
var full = null
|
|
var _do = null
|
|
if (!write_mount) { callback(null, "no write mount set"); return }
|
|
full = fd.join_paths(write_mount.source, path)
|
|
_do = function() {
|
|
fd.slurpwrite(full, _data)
|
|
callback(true)
|
|
} disruption {
|
|
callback(null, "write failed: " + path)
|
|
}
|
|
_do()
|
|
}
|
|
}
|
|
|
|
cellfs.mount = mount
|
|
cellfs.mount_package = mount_package
|
|
cellfs.unmount = unmount
|
|
cellfs.slurp = slurp
|
|
cellfs.slurpwrite = slurpwrite
|
|
cellfs.exists = exists
|
|
cellfs.is_directory = is_directory
|
|
cellfs.stat = stat
|
|
cellfs.searchpath = searchpath
|
|
cellfs.match = match
|
|
cellfs.enumerate = enumerate
|
|
cellfs.globfs = globfs
|
|
cellfs.rm = rm
|
|
cellfs.mkdir = mkdir
|
|
cellfs.writepath = set_writepath
|
|
cellfs.basedir = basedir
|
|
cellfs.prefdir = prefdir
|
|
cellfs.realdir = realdir
|
|
cellfs.get = get
|
|
cellfs.put = put
|
|
cellfs.resolve = resolve
|
|
|
|
return cellfs
|