Commit 69f05370 authored by Victor's avatar Victor Committed by GitHub

feat: stat method and readdir with stats (#66)

parent f5a5c710
......@@ -108,12 +108,33 @@ smb2Client.mkdir('path\\to\\the\\directory', function(err) {
});
```
> `smb2Client.readdir ( path, callback )`
> `smb2Client.readdir ( path, [options], callback )`
- `path` String
- `options` Object
- `encoding` String | Null default = null
- `callback` Function
Asynchronous `readdir(3)`: reads the contents of a directory.
The result is an array of the names of the files in the directory excluding `'.'` and `'..'`.
If you want the response to include stats, you need to pass the `stats: true`. Response will be an Array of this form:
```
[
{
name: String,
birthtime: Date,
mtime: Date,
atime: Date,
ctime: Date,
isDirectory(): boolean
},
...
]
```
Example:
```javascript
......@@ -123,6 +144,25 @@ smb2Client.readdir('Windows\\System32', function(err, files) {
});
```
> `smb2Client.stat ( path, callback )`
- `path` String
- `callback` Function
Asynchronous `stat`: query stats of a directory or file.
Response will be an object with the following structure :
```
{
birthtime: Date,
mtime: Date,
atime: Date,
ctime: Date,
isDirectory(): boolean
}
```
> `smb2Client.readFile ( path, [options], callback )`
- `path` String
......
var SMB2Forge = require('../tools/smb2-forge');
var SMB2Request = SMB2Forge.request;
var stats = require('../tools/stats.js');
/*
* readdir
* =======
......@@ -14,33 +14,44 @@ var SMB2Request = SMB2Forge.request;
* - close the directory
*
*/
module.exports = function readdir(path, cb) {
module.exports = function readdir(path, options, cb) {
var connection = this;
if (typeof options === 'function') {
cb = options;
options = {};
}
var mapping = options.stats
? function(v) {
var obj = stats(v);
obj.name = v.Filename;
return obj;
}
: function(v) {
return v.Filename;
};
function queryDirectory(filesBatch, file, connection, cb) {
SMB2Request('query_directory', file, connection, function(err, files) {
if (err) {
if(err.code === 'STATUS_NO_MORE_FILES') {
if (err.code === 'STATUS_NO_MORE_FILES') {
cb(null, filesBatch);
} else {
cb(err);
}
} else {
filesBatch.push(
files
.map(function(v) {
// get the filename only
return v.Filename;
})
.filter(function(v) {
// remove '.' and '..' values
return v !== '.' && v !== '..';
})
.filter(function(v) {
// remove '.' and '..' values
return v.Filename !== '.' && v.Filename !== '..';
})
.map(mapping)
);
queryDirectory(filesBatch, file, connection, cb);
}
});
}
});
}
function openDirectory(path, connection, cb) {
......@@ -54,19 +65,18 @@ module.exports = function readdir(path, cb) {
}
function closeDirectory(file, connection, cb) {
// SMB2 query directory
// SMB2 query directory
SMB2Request('close', file, connection, function(err, res) {
if (err) {
if(err.code !== 'STATUS_FILE_CLOSED') {
if (err.code !== 'STATUS_FILE_CLOSED') {
cb(err);
}
}
// SMB2 close directory
cb(null, res);
cb(null, res);
});
}
openDirectory(path, connection, function(err, file) {
var totalFiles = [];
var filesBatch = [];
......@@ -83,10 +93,10 @@ module.exports = function readdir(path, cb) {
} else {
totalFiles = [].concat(...filesBatch);
cb(null, totalFiles);
}
}
});
}
})
}
});
}
});
};
var stats = require('../tools/stats.js');
var request = require('../tools/smb2-forge').request;
module.exports = function stat(path, cb) {
var connection = this;
request('open', { path: path }, connection, function(err, file) {
if (err != null) {
return cb(err);
}
request('close', file, connection, function() {
cb(null, stats(file));
});
});
};
......@@ -113,6 +113,8 @@ proto.getSize = autoPromise(
SMB2Connection.requireConnect(require('./api/getSize'))
);
proto.stat = autoPromise(SMB2Connection.requireConnect(require('./api/stat')));
proto.open = autoPromise(SMB2Connection.requireConnect(require('./api/open')));
proto.read = autoPromise(require('./api/read'), function(bytesRead, buffer) {
return {
......
......@@ -30,4 +30,11 @@ module.exports = {
READ_CONTROL: 0x00020000,
SYNCHRONIZE: 0x00100000,
WRITE_DAC: 0x00040000,
/**
* https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/ca28ec38-f155-4768-81d6-4bfeb8586fc9
* FileAttributes values
*/
DIRECTORY: 16,
};
// https://stackoverflow.com/questions/6161776/convert-windows-filetime-to-second-in-unix-linux
var winTicks = 10000000;
var uEpoch = 11644473600;
module.exports = function convert(time) {
var unixTime = time / winTicks - uEpoch;
return new Date(unixTime * 1000);
};
var convert = require('./convert_time');
var BigInt = require('./bigint');
var DIRECTORY = require('../structures/constants').DIRECTORY;
module.exports = function stat(file) {
return {
birthtime: convert(BigInt.fromBuffer(file.CreationTime).toNumber()),
atime: convert(BigInt.fromBuffer(file.LastAccessTime).toNumber()),
mtime: convert(BigInt.fromBuffer(file.LastWriteTime).toNumber()),
ctime: convert(BigInt.fromBuffer(file.ChangeTime).toNumber()),
size: BigInt.fromBuffer(file.EndofFile).toNumber(),
isDirectory: function() {
var attr = file.FileAttributes;
if (typeof attr !== 'number') {
attr = BigInt.fromBuffer(attr).toNumber();
}
return attr === DIRECTORY;
},
};
};
......@@ -14,6 +14,7 @@ const Smb2 = require('../');
const dir = 'smb2-tests-' + Date.now();
const file = dir + '\\file.txt';
const file2 = dir + '\\file2.txt';
const data = Buffer.from(
Array.from({ length: 1024 }, function() {
return Math.floor(Math.random() * 256);
......@@ -32,15 +33,96 @@ const tests = {
t.same(result, data);
});
},
statFile: function(client) {
return client.stat(file).then(function(result) {
t.same(result.size, data.length);
t.same(result.isDirectory(), false);
});
},
getSize: function(client) {
return client.getSize(file).then(function(result) {
t.same(result, data.length);
});
},
exists: function(client) {
return client
.exists(file)
.then(function(result) {
t.same(result, true);
return client.exists(file2);
})
.then(function(result) {
t.same(result, false);
});
},
statDir: function(client) {
return client.stat(dir).then(function(result) {
t.same(result.size, 0);
t.same(result.isDirectory(), true);
});
},
readdir: function(client) {
return client.readdir(dir).then(function(result) {
t.same(result.length, 1);
});
},
readdirWithStats: function(client) {
return client.readdir(dir, { stats: true }).then(function(result) {
t.same(result.length, 1);
t.same(result[0].filename, 'file.txt');
t.same(result[0].size, data.length);
t.same(result[0].isDirectory(), false);
});
},
rename: function(client) {
return client
.rename(file, file2)
.then(function() {
return client.readdir(dir);
})
.then(function(result) {
t.same(result.length, 1);
t.same(result[0], 'file2.txt');
})
.then(function() {
return client.rename(file2, file);
})
.then(function() {
return client.readdir(dir);
})
.then(function(result) {
t.same(result.length, 1);
t.same(result[0], 'file.txt');
});
},
truncate: function(client) {
return client
.truncate(file, 10)
.then(function() {
return client.stat(file);
})
.then(function(result) {
t.same(result.size, 10);
});
},
unlink: function(client) {
return client.unlink(file);
},
'open new file and write': asyncFn(function*(client) {
const fd = yield client.open(file, 'w');
var fd = yield client.open(file, 'w');
try {
t.same(yield client.write(fd, data, undefined, undefined, 0), {
bytesWritten: data.length,
buffer: data,
});
} finally {
yield client.close(fd);
}
fd = yield client.open(file, 'r');
try {
try {
t.same(yield client.write(fd, data, undefined, undefined, 0), {
bytesWritten: data.length,
t.same(yield client.read(fd, Buffer.alloc(1024), 0, 1024, 0), {
bytesRead: data.length,
buffer: data,
});
} finally {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment