From 80155d398c6cb498cbb9601ed7db1600c7c2ea30 Mon Sep 17 00:00:00 2001 From: Florian MARGAINE Date: Wed, 24 Feb 2016 22:17:44 +0100 Subject: [PATCH] fs: add the fs.mkdtemp() function. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This uses libuv's mkdtemp function to provide a way to create a temporary folder, using a prefix as the path. The prefix is appended six random characters. The callback function will receive the name of the folder that was created. Usage example: fs.mkdtemp('/tmp/foo-', function(err, folder) { console.log(folder); // Prints: /tmp/foo-Tedi42 }); The fs.mkdtempSync version is also provided. Usage example: console.log(fs.mkdtemp('/tmp/foo-')); // Prints: tmp/foo-Tedi42 This pull request also includes the relevant documentation changes and tests. PR-URL: https://github.com/nodejs/node/pull/5333 Reviewed-By: Sakthipriyan Vairamani Reviewed-By: Trevor Norris Reviewed-By: Saúl Ibarra Corretgé Reviewed-By: James M Snell --- doc/api/fs.markdown | 24 ++++++++++++++++++++++++ lib/fs.js | 21 +++++++++++++++++++++ src/node_file.cc | 26 ++++++++++++++++++++++++++ test/parallel/test-fs-mkdtemp.js | 27 +++++++++++++++++++++++++++ 4 files changed, 98 insertions(+) create mode 100644 test/parallel/test-fs-mkdtemp.js diff --git a/doc/api/fs.markdown b/doc/api/fs.markdown index e8ec2f9563098b..61430cf6573b1c 100644 --- a/doc/api/fs.markdown +++ b/doc/api/fs.markdown @@ -532,6 +532,30 @@ to the completion callback. `mode` defaults to `0o777`. Synchronous mkdir(2). Returns `undefined`. +## fs.mkdtemp(prefix, callback) + +Creates a unique temporary directory. + +Generates six random characters to be appended behind a required +`prefix` to create a unique temporary directory. + +The created folder path is passed as a string to the callback's second +parameter. + +Example: + +```js +fs.mkdtemp('/tmp/foo-', (err, folder) => { + console.log(folder); + // Prints: /tmp/foo-itXde2 +}); +``` + +## fs.mkdtempSync(template) + +The synchronous version of [`fs.mkdtemp()`][]. Returns the created +folder path. + ## fs.open(path, flags[, mode], callback) Asynchronous file open. See open(2). `flags` can be: diff --git a/lib/fs.js b/lib/fs.js index 42a5098cd578ce..5a3176158f6718 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -2078,3 +2078,24 @@ SyncWriteStream.prototype.destroy = function() { }; SyncWriteStream.prototype.destroySoon = SyncWriteStream.prototype.destroy; + +fs.mkdtemp = function(prefix, callback) { + if (typeof callback !== 'function') { + throw new TypeError('"callback" argument must be a function'); + } + + if (!nullCheck(prefix, callback)) { + return; + } + + var req = new FSReqWrap(); + req.oncomplete = callback; + + binding.mkdtemp(prefix + 'XXXXXX', req); +}; + +fs.mkdtempSync = function(prefix) { + nullCheck(prefix); + + return binding.mkdtemp(prefix + 'XXXXXX'); +}; diff --git a/src/node_file.cc b/src/node_file.cc index 5c1b39864c5508..3fbec265663910 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -200,6 +200,11 @@ static void After(uv_fs_t *req) { static_cast(req->ptr)); break; + case UV_FS_MKDTEMP: + argv[1] = String::NewFromUtf8(env->isolate(), + static_cast(req->path)); + break; + case UV_FS_READLINK: argv[1] = String::NewFromUtf8(env->isolate(), static_cast(req->ptr)); @@ -1291,6 +1296,25 @@ static void FUTimes(const FunctionCallbackInfo& args) { } } +static void Mkdtemp(const FunctionCallbackInfo& args) { + Environment* env = Environment::GetCurrent(args); + + if (args.Length() < 1) + return TYPE_ERROR("template is required"); + if (!args[0]->IsString()) + return TYPE_ERROR("template must be a string"); + + node::Utf8Value tmpl(env->isolate(), args[0]); + + if (args[1]->IsObject()) { + ASYNC_CALL(mkdtemp, args[1], *tmpl); + } else { + SYNC_CALL(mkdtemp, *tmpl, *tmpl); + args.GetReturnValue().Set(String::NewFromUtf8(env->isolate(), + SYNC_REQ.path)); + } +} + void FSInitialize(const FunctionCallbackInfo& args) { Local stats_constructor = args[0].As(); CHECK(stats_constructor->IsFunction()); @@ -1344,6 +1368,8 @@ void InitFs(Local target, env->SetMethod(target, "utimes", UTimes); env->SetMethod(target, "futimes", FUTimes); + env->SetMethod(target, "mkdtemp", Mkdtemp); + StatWatcher::Initialize(env, target); // Create FunctionTemplate for FSReqWrap diff --git a/test/parallel/test-fs-mkdtemp.js b/test/parallel/test-fs-mkdtemp.js new file mode 100644 index 00000000000000..ad8a6cb46e02eb --- /dev/null +++ b/test/parallel/test-fs-mkdtemp.js @@ -0,0 +1,27 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const Buffer = require('buffer').Buffer; + +common.refreshTmpDir(); + +const tmpFolder = fs.mkdtempSync(path.join(common.tmpDir, 'foo.')); + +assert(path.basename(tmpFolder).length === 'foo.XXXXXX'.length); +assert(common.fileExists(tmpFolder)); + +const utf8 = fs.mkdtempSync(path.join(common.tmpDir, '\u0222abc.')); +assert.equal(Buffer.byteLength(path.basename(utf8)), + Buffer.byteLength('\u0222abc.XXXXXX')); +assert(common.fileExists(utf8)); + +fs.mkdtemp( + path.join(common.tmpDir, 'bar.'), + common.mustCall(function(err, folder) { + assert.ifError(err); + assert(common.fileExists(folder)); + }) +);