Skip to content

Commit

Permalink
update to new volume driver format
Browse files Browse the repository at this point in the history
  • Loading branch information
natevw committed May 28, 2014
1 parent c1fd7aa commit 9510b43
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 40 deletions.
54 changes: 37 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,38 +34,58 @@ Expected differences:
- watch/watchFile will be low priority


## "Block driver" API
## "Volume driver" API

To use 'fatfs', you must provide a driver object with the following properties/methods:

**NOTE**: this will likely be changing to something like `{readSector,writeSector,flush}` soon
* `driver.sectorSize` — number of bytes per sector on this device
* `driver.numSectors` — count of sectors available on this media
* `driver.readSector(n, cb)` — returns the requested block to `cb(e, data)`
* `driver.writeSector(n, data, cb)` — (optional) writes the data and notifies `cb(e)`

If you do not provide a `writeSector` method, then `fatfs` will work in readonly mode. Pretty simple, eh? And the 'fatfs' module makes a good effort to check the parameters passed to your driver methods!

**TBD:** if we're going to support sync methods, we'll need sync versions too
**TBD:** probably need at least a `.size` (and `.blksize`?)
**TBD:** document 'noatime' property or whatever final public way of handling that may be
**TBD:** to facilitate proper cache handling, this module might add an optional `driver.flush(cb)` method at some point in the future.

