Skip to content

Commit 7ff50f9

Browse files
committed
fs: undeprecate lchown()
uv_fs_lchown() exists, as of libuv 1.21.0. fs.lchown() can now be undeprecated. This commit also adds tests, as there were none. PR-URL: nodejs#21498 Fixes: nodejs#19868 Reviewed-By: Wyatt Preul <[email protected]>
1 parent 4750ce2 commit 7ff50f9

9 files changed

+140
-55
lines changed

doc/api/deprecations.md

-16
Original file line numberDiff line numberDiff line change
@@ -351,20 +351,6 @@ Type: Documentation-only
351351

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

354-
<a id="DEP0037"></a>
355-
### DEP0037: fs.lchown(path, uid, gid, callback)
356-
357-
Type: Documentation-only
358-
359-
The [`fs.lchown(path, uid, gid, callback)`][] API is deprecated.
360-
361-
<a id="DEP0038"></a>
362-
### DEP0038: fs.lchownSync(path, uid, gid)
363-
364-
Type: Documentation-only
365-
366-
The [`fs.lchownSync(path, uid, gid)`][] API is deprecated.
367-
368354
<a id="DEP0039"></a>
369355
### DEP0039: require.extensions
370356

@@ -1049,8 +1035,6 @@ The option `produceCachedData` has been deprecated. Use
10491035
[`fs.exists(path, callback)`]: fs.html#fs_fs_exists_path_callback
10501036
[`fs.lchmod(path, mode, callback)`]: fs.html#fs_fs_lchmod_path_mode_callback
10511037
[`fs.lchmodSync(path, mode)`]: fs.html#fs_fs_lchmodsync_path_mode
1052-
[`fs.lchown(path, uid, gid, callback)`]: fs.html#fs_fs_lchown_path_uid_gid_callback
1053-
[`fs.lchownSync(path, uid, gid)`]: fs.html#fs_fs_lchownsync_path_uid_gid
10541038
[`fs.read()`]: fs.html#fs_fs_read_fd_buffer_offset_length_position_callback
10551039
[`fs.readSync()`]: fs.html#fs_fs_readsync_fd_buffer_offset_length_position
10561040
[`fs.stat()`]: fs.html#fs_fs_stat_path_callback

doc/api/documentation.md

-5
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,6 @@ which simply wrap a syscall,
8383
like [`fs.open()`][], will document that. The docs link to the corresponding man
8484
pages (short for manual pages) which describe how the syscalls work.
8585

86-
Some syscalls, like lchown(2), are BSD-specific. That means, for
87-
example, that [`fs.lchown()`][] only works on macOS and other BSD-derived
88-
systems, and is not available on Linux.
89-
9086
Most Unix syscalls have Windows equivalents, but behavior may differ on Windows
9187
relative to Linux and macOS. For an example of the subtle ways in which it's
9288
sometimes impossible to replace Unix syscall semantics on Windows, see [Node
@@ -95,6 +91,5 @@ issue 4760](https://github.com/nodejs/node/issues/4760).
9591
[`'warning'`]: process.html#process_event_warning
9692
[`stderr`]: process.html#process_process_stderr
9793
[`fs.open()`]: fs.html#fs_fs_open_path_flags_mode_callback
98-
[`fs.lchown()`]: fs.html#fs_fs_lchown_path_uid_gid_callback
9994
[submit an issue]: https://github.com/nodejs/node/issues/new
10095
[the contributing guide]: https://github.com/nodejs/node/blob/master/CONTRIBUTING.md

doc/api/fs.md

+13-4
Original file line numberDiff line numberDiff line change
@@ -1908,8 +1908,10 @@ Synchronous lchmod(2). Returns `undefined`.
19081908

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

19321934
## fs.lchownSync(path, uid, gid)
19331935
<!-- YAML
1934-
deprecated: v0.4.7
1936+
changes:
1937+
- version: REPLACEME
1938+
pr-url: https://github.com/nodejs/node/pull/21498
1939+
description: This API is no longer deprecated.
19351940
-->
19361941

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

39053910
### fsPromises.lchown(path, uid, gid)
39063911
<!-- YAML
3907-
deprecated: v10.0.0
3912+
added: v10.0.0
3913+
changes:
3914+
- version: REPLACEME
3915+
pr-url: https://github.com/nodejs/node/pull/21498
3916+
description: This API is no longer deprecated.
39083917
-->
39093918

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

39153924
Changes the ownership on a symbolic link then resolves the `Promise` with
3916-
no arguments upon success. This method is only implemented on macOS.
3925+
no arguments upon success.
39173926

39183927
### fsPromises.link(existingPath, newPath)
39193928
<!-- YAML

lib/fs.js

+17-24
Original file line numberDiff line numberDiff line change
@@ -997,31 +997,24 @@ function chmodSync(path, mode) {
997997
}
998998

999999
function lchown(path, uid, gid, callback) {
1000-
callback = maybeCallback(callback);
1001-
fs.open(path, O_WRONLY | O_SYMLINK, function(err, fd) {
1002-
if (err) {
1003-
callback(err);
1004-
return;
1005-
}
1006-
// Prefer to return the chown error, if one occurs,
1007-
// but still try to close, and report closing errors if they occur.
1008-
fs.fchown(fd, uid, gid, function(err) {
1009-
fs.close(fd, function(err2) {
1010-
callback(err || err2);
1011-
});
1012-
});
1013-
});
1000+
callback = makeCallback(callback);
1001+
path = getPathFromURL(path);
1002+
validatePath(path);
1003+
validateUint32(uid, 'uid');
1004+
validateUint32(gid, 'gid');
1005+
const req = new FSReqWrap();
1006+
req.oncomplete = callback;
1007+
binding.lchown(pathModule.toNamespacedPath(path), uid, gid, req);
10141008
}
10151009

10161010
function lchownSync(path, uid, gid) {
1017-
const fd = fs.openSync(path, O_WRONLY | O_SYMLINK);
1018-
let ret;
1019-
try {
1020-
ret = fs.fchownSync(fd, uid, gid);
1021-
} finally {
1022-
fs.closeSync(fd);
1023-
}
1024-
return ret;
1011+
path = getPathFromURL(path);
1012+
validatePath(path);
1013+
validateUint32(uid, 'uid');
1014+
validateUint32(gid, 'gid');
1015+
const ctx = { path };
1016+
binding.lchown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx);
1017+
handleErrorFromBinding(ctx);
10251018
}
10261019

10271020
function fchown(fd, uid, gid, callback) {
@@ -1753,8 +1746,8 @@ module.exports = fs = {
17531746
ftruncateSync,
17541747
futimes,
17551748
futimesSync,
1756-
lchown: constants.O_SYMLINK !== undefined ? lchown : undefined,
1757-
lchownSync: constants.O_SYMLINK !== undefined ? lchownSync : undefined,
1749+
lchown,
1750+
lchownSync,
17581751
lchmod: constants.O_SYMLINK !== undefined ? lchmod : undefined,
17591752
lchmodSync: constants.O_SYMLINK !== undefined ? lchmodSync : undefined,
17601753
link,

lib/internal/fs/promises.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -377,11 +377,12 @@ async function lchmod(path, mode) {
377377
}
378378

379379
async function lchown(path, uid, gid) {
380-
if (O_SYMLINK === undefined)
381-
throw new ERR_METHOD_NOT_IMPLEMENTED();
382-
383-
const fd = await open(path, O_WRONLY | O_SYMLINK);
384-
return fchown(fd, uid, gid).finally(fd.close.bind(fd));
380+
path = getPathFromURL(path);
381+
validatePath(path);
382+
validateUint32(uid, 'uid');
383+
validateUint32(gid, 'gid');
384+
return binding.lchown(pathModule.toNamespacedPath(path),
385+
uid, gid, kUsePromises);
385386
}
386387

387388
async function fchown(handle, uid, gid) {

src/node_file.cc

+31-1
Original file line numberDiff line numberDiff line change
@@ -1771,6 +1771,36 @@ static void FChown(const FunctionCallbackInfo<Value>& args) {
17711771
}
17721772

17731773

1774+
static void LChown(const FunctionCallbackInfo<Value>& args) {
1775+
Environment* env = Environment::GetCurrent(args);
1776+
1777+
const int argc = args.Length();
1778+
CHECK_GE(argc, 3);
1779+
1780+
BufferValue path(env->isolate(), args[0]);
1781+
CHECK_NOT_NULL(*path);
1782+
1783+
CHECK(args[1]->IsUint32());
1784+
const uv_uid_t uid = static_cast<uv_uid_t>(args[1].As<Uint32>()->Value());
1785+
1786+
CHECK(args[2]->IsUint32());
1787+
const uv_gid_t gid = static_cast<uv_gid_t>(args[2].As<Uint32>()->Value());
1788+
1789+
FSReqBase* req_wrap_async = GetReqWrap(env, args[3]);
1790+
if (req_wrap_async != nullptr) { // lchown(path, uid, gid, req)
1791+
AsyncCall(env, req_wrap_async, args, "lchown", UTF8, AfterNoArgs,
1792+
uv_fs_lchown, *path, uid, gid);
1793+
} else { // lchown(path, uid, gid, undefined, ctx)
1794+
CHECK_EQ(argc, 5);
1795+
FSReqWrapSync req_wrap_sync;
1796+
FS_SYNC_TRACE_BEGIN(lchown);
1797+
SyncCall(env, args[4], &req_wrap_sync, "lchown",
1798+
uv_fs_lchown, *path, uid, gid);
1799+
FS_SYNC_TRACE_END(lchown);
1800+
}
1801+
}
1802+
1803+
17741804
static void UTimes(const FunctionCallbackInfo<Value>& args) {
17751805
Environment* env = Environment::GetCurrent(args);
17761806

@@ -1904,7 +1934,7 @@ void Initialize(Local<Object> target,
19041934

19051935
env->SetMethod(target, "chown", Chown);
19061936
env->SetMethod(target, "fchown", FChown);
1907-
// env->SetMethod(target, "lchown", LChown);
1937+
env->SetMethod(target, "lchown", LChown);
19081938

19091939
env->SetMethod(target, "utimes", UTimes);
19101940
env->SetMethod(target, "futimes", FUTimes);

test/parallel/test-fs-lchown.js

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const tmpdir = require('../common/tmpdir');
5+
const assert = require('assert');
6+
const fs = require('fs');
7+
const path = require('path');
8+
const { promises } = fs;
9+
10+
common.crashOnUnhandledRejection();
11+
12+
// Validate the path argument.
13+
[false, 1, {}, [], null, undefined].forEach((i) => {
14+
const err = { type: TypeError, code: 'ERR_INVALID_ARG_TYPE' };
15+
16+
common.expectsError(() => fs.lchown(i, 1, 1, common.mustNotCall()), err);
17+
common.expectsError(() => fs.lchownSync(i, 1, 1), err);
18+
promises.lchown(false, 1, 1)
19+
.then(common.mustNotCall())
20+
.catch(common.expectsError(err));
21+
});
22+
23+
// Validate the uid and gid arguments.
24+
[false, 'test', {}, [], null, undefined].forEach((i) => {
25+
const err = { type: TypeError, code: 'ERR_INVALID_ARG_TYPE' };
26+
27+
common.expectsError(
28+
() => fs.lchown('not_a_file_that_exists', i, 1, common.mustNotCall()),
29+
err
30+
);
31+
common.expectsError(
32+
() => fs.lchown('not_a_file_that_exists', 1, i, common.mustNotCall()),
33+
err
34+
);
35+
common.expectsError(() => fs.lchownSync('not_a_file_that_exists', i, 1), err);
36+
common.expectsError(() => fs.lchownSync('not_a_file_that_exists', 1, i), err);
37+
38+
promises.lchown('not_a_file_that_exists', i, 1)
39+
.then(common.mustNotCall())
40+
.catch(common.expectsError(err));
41+
42+
promises.lchown('not_a_file_that_exists', 1, i)
43+
.then(common.mustNotCall())
44+
.catch(common.expectsError(err));
45+
});
46+
47+
// Validate the callback argument.
48+
[false, 1, 'test', {}, [], null, undefined].forEach((i) => {
49+
common.expectsError(() => fs.lchown('not_a_file_that_exists', 1, 1, i), {
50+
type: TypeError,
51+
code: 'ERR_INVALID_CALLBACK'
52+
});
53+
});
54+
55+
if (!common.isWindows) {
56+
const testFile = path.join(tmpdir.path, path.basename(__filename));
57+
const uid = process.geteuid();
58+
const gid = process.getegid();
59+
60+
tmpdir.refresh();
61+
fs.copyFileSync(__filename, testFile);
62+
fs.lchownSync(testFile, uid, gid);
63+
fs.lchown(testFile, uid, gid, common.mustCall(async (err) => {
64+
assert.ifError(err);
65+
await promises.lchown(testFile, uid, gid);
66+
}));
67+
}

test/parallel/test-fs-null-bytes.js

+3
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ check(fs.chmod, fs.chmodSync, 'foo\u0000bar', '0644');
5959
check(fs.chown, fs.chownSync, 'foo\u0000bar', 12, 34);
6060
check(fs.copyFile, fs.copyFileSync, 'foo\u0000bar', 'abc');
6161
check(fs.copyFile, fs.copyFileSync, 'abc', 'foo\u0000bar');
62+
check(fs.lchown, fs.lchownSync, 'foo\u0000bar', 12, 34);
6263
check(fs.link, fs.linkSync, 'foo\u0000bar', 'foobar');
6364
check(fs.link, fs.linkSync, 'foobar', 'foo\u0000bar');
6465
check(fs.lstat, fs.lstatSync, 'foo\u0000bar');
@@ -92,6 +93,7 @@ check(fs.chmod, fs.chmodSync, fileUrl, '0644');
9293
check(fs.chown, fs.chownSync, fileUrl, 12, 34);
9394
check(fs.copyFile, fs.copyFileSync, fileUrl, 'abc');
9495
check(fs.copyFile, fs.copyFileSync, 'abc', fileUrl);
96+
check(fs.lchown, fs.lchownSync, fileUrl, 12, 34);
9597
check(fs.link, fs.linkSync, fileUrl, 'foobar');
9698
check(fs.link, fs.linkSync, 'foobar', fileUrl);
9799
check(fs.lstat, fs.lstatSync, fileUrl);
@@ -122,6 +124,7 @@ check(fs.chmod, fs.chmodSync, fileUrl2, '0644');
122124
check(fs.chown, fs.chownSync, fileUrl2, 12, 34);
123125
check(fs.copyFile, fs.copyFileSync, fileUrl2, 'abc');
124126
check(fs.copyFile, fs.copyFileSync, 'abc', fileUrl2);
127+
check(fs.lchown, fs.lchownSync, fileUrl2, 12, 34);
125128
check(fs.link, fs.linkSync, fileUrl2, 'foobar');
126129
check(fs.link, fs.linkSync, 'foobar', fileUrl2);
127130
check(fs.lstat, fs.lstatSync, fileUrl2);

test/parallel/test-trace-events-fs-sync.js

+3
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ tests['fs.sync.futimes'] = 'fs.writeFileSync("fs.txt", "123", "utf8");' +
6060
'const fd = fs.openSync("fs.txt", "r+");' +
6161
'fs.futimesSync(fd,1,1);' +
6262
'fs.unlinkSync("fs.txt")';
63+
tests['fs.sync.lchown'] = 'fs.writeFileSync("fs.txt", "123", "utf8");' +
64+
'fs.lchownSync("fs.txt",' + uid + ',' + gid + ');' +
65+
'fs.unlinkSync("fs.txt")';
6366
tests['fs.sync.link'] = 'fs.writeFileSync("fs.txt", "123", "utf8");' +
6467
'fs.linkSync("fs.txt", "linkx");' +
6568
'fs.unlinkSync("linkx");' +

0 commit comments

Comments
 (0)