Skip to content

Commit 0544741

Browse files
authored
src: report libuv error when openAsBlob cannot stat
`FdEntry::Create()` returned nullptr for any failed `uv_fs_stat()`, discarding the status, and `BlobFromFilePath()` turned that into `ERR_INVALID_ARG_VALUE: Unable to open file as blob`. A missing file is not a malformed argument, and the resulting `TypeError` carried no `errno`, `syscall`, or `path`, so ENOENT could not be told apart from any other reason the path was unusable. Thread the libuv status out of `CreateFdEntry()` and throw a `UVException` instead, so `fs.openAsBlob()` reports the same error `fs.stat()` does for the same path. Fixes: #65514 Signed-off-by: Paul Bouchon <mail@bitpshr.net> PR-URL: #65517 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent e5a1ce2 commit 0544741

4 files changed

Lines changed: 33 additions & 7 deletions

File tree

src/dataqueue/queue.cc

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -868,13 +868,19 @@ class FdEntry final : public EntryImpl {
868868
// the race
869869
// condition described in the comment above.
870870
public:
871-
static std::unique_ptr<FdEntry> Create(Environment* env, Local<Value> path) {
871+
static std::unique_ptr<FdEntry> Create(Environment* env,
872+
Local<Value> path,
873+
int* status) {
872874
// We're only going to create the FdEntry if the file exists.
873875
uv_fs_t req = uv_fs_t();
874876
auto cleanup = OnScopeLeave([&] { uv_fs_req_cleanup(&req); });
875877

876878
auto buf = std::make_shared<BufferValue>(env->isolate(), path);
877-
if (uv_fs_stat(nullptr, &req, buf->out(), nullptr) < 0) return nullptr;
879+
int err = uv_fs_stat(nullptr, &req, buf->out(), nullptr);
880+
if (err < 0) {
881+
if (status != nullptr) *status = err;
882+
return nullptr;
883+
}
878884

879885
return std::make_unique<FdEntry>(
880886
env, std::move(buf), req.statbuf, 0, req.statbuf.st_size);
@@ -1183,8 +1189,9 @@ std::unique_ptr<DataQueue::Entry> DataQueue::CreateDataQueueEntry(
11831189
}
11841190

11851191
std::unique_ptr<DataQueue::Entry> DataQueue::CreateFdEntry(Environment* env,
1186-
Local<Value> path) {
1187-
return FdEntry::Create(env, path);
1192+
Local<Value> path,
1193+
int* status) {
1194+
return FdEntry::Create(env, path, status);
11881195
}
11891196

11901197
void DataQueue::Initialize(Environment* env, v8::Local<v8::Object> target) {

src/dataqueue/queue.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,11 @@ class DataQueue : public MemoryRetainer {
226226
static std::unique_ptr<Entry> CreateDataQueueEntry(
227227
std::shared_ptr<DataQueue> data_queue);
228228

229+
// Returns nullptr if the file cannot be stat'd. When `status` is given, it
230+
// is set to the libuv error code so callers can report why.
229231
static std::unique_ptr<Entry> CreateFdEntry(Environment* env,
230-
v8::Local<v8::Value> path);
232+
v8::Local<v8::Value> path,
233+
int* status = nullptr);
231234

232235
// Creates a Reader for the given queue. If the queue is idempotent,
233236
// any number of readers can be created, all of which are guaranteed

src/node_blob.cc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,12 @@ void BlobFromFilePath(const FunctionCallbackInfo<Value>& args) {
110110
ToNamespacedPath(env, &path);
111111
THROW_IF_INSUFFICIENT_PERMISSIONS(
112112
env, permission::PermissionScope::kFileSystemRead, path.ToStringView());
113-
auto entry = DataQueue::CreateFdEntry(env, args[0]);
113+
int status = 0;
114+
auto entry = DataQueue::CreateFdEntry(env, args[0], &status);
114115
if (entry == nullptr) {
115-
return THROW_ERR_INVALID_ARG_VALUE(env, "Unable to open file as blob");
116+
// The file could not be stat'd. Report the libuv error so callers can tell
117+
// ENOENT apart from any other reason the path could not be used.
118+
return env->ThrowUVException(status, "stat", nullptr, *path);
116119
}
117120

118121
std::vector<std::unique_ptr<DataQueue::Entry>> entries;

test/parallel/test-blob-file-backed.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,19 @@ writeFileSync(testfile5, '');
129129
await unlink(testfile5);
130130
})().then(common.mustCall());
131131

132+
(async () => {
133+
// A path that cannot be stat'd reports the underlying libuv error rather
134+
// than a generic argument error, so ENOENT can be told apart from any other
135+
// reason the file could not be used.
136+
// Refs: https://github.com/nodejs/node/issues/65514
137+
const missing = tmpdir.resolve('does-not-exist.txt');
138+
await assert.rejects(async () => openAsBlob(missing), {
139+
code: 'ENOENT',
140+
syscall: 'stat',
141+
path: missing,
142+
});
143+
})().then(common.mustCall());
144+
132145
(async () => {
133146
// We currently do not allow File-backed blobs to be cloned or transferred
134147
// across worker threads. This is largely because the underlying FdEntry

0 commit comments

Comments
 (0)