Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fs: undeprecate lchown() #21498

Merged
merged 1 commit into from
Jun 27, 2018
Merged
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
16 changes: 0 additions & 16 deletions doc/api/deprecations.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,20 +351,6 @@ Type: Documentation-only

The [`fs.lchmodSync(path, mode)`][] API is deprecated.

<a id="DEP0037"></a>
### DEP0037: fs.lchown(path, uid, gid, callback)

Type: Documentation-only

The [`fs.lchown(path, uid, gid, callback)`][] API is deprecated.

<a id="DEP0038"></a>
### DEP0038: fs.lchownSync(path, uid, gid)

Type: Documentation-only

The [`fs.lchownSync(path, uid, gid)`][] API is deprecated.

<a id="DEP0039"></a>
### DEP0039: require.extensions

Expand Down Expand Up @@ -1049,8 +1035,6 @@ The option `produceCachedData` has been deprecated. Use
[`fs.exists(path, callback)`]: fs.html#fs_fs_exists_path_callback
[`fs.lchmod(path, mode, callback)`]: fs.html#fs_fs_lchmod_path_mode_callback
[`fs.lchmodSync(path, mode)`]: fs.html#fs_fs_lchmodsync_path_mode
[`fs.lchown(path, uid, gid, callback)`]: fs.html#fs_fs_lchown_path_uid_gid_callback
[`fs.lchownSync(path, uid, gid)`]: fs.html#fs_fs_lchownsync_path_uid_gid
[`fs.read()`]: fs.html#fs_fs_read_fd_buffer_offset_length_position_callback
[`fs.readSync()`]: fs.html#fs_fs_readsync_fd_buffer_offset_length_position
[`fs.stat()`]: fs.html#fs_fs_stat_path_callback
Expand Down
5 changes: 0 additions & 5 deletions doc/api/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,6 @@ which simply wrap a syscall,
like [`fs.open()`][], will document that. The docs link to the corresponding man
pages (short for manual pages) which describe how the syscalls work.

Some syscalls, like lchown(2), are BSD-specific. That means, for
example, that [`fs.lchown()`][] only works on macOS and other BSD-derived
systems, and is not available on Linux.

Most Unix syscalls have Windows equivalents, but behavior may differ on Windows
relative to Linux and macOS. For an example of the subtle ways in which it's
sometimes impossible to replace Unix syscall semantics on Windows, see [Node
Expand All @@ -95,6 +91,5 @@ issue 4760](https://github.com/nodejs/node/issues/4760).
[`'warning'`]: process.html#process_event_warning
[`stderr`]: process.html#process_process_stderr
[`fs.open()`]: fs.html#fs_fs_open_path_flags_mode_callback
[`fs.lchown()`]: fs.html#fs_fs_lchown_path_uid_gid_callback
[submit an issue]: https://github.com/nodejs/node/issues/new
[the contributing guide]: https://github.com/nodejs/node/blob/master/CONTRIBUTING.md
17 changes: 13 additions & 4 deletions doc/api/fs.md
Original file line number Diff line number Diff line change
Expand Up @@ -1908,8 +1908,10 @@ Synchronous lchmod(2). Returns `undefined`.

## fs.lchown(path, uid, gid, callback)
<!-- YAML
deprecated: v0.4.7
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/21498
description: This API is no longer deprecated.
- version: v10.0.0
pr-url: https://github.com/nodejs/node/pull/12562
description: The `callback` parameter is no longer optional. Not passing
Expand All @@ -1931,7 +1933,10 @@ to the completion callback.

## fs.lchownSync(path, uid, gid)
<!-- YAML
deprecated: v0.4.7
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/21498
description: This API is no longer deprecated.
-->

* `path` {string|Buffer|URL}
Expand Down Expand Up @@ -3904,7 +3909,11 @@ no arguments upon success. This method is only implemented on macOS.

### fsPromises.lchown(path, uid, gid)
<!-- YAML
deprecated: v10.0.0
added: v10.0.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/21498
description: This API is no longer deprecated.
-->

* `path` {string|Buffer|URL}
Expand All @@ -3913,7 +3922,7 @@ deprecated: v10.0.0
* Returns: {Promise}

Changes the ownership on a symbolic link then resolves the `Promise` with
no arguments upon success. This method is only implemented on macOS.
no arguments upon success.

### fsPromises.link(existingPath, newPath)
<!-- YAML
Expand Down
41 changes: 17 additions & 24 deletions lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -997,31 +997,24 @@ function chmodSync(path, mode) {
}

function lchown(path, uid, gid, callback) {
callback = maybeCallback(callback);
fs.open(path, O_WRONLY | O_SYMLINK, function(err, fd) {
if (err) {
callback(err);
return;
}
// Prefer to return the chown error, if one occurs,
// but still try to close, and report closing errors if they occur.
fs.fchown(fd, uid, gid, function(err) {
fs.close(fd, function(err2) {
callback(err || err2);
});
});
});
callback = makeCallback(callback);
path = getPathFromURL(path);
validatePath(path);
validateUint32(uid, 'uid');
validateUint32(gid, 'gid');
const req = new FSReqWrap();
req.oncomplete = callback;
binding.lchown(pathModule.toNamespacedPath(path), uid, gid, req);
}

function lchownSync(path, uid, gid) {
const fd = fs.openSync(path, O_WRONLY | O_SYMLINK);
let ret;
try {
ret = fs.fchownSync(fd, uid, gid);
} finally {
fs.closeSync(fd);
}
return ret;
path = getPathFromURL(path);
validatePath(path);
validateUint32(uid, 'uid');
validateUint32(gid, 'gid');
const ctx = { path };
binding.lchown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx);
handleErrorFromBinding(ctx);
}

