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

EBADF from fs.promises.readFile() and O_CREAT #288

Closed
CynicalBusiness opened this issue Jan 5, 2020 · 5 comments · Fixed by #289
Closed

EBADF from fs.promises.readFile() and O_CREAT #288

CynicalBusiness opened this issue Jan 5, 2020 · 5 comments · Fixed by #289

Comments

@CynicalBusiness
Copy link

Had a bunch of tests failing with EBADF even after updating to ^4.10 (as mentioned in #245), finally managed to track it down.

When trying to mock fs.promises.readFile() with the fs.constants.O_CREAT flag, an EBADF is raised if the file does not previously exist. If the file is created manually or the flag is not used, it reads normally or raises ENOENT as it should.

Reproducable:

import mock from "mock-fs";
import { promises, constants } from "fs";

mock({ "foo.txt": "bar" });
const str1 = await promises.readFile("foo.txt"); // works fine
const str2 = await promises.readFile("bar.txt"); // ENOENT, as expected
const str3 = await promises.readFile("foo.txt", constants.O_CREAT | cosntants.O_RDONLY); // works
const str4 = await promises.readFile("bar.txt", constants.O_CREAT | constants.O_RDONLY); // EBADF, unexpected behavior.

node: 12.6.0
mock-fs: 4.10.4

@3cp
Copy link
Collaborator

3cp commented Jan 5, 2020

O_RDONLY is 0, so constants.O_CREAT | constants.O_RDONLY is literally just constants.O_CREAT.

mockfs got confused about the read mode.

FileDescriptor.prototype.isRead = function() {
// special treatment because O_RDONLY is 0
return (
this._flags === constants.O_RDONLY ||
this._flags === (constants.O_RDONLY | constants.O_SYNC) ||
(this._flags & constants.O_RDWR) === constants.O_RDWR
);
};

The implementation should be fixed and simplified as

FileDescriptor.prototype.isRead = function() {
  return (this._flags & constants.O_WRONLY) !== constants.O_WRONLY;
};

If I understand open(2) man page correctly. http://man7.org/linux/man-pages/man2/open.2.html

The argument flags must include one of the following access modes:
O_RDONLY, O_WRONLY, or O_RDWR.  These request opening the file read-
only, write-only, or read/write, respectively.

O_RDONLY is 0, O_WRONLY is 1, O_RDWR is 2, the flag uses the 2 bits for 3 states: 0 (0b00) / 1 (0b01) / 2 (0b10), the 2 bits will never be 3 (0b11).

@tschaub does this fix make sense?

@3cp
Copy link
Collaborator

3cp commented Jan 5, 2020

Note this is not a new bug on promises api, it happens on fs.readFile and fs.readFileSync too.

3cp added a commit that referenced this issue Jan 5, 2020
The argument flags must include one of the following access modes:
O_RDONLY, O_WRONLY, or O_RDWR.  These request opening the file read-
only, write-only, or read/write, respectively.
O_RDONLY is 0, O_WRONLY is 1, O_RDWR is 2, the flags use 2 bits for 3 states: 0 (0b00) / 1 (0b01) / 2 (0b10), the 2 bits will never be 3 (0b11).

closes #288
3cp added a commit that referenced this issue Jan 5, 2020
The argument flags must include one of the following access modes:
O_RDONLY, O_WRONLY, or O_RDWR.  These request opening the file read-
only, write-only, or read/write, respectively.
O_RDONLY is 0, O_WRONLY is 1, O_RDWR is 2, the flags use 2 bits for 3 states: 0 (0b00) / 1 (0b01) / 2 (0b10), the 2 bits will never be 3 (0b11).

closes #288
@tschaub
Copy link
Owner

tschaub commented Jan 7, 2020

Thanks for the report @CynicalBusiness and for the proposed fix @3cp.

Let's come up with a valid test case first. The examples given in the description above (promises.readFile("foo.txt", constants.O_CREAT | cosntants.O_RDONLY)) aren't valid because they provide a number as the second argument to readFile instead of an object or a string. This would throw TypeError [ERR_INVALID_ARG_TYPE]: The "options" argument must be one of type string or Object. Received type number.

@3cp
Copy link
Collaborator

3cp commented Jan 7, 2020

Based on what I tested, although nodejs doc did not say, the readFile/sync/promise APIs support numeric option as if it's {flag: number}.

3cp added a commit that referenced this issue Apr 20, 2020
The argument flags must include one of the following access modes:
O_RDONLY, O_WRONLY, or O_RDWR.  These request opening the file read-
only, write-only, or read/write, respectively.
O_RDONLY is 0, O_WRONLY is 1, O_RDWR is 2, the flags use 2 bits for 3 states: 0 (0b00) / 1 (0b01) / 2 (0b10), the 2 bits will never be 3 (0b11).

closes #288
@tschaub
Copy link
Owner

tschaub commented Apr 24, 2020

Fix published in mock-fs@4.12.0. Thanks, @3cp.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants