Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
node_modules
test.js
.vscode
74 changes: 57 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ npm install -S smb2
### var smb2Client = new SMB2 ( options )
The SMB2 class is the constructor of your SMB2 client.

the parameter ```options``` accepts this list of attributes:
the parameter `options` accepts this list of attributes:

- ```share``` (mandatory): the share you want to access
- ```domain``` (mandatory): the domain of which the user is registred
- ```username``` (mandatory): the username of the user that access the share
- ```password``` (mandatory): the password
- ```port``` (optional): default ```445```, the port of the SMB server
- ```packetConcurrency``` (optional): default ```20```, the number of simulatanous packet when writting / reading data from the share
- ```autoCloseTimeout``` (optional): default ```10000```, the timeout in milliseconds before to close the SMB2 session and the socket, if setted to ```0``` the connection will never be closed unless you do it
- `share` (mandatory): the share you want to access
- `domain` (mandatory): the domain of which the user is registred
- `username` (mandatory): the username of the user that access the share
- `password` (mandatory): the password
- `port` (optional): default `445`, the port of the SMB server
- `packetConcurrency` (optional): default `20`, the number of simulatanous packet when writting / reading data from the share
- `autoCloseTimeout` (optional): default `10000`, the timeout in milliseconds before to close the SMB2 session and the socket, if setted to `0` the connection will never be closed unless you do it

Example:
```javascript
Expand Down Expand Up @@ -55,10 +55,10 @@ smb2Client.readdir('Windows\\System32', function(err, files){
```

### smb2Client.readFile ( filename, [options], callback )
- ```filename``` String
- ```options``` Object
- ```encoding``` String | Null default = null
- ```callback``` Function
- `filename` String
- `options` Object
- `encoding` String | Null default = null
- `callback` Function

Asynchronously reads the entire contents of a file. Example:
```javascript
Expand All @@ -72,11 +72,11 @@ The callback is passed two arguments (err, data), where data is the contents of
If no encoding is specified, then the raw buffer is returned.

### smb2Client.writeFile ( filename, data, [options], callback )
- ```filename``` String
- ```data``` String | Buffer
- ```options``` Object
- ```encoding``` String | Null default = 'utf8'
- ```callback``` Function
- `filename` String
- `data` String | Buffer
- `options` Object
- `encoding` String | Null default = 'utf8'
- `callback` Function

Asynchronously writes data to a file, replacing the file if it already exists. data can be a string or a buffer.

Expand Down Expand Up @@ -139,6 +139,44 @@ smb2Client.rename('path\\to\\my\\file.txt', 'new\\path\\to\\my\\new-file-name.tx
});
```

### smb2Client.createReadStream ( path, [options] )
- `path` String
- `options` Object
- `start` Number default = '0'
- `end` Number default = 'Inf'

Creates a readable stream.
`options` can include `start` and `end` values to read a range of bytes from the file instead of the entire file. Both `start` and `end` are inclusive and start counting at 0.

```javascript
var smbStream = smb2Client.createReadStream('path\\to\\my\\file.txt');
smbStream.pipe(fs.createWriteStream('path\\to\\local\\file.txt'));
smbStream.on('end', function () {
console.log('File copied');
});
```

An example to read the last 10 bytes of a file which is 100 bytes long:

```javascript
smb2Client.createReadStream('sample.txt', { start: 90, end: 99 });
```

Note that when using readable streams, it might make sense to disable auto closing of the SMB2 session by setting the `autoCloseTimeout`to `0`.
Note that when `start` > file lenght stream fails with error. (`end` still can be greater than file lenght e.g. `Inf`)

### smb2Client.createWriteStream ( path )
Creates a writeable stream.
```javascript
var smbStream = smb2Client.createWriteStream('path\\to\\my\\file.txt');
fs.createReadStream('path\\to\\local\\file.txt').pipe(smbStream);
smbStream.on('finish', function () {
console.log('File copied');
});
```

Note that when using writeable streams, it might make sense to disable auto closing of the SMB2 session by setting the `autoCloseTimeout`to `0`.

### smb2Client.close ( )
This function will close the open connection if opened, it will be called automatically after ```autoCloseTimeout``` ms of no SMB2 call on the server.