function fchown(fd, uid, gid, callback) {
Expand Down Expand Up @@ -1753,8 +1746,8 @@ module.exports = fs = {
ftruncateSync,
futimes,
futimesSync,
lchown: constants.O_SYMLINK !== undefined ? lchown : undefined,
lchownSync: constants.O_SYMLINK !== undefined ? lchownSync : undefined,
lchown,
lchownSync,
lchmod: constants.O_SYMLINK !== undefined ? lchmod : undefined,
lchmodSync: constants.O_SYMLINK !== undefined ? lchmodSync : undefined,
link,
Expand Down
11 changes: 6 additions & 5 deletions lib/internal/fs/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,11 +377,12 @@ async function lchmod(path, mode) {
}

async function lchown(path, uid, gid) {
if (O_SYMLINK === undefined)
throw new ERR_METHOD_NOT_IMPLEMENTED();

const fd = await open(path, O_WRONLY | O_SYMLINK);
return fchown(fd, uid, gid).finally(fd.close.bind(fd));
path = getPathFromURL(path);
validatePath(path);
validateUint32(uid, 'uid');
validateUint32(gid, 'gid');
return binding.lchown(pathModule.toNamespacedPath(path),
uid, gid, kUsePromises);
}

async function fchown(handle, uid, gid) {
Expand Down
32 changes: 31 additions & 1 deletion src/node_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1771,6 +1771,36 @@ static void FChown(const FunctionCallbackInfo<Value>& args) {
}


static void LChown(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

const int argc = args.Length();
CHECK_GE(argc, 3);

BufferValue path(env->isolate(), args[0]);

This comment was marked as off-topic.

CHECK_NOT_NULL(*path);

CHECK(args[1]->IsUint32());
const uv_uid_t uid = static_cast<uv_uid_t>(args[1].As<Uint32>()->Value());

CHECK(args[2]->IsUint32());
const uv_gid_t gid = static_cast<uv_gid_t>(args[2].As<Uint32>()->Value());

FSReqBase* req_wrap_async = GetReqWrap(env, args[3]);
if (req_wrap_async != nullptr) { // lchown(path, uid, gid, req)
AsyncCall(env, req_wrap_async, args, "lchown", UTF8, AfterNoArgs,
uv_fs_lchown, *path, uid, gid);
} else { // lchown(path, uid, gid, undefined, ctx)
CHECK_EQ(argc, 5);
FSReqWrapSync req_wrap_sync;
FS_SYNC_TRACE_BEGIN(lchown);
SyncCall(env, args[4], &req_wrap_sync, "lchown",
uv_fs_lchown, *path, uid, gid);
FS_SYNC_TRACE_END(lchown);
}
}


static void UTimes(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

Expand Down Expand Up @@ -1904,7 +1934,7 @@ void Initialize(Local<Object> target,

env->SetMethod(target, "chown", Chown);
env->SetMethod(target, "fchown", FChown);
// env->SetMethod(target, "lchown", LChown);
env->SetMethod(target, "lchown", LChown);

env->SetMethod(target, "utimes", UTimes);
env->SetMethod(target, "futimes", FUTimes);
Expand Down
67 changes: 67 additions & 0 deletions test/parallel/test-fs-lchown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
'use strict';

const common = require('../common');
Copy link
Member

Choose a reason for hiding this comment

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

If there are other tests you think we should add to lchown, let me know and we'll make good-first-issues out of them

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We're pretty light on chown(), fchown(), and lchown() happy path tests in general. My guess is because of platform support and getting the uid and gid parts right on the CI.

const tmpdir = require('../common/tmpdir');
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const { promises } = fs;

common.crashOnUnhandledRejection();

// Validate the path argument.
[false, 1, {}, [], null, undefined].forEach((i) => {
const err = { type: TypeError, code: 'ERR_INVALID_ARG_TYPE' };

common.expectsError(() => fs.lchown(i, 1, 1, common.mustNotCall()), err);
common.expectsError(() => fs.lchownSync(i, 1, 1), err);
promises.lchown(false, 1, 1)
.then(common.mustNotCall())
.catch(common.expectsError(err));
});

// Validate the uid and gid arguments.
[false, 'test', {}, [], null, undefined].forEach((i) => {
const err = { type: TypeError, code: 'ERR_INVALID_ARG_TYPE' };

common.expectsError(
() => fs.lchown('not_a_file_that_exists', i, 1, common.mustNotCall()),
err
);
common.expectsError(
() => fs.lchown('not_a_file_that_exists', 1, i, common.mustNotCall()),
err
);
common.expectsError(() => fs.lchownSync('not_a_file_that_exists', i, 1), err);
common.expectsError(() => fs.lchownSync('not_a_file_that_exists', 1, i), err);

promises.lchown('not_a_file_that_exists', i, 1)
.then(common.mustNotCall())
.catch(common.expectsError(err));

promises.lchown('not_a_file_that_exists', 1, i)
.then(common.mustNotCall())
.catch(common.expectsError(err));
});

// Validate the callback argument.
[false, 1, 'test', {}, [], null, undefined].forEach((i) => {
common.expectsError(() => fs.lchown('not_a_file_that_exists', 1, 1, i), {
type: TypeError,
code: 'ERR_INVALID_CALLBACK'
});
});

if (!common.isWindows) {
const testFile = path.join(tmpdir.path, path.basename(__filename));
const uid = process.geteuid();
const gid = process.getegid();

tmpdir.refresh();
fs.copyFileSync(__filename, testFile);
fs.lchownSync(testFile, uid, gid);
fs.lchown(testFile, uid, gid, common.mustCall(async (err) => {
assert.ifError(err);
await promises.lchown(testFile, uid, gid);
}));
}
3 changes: 3 additions & 0 deletions test/parallel/test-fs-null-bytes.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ check(fs.chmod, fs.chmodSync, 'foo\u0000bar', '0644');
check(fs.chown, fs.chownSync, 'foo\u0000bar', 12, 34);
check(fs.copyFile, fs.copyFileSync, 'foo\u0000bar', 'abc');
check(fs.copyFile, fs.copyFileSync, 'abc', 'foo\u0000bar');
check(fs.lchown, fs.lchownSync, 'foo\u0000bar', 12, 34);
check(fs.link, fs.linkSync, 'foo\u0000bar', 'foobar');
check(fs.link, fs.linkSync, 'foobar', 'foo\u0000bar');
check(fs.lstat, fs.lstatSync, 'foo\u0000bar');
Expand Down Expand Up @@ -92,6 +93,7 @@ check(fs.chmod, fs.chmodSync, fileUrl, '0644');
check(fs.chown, fs.chownSync, fileUrl, 12, 34);
check(fs.copyFile, fs.copyFileSync, fileUrl, 'abc');
check(fs.copyFile, fs.copyFileSync, 'abc', fileUrl);
check(fs.lchown, fs.lchownSync, fileUrl, 12, 34);
check(fs.link, fs.linkSync, fileUrl, 'foobar');
check(fs.link, fs.linkSync, 'foobar', fileUrl);
check(fs.lstat, fs.lstatSync, fileUrl);
Expand Down Expand Up @@ -122,6 +124,7 @@ check(fs.chmod, fs.chmodSync, fileUrl2, '0644');
check(fs.chown, fs.chownSync, fileUrl2, 12, 34);
check(fs.copyFile, fs.copyFileSync, fileUrl2, 'abc');
check(fs.copyFile, fs.copyFileSync, 'abc', fileUrl2);
check(fs.lchown, fs.lchownSync, fileUrl2, 12, 34);
check(fs.link, fs.linkSync, fileUrl2, 'foobar');
check(fs.link, fs.linkSync, 'foobar', fileUrl2);
check(fs.lstat, fs.lstatSync, fileUrl2);
Expand Down
3 changes: 3 additions & 0 deletions test/parallel/test-trace-events-fs-sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ tests['fs.sync.futimes'] = 'fs.writeFileSync("fs.txt", "123", "utf8");' +
'const fd = fs.openSync("fs.txt", "r+");' +
'fs.futimesSync(fd,1,1);' +
'fs.unlinkSync("fs.txt")';
tests['fs.sync.lchown'] = 'fs.writeFileSync("fs.txt", "123", "utf8");' +
'fs.lchownSync("fs.txt",' + uid + ',' + gid + ');' +
'fs.unlinkSync("fs.txt")';
tests['fs.sync.link'] = 'fs.writeFileSync("fs.txt", "123", "utf8");' +
'fs.linkSync("fs.txt", "linkx");' +
'fs.unlinkSync("linkx");' +
Expand Down