Basically you just need to provide an object with two methods, `read` and `write`. These should behave like the node.js [fs.read](http://nodejs.org/api/fs.html#fs_fs_read_fd_buffer_offset_length_position_callback) and [fs.write](http://nodejs.org/api/fs.html#fs_fs_write_fd_buffer_offset_length_position_callback) methods. They will *always* be called with an explicit position, so you do not need to keep track.

If you do not provide a `write` method, then `fatfs` will work in readonly mode.

For example:
Here's an example taken from code used to run this module's own tests:

```js
// NOTE: this assumes image has no partition table
// …if it did, you'd need to translate positions
var fs = require('fs'),
fd = fs.openSync("image", 'r+'),
ro = true;

var exampleDriver = {
read: fs.read.bind(fs, fd),
write: (ro) ? fs.write.bind(fs, fd) : null
// NOTE: this assumes image at `path` has no partition table.
// If it did, you'd need to translate positions, natch…
var fs = require('fs');

exports.createDriverSync = function (path, opts) {
opts || (opts = {});

var secSize = 512,
ro = opts.readOnly || false,
fd = fs.openSync(path, (ro) ? 'r' : 'r+'),
s = fs.fstatSync(fd);

return {
sectorSize: secSize,
numSectors: s.size / secSize,
readSector: function (n, cb) {
fs.read(fd, Buffer(secSize), 0, secSize, n*secSize, function (e,n,d) {
cb(e,d);
});
},
writeSector: (ro) ? null : function (n, data, cb) {
fs.write(fd, data, 0, secSize, n*secSize, function (e) {
cb(e);
});
}
};
};
```


## License

© 2014 Nathan Vander Wilt
© 2014 Nathan Vander Wilt.
Funding for this work was provided by Technical Machine, Inc.

Reuse under your choice of:
Expand Down
21 changes: 17 additions & 4 deletions img_volume.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,23 @@ var fs = require('fs');
exports.createDriverSync = function (path, opts) {
opts || (opts = {});

var ro = opts.readOnly || false,
fd = fs.openSync(path, (ro) ? 'r' : 'r+');
var secSize = 512,
ro = opts.readOnly || false,
fd = fs.openSync(path, (ro) ? 'r' : 'r+'),
s = fs.fstatSync(fd);

return {
read: fs.read.bind(fs, fd),
write: (ro) ? null : fs.write.bind(fs, fd)
sectorSize: secSize,
numSectors: s.size / secSize,
readSector: function (n, cb) {
fs.read(fd, Buffer(secSize), 0, secSize, n*secSize, function (e,n,d) {
cb(e,d);
});
},
writeSector: (ro) ? null : function (n, data, cb) {
fs.write(fd, data, 0, secSize, n*secSize, function (e) {
cb(e);
});
}
};
};
16 changes: 10 additions & 6 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ var fifolock = require('fifolock'),
S = require("./structs.js"),
_ = require("./helpers.js");

exports.createFileSystem = function (volume) {
exports.createFileSystem = function (volume, bootSector) {
var fs = {},
vol = null,
dir = require("./dir.js"),
Expand All @@ -11,18 +11,22 @@ exports.createFileSystem = function (volume) {

var GROUP = q.TRANSACTION_WRAPPER;

// TODO: have our own caller pass in, or fire 'ready' event when done…
var bootSector = new Buffer(512);
volume.read(bootSector, 0, bootSector.length, 0, function (e) {
if (bootSector) init(bootSector);
else volume.readSector(0, function (e,d) {
// TODO: emit events like a decent chap… (if this interface is to be documented/public)
if (e) throw e;
else init(d);
});

function init(bootSector) {
vol = require("./vol.js").init(volume, bootSector);
bootSector = null; // allow GC
fs._dirIterator = dir.iterator.bind(dir);
fs._entryForPath = dir.entryForPath.bind(dir, vol);
fs._updateEntry = dir.updateEntry.bind(dir, vol);
fs._addFile = dir.addFile.bind(dir, vol);
fs._initDir = dir.init.bind(dir, vol);
});
}

// NOTE: we really don't share namespace, but avoid first three anyway…
var fileDescriptors = [null,null,null];
Expand All @@ -36,7 +40,7 @@ exports.createFileSystem = function (volume) {
cb = GROUP(cb, function () {
var _fd = {flags:null,stats:null,chain:null,pos:0},
f = _.parseFlags(flags);
if (!volume.write && (f.write || f.create || f.truncate)) return _.delayedCall(cb, S.err.ROFS());
if (!volume.writeSector && (f.write || f.create || f.truncate)) return _.delayedCall(cb, S.err.ROFS());
else _fd.flags = f;

fs._entryForPath(path, function (e,stats,chain) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fatfs",
"version": "0.4.0",
"version": "0.5.0",
"description": "fs implementation on top of raw FAT16/FAT32 block source",
"main": "index.js",
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ setTimeout(function () { // HACK: should wait for 'ready' event or so
fs.mkdir(BASE_DIR, function (e) {
assert(!e, "No error from fs.mkdir");
fs.readdir(BASE_DIR, function (e,arr) {
if (e) console.log(e.stack);
assert(!e, "No error from fs.readdir");
assert(arr.length === 0 , "No files in BASE_DIR yet.");
});
Expand Down
21 changes: 9 additions & 12 deletions vol.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ exports.init = function (volume, bootSector) {
var isFAT16 = bootSector.readUInt16LE(S.boot16.fields['FATSz16'].offset),
bootStruct = (isFAT16) ? S.boot16 : S.boot32,
BS = bootStruct.valueFromBytes(bootSector);
//console.log(BS);
bootSector = null; // allow GC
if (!BS.BytsPerSec) throw Error("This looks like an ExFAT volume! (unsupported)");

//console.log(BS);
else if (BS.BytsPerSec !== volume.sectorSize) throw Error("Sector size mismatch with FAT table.");

var FATSz = (isFAT16) ? BS.FATSz16 : BS.FATSz32,
rootDirSectors = Math.ceil((BS.RootEntCnt * 32) / BS.BytsPerSec),
Expand All @@ -20,7 +20,7 @@ exports.init = function (volume, bootSector) {
dataSec = totSec - firstDataSector,
countofClusters = Math.floor(dataSec / BS.SecPerClus);
// avoid corrupting sectors from other partitions or whatnot
if (totSec > volume.totalSectors) throw Error("Volume size mismatch!");
if (totSec > volume.numSectors) throw Error("Volume size mismatch!");

var fatType;
if (countofClusters < 4085) {
Expand All @@ -42,20 +42,17 @@ exports.init = function (volume, bootSector) {
};

vol._readSector = function (secNum, cb) {
var secSize = vol._sectorSize,
sectorBuffer = new Buffer(secSize);
volume.read(sectorBuffer, 0, secSize, secNum*secSize, function (e) {
cb(e, sectorBuffer);
});
if (secNum < volume.numSectors) volume.readSector(secNum, cb);
else throw Error("Invalid sector number!");
};

vol._writeSector = function (secNum, data, cb) {
//console.log("_writeSector of", data.length, "bytes to sector", secNum);
var secSize = vol._sectorSize;
// NOTE: these are internal assertions, public API will get proper `S.err`s
if (data.length !== secSize) throw Error("Must write complete sector");
else if (!volume.write) throw Error("Read-only filesystem");
else volume.write(data, 0, secSize, secNum*secSize, cb);
if (data.length !== volume.sectorSize) throw Error("Buffer does not match sector size");
else if (!volume.writeSector) throw Error("Read-only filesystem");
else if (secNum < volume.numSectors) volume.writeSector(secNum, data, cb);
else throw Error("Invalid sector number!");
};

function fatInfoForCluster(n) {
Expand Down

0 comments on commit 9510b43

Please sign in to comment.