Expand All @@ -147,6 +185,8 @@ This function will close the open connection if opened, it will be called automa
- [Fabrice Marsaud](https://github.com/marsaud)
- [Jay McAliley](https://github.com/jaymcaliley)
- [eldrago](https://github.com/eldrago)
- [Friðjón Guðjohnsen](https://github.com/fridjon)
- [Bartłomiej Wierciński](https://github.com/bwiercinski)

## References

Expand Down
12 changes: 6 additions & 6 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# TODO:

## New functions
- fs.appendFile(filename, data, [options], callback)
- fs.chmod(path, mode, callback)
- fs.stat(path, callback)
- fs.watchFile(filename, [options], listener)
- fs.unwatchFile(filename, [listener])
- fs.watch(filename, [options], [listener])
- `fs.appendFile(filename, data, [options], callback)`
- `fs.chmod(path, mode, callback)`
- `fs.stat(path, callback)`
- `fs.watchFile(filename, [options], listener)`
- `fs.unwatchFile(filename, [listener])`
- `fs.watch(filename, [options], [listener])`

## Implementation on existing functions
- support of mode in mkdir
Expand Down
148 changes: 148 additions & 0 deletions lib/api/createreadstream.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
var RS = require('readable-stream')
, SMB2Forge = require('../tools/smb2-forge')
, SMB2Request = SMB2Forge.request
, bigint = require('../tools/bigint')
;

/*
* createReadStream
* ================
*
* Return a read stream for a file on the share
*/
module.exports = function(filename, options){
options = processOptions(options);

var connection = this
, file
, bufferedChunks = []
, chunckPushed = 0
, chunckRequested = 0
, opened = false
, fileLength = 0
, offset = new bigint(8).add(options.start)
, stop = false
, nbRemainingPackets = 0
, maxPacketSize = 0x00010000
, readable = new RS.Readable({
read: function(size) {
if (opened) {
readNext();
}
}
})
;

var close = function () {
stop = true;
if (opened) {
opened = false;
SMB2Request('close', file, connection, function(err){
if(err){
readable.emit('error', err);
}
});
}
};

SMB2Request('open', {path:filename}, connection, function(err, openedFile) {
file = openedFile;
opened = true;
if(err) {
readable.emit('error', err);
return close();
} else {
fileLength = file.EndofFile.readUInt16LE();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whoops I think it should be readUInt32LE() sorry

Suggested change
fileLength = file.EndofFile.readUInt16LE();
fileLength = file.EndofFile.readUInt32LE();

if(options.start >= fileLength) {
readable.emit('error', new Error('start (' + options.start + ') is greater equal than file length (' + fileLength + ')!'));
return close();
}
fileLength = Math.min(fileLength, options.end + 1);
readNext();
}
});


function callback(position, offset) {
return function(err, content){
if(stop) {
return close();
}
if(err) {
readable.emit('error', err)
return close();
} else {
bufferedChunks[position] = { loaded: true, content: content };
if (position === chunckPushed) {
while (bufferedChunks[chunckPushed] && bufferedChunks[chunckPushed].loaded) {
readable.push(bufferedChunks[chunckPushed].content);
delete bufferedChunks[chunckPushed];
chunckPushed++;
}
}
nbRemainingPackets--;
readNext();
if (chunckPushed == chunckRequested && nbRemainingPackets == 0) {
return close();
}
}
}
}

function readNext() {
while (nbRemainingPackets < connection.packetConcurrency && offset.lt(fileLength)){
// process packet size
var rest = offset.sub(fileLength).neg();
var packetSize = rest.gt(maxPacketSize) ? maxPacketSize : rest.toNumber();
// generate buffer
SMB2Request('read', {
'FileId':file.FileId
, 'Length':packetSize
, 'Offset':offset.toBuffer()
}, connection, callback(chunckRequested, offset));
offset = offset.add(packetSize);
chunckRequested++;
nbRemainingPackets++;
}
}

return readable;
}

function processOptions(options) {

// create the options object
if (options === undefined) {
options = {};
} else if (typeof options === 'string') {
options = { encoding: options };
} else if (typeof options === 'object') {
// this is a valid options object
} else {
throw new TypeError('"options" argument must be a string or an object');
}

// check the start option
if (options.start === undefined) {
options.start = 0;
} else if (typeof options.start !== 'number') {
throw new TypeError('start (' + options.start + ') must be a Number');
}

// check the end option
if (options.end === undefined) {
options.end = Infinity;
} else if (typeof options.end !== 'number') {
throw new TypeError('end (' + options.end + ') must be a Number');
}

// check logical values for start and end
if (options.start > options.end) {
throw new Error('start (' + options.start + ') must be <= end (' + options.end + ')');
} else if (options.start < 0) {
throw new Error('start (' + options.start + ') must be >= zero');
}

return options;

}
Loading