Merge pull request #373 from humphd/9p-filer

Update Filer to support Plan 9 virtio filesystem API in v86
This commit is contained in:
Alan K 2018-07-14 10:21:35 -04:00 committed by GitHub
commit 2e90fdae0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 16113 additions and 7540 deletions

121
README.md
View File

@ -294,6 +294,10 @@ var fs = new Filer.FileSystem();
* [fs.close(fd, callback)](#close)
* [fs.open(path, flags, [mode], callback)](#open)
* [fs.utimes(path, atime, mtime, callback)](#utimes)
* [fs.chown(path, uid, gid, callback)](#chown)
* [fs.fchown(fd, uid, gid, callback)](#fchown)
* [fs.chmod(path, mode, callback)](#chmod)
* [fs.fchmod(fd, mode, callback)](#fchmod)
* [fs.futimes(fd, atime, mtime, callback)](#fsutimes)
* [fs.fsync(fd, callback)](#fsync)
* [fs.write(fd, buffer, offset, length, position, callback)](#write)
@ -397,14 +401,19 @@ Callback gets `(error, stats)`, where `stats` is an object with the following pr
```
{
node: <string> // internal node id (unique)
dev: <string> // file system name
size: <number> // file size in bytes
nlinks: <number> // number of links
atime: <number> // last access time
mtime: <number> // last modified time
ctime: <number> // creation time
type: <string> // file type (FILE, DIRECTORY, SYMLINK)
node: <string> // internal node id (unique)
dev: <string> // file system name
name: <string> // the entry's name (basename)
size: <number> // file size in bytes
nlinks: <number> // number of links
atime: <number> // last access time
mtime: <number> // last modified time
ctime: <number> // creation time
type: <string> // file type (FILE, DIRECTORY, SYMLINK),
gid: <number> // group name
uid: <number> // owner name
mode: <number> // permissions
version: <number> // version of the node
}
```
@ -749,6 +758,89 @@ fs.open('/myfile.txt', function(err, fd) {
});
```
#### fs.chown(path, uid, gid, callback)<a name="chown"></a>
Changes the owner and group of a file. Asynchronous [chown(2)](http://pubs.opengroup.org/onlinepubs/009695399/functions/chown.html). Callback gets no additional arguments. Both `uid` (user id) and `gid` (group id) arguments should be a JavaScript Number. By default, `0x0` is used (i.e., `root:root` ownership).
Example:
```javascript
fs.chown('/myfile.txt', 500, 500, function(err) {
if(err) throw err;
// /myfile.txt is now owned by user with id 500, group 500
});
```
#### fs.fchown(fd, uid, gid, callback)<a name="fchown"></a>
Changes the owner and group of a file. Asynchronous [chown(2)](http://pubs.opengroup.org/onlinepubs/009695399/functions/chown.html). Callback gets no additional arguments. Both `uid` (user id) and `gid` (group id) arguments should be a JavaScript Number. By default, `0x0` is used (i.e., `root:root` ownership).
Example:
```javascript
fs.open('/myfile.txt', function(err, fd) {
if(err) throw err;
fs.fchown(fd, 500, 500, function(err) {
if(err) throw err;
// /myfile.txt is now owned by user with id 500, group 500
fs.close(fd);
});
});
```
#### fs.chmod(path, mode, callback)<a name="chmod"></a>
Changes the mode of a file. Asynchronous [chmod(2)](http://pubs.opengroup.org/onlinepubs/009695399/functions/chmod.html). Callback gets no additional arguments. The `mode` argument should be a JavaScript Number, which combines file type and permission information. Here are a list of common values useful for setting the `mode`:
* File type `S_IFREG=0x8000`
* Dir type `S_IFDIR=0x4000`
* Link type `S_IFLNK=0xA000`
* Permissions `755=0x1ED`
* Permissions `644=0x1A4`
* Permissions `777=0x1FF`
* Permissions `666=0x1B6`
By default, directories use `(0x4000 | 0x1ED)` and files use `(0x8000 | 0x1A4)`.
Example:
```javascript
// S_IFREG | 0o777
var mode = 0x8000 | 0x1FF
fs.chmod('/myfile.txt', mode, function(err) {
if(err) throw err;
// /myfile.txt is a regular file with permissions 777
});
```
#### fs.fchmod(fd, mode, callback)<a name="fchmod"></a>
Changes the mode of a file. Asynchronous [chmod(2)](http://pubs.opengroup.org/onlinepubs/009695399/functions/chmod.html). Callback gets no additional arguments. The `mode` argument should be a JavaScript Number, which combines file type and permission information. By default, `755` (dir) and `644` (file) are used.
Example:
```javascript
fs.open('/myfile.txt', function(err, fd) {
if(err) throw err;
// S_IFREG | 0o777
var mode = 0x8000 | 0x1FF
fs.fchmod(fd, mode, function(err) {
if(err) throw err;
// /myfile.txt is a regular file with permissions 777
fs.close(fd);
});
});
```
#### fs.fsync(fd, callback)<a name="fsync"></a>
NOTE: Not yet implemented, see https://github.com/filerjs/filer/issues/87
@ -1263,17 +1355,8 @@ sh.find('/app/user', {
#### sh.ls(dir, [options], callback)<a name="ls"></a>
Get the listing of a directory, returning an array of directory entries
in the following form:
```
{
path: <String> the basename of the directory entry
links: <Number> the number of links to the entry
size: <Number> the size in bytes of the entry
modified: <Number> the last modified date/time
type: <String> the type of the entry
contents: <Array> an optional array of child entries, if this entry is itself a directory
}
```
in the same form as [fs.stat()](#stat), with the exception that a new Array named
`contents` is added for directory entries, containing child entries.
By default `sh.ls()` gives a shallow listing. If you want to follow
directories as they are encountered, use the `recursive=true` option. NOTE:

648
dist/buffer.js vendored
View File

@ -1,21 +1,150 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.FilerBuffer = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
;(function (exports) {
'use strict';
var Arr = (typeof Uint8Array !== 'undefined')
? Uint8Array
: Array
var PLUS = '+'.charCodeAt(0)
var SLASH = '/'.charCodeAt(0)
var NUMBER = '0'.charCodeAt(0)
var LOWER = 'a'.charCodeAt(0)
var UPPER = 'A'.charCodeAt(0)
var PLUS_URL_SAFE = '-'.charCodeAt(0)
var SLASH_URL_SAFE = '_'.charCodeAt(0)
function decode (elt) {
var code = elt.charCodeAt(0)
if (code === PLUS ||
code === PLUS_URL_SAFE)
return 62 // '+'
if (code === SLASH ||
code === SLASH_URL_SAFE)
return 63 // '/'
if (code < NUMBER)
return -1 //no match
if (code < NUMBER + 10)
return code - NUMBER + 26 + 26
if (code < UPPER + 26)
return code - UPPER
if (code < LOWER + 26)
return code - LOWER + 26
}
function b64ToByteArray (b64) {
var i, j, l, tmp, placeHolders, arr
if (b64.length % 4 > 0) {
throw new Error('Invalid string. Length must be a multiple of 4')
}
// the number of equal signs (place holders)
// if there are two placeholders, than the two characters before it
// represent one byte
// if there is only one, then the three characters before it represent 2 bytes
// this is just a cheap hack to not do indexOf twice
var len = b64.length
placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0
// base64 is 4/3 + up to two characters of the original data
arr = new Arr(b64.length * 3 / 4 - placeHolders)
// if there are placeholders, only get up to the last complete 4 chars
l = placeHolders > 0 ? b64.length - 4 : b64.length
var L = 0
function push (v) {
arr[L++] = v
}
for (i = 0, j = 0; i < l; i += 4, j += 3) {
tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3))
push((tmp & 0xFF0000) >> 16)
push((tmp & 0xFF00) >> 8)
push(tmp & 0xFF)
}
if (placeHolders === 2) {
tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4)
push(tmp & 0xFF)
} else if (placeHolders === 1) {
tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2)
push((tmp >> 8) & 0xFF)
push(tmp & 0xFF)
}
return arr
}
function uint8ToBase64 (uint8) {
var i,
extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes
output = "",
temp, length
function encode (num) {
return lookup.charAt(num)
}
function tripletToBase64 (num) {
return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F)
}
// go through the array every three bytes, we'll deal with trailing stuff later
for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
output += tripletToBase64(temp)
}
// pad the end with zeros, but make sure to not forget the extra bytes
switch (extraBytes) {
case 1:
temp = uint8[uint8.length - 1]
output += encode(temp >> 2)
output += encode((temp << 4) & 0x3F)
output += '=='
break
case 2:
temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
output += encode(temp >> 10)
output += encode((temp >> 4) & 0x3F)
output += encode((temp << 2) & 0x3F)
output += '='
break
}
return output
}
exports.toByteArray = b64ToByteArray
exports.fromByteArray = uint8ToBase64
}(typeof exports === 'undefined' ? (this.base64js = {}) : exports))
},{}],2:[function(require,module,exports){
(function (global){
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
* @license MIT
*/
/* eslint-disable no-proto */
'use strict'
var base64 = require('base64-js')
var ieee754 = require('ieee754')
var isArray = require('is-array')
var isArray = require('isarray')
exports.Buffer = Buffer
exports.SlowBuffer = SlowBuffer
exports.INSPECT_MAX_BYTES = 50
Buffer.poolSize = 8192 // not used by this implementation
var kMaxLength = 0x3fffffff
var rootParent = {}
/**
@ -26,32 +155,49 @@ var rootParent = {}
* Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
* Opera 11.6+, iOS 4.2+.
*
* Due to various browser bugs, sometimes the Object implementation will be used even
* when the browser supports typed arrays.
*
* Note:
*
* - Implementation must support adding new properties to `Uint8Array` instances.
* Firefox 4-29 lacked support, fixed in Firefox 30+.
* See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
* - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,
* See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
*
* - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
* - Safari 5-7 lacks support for changing the `Object.prototype.constructor` property
* on objects.
*
* - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
* incorrect length in some situations.
* - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
*
* We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they will
* get the Object implementation, which is slower but will work correctly.
* - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
* incorrect length in some situations.
* We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they
* get the Object implementation, which is slower but behaves correctly.
*/
Buffer.TYPED_ARRAY_SUPPORT = (function () {
Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined
? global.TYPED_ARRAY_SUPPORT
: typedArraySupport()
function typedArraySupport () {
function Bar () {}
try {
var buf = new ArrayBuffer(0)
var arr = new Uint8Array(buf)
var arr = new Uint8Array(1)
arr.foo = function () { return 42 }
arr.constructor = Bar
return arr.foo() === 42 && // typed array instances can be augmented
arr.constructor === Bar && // constructor can be set
typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
new Uint8Array(1).subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
} catch (e) {
return false
}
})()
}
function kMaxLength () {
return Buffer.TYPED_ARRAY_SUPPORT
? 0x7fffffff
: 0x3fffffff
}
/**
* Class: Buffer
@ -72,8 +218,10 @@ function Buffer (arg) {
return new Buffer(arg)
}
this.length = 0
this.parent = undefined
if (!Buffer.TYPED_ARRAY_SUPPORT) {
this.length = 0
this.parent = undefined
}
// Common case.
if (typeof arg === 'number') {
@ -119,8 +267,13 @@ function fromObject (that, object) {
throw new TypeError('must start with number, buffer, array or string')
}
if (typeof ArrayBuffer !== 'undefined' && object.buffer instanceof ArrayBuffer) {
return fromTypedArray(that, object)
if (typeof ArrayBuffer !== 'undefined') {
if (object.buffer instanceof ArrayBuffer) {
return fromTypedArray(that, object)
}
if (object instanceof ArrayBuffer) {
return fromArrayBuffer(that, object)
}
}
if (object.length) return fromArrayLike(that, object)
@ -157,6 +310,18 @@ function fromTypedArray (that, array) {
return that
}
function fromArrayBuffer (that, array) {
if (Buffer.TYPED_ARRAY_SUPPORT) {
// Return an augmented `Uint8Array` instance, for best performance
array.byteLength
that = Buffer._augment(new Uint8Array(array))
} else {
// Fallback: Return an object instance of the Buffer class
that = fromTypedArray(that, new Uint8Array(array))
}
return that
}
function fromArrayLike (that, array) {
var length = checked(array.length) | 0
that = allocate(that, length)
@ -184,10 +349,20 @@ function fromJsonObject (that, object) {
return that
}
if (Buffer.TYPED_ARRAY_SUPPORT) {
Buffer.prototype.__proto__ = Uint8Array.prototype
Buffer.__proto__ = Uint8Array
} else {
// pre-set for values that may exist in the future
Buffer.prototype.length = undefined
Buffer.prototype.parent = undefined
}
function allocate (that, length) {
if (Buffer.TYPED_ARRAY_SUPPORT) {
// Return an augmented `Uint8Array` instance, for best performance
that = Buffer._augment(new Uint8Array(length))
that.__proto__ = Buffer.prototype
} else {
// Fallback: Return an object instance of the Buffer class
that.length = length
@ -203,9 +378,9 @@ function allocate (that, length) {
function checked (length) {
// Note: cannot use `length < kMaxLength` here because that fails when
// length is NaN (which is otherwise coerced to zero.)
if (length >= kMaxLength) {
if (length >= kMaxLength()) {
throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
'size: 0x' + kMaxLength.toString(16) + ' bytes')
'size: 0x' + kMaxLength().toString(16) + ' bytes')
}
return length | 0
}
@ -274,8 +449,6 @@ Buffer.concat = function concat (list, length) {
if (list.length === 0) {
return new Buffer(0)
} else if (list.length === 1) {
return list[0]
}
var i
@ -297,39 +470,43 @@ Buffer.concat = function concat (list, length) {
}
function byteLength (string, encoding) {
if (typeof string !== 'string') string = String(string)
if (typeof string !== 'string') string = '' + string
if (string.length === 0) return 0
var len = string.length
if (len === 0) return 0
switch (encoding || 'utf8') {
case 'ascii':
case 'binary':
case 'raw':
return string.length
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
return string.length * 2
case 'hex':
return string.length >>> 1
case 'utf8':
case 'utf-8':
return utf8ToBytes(string).length
case 'base64':
return base64ToBytes(string).length
default:
return string.length
// Use a for loop to avoid recursion
var loweredCase = false
for (;;) {
switch (encoding) {
case 'ascii':
case 'binary':
// Deprecated
case 'raw':
case 'raws':
return len
case 'utf8':
case 'utf-8':
return utf8ToBytes(string).length
case 'ucs2':
case 'ucs-2':
case 'utf16le':
case 'utf-16le':
return len * 2
case 'hex':
return len >>> 1
case 'base64':
return base64ToBytes(string).length
default:
if (loweredCase) return utf8ToBytes(string).length // assume utf8
encoding = ('' + encoding).toLowerCase()
loweredCase = true
}
}
}
Buffer.byteLength = byteLength
// pre-set for values that may exist in the future
Buffer.prototype.length = undefined
Buffer.prototype.parent = undefined
// toString(encoding, start=0, end=buffer.length)
Buffer.prototype.toString = function toString (encoding, start, end) {
function slowToString (encoding, start, end) {
var loweredCase = false
start = start | 0
@ -372,6 +549,13 @@ Buffer.prototype.toString = function toString (encoding, start, end) {
}
}
Buffer.prototype.toString = function toString () {
var length = this.length | 0
if (length === 0) return ''
if (arguments.length === 0) return utf8Slice(this, 0, length)
return slowToString.apply(this, arguments)
}
Buffer.prototype.equals = function equals (b) {
if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
if (this === b) return true
@ -435,13 +619,13 @@ Buffer.prototype.indexOf = function indexOf (val, byteOffset) {
throw new TypeError('val must be string, number or Buffer')
}
// `get` will be removed in Node 0.13+
// `get` is deprecated
Buffer.prototype.get = function get (offset) {
console.log('.get() is deprecated. Access using array indexes instead.')
return this.readUInt8(offset)
}
// `set` will be removed in Node 0.13+
// `set` is deprecated
Buffer.prototype.set = function set (v, offset) {
console.log('.set() is deprecated. Access using array indexes instead.')
return this.writeUInt8(v, offset)
@ -582,20 +766,99 @@ function base64Slice (buf, start, end) {
}
function utf8Slice (buf, start, end) {
var res = ''
var tmp = ''
end = Math.min(buf.length, end)
var res = []
for (var i = start; i < end; i++) {
if (buf[i] <= 0x7F) {
res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i])
tmp = ''
} else {
tmp += '%' + buf[i].toString(16)
var i = start
while (i < end) {
var firstByte = buf[i]
var codePoint = null
var bytesPerSequence = (firstByte > 0xEF) ? 4
: (firstByte > 0xDF) ? 3
: (firstByte > 0xBF) ? 2
: 1
if (i + bytesPerSequence <= end) {
var secondByte, thirdByte, fourthByte, tempCodePoint
switch (bytesPerSequence) {
case 1:
if (firstByte < 0x80) {
codePoint = firstByte
}
break
case 2:
secondByte = buf[i + 1]
if ((secondByte & 0xC0) === 0x80) {
tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
if (tempCodePoint > 0x7F) {
codePoint = tempCodePoint
}
}
break
case 3:
secondByte = buf[i + 1]
thirdByte = buf[i + 2]
if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
codePoint = tempCodePoint
}
}
break
case 4:
secondByte = buf[i + 1]
thirdByte = buf[i + 2]
fourthByte = buf[i + 3]
if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
codePoint = tempCodePoint
}
}
}
}
if (codePoint === null) {
// we did not generate a valid codePoint so insert a
// replacement char (U+FFFD) and advance only 1 byte
codePoint = 0xFFFD
bytesPerSequence = 1
} else if (codePoint > 0xFFFF) {
// encode to utf16 (surrogate pair dance)
codePoint -= 0x10000
res.push(codePoint >>> 10 & 0x3FF | 0xD800)
codePoint = 0xDC00 | codePoint & 0x3FF
}
res.push(codePoint)
i += bytesPerSequence
}
return res + decodeUtf8Char(tmp)
return decodeCodePointsArray(res)
}
// Based on http://stackoverflow.com/a/22747272/680742, the browser with
// the lowest limit is Chrome, with 0x10000 args.
// We go 1 magnitude less, for safety
var MAX_ARGUMENTS_LENGTH = 0x1000
function decodeCodePointsArray (codePoints) {
var len = codePoints.length
if (len <= MAX_ARGUMENTS_LENGTH) {
return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
}
// Decode in chunks to avoid "call stack size exceeded".
var res = ''
var i = 0
while (i < len) {
res += String.fromCharCode.apply(
String,
codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
)
}
return res
}
function asciiSlice (buf, start, end) {
@ -884,7 +1147,7 @@ Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
offset = offset | 0
if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
this[offset] = value
this[offset] = (value & 0xff)
return offset + 1
}
@ -901,7 +1164,7 @@ Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert
offset = offset | 0
if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
if (Buffer.TYPED_ARRAY_SUPPORT) {
this[offset] = value
this[offset] = (value & 0xff)
this[offset + 1] = (value >>> 8)
} else {
objectWriteUInt16(this, value, offset, true)
@ -915,7 +1178,7 @@ Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert
if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
if (Buffer.TYPED_ARRAY_SUPPORT) {
this[offset] = (value >>> 8)
this[offset + 1] = value
this[offset + 1] = (value & 0xff)
} else {
objectWriteUInt16(this, value, offset, false)
}
@ -937,7 +1200,7 @@ Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert
this[offset + 3] = (value >>> 24)
this[offset + 2] = (value >>> 16)
this[offset + 1] = (value >>> 8)
this[offset] = value
this[offset] = (value & 0xff)
} else {
objectWriteUInt32(this, value, offset, true)
}
@ -952,7 +1215,7 @@ Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert
this[offset] = (value >>> 24)
this[offset + 1] = (value >>> 16)
this[offset + 2] = (value >>> 8)
this[offset + 3] = value
this[offset + 3] = (value & 0xff)
} else {
objectWriteUInt32(this, value, offset, false)
}
@ -1005,7 +1268,7 @@ Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
if (value < 0) value = 0xff + value + 1
this[offset] = value
this[offset] = (value & 0xff)
return offset + 1
}
@ -1014,7 +1277,7 @@ Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert)
offset = offset | 0
if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
if (Buffer.TYPED_ARRAY_SUPPORT) {
this[offset] = value
this[offset] = (value & 0xff)
this[offset + 1] = (value >>> 8)
} else {
objectWriteUInt16(this, value, offset, true)
@ -1028,7 +1291,7 @@ Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert)
if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
if (Buffer.TYPED_ARRAY_SUPPORT) {
this[offset] = (value >>> 8)
this[offset + 1] = value
this[offset + 1] = (value & 0xff)
} else {
objectWriteUInt16(this, value, offset, false)
}
@ -1040,7 +1303,7 @@ Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert)
offset = offset | 0
if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
if (Buffer.TYPED_ARRAY_SUPPORT) {
this[offset] = value
this[offset] = (value & 0xff)
this[offset + 1] = (value >>> 8)
this[offset + 2] = (value >>> 16)
this[offset + 3] = (value >>> 24)
@ -1059,7 +1322,7 @@ Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert)
this[offset] = (value >>> 24)
this[offset + 1] = (value >>> 16)
this[offset + 2] = (value >>> 8)
this[offset + 3] = value
this[offset + 3] = (value & 0xff)
} else {
objectWriteUInt32(this, value, offset, false)
}
@ -1130,9 +1393,16 @@ Buffer.prototype.copy = function copy (target, targetStart, start, end) {
}
var len = end - start
var i
if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {
for (var i = 0; i < len; i++) {
if (this === target && start < targetStart && targetStart < end) {
// descending copy from end
for (i = len - 1; i >= 0; i--) {
target[i + targetStart] = this[i + start]
}
} else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {
// ascending copy from start
for (i = 0; i < len; i++) {
target[i + targetStart] = this[i + start]
}
} else {
@ -1208,7 +1478,7 @@ Buffer._augment = function _augment (arr) {
// save reference to original Uint8Array set method before overwriting
arr._set = arr.set
// deprecated, will be removed in node 0.13+
// deprecated
arr.get = BP.get
arr.set = BP.set
@ -1264,7 +1534,7 @@ Buffer._augment = function _augment (arr) {
return arr
}
var INVALID_BASE64_RE = /[^+\/0-9A-z\-]/g
var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g
function base64clean (str) {
// Node strips out invalid characters like \n and \t from the string, base64-js does not
@ -1294,28 +1564,15 @@ function utf8ToBytes (string, units) {
var length = string.length
var leadSurrogate = null
var bytes = []
var i = 0
for (; i < length; i++) {
for (var i = 0; i < length; i++) {
codePoint = string.charCodeAt(i)
// is surrogate component
if (codePoint > 0xD7FF && codePoint < 0xE000) {
// last char was a lead
if (leadSurrogate) {
// 2 leads in a row
if (codePoint < 0xDC00) {
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
leadSurrogate = codePoint
continue
} else {
// valid surrogate pair
codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000
leadSurrogate = null
}
} else {
if (!leadSurrogate) {
// no lead yet
if (codePoint > 0xDBFF) {
// unexpected trail
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
@ -1324,18 +1581,30 @@ function utf8ToBytes (string, units) {
// unpaired lead
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
continue
} else {
// valid lead
leadSurrogate = codePoint
continue
}
// valid lead
leadSurrogate = codePoint
continue
}
// 2 leads in a row
if (codePoint < 0xDC00) {
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
leadSurrogate = codePoint
continue
}
// valid surrogate pair
codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000
} else if (leadSurrogate) {
// valid bmp char, but last char was a lead
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
leadSurrogate = null
}
leadSurrogate = null
// encode utf8
if (codePoint < 0x80) {
if ((units -= 1) < 0) break
@ -1353,7 +1622,7 @@ function utf8ToBytes (string, units) {
codePoint >> 0x6 & 0x3F | 0x80,
codePoint & 0x3F | 0x80
)
} else if (codePoint < 0x200000) {
} else if (codePoint < 0x110000) {
if ((units -= 4) < 0) break
bytes.push(
codePoint >> 0x12 | 0xF0,
@ -1406,144 +1675,18 @@ function blitBuffer (src, dst, offset, length) {
return i
}
function decodeUtf8Char (str) {
try {
return decodeURIComponent(str)
} catch (err) {
return String.fromCharCode(0xFFFD) // UTF 8 invalid char
}
}
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"base64-js":1,"ieee754":4,"isarray":3}],3:[function(require,module,exports){
var toString = {}.toString;
},{"base64-js":2,"ieee754":3,"is-array":4}],2:[function(require,module,exports){
var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
module.exports = Array.isArray || function (arr) {
return toString.call(arr) == '[object Array]';
};
;(function (exports) {
'use strict';
var Arr = (typeof Uint8Array !== 'undefined')
? Uint8Array
: Array
var PLUS = '+'.charCodeAt(0)
var SLASH = '/'.charCodeAt(0)
var NUMBER = '0'.charCodeAt(0)
var LOWER = 'a'.charCodeAt(0)
var UPPER = 'A'.charCodeAt(0)
var PLUS_URL_SAFE = '-'.charCodeAt(0)
var SLASH_URL_SAFE = '_'.charCodeAt(0)
function decode (elt) {
var code = elt.charCodeAt(0)
if (code === PLUS ||
code === PLUS_URL_SAFE)
return 62 // '+'
if (code === SLASH ||
code === SLASH_URL_SAFE)
return 63 // '/'
if (code < NUMBER)
return -1 //no match
if (code < NUMBER + 10)
return code - NUMBER + 26 + 26
if (code < UPPER + 26)
return code - UPPER
if (code < LOWER + 26)
return code - LOWER + 26
}
function b64ToByteArray (b64) {
var i, j, l, tmp, placeHolders, arr
if (b64.length % 4 > 0) {
throw new Error('Invalid string. Length must be a multiple of 4')
}
// the number of equal signs (place holders)
// if there are two placeholders, than the two characters before it
// represent one byte
// if there is only one, then the three characters before it represent 2 bytes
// this is just a cheap hack to not do indexOf twice
var len = b64.length
placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0
// base64 is 4/3 + up to two characters of the original data
arr = new Arr(b64.length * 3 / 4 - placeHolders)
// if there are placeholders, only get up to the last complete 4 chars
l = placeHolders > 0 ? b64.length - 4 : b64.length
var L = 0
function push (v) {
arr[L++] = v
}
for (i = 0, j = 0; i < l; i += 4, j += 3) {
tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3))
push((tmp & 0xFF0000) >> 16)
push((tmp & 0xFF00) >> 8)
push(tmp & 0xFF)
}
if (placeHolders === 2) {
tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4)
push(tmp & 0xFF)
} else if (placeHolders === 1) {
tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2)
push((tmp >> 8) & 0xFF)
push(tmp & 0xFF)
}
return arr
}
function uint8ToBase64 (uint8) {
var i,
extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes
output = "",
temp, length
function encode (num) {
return lookup.charAt(num)
}
function tripletToBase64 (num) {
return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F)
}
// go through the array every three bytes, we'll deal with trailing stuff later
for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
output += tripletToBase64(temp)
}
// pad the end with zeros, but make sure to not forget the extra bytes
switch (extraBytes) {
case 1:
temp = uint8[uint8.length - 1]
output += encode(temp >> 2)
output += encode((temp << 4) & 0x3F)
output += '=='
break
case 2:
temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
output += encode(temp >> 10)
output += encode((temp >> 4) & 0x3F)
output += encode((temp << 2) & 0x3F)
output += '='
break
}
return output
}
exports.toByteArray = b64ToByteArray
exports.fromByteArray = uint8ToBase64
}(typeof exports === 'undefined' ? (this.base64js = {}) : exports))
},{}],3:[function(require,module,exports){
},{}],4:[function(require,module,exports){
exports.read = function (buffer, offset, isLE, mLen, nBytes) {
var e, m
var eLen = nBytes * 8 - mLen - 1
var eLen = (nBytes * 8) - mLen - 1
var eMax = (1 << eLen) - 1
var eBias = eMax >> 1
var nBits = -7
@ -1556,12 +1699,12 @@ exports.read = function (buffer, offset, isLE, mLen, nBytes) {
e = s & ((1 << (-nBits)) - 1)
s >>= (-nBits)
nBits += eLen
for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {}
for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}
m = e & ((1 << (-nBits)) - 1)
e >>= (-nBits)
nBits += mLen
for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {}
for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}
if (e === 0) {
e = 1 - eBias
@ -1576,7 +1719,7 @@ exports.read = function (buffer, offset, isLE, mLen, nBytes) {
exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
var e, m, c
var eLen = nBytes * 8 - mLen - 1
var eLen = (nBytes * 8) - mLen - 1
var eMax = (1 << eLen) - 1
var eBias = eMax >> 1
var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
@ -1609,7 +1752,7 @@ exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
m = 0
e = eMax
} else if (e + eBias >= 1) {
m = (value * c - 1) * Math.pow(2, mLen)
m = ((value * c) - 1) * Math.pow(2, mLen)
e = e + eBias
} else {
m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
@ -1626,41 +1769,6 @@ exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
buffer[offset + i - d] |= s * 128
}
},{}],4:[function(require,module,exports){
/**
* isArray
*/
var isArray = Array.isArray;
/**
* toString
*/
var str = Object.prototype.toString;
/**
* Whether or not the given `val`
* is an array.
*
* example:
*
* isArray([]);
* // > true
* isArray(arguments);
* // > false
* isArray('');
* // > false
*
* @param {mixed} val
* @return {bool}
*/
module.exports = isArray || function (val) {
return !! val && '[object Array]' == str.call(val);
};
},{}],5:[function(require,module,exports){
(function (Buffer){
function FilerBuffer (subject, encoding, nonZero) {
@ -1688,5 +1796,5 @@ Object.keys(Buffer).forEach(function (p) {
module.exports = FilerBuffer;
}).call(this,require("buffer").Buffer)
},{"buffer":1}]},{},[5])(5)
},{"buffer":2}]},{},[5])(5)
});

4
dist/buffer.min.js vendored

File diff suppressed because one or more lines are too long

4683
dist/filer-perf.js vendored

File diff suppressed because it is too large Load Diff

8204
dist/filer-test.js vendored

File diff suppressed because it is too large Load Diff

2137
dist/filer.js vendored

File diff suppressed because it is too large Load Diff

8
dist/filer.min.js vendored

File diff suppressed because one or more lines are too long

4
dist/path.min.js vendored
View File

@ -1,2 +1,2 @@
/*! filer 0.0.44 2017-05-25 */
!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.Path=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){function d(a,b){for(var c=0,d=a.length-1;d>=0;d--){var e=a[d];"."===e?a.splice(d,1):".."===e?(a.splice(d,1),c++):c&&(a.splice(d,1),c--)}if(b)for(;c--;c)a.unshift("..");return a}function e(){for(var a="",b=!1,c=arguments.length-1;c>=-1&&!b;c--){var e=c>=0?arguments[c]:"/";"string"==typeof e&&e&&(a=e+"/"+a,b="/"===e.charAt(0))}return a=d(a.split("/").filter(function(a){return!!a}),!b).join("/"),(b?"/":"")+a||"."}function f(a){var b="/"===a.charAt(0);"/"===a.substr(-1);return a=d(a.split("/").filter(function(a){return!!a}),!b).join("/"),a||b||(a="."),(b?"/":"")+a}function g(){var a=Array.prototype.slice.call(arguments,0);return f(a.filter(function(a,b){return a&&"string"==typeof a}).join("/"))}function h(a,b){function c(a){for(var b=0;b<a.length&&""===a[b];b++);for(var c=a.length-1;c>=0&&""===a[c];c--);return b>c?[]:a.slice(b,c-b+1)}a=e(a).substr(1),b=e(b).substr(1);for(var d=c(a.split("/")),f=c(b.split("/")),g=Math.min(d.length,f.length),h=g,i=0;g>i;i++)if(d[i]!==f[i]){h=i;break}for(var j=[],i=h;i<d.length;i++)j.push("..");return j=j.concat(f.slice(h)),j.join("/")}function i(a){var b=q(a),c=b[0],d=b[1];return c||d?(d&&(d=d.substr(0,d.length-1)),c+d):"."}function j(a,b){var c=q(a)[2];return b&&c.substr(-1*b.length)===b&&(c=c.substr(0,c.length-b.length)),""===c?"/":c}function k(a){return q(a)[3]}function l(a){return"/"===a.charAt(0)?!0:!1}function m(a){return-1!==(""+a).indexOf("\x00")?!0:!1}function n(a){return a.replace(/\/*$/,"/")}function o(a){return a=a.replace(/\/*$/,""),""===a?"/":a}var p=/^(\/?)([\s\S]+\/(?!$)|\/)?((?:\.{1,2}$|[\s\S]+?)?(\.[^.\/]*)?)$/,q=function(a){var b=p.exec(a);return[b[1]||"",b[2]||"",b[3]||"",b[4]||""]};b.exports={normalize:f,resolve:e,join:g,relative:h,sep:"/",delimiter:":",dirname:i,basename:j,extname:k,isAbsolute:l,isNull:m,addTrailing:n,removeTrailing:o}},{}]},{},[1])(1)});
/*! filer 0.0.44 2018-06-25 */
!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.Path=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c||a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){function d(a,b){for(var c=0,d=a.length-1;d>=0;d--){var e=a[d];"."===e?a.splice(d,1):".."===e?(a.splice(d,1),c++):c&&(a.splice(d,1),c--)}if(b)for(;c--;c)a.unshift("..");return a}function e(){for(var a="",b=!1,c=arguments.length-1;c>=-1&&!b;c--){var e=c>=0?arguments[c]:"/";"string"==typeof e&&e&&(a=e+"/"+a,b="/"===e.charAt(0))}return a=d(a.split("/").filter(function(a){return!!a}),!b).join("/"),(b?"/":"")+a||"."}function f(a){var b="/"===a.charAt(0);a.substr(-1);return a=d(a.split("/").filter(function(a){return!!a}),!b).join("/"),a||b||(a="."),(b?"/":"")+a}function g(){return f(Array.prototype.slice.call(arguments,0).filter(function(a,b){return a&&"string"==typeof a}).join("/"))}function h(a,b){function c(a){for(var b=0;b<a.length&&""===a[b];b++);for(var c=a.length-1;c>=0&&""===a[c];c--);return b>c?[]:a.slice(b,c-b+1)}a=e(a).substr(1),b=e(b).substr(1);for(var d=c(a.split("/")),f=c(b.split("/")),g=Math.min(d.length,f.length),h=g,i=0;i<g;i++)if(d[i]!==f[i]){h=i;break}for(var j=[],i=h;i<d.length;i++)j.push("..");return j=j.concat(f.slice(h)),j.join("/")}function i(a){var b=q(a),c=b[0],d=b[1];return c||d?(d&&(d=d.substr(0,d.length-1)),c+d):"."}function j(a,b){var c=q(a)[2];return b&&c.substr(-1*b.length)===b&&(c=c.substr(0,c.length-b.length)),""===c?"/":c}function k(a){return q(a)[3]}function l(a){return"/"===a.charAt(0)}function m(a){return-1!==(""+a).indexOf("\0")}function n(a){return a.replace(/\/*$/,"/")}function o(a){return a=a.replace(/\/*$/,""),""===a?"/":a}var p=/^(\/?)([\s\S]+\/(?!$)|\/)?((?:\.{1,2}$|[\s\S]+?)?(\.[^.\/]*)?)$/,q=function(a){var b=p.exec(a);return[b[1]||"",b[2]||"",b[3]||"",b[4]||""]};b.exports={normalize:f,resolve:e,join:g,relative:h,sep:"/",delimiter:":",dirname:i,basename:j,extname:k,isAbsolute:l,isNull:m,addTrailing:n,removeTrailing:o}},{}]},{},[1])(1)});

7061
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -19,10 +19,19 @@ module.exports = {
WSQL_SIZE: 5 * 1024 * 1024,
WSQL_DESC: "FileSystem Storage",
MODE_FILE: 'FILE',
MODE_DIRECTORY: 'DIRECTORY',
MODE_SYMBOLIC_LINK: 'SYMLINK',
MODE_META: 'META',
NODE_TYPE_FILE: 'FILE',
NODE_TYPE_DIRECTORY: 'DIRECTORY',
NODE_TYPE_SYMBOLIC_LINK: 'SYMLINK',
NODE_TYPE_META: 'META',
S_IFREG: 0x8000,
S_IFDIR: 0x4000,
S_IFLNK: 0xA000,
DEFAULT_DIR_PERMISSIONS: 0x1ED, // 755
DEFAULT_FILE_PERMISSIONS: 0x1A4, // 644
FULL_READ_WRITE_EXEC_PERMISSIONS: 0x1FF, // 777
READ_WRITE_PERMISSIONS: 0x1B6, /// 666
SYMLOOP_MAX: 10,
@ -76,5 +85,49 @@ module.exports = {
ENVIRONMENT: {
TMP: '/tmp',
PATH: ''
},
// Duplicate Node's fs.constants
fsConstants: {
O_RDONLY: 0,
O_WRONLY: 1,
O_RDWR: 2,
S_IFMT: 61440,
S_IFREG: 32768,
S_IFDIR: 16384,
S_IFCHR: 8192,
S_IFBLK: 24576,
S_IFIFO: 4096,
S_IFLNK: 40960,
S_IFSOCK: 49152,
O_CREAT: 512,
O_EXCL: 2048,
O_NOCTTY: 131072,
O_TRUNC: 1024,
O_APPEND: 8,
O_DIRECTORY: 1048576,
O_NOFOLLOW: 256,
O_SYNC: 128,
O_DSYNC: 4194304,
O_SYMLINK: 2097152,
O_NONBLOCK: 4,
S_IRWXU: 448,
S_IRUSR: 256,
S_IWUSR: 128,
S_IXUSR: 64,
S_IRWXG: 56,
S_IRGRP: 32,
S_IWGRP: 16,
S_IXGRP: 8,
S_IRWXO: 7,
S_IROTH: 4,
S_IWOTH: 2,
S_IXOTH: 1,
F_OK: 0,
R_OK: 4,
W_OK: 2,
X_OK: 1,
UV_FS_COPYFILE_EXCL: 1,
COPYFILE_EXCL: 1
}
};

View File

@ -1,6 +1,6 @@
var MODE_FILE = require('./constants.js').MODE_FILE;
var NODE_TYPE_FILE = require('./constants.js').NODE_TYPE_FILE;
module.exports = function DirectoryEntry(id, type) {
this.id = id;
this.type = type || MODE_FILE;
this.type = type || NODE_TYPE_FILE;
};

View File

@ -8,10 +8,14 @@ var isAbsolutePath = Path.isAbsolute;
var isNullPath = Path.isNull;
var Constants = require('../constants.js');
var MODE_FILE = Constants.MODE_FILE;
var MODE_DIRECTORY = Constants.MODE_DIRECTORY;
var MODE_SYMBOLIC_LINK = Constants.MODE_SYMBOLIC_LINK;
var MODE_META = Constants.MODE_META;
var NODE_TYPE_FILE = Constants.NODE_TYPE_FILE;
var NODE_TYPE_DIRECTORY = Constants.NODE_TYPE_DIRECTORY;
var NODE_TYPE_SYMBOLIC_LINK = Constants.NODE_TYPE_SYMBOLIC_LINK;
var NODE_TYPE_META = Constants.NODE_TYPE_META;
var DEFAULT_FILE_PERMISSIONS = Constants.DEFAULT_FILE_PERMISSIONS;
var DEFAULT_DIR_PERMISSIONS = Constants.DEFAULT_DIR_PERMISSIONS;
var FULL_READ_WRITE_EXEC_PERMISSIONS = Constants.FULL_READ_WRITE_EXEC_PERMISSIONS;
var ROOT_DIRECTORY_NAME = Constants.ROOT_DIRECTORY_NAME;
var SUPER_NODE_ID = Constants.SUPER_NODE_ID;
@ -91,9 +95,9 @@ function update_node_times(context, path, node, times, callback) {
*/
// in: file or directory path
// out: new node representing file/directory
function make_node(context, path, mode, callback) {
if(mode !== MODE_DIRECTORY && mode !== MODE_FILE) {
return callback(new Errors.EINVAL('mode must be a directory or file', path));
function make_node(context, path, type, callback) {
if(type !== NODE_TYPE_DIRECTORY && type !== NODE_TYPE_FILE) {
return callback(new Errors.EINVAL('type must be a directory or file', path));
}
path = normalize(path);
@ -108,7 +112,7 @@ function make_node(context, path, mode, callback) {
function create_node_in_parent(error, parentDirectoryNode) {
if(error) {
callback(error);
} else if(parentDirectoryNode.mode !== MODE_DIRECTORY) {
} else if(parentDirectoryNode.type !== NODE_TYPE_DIRECTORY) {
callback(new Errors.ENOTDIR('a component of the path prefix is not a directory', path));
} else {
parentNode = parentDirectoryNode;
@ -133,7 +137,10 @@ function make_node(context, path, mode, callback) {
callback(error);
} else {
parentNodeData = result;
Node.create({guid: context.guid, mode: mode}, function(error, result) {
Node.create({
guid: context.guid,
type: type
}, function(error, result) {
if(error) {
callback(error);
return;
@ -160,7 +167,7 @@ function make_node(context, path, mode, callback) {
if(error) {
callback(error);
} else {
parentNodeData[name] = new DirectoryEntry(node.id, mode);
parentNodeData[name] = new DirectoryEntry(node.id, type);
context.putObject(parentNode.data, parentNodeData, update_time);
}
}
@ -186,7 +193,7 @@ function find_node(context, path, callback) {
function read_root_directory_node(error, superNode) {
if(error) {
callback(error);
} else if(!superNode || superNode.mode !== MODE_META || !superNode.rnode) {
} else if(!superNode || superNode.type !== NODE_TYPE_META || !superNode.rnode) {
callback(new Errors.EFILESYSTEMERROR());
} else {
context.getObject(superNode.rnode, check_root_directory_node);
@ -208,7 +215,7 @@ function find_node(context, path, callback) {
function read_parent_directory_data(error, parentDirectoryNode) {
if(error) {
callback(error);
} else if(parentDirectoryNode.mode !== MODE_DIRECTORY || !parentDirectoryNode.data) {
} else if(parentDirectoryNode.type !== NODE_TYPE_DIRECTORY || !parentDirectoryNode.data) {
callback(new Errors.ENOTDIR('a component of the path prefix is not a directory', path));
} else {
context.getObject(parentDirectoryNode.data, get_node_from_parent_directory_data);
@ -234,7 +241,7 @@ function find_node(context, path, callback) {
if(error) {
callback(error);
} else {
if(node.mode == MODE_SYMBOLIC_LINK) {
if(node.type === NODE_TYPE_SYMBOLIC_LINK) {
followedCount++;
if(followedCount > SYMLOOP_MAX){
callback(new Errors.ELOOP(null, path));
@ -251,14 +258,14 @@ function find_node(context, path, callback) {
data = normalize(data);
parentPath = dirname(data);
name = basename(data);
if(ROOT_DIRECTORY_NAME == name) {
if(ROOT_DIRECTORY_NAME === name) {
context.getObject(SUPER_NODE_ID, read_root_directory_node);
} else {
find_node(context, parentPath, read_parent_directory_data);
}
}
if(ROOT_DIRECTORY_NAME == name) {
if(ROOT_DIRECTORY_NAME === name) {
context.getObject(SUPER_NODE_ID, read_root_directory_node);
} else {
find_node(context, parentPath, read_parent_directory_data);
@ -326,7 +333,11 @@ function ensure_root_directory(context, callback) {
if(error) {
callback(error);
} else {
Node.create({guid: context.guid, id: superNode.rnode, mode: MODE_DIRECTORY}, function(error, result) {
Node.create({
guid: context.guid,
id: superNode.rnode,
type: NODE_TYPE_DIRECTORY
}, function(error, result) {
if(error) {
callback(error);
return;
@ -387,7 +398,10 @@ function make_directory(context, path, callback) {
callback(error);
} else {
parentDirectoryData = result;
Node.create({guid: context.guid, mode: MODE_DIRECTORY}, function(error, result) {
Node.create({
guid: context.guid,
type: NODE_TYPE_DIRECTORY
}, function(error, result) {
if(error) {
callback(error);
return;
@ -421,7 +435,7 @@ function make_directory(context, path, callback) {
if(error) {
callback(error);
} else {
parentDirectoryData[name] = new DirectoryEntry(directoryNode.id, MODE_DIRECTORY);
parentDirectoryData[name] = new DirectoryEntry(directoryNode.id, NODE_TYPE_DIRECTORY);
context.putObject(parentDirectoryNode.data, parentDirectoryData, update_time);
}
}
@ -454,7 +468,7 @@ function remove_directory(context, path, callback) {
function check_if_node_exists(error, result) {
if(error) {
callback(error);
} else if(ROOT_DIRECTORY_NAME == name) {
} else if(ROOT_DIRECTORY_NAME === name) {
callback(new Errors.EBUSY(null, path));
} else if(!_(result).has(name)) {
callback(new Errors.ENOENT(null, path));
@ -468,7 +482,7 @@ function remove_directory(context, path, callback) {
function check_if_node_is_directory(error, result) {
if(error) {
callback(error);
} else if(result.mode != MODE_DIRECTORY) {
} else if(result.type !== NODE_TYPE_DIRECTORY) {
callback(new Errors.ENOTDIR(null, path));
} else {
directoryNode = result;
@ -535,7 +549,7 @@ function open_file(context, path, flags, callback) {
var followedCount = 0;
if(ROOT_DIRECTORY_NAME == name) {
if(ROOT_DIRECTORY_NAME === name) {
if(_(flags).contains(O_WRITE)) {
callback(new Errors.EISDIR('the named file is a directory and O_WRITE is set', path));
} else {
@ -548,7 +562,7 @@ function open_file(context, path, flags, callback) {
function read_directory_data(error, result) {
if(error) {
callback(error);
} else if(result.mode !== MODE_DIRECTORY) {
} else if(result.type !== NODE_TYPE_DIRECTORY) {
callback(new Errors.ENOENT(null, path));
} else {
directoryNode = result;
@ -566,7 +580,7 @@ function open_file(context, path, flags, callback) {
callback(new Errors.ENOENT('O_CREATE and O_EXCLUSIVE are set, and the named file exists', path));
} else {
directoryEntry = directoryData[name];
if(directoryEntry.type == MODE_DIRECTORY && _(flags).contains(O_WRITE)) {
if(directoryEntry.type === NODE_TYPE_DIRECTORY && _(flags).contains(O_WRITE)) {
callback(new Errors.EISDIR('the named file is a directory and O_WRITE is set', path));
} else {
context.getObject(directoryEntry.id, check_if_symbolic_link);
@ -587,7 +601,7 @@ function open_file(context, path, flags, callback) {
callback(error);
} else {
var node = result;
if(node.mode == MODE_SYMBOLIC_LINK) {
if(node.type === NODE_TYPE_SYMBOLIC_LINK) {
followedCount++;
if(followedCount > SYMLOOP_MAX){
callback(new Errors.ELOOP(null, path));
@ -604,7 +618,7 @@ function open_file(context, path, flags, callback) {
data = normalize(data);
parentPath = dirname(data);
name = basename(data);
if(ROOT_DIRECTORY_NAME == name) {
if(ROOT_DIRECTORY_NAME === name) {
if(_(flags).contains(O_WRITE)) {
callback(new Errors.EISDIR('the named file is a directory and O_WRITE is set', path));
} else {
@ -624,7 +638,10 @@ function open_file(context, path, flags, callback) {
}
function write_file_node() {
Node.create({guid: context.guid, mode: MODE_FILE}, function(error, result) {
Node.create({
guid: context.guid,
type: NODE_TYPE_FILE
}, function(error, result) {
if(error) {
callback(error);
return;
@ -658,7 +675,7 @@ function open_file(context, path, flags, callback) {
if(error) {
callback(error);
} else {
directoryData[name] = new DirectoryEntry(fileNode.id, MODE_FILE);
directoryData[name] = new DirectoryEntry(fileNode.id, NODE_TYPE_FILE);
context.putObject(directoryNode.data, directoryData, update_time);
}
}
@ -813,7 +830,7 @@ function read_data(context, ofd, buffer, offset, length, position, callback) {
function read_file_data(error, result) {
if(error) {
callback(error);
} else if(result.mode === 'DIRECTORY') {
} else if(result.type === NODE_TYPE_DIRECTORY) {
callback(new Errors.EISDIR('the named file is a directory', ofd.path));
} else {
fileNode = result;
@ -842,7 +859,7 @@ function lstat_file(context, path, callback) {
var directoryNode;
var directoryData;
if(ROOT_DIRECTORY_NAME == name) {
if(ROOT_DIRECTORY_NAME === name) {
find_node(context, path, callback);
} else {
find_node(context, parentPath, read_directory_data);
@ -879,18 +896,20 @@ function link_node(context, oldpath, newpath, callback) {
newpath = normalize(newpath);
var newname = basename(newpath);
var newParentPath = dirname(newpath);
var ctime = Date.now();
var oldDirectoryNode;
var oldDirectoryData;
var newDirectoryNode;
var newDirectoryData;
var fileNodeID;
var fileNode;
function update_time(error) {
if(error) {
callback(error);
} else {
update_node_times(context, newpath, fileNode, { ctime: Date.now() }, callback);
update_node_times(context, newpath, fileNode, { ctime: ctime }, callback);
}
}
@ -904,11 +923,11 @@ function link_node(context, oldpath, newpath, callback) {
}
}
function read_directory_entry(error, result) {
function read_file_node(error, result) {
if(error) {
callback(error);
} else {
context.getObject(newDirectoryData[newname].id, update_file_node);
context.getObject(fileNodeID, update_file_node);
}
}
@ -921,7 +940,8 @@ function link_node(context, oldpath, newpath, callback) {
callback(new Errors.EEXIST('newpath resolves to an existing file', newname));
} else {
newDirectoryData[newname] = oldDirectoryData[oldname];
context.putObject(newDirectoryNode.data, newDirectoryData, read_directory_entry);
fileNodeID = newDirectoryData[newname].id;
context.putObject(newDirectoryNode.data, newDirectoryData, read_file_node);
}
}
}
@ -942,7 +962,7 @@ function link_node(context, oldpath, newpath, callback) {
oldDirectoryData = result;
if(!_(oldDirectoryData).has(oldname)) {
callback(new Errors.ENOENT('a component of either path prefix does not exist', oldname));
} else if(oldDirectoryData[oldname].type === 'DIRECTORY') {
} else if(oldDirectoryData[oldname].type === NODE_TYPE_DIRECTORY) {
callback(new Errors.EPERM('oldpath refers to a directory'));
} else {
find_node(context, newParentPath, read_new_directory_data);
@ -1010,7 +1030,7 @@ function unlink_node(context, path, callback) {
function check_if_node_is_directory(error, result) {
if(error) {
callback(error);
} else if(result.mode === 'DIRECTORY') {
} else if(result.type === NODE_TYPE_DIRECTORY) {
callback(new Errors.EPERM('unlink not permitted on directories', name));
} else {
update_file_node(null, result);
@ -1062,7 +1082,7 @@ function read_directory(context, path, callback) {
function read_directory_data(error, result) {
if(error) {
callback(error);
} else if(result.mode !== MODE_DIRECTORY) {
} else if(result.type !== NODE_TYPE_DIRECTORY) {
callback(new Errors.ENOTDIR(null, path));
} else {
directoryNode = result;
@ -1082,7 +1102,7 @@ function make_symbolic_link(context, srcpath, dstpath, callback) {
var directoryData;
var fileNode;
if(ROOT_DIRECTORY_NAME == name) {
if(ROOT_DIRECTORY_NAME === name) {
callback(new Errors.EEXIST(null, name));
} else {
find_node(context, parentPath, read_directory_data);
@ -1111,15 +1131,27 @@ function make_symbolic_link(context, srcpath, dstpath, callback) {
}
function write_file_node() {
Node.create({guid: context.guid, mode: MODE_SYMBOLIC_LINK}, function(error, result) {
Node.create({
guid: context.guid,
type: NODE_TYPE_SYMBOLIC_LINK
}, function(error, result) {
if(error) {
callback(error);
return;
}
fileNode = result;
fileNode.nlinks += 1;
// If the srcpath isn't absolute, resolve it relative to the dstpath
// but store both versions, since we'll use the relative one in readlink().
if(!isAbsolutePath(srcpath)) {
fileNode.symlink_relpath = srcpath;
srcpath = Path.resolve(parentPath, srcpath);
}
fileNode.size = srcpath.length;
fileNode.data = srcpath;
context.putObject(fileNode.id, fileNode, update_directory_data);
});
}
@ -1137,7 +1169,7 @@ function make_symbolic_link(context, srcpath, dstpath, callback) {
if(error) {
callback(error);
} else {
directoryData[name] = new DirectoryEntry(fileNode.id, MODE_SYMBOLIC_LINK);
directoryData[name] = new DirectoryEntry(fileNode.id, NODE_TYPE_SYMBOLIC_LINK);
context.putObject(directoryNode.data, directoryData, update_time);
}
}
@ -1175,14 +1207,17 @@ function read_link(context, path, callback) {
}
}
function check_if_symbolic(error, result) {
function check_if_symbolic(error, fileNode) {
if(error) {
callback(error);
} else {
if(result.mode != MODE_SYMBOLIC_LINK) {
if(fileNode.type !== NODE_TYPE_SYMBOLIC_LINK) {
callback(new Errors.EINVAL('path not a symbolic link', path));
} else {
callback(null, result.data);
// If we were originally given a relative path, return that now vs. the
// absolute path we've generated and use elsewhere internally.
var target = fileNode.symlink_relpath ? fileNode.symlink_relpath : fileNode.data;
callback(null, target);
}
}
}
@ -1196,7 +1231,7 @@ function truncate_file(context, path, length, callback) {
function read_file_data (error, node) {
if (error) {
callback(error);
} else if(node.mode == MODE_DIRECTORY ) {
} else if(node.type === NODE_TYPE_DIRECTORY ) {
callback(new Errors.EISDIR(null, path));
} else{
fileNode = node;
@ -1252,7 +1287,7 @@ function ftruncate_file(context, ofd, length, callback) {
function read_file_data (error, node) {
if (error) {
callback(error);
} else if(node.mode == MODE_DIRECTORY ) {
} else if(node.type === NODE_TYPE_DIRECTORY ) {
callback(new Errors.EISDIR());
} else{
fileNode = node;
@ -1315,7 +1350,7 @@ function utimes_file(context, path, atime, mtime, callback) {
}
}
if (typeof atime != 'number' || typeof mtime != 'number') {
if (typeof atime !== 'number' || typeof mtime !== 'number') {
callback(new Errors.EINVAL('atime and mtime must be number', path));
}
else if (atime < 0 || mtime < 0) {
@ -1336,7 +1371,7 @@ function futimes_file(context, ofd, atime, mtime, callback) {
}
}
if (typeof atime != 'number' || typeof mtime != 'number') {
if (typeof atime !== 'number' || typeof mtime !== 'number') {
callback(new Errors.EINVAL('atime and mtime must be a number'));
}
else if (atime < 0 || mtime < 0) {
@ -1357,7 +1392,7 @@ function setxattr_file(context, path, name, value, flag, callback) {
set_extended_attribute(context, path, node, name, value, flag, callback);
}
if (typeof name != 'string') {
if (typeof name !== 'string') {
callback(new Errors.EINVAL('attribute name must be a string', path));
}
else if (!name) {
@ -1413,7 +1448,7 @@ function getxattr_file (context, path, name, callback) {
}
}
if (typeof name != 'string') {
if (typeof name !== 'string') {
callback(new Errors.EINVAL('attribute name must be a string', path));
}
else if (!name) {
@ -1441,7 +1476,7 @@ function fgetxattr_file (context, ofd, name, callback) {
}
}
if (typeof name != 'string') {
if (typeof name !== 'string') {
callback(new Errors.EINVAL());
}
else if (!name) {
@ -1516,7 +1551,7 @@ function fremovexattr_file (context, ofd, name, callback) {
}
}
if (typeof name != 'string') {
if (typeof name !== 'string') {
callback(new Errors.EINVAL('attribute name must be a string'));
}
else if (!name) {
@ -1545,14 +1580,19 @@ function validate_file_options(options, enc, fileMode){
return options;
}
function pathCheck(path, callback) {
function pathCheck(path, allowRelative, callback) {
var err;
if(typeof allowRelative === 'function') {
callback = allowRelative;
allowRelative = false;
}
if(!path) {
err = new Errors.EINVAL('Path must be a string', path);
} else if(isNullPath(path)) {
err = new Errors.EINVAL('Path must be a string without null bytes.', path);
} else if(!isAbsolutePath(path)) {
} else if(!allowRelative && !isAbsolutePath(path)) {
err = new Errors.EINVAL('Path must be absolute.', path);
}
@ -1565,8 +1605,22 @@ function pathCheck(path, callback) {
function open(fs, context, path, flags, mode, callback) {
// NOTE: we support the same signature as node with a `mode` arg,
// but ignore it.
/**
* NOTE: we support the same signature as node with a `mode` arg,
* but ignore it. We need to add it. Here is what node.js does:
* function open(path, flags, mode, callback) {
* path = getPathFromURL(path);
* validatePath(path);
* const flagsNumber = stringToFlags(flags);
* if (arguments.length < 4) {
* callback = makeCallback(mode);
* mode = 0o666;
* } else {
* mode = validateAndMaskMode(mode, 'mode', 0o666);
* callback = makeCallback(callback);
* }
*/
callback = arguments[arguments.length - 1];
if(!pathCheck(path, callback)) return;
@ -1603,14 +1657,20 @@ function close(fs, context, fd, callback) {
}
}
function mknod(fs, context, path, mode, callback) {
function mknod(fs, context, path, type, callback) {
if(!pathCheck(path, callback)) return;
make_node(context, path, mode, callback);
make_node(context, path, type, callback);
}
function mkdir(fs, context, path, mode, callback) {
// NOTE: we support passing a mode arg, but we ignore it internally for now.
callback = arguments[arguments.length - 1];
if (arguments.length < 5) {
callback = mode;
mode = FULL_READ_WRITE_EXEC_PERMISSIONS;
} else {
mode = validateAndMaskMode(mode, FULL_READ_WRITE_EXEC_PERMISSIONS, callback);
if(!mode) return;
}
if(!pathCheck(path, callback)) return;
make_directory(context, path, callback);
}
@ -1627,7 +1687,7 @@ function stat(fs, context, path, callback) {
if(error) {
callback(error);
} else {
var stats = new Stats(result, fs.name);
var stats = new Stats(path, result, fs.name);
callback(null, stats);
}
}
@ -1640,7 +1700,7 @@ function fstat(fs, context, fd, callback) {
if(error) {
callback(error);
} else {
var stats = new Stats(result, fs.name);
var stats = new Stats(ofd.path, result, fs.name);
callback(null, stats);
}
}
@ -1713,7 +1773,7 @@ function readFile(fs, context, path, options, callback) {
return callback(err);
}
var stats = new Stats(fstatResult, fs.name);
var stats = new Stats(ofd.path, fstatResult, fs.name);
if(stats.isDirectory()) {
cleanup();
@ -1841,6 +1901,121 @@ function exists(fs, context, path, callback) {
stat(fs, context, path, cb);
}
// Based on https://github.com/nodejs/node/blob/c700cc42da9cf73af9fec2098520a6c0a631d901/lib/internal/validators.js#L21
var octalReg = /^[0-7]+$/;
var modeDesc = 'must be a 32-bit unsigned integer or an octal string';
function isUint32(value) {
return value === (value >>> 0);
}
// Validator for mode_t (the S_* constants). Valid numbers or octal strings
// will be masked with 0o777 to be consistent with the behavior in POSIX APIs.
function validateAndMaskMode(value, def, callback) {
if(typeof def === 'function') {
callback = def;
def = undefined;
}
if (isUint32(value)) {
return value & FULL_READ_WRITE_EXEC_PERMISSIONS;
}
if (typeof value === 'number') {
if (!Number.isInteger(value)) {
callback(new Errors.EINVAL('mode not a valid an integer value', value));
return false;
} else {
// 2 ** 32 === 4294967296
callback(new Errors.EINVAL('mode not a valid an integer value', value));
return false;
}
}
if (typeof value === 'string') {
if (!octalReg.test(value)) {
callback(new Errors.EINVAL('mode not a valid octal string', value));
return false;
}
var parsed = parseInt(value, 8);
return parsed & FULL_READ_WRITE_EXEC_PERMISSIONS;
}
// TODO(BridgeAR): Only return `def` in case `value === null`
if (def !== undefined) {
return def;
}
callback(new Errors.EINVAL('mode not valid', value));
return false;
}
function chmod_file(context, path, mode, callback) {
path = normalize(path);
function update_mode(error, node) {
if (error) {
callback(error);
} else {
Node.setMode(mode, node);
update_node_times(context, path, node, { mtime: Date.now() }, callback);
}
}
if (typeof mode !== 'number') {
callback(new Errors.EINVAL('mode must be number', path));
}
else {
find_node(context, path, update_mode);
}
}
function fchmod_file(context, ofd, mode, callback) {
function update_mode(error, node) {
if (error) {
callback(error);
} else {
node.mode = mode;
update_node_times(context, ofd.path, node, { mtime: Date.now() }, callback);
}
}
if (typeof mode !== 'number') {
callback(new Errors.EINVAL('mode must be a number'));
}
else {
ofd.getNode(context, update_mode);
}
}
function chown_file(context, path, uid, gid, callback) {
path = normalize(path);
function update_owner(error, node) {
if (error) {
callback(error);
} else {
node.uid = uid;
node.gid = gid;
update_node_times(context, path, node, { mtime: Date.now() }, callback);
}
}
find_node(context, path, update_owner);
}
function fchown_file(context, ofd, uid, gid, callback) {
function update_owner(error, node) {
if (error) {
callback(error);
} else {
node.uid = uid;
node.gid = gid;
update_node_times(context, ofd.path, node, { mtime: Date.now() }, callback);
}
}
ofd.getNode(context, update_owner);
}
function getxattr(fs, context, path, name, callback) {
if (!pathCheck(path, callback)) return;
getxattr_file(context, path, name, callback);
@ -1972,6 +2147,58 @@ function futimes(fs, context, fd, atime, mtime, callback) {
}
}
function chmod(fs, context, path, mode, callback) {
if(!pathCheck(path, callback)) return;
mode = validateAndMaskMode(mode, 'mode');
if(!mode) return;
chmod_file(context, path, mode, callback);
}
function fchmod(fs, context, fd, mode, callback) {
mode = validateAndMaskMode(mode, 'mode');
if(!mode) return;
var ofd = fs.openFiles[fd];
if(!ofd) {
callback(new Errors.EBADF());
} else if(!_(ofd.flags).contains(O_WRITE)) {
callback(new Errors.EBADF('descriptor does not permit writing'));
} else {
fchmod_file(context, ofd, mode, callback);
}
}
function chown(fs, context, path, uid, gid, callback) {
if(!pathCheck(path, callback)) return;
if(!isUint32(uid)) {
return callback(new Errors.EINVAL('uid must be a valid integer', uid));
}
if(!isUint32(gid)) {
return callback(new Errors.EINVAL('gid must be a valid integer', gid));
}
chown_file(context, path, uid, gid, callback);
}
function fchown(fs, context, fd, uid, gid, callback) {
if(!isUint32(uid)) {
return callback(new Errors.EINVAL('uid must be a valid integer', uid));
}
if(!isUint32(gid)) {
return callback(new Errors.EINVAL('gid must be a valid integer', gid));
}
var ofd = fs.openFiles[fd];
if(!ofd) {
callback(new Errors.EBADF());
} else if(!_(ofd.flags).contains(O_WRITE)) {
callback(new Errors.EBADF('descriptor does not permit writing'));
} else {
fchown_file(context, ofd, uid, gid, callback);
}
}
function rename(fs, context, oldpath, newpath, callback) {
if(!pathCheck(oldpath, callback)) return;
if(!pathCheck(newpath, callback)) return;
@ -1985,12 +2212,15 @@ function rename(fs, context, oldpath, newpath, callback) {
var newName = Path.basename(newpath);
var oldParentDirectory, oldParentData;
var newParentDirectory, newParentData;
var ctime = Date.now();
var fileNode;
function update_times(error, newNode) {
function update_times(error, result) {
if(error) {
callback(error);
} else {
update_node_times(context, newpath, newNode, { ctime: Date.now() }, callback);
fileNode = result;
update_node_times(context, newpath, fileNode, { ctime: ctime }, callback);
}
}
@ -2074,7 +2304,7 @@ function rename(fs, context, oldpath, newpath, callback) {
function check_node_type(error, node) {
if(error) {
callback(error);
} else if(node.mode === 'DIRECTORY') {
} else if(node.type === NODE_TYPE_DIRECTORY) {
find_node(context, oldParentPath, read_parent_directory_data);
} else {
link_node(context, oldpath, newpath, unlink_old_file);
@ -2087,8 +2317,13 @@ function rename(fs, context, oldpath, newpath, callback) {
function symlink(fs, context, srcpath, dstpath, type, callback) {
// NOTE: we support passing the `type` arg, but ignore it.
callback = arguments[arguments.length - 1];
if(!pathCheck(srcpath, callback)) return;
// Special Case: allow srcpath to be relative, which we normally don't permit.
// If the srcpath is relative, we assume it's relative to the dirpath of
// dstpath.
if(!pathCheck(srcpath, true, callback)) return;
if(!pathCheck(dstpath, callback)) return;
make_symbolic_link(context, srcpath, dstpath, callback);
}
@ -2104,7 +2339,7 @@ function lstat(fs, context, path, callback) {
if(error) {
callback(error);
} else {
var stats = new Stats(result, fs.name);
var stats = new Stats(path, result, fs.name);
callback(null, stats);
}
}
@ -2139,6 +2374,10 @@ function ftruncate(fs, context, fd, length, callback) {
module.exports = {
ensureRootDirectory: ensure_root_directory,
open: open,
chmod: chmod,
fchmod: fchmod,
chown: chown,
fchown: fchown,
close: close,
mknod: mknod,
mkdir: mkdir,

View File

@ -94,6 +94,9 @@ function FileSystem(options, callback) {
fs.stdout = STDOUT;
fs.stderr = STDERR;
// Expose Node's fs.constants to users
fs.constants = Constants.fsConstants;
// Expose Shell constructor
this.Shell = Shell.bind(undefined, this);
@ -268,6 +271,10 @@ FileSystem.providers = providers;
*/
[
'open',
'chmod',
'fchmod',
'chown',
'fchown',
'close',
'mknod',
'mkdir',

View File

@ -1,10 +1,36 @@
var MODE_FILE = require('./constants.js').MODE_FILE;
var NODE_TYPE_FILE = require('./constants.js').NODE_TYPE_FILE;
var NODE_TYPE_DIRECTORY = require('./constants.js').NODE_TYPE_DIRECTORY;
var NODE_TYPE_SYMBOLIC_LINK = require('./constants.js').NODE_TYPE_SYMBOLIC_LINK;
var NODE_TYPE_META = require('./constants.js').NODE_TYPE_META;
var ROOT_DIRECTORY_NAME = require('./constants.js').ROOT_DIRECTORY_NAME;
var S_IFREG = require('./constants.js').S_IFREG;
var S_IFDIR = require('./constants.js').S_IFDIR;
var S_IFLNK = require('./constants.js').S_IFLNK;
var DEFAULT_FILE_PERMISSIONS = require('./constants.js').DEFAULT_FILE_PERMISSIONS;
var DEFAULT_DIR_PERMISSIONS = require('./constants.js').DEFAULT_DIR_PERMISSIONS;
function getMode(type, mode) {
switch(type) {
case NODE_TYPE_DIRECTORY:
return (mode || DEFAULT_DIR_PERMISSIONS) | S_IFDIR;
case NODE_TYPE_SYMBOLIC_LINK:
return (mode || DEFAULT_FILE_PERMISSIONS) | S_IFLNK;
/* jshint -W086 */
case NODE_TYPE_FILE:
// falls through
default:
return (mode || DEFAULT_FILE_PERMISSIONS) | S_IFREG;
}
}
function Node(options) {
var now = Date.now();
this.id = options.id;
this.mode = options.mode || MODE_FILE; // node type (file, directory, etc)
this.type = options.type || NODE_TYPE_FILE; // node type (file, directory, etc)
this.size = options.size || 0; // size (bytes for files, entries for directories)
this.atime = options.atime || now; // access time (will mirror ctime after creation)
this.ctime = options.ctime || now; // creation/change time
@ -12,10 +38,13 @@ function Node(options) {
this.flags = options.flags || []; // file flags
this.xattrs = options.xattrs || {}; // extended attributes
this.nlinks = options.nlinks || 0; // links count
this.version = options.version || 0; // node version
this.blksize = undefined; // block size
this.nblocks = 1; // blocks count
this.data = options.data; // id for data object
this.version = options.version || 1;
// permissions and flags
this.mode = options.mode || (getMode(this.type));
this.uid = options.uid || 0x0; // owner name
this.gid = options.gid || 0x0; // group name
}
// Make sure the options object has an id on property,
@ -50,4 +79,9 @@ Node.create = function(options, callback) {
});
};
// Update the node's mode (permissions), taking file type bits into account.
Node.setMode = function(mode, node) {
node.mode = getMode(node.type, mode);
};
module.exports = Node;

View File

@ -89,12 +89,7 @@ IndexedDBContext.prototype.putObject = function(key, value, callback) {
this._put(key, value, callback);
};
IndexedDBContext.prototype.putBuffer = function(key, uint8BackedBuffer, callback) {
var buf;
if(!Buffer._useTypedArrays) { // workaround for fxos 1.3
buf = uint8BackedBuffer.toArrayBuffer();
} else {
buf = uint8BackedBuffer.buffer;
}
var buf = uint8BackedBuffer.buffer;
this._put(key, buf, callback);
};

View File

@ -233,16 +233,10 @@ Shell.prototype.ls = function(dir, options, callback) {
callback(error);
return;
}
var entry = {
path: Path.basename(name),
links: stats.nlinks,
size: stats.size,
modified: stats.mtime,
type: stats.type
};
var entry = stats;
if(options.recursive && stats.type === 'DIRECTORY') {
list(Path.join(pathname, entry.path), function(error, items) {
list(Path.join(pathname, entry.name), function(error, items) {
if(error) {
callback(error);
return;

View File

@ -1,26 +1,32 @@
var Constants = require('./constants.js');
var Path = require('./path.js');
function Stats(fileNode, devName) {
this.node = fileNode.id;
function Stats(path, fileNode, devName) {
this.dev = devName;
this.node = fileNode.id;
this.type = fileNode.type;
this.size = fileNode.size;
this.nlinks = fileNode.nlinks;
this.atime = fileNode.atime;
this.mtime = fileNode.mtime;
this.ctime = fileNode.ctime;
this.type = fileNode.mode;
this.version = fileNode.version;
this.mode = fileNode.mode;
this.uid = fileNode.uid;
this.gid = fileNode.gid;
this.name = Path.basename(path);
}
Stats.prototype.isFile = function() {
return this.type === Constants.MODE_FILE;
return this.type === Constants.NODE_TYPE_FILE;
};
Stats.prototype.isDirectory = function() {
return this.type === Constants.MODE_DIRECTORY;
return this.type === Constants.NODE_TYPE_DIRECTORY;
};
Stats.prototype.isSymbolicLink = function() {
return this.type === Constants.MODE_SYMBOLIC_LINK;
return this.type === Constants.NODE_TYPE_SYMBOLIC_LINK;
};
// These will always be false in Filer.

View File

@ -4,7 +4,7 @@ function SuperNode(options) {
var now = Date.now();
this.id = Constants.SUPER_NODE_ID;
this.mode = Constants.MODE_META;
this.type = Constants.NODE_TYPE_META;
this.atime = options.atime || now;
this.ctime = options.ctime || now;
this.mtime = options.mtime || now;

View File

@ -39,6 +39,8 @@ require("./spec/time-flags.spec");
require("./spec/fs.watch.spec");
require("./spec/errors.spec");
require("./spec/fs.shell.spec");
require("./spec/fs.chmod.spec");
require("./spec/fs.chown.spec")
// Filer.FileSystem.providers.*
require("./spec/providers/providers.spec");

View File

@ -0,0 +1,74 @@
var Filer = require('../..');
var util = require('../lib/test-utils.js');
var expect = require('chai').expect;
describe('fs.chmod, fs.fchmod', function() {
beforeEach(util.setup);
afterEach(util.cleanup);
it('should be functions', function() {
var fs = util.fs();
expect(typeof fs.chmod).to.equal('function');
expect(typeof fs.fchmod).to.equal('function');
});
it('should automatically set mode=755 for a directory', function(done) {
var fs = util.fs();
fs.mkdir('/dir', function(err) {
if(err) throw err;
fs.stat('/dir', function(err, stats) {
if(err) throw err;
expect(stats.mode & 0o755).to.equal(0o755);
done();
});
});
});
it('should automatically set mode=644 for a file', function(done) {
var fs = util.fs();
fs.open('/file', 'w', function(err, fd) {
if(err) throw err;
fs.fstat(fd, function(err, stats) {
if(err) throw err;
expect(stats.mode & 0o644).to.equal(0o644);
fs.close(fd, done);
});
});
});
it('should allow for updating mode of a given file', function(done) {
var fs = util.fs();
fs.open('/file', 'w', function(err, fd) {
if(err) throw err;
fs.fchmod(fd, 0o777, function(err) {
if(err) throw err;
fs.fstat(fd, function(err, stats) {
if(err) throw err;
expect(stats.mode & 0o777).to.equal(0o777);
fs.close(fd, function(err) {
if(err) throw err;
fs.chmod('/file', 0o444, function(err) {
if(err) throw err;
fs.stat('/file', function(err, stats) {
if(err) throw err;
expect(stats.mode & 0o444).to.equal(0o444);
done();
});
});
});
});
});
});
});
});

View File

@ -0,0 +1,65 @@
var Filer = require('../..');
var util = require('../lib/test-utils.js');
var expect = require('chai').expect;
describe('fs.chown, fs.fchown', function() {
beforeEach(util.setup);
afterEach(util.cleanup);
it('should be functions', function() {
var fs = util.fs();
expect(typeof fs.chown).to.equal('function');
expect(typeof fs.fchown).to.equal('function');
});
it('should automatically set a file\s uid and gid to 0 (i.e., root)', function(done) {
var fs = util.fs();
fs.open('/file', 'w', function(err, fd) {
if(err) throw err;
fs.fstat(fd, function(err, stats) {
if(err) throw err;
expect(stats.uid).to.equal(0);
expect(stats.gid).to.equal(0);
fs.close(fd, done);
});
});
});
it('should allow updating gid and uid for a file', function(done) {
var fs = util.fs();
fs.open('/file', 'w', function(err, fd) {
if(err) throw err;
fs.fchown(fd, 1001, 1001, function(err) {
if(err) throw err;
fs.fstat(fd, function(err, stats) {
if(err) throw err;
expect(stats.uid).to.equal(1001);
expect(stats.gid).to.equal(1001);
fs.close(fd, function(err) {
if(err) throw err;
fs.chown('/file', 500, 500, function(err) {
if(err) throw err;
fs.stat('/file', function(err, stats) {
if(err) throw err;
expect(stats.uid).to.equal(500);
expect(stats.gid).to.equal(500);
done();
});
});
});
});
});
});
});
});

View File

@ -42,6 +42,30 @@ describe('fs.link', function() {
});
});
it('should create hard link to identical data node', function(done) {
var fs = util.fs();
var contents = "Hello World!";
fs.writeFile('/file', contents, function(err) {
if(err) throw err;
fs.link('/file', '/hlink', function(err) {
if(err) throw err;
fs.readFile('/file', 'utf8', function(err, fileData) {
if(err) throw err;
fs.readFile('/hlink', 'utf8', function(err, hlinkData) {
if(err) throw err;
expect(fileData).to.equal(hlinkData);
done();
});
});
});
});
});
it('should not follow symbolic links', function(done) {
var fs = util.fs();

View File

@ -1,4 +1,5 @@
var Filer = require('../..');
var Path = Filer.Path;
var util = require('../lib/test-utils.js');
var expect = require('chai').expect;
@ -256,4 +257,41 @@ describe('fs.stats', function() {
});
});
});
describe('generated name property', function() {
beforeEach(util.setup);
afterEach(util.cleanup);
it('should correct return name for a file', function(done) {
var fs = util.fs();
var filepath = '/a';
fs.writeFile(filepath, 'data', function(err) {
if(err) throw err;
fs.stat(filepath, function(err, stats) {
if(err) throw err;
expect(stats.name).to.equal(Path.basename(filepath));
done();
});
})
});
it('should correct return name for an fd', function(done) {
var fs = util.fs();
var filepath = '/a';
fs.open(filepath, 'w', function(err, fd) {
if(err) throw err;
fs.fstat(fd, function(err, stats) {
if(err) throw err;
expect(stats.name).to.equal(Path.basename(filepath));
done();
});
})
});
})
});

View File

@ -26,7 +26,6 @@ describe("node.js tests: https://github.com/joyent/node/blob/master/test/simple/
done();
}
});
fn.apply(fs, args);
}
@ -49,11 +48,10 @@ describe("node.js tests: https://github.com/joyent/node/blob/master/test/simple/
check(fs.appendFile, '/foo\u0000bar');
check(fs.truncate, '/foo\u0000bar');
check(fs.utimes, '/foo\u0000bar', 0, 0);
// TODO - need to be implemented still...
// check(fs.realpath, '/foo\u0000bar');
// check(fs.chmod, '/foo\u0000bar', '0644');
// check(fs.chown, '/foo\u0000bar', 12, 34);
// check(fs.realpath, '/foo\u0000bar');
// Not implemented
// check(fs.realpath, '/foo\u0000bar');
check(fs.chmod, '/foo\u0000bar', '0644');
check(fs.chown, '/foo\u0000bar', 12, 34);
checks.forEach(function(fn){
fn();

View File

@ -40,18 +40,18 @@ describe('FileSystemShell.ls', function() {
expect(list.length).to.equal(2);
var item0 = list[0];
expect(item0.path).to.equal('file');
expect(item0.links).to.equal(1);
expect(item0.name).to.equal('file');
expect(item0.nlinks).to.equal(1);
expect(item0.size).to.equal(1);
expect(item0.modified).to.be.a('number');
expect(item0.mtime).to.be.a('number');
expect(item0.type).to.equal('FILE');
expect(item0.contents).not.to.exist;
var item1 = list[1];
expect(item1.path).to.equal('file2');
expect(item1.links).to.equal(1);
expect(item1.name).to.equal('file2');
expect(item1.nlinks).to.equal(1);
expect(item1.size).to.equal(2);
expect(item1.modified).to.be.a('number');
expect(item1.mtime).to.be.a('number');
expect(item1.type).to.equal('FILE');
expect(item0.contents).not.to.exist;
@ -84,19 +84,19 @@ describe('FileSystemShell.ls', function() {
// We shouldn't rely on the order we'll get the listing
list.forEach(function(item, i, arr) {
switch(item.path) {
switch(item.name) {
case 'dir2':
expect(item.links).to.equal(1);
expect(item.nlinks).to.equal(1);
expect(item.size).to.be.a('number');
expect(item.modified).to.be.a('number');
expect(item.mtime).to.be.a('number');
expect(item.type).to.equal('DIRECTORY');
expect(item.contents).not.to.exist;
break;
case 'file':
case 'file2':
expect(item.links).to.equal(1);
expect(item.nlinks).to.equal(1);
expect(item.size).to.equal(1);
expect(item.modified).to.be.a('number');
expect(item.mtime).to.be.a('number');
expect(item.type).to.equal('FILE');
expect(item.contents).not.to.exist;
break;
@ -143,27 +143,27 @@ describe('FileSystemShell.ls', function() {
// We shouldn't rely on the order we'll get the listing
list.forEach(function(item, i, arr) {
switch(item.path) {
switch(item.name) {
case 'dir2':
expect(item.links).to.equal(1);
expect(item.nlinks).to.equal(1);
expect(item.size).to.be.a('number');
expect(item.modified).to.be.a('number');
expect(item.mtime).to.be.a('number');
expect(item.type).to.equal('DIRECTORY');
expect(item.contents).to.exist;
expect(item.contents.length).to.equal(1);
var contents0 = item.contents[0];
expect(contents0.path).to.equal('file');
expect(contents0.links).to.equal(1);
expect(contents0.name).to.equal('file');
expect(contents0.nlinks).to.equal(1);
expect(contents0.size).to.equal(1);
expect(contents0.modified).to.be.a('number');
expect(contents0.mtime).to.be.a('number');
expect(contents0.type).to.equal('FILE');
expect(contents0.contents).not.to.exist;
break;
case 'file':
case 'file2':
expect(item.links).to.equal(1);
expect(item.nlinks).to.equal(1);
expect(item.size).to.equal(1);
expect(item.modified).to.be.a('number');
expect(item.mtime).to.be.a('number');
expect(item.type).to.equal('FILE');
expect(item.contents).not.to.exist;
break;