Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

fs: Add file descriptor support to *File() funcs #8522

Closed
wants to merge 1 commit into from
Closed

fs: Add file descriptor support to *File() funcs #8522

wants to merge 1 commit into from

Conversation

jwueller
Copy link

@jwueller jwueller commented Oct 6, 2014

These changes affect the following functions and their synchronous counterparts:

  • fs.readFile()
  • fs.writeFile()
  • fs.appendFile()

If the first parameter is a uint >= 0, it is treated as a file descriptor. In all other cases, the original implementation is used to ensure backwards compatibility. File descriptor ownership is never taken from the user.

The documentation was adjusted to reflect these API changes. A note was added to make the user aware of file descriptor ownership and the conditions under which a file descriptor can be used by each of these functions.

Tests were extended to test for file descriptor parameters under the conditions noted in the relevant documentation.

@jwueller
Copy link
Author

jwueller commented Oct 6, 2014

as requested by @rlidwka and @chrisdickinson in #8471


If the `encoding` option is specified then this function returns a
string. Otherwise it returns a buffer.

Choose a reason for hiding this comment

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

Why remove these two lines?

Copy link
Author

Choose a reason for hiding this comment

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

A similar note is part of the fs.readFile(file[, options], callback) documentation, which is referenced by fs.readFileSync(file[, options]). No other synchronous method has any additional notes aside from referencing the asynchronous version. I tried to make the documentation more uniform, despite the note being accurate. Less duplication, less potential for errors.

Would you prefer me putting it back in?

Choose a reason for hiding this comment

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

Go ahead and leave it in. Most other functions *Sync() functions can refer the user to a man page. This is a special case sense the return value is not always the same (i.e. Buffer or String).

@jwueller
Copy link
Author

jwueller commented Oct 9, 2014

Thanks for the review, @trevnorris! I made the proposed changes and updated the branch. Let me know if there is anything else you would like changed.

@jwueller
Copy link
Author

@trevnorris I am not sure whether you got notified (sorry to disturb if you are just busy). Is there anything else I can do to improve this?

@trevnorris
Copy link

@jwueller No worries about bugging me. It's helpful to be pinged. :)

So far everything is looking good.

/cc @indutny @tjfontaine Either of you have a problem with this change?

@jwueller
Copy link
Author

jwueller commented Nov 3, 2014

Since @trevnorris said it's helpful, I'll shamelessly ping again ;) @indutny @tjfontaine

@@ -113,6 +113,10 @@ function nullCheck(path, callback) {
return true;
}

function isFd(path) {
return (path >>> 0) === path && path > 0;
Copy link
Member

Choose a reason for hiding this comment

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

Maybe just typeof(path) === 'number' && fd >= 0? Zero is a valid file descriptor.

Choose a reason for hiding this comment

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

That allows path to be NaN. The point of the uint32 conversion check is that detects that, and is less prone to deoptimization than typeof.

Copy link
Member

Choose a reason for hiding this comment

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

Okay, but the point about zero being a valid file descriptor still stands. :-)

Choose a reason for hiding this comment

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

True.

@jwueller Please change it to (path >>> 0) === path && path >= 0.

Copy link
Author

Choose a reason for hiding this comment

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

Yes, I already did that and the other suggestions locally. It will get pushed as soon as I am back at my workstation (probably Monday).

@jwueller
Copy link
Author

@bnoordhuis @trevnorris There you go; finally got around to pushing my changes.

} else {
fs.close(fd, function() {
callback(er);
});

Choose a reason for hiding this comment

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

Potential problem is that the fs.close() itself could error. Not sure what the best solution would be in that case, but seems like a potential fd leak if we ignore any errors coming from the fs.close() cb.

@bnoordhuis Thoughts on how to deal w/ this?

Copy link
Author

Choose a reason for hiding this comment

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

@trevnorris Can one even recover from a file failing to close? It appears that the state of a file descriptor is unspecified if it fails:

If close() is interrupted by a signal that is to be caught, it shall return -1 with errno set to [EINTR] and the state of fildes is unspecified. If an I/O error occurred while reading from or writing to the file system during close(), it may return -1 with errno set to [EIO]; if this error is returned, the state of fildes is unspecified.

Choose a reason for hiding this comment

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

Assuming fs.close() calls close(2), it cannot fail to release the fd. The error returns from close are purely advisory. Linux documents only EBADF (which isn't a leak, obviously), EIO (which is a delayed error from a prev i/o operation, interesting, but not a leak, and only NFS, I think), and EINTR, of which the following note applies:

Note that the return value should only  be
used  for  diagnostics.   In  particular close() should not be retried after an EINTR since this may
cause a reused descriptor from another thread to be closed.
  • man 2 close

Copy link
Author

Choose a reason for hiding this comment

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

@sam-github According to the fs documentation, we are dealing with close(2). I haven't looked at the actual code, though.

Choose a reason for hiding this comment

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

Hm, looks like the open group might've had to align to solaris: https://docs.oracle.com/cd/E23824_01/html/821-1463/close-2.html#scrolltoc which allows the fd to be left in an unspecified condition. Wtf? Anyhow, no recover possible, indeed.

Choose a reason for hiding this comment

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

Wow. WTF are you supposed to do w/ a fd in an "unspecified condition"?

Either way, should we report this to the user, and if so then how?

Copy link
Author

Choose a reason for hiding this comment

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

Reporting it would probably not be very helpful, since we are already in an error state at this point. The first failure is likely more interesting to the user, as he might be able to do something about it. There is nothing a user can do about close(2) failing. The second error may even be a subsequent failure of the first. I propose we follow the usual paradigm and report the "topmost" error to the user.

Copy link
Member

Choose a reason for hiding this comment

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

I second @jwueller's suggestion. It's what libuv does.

Choose a reason for hiding this comment

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

Sounds good.

@trevnorris
Copy link

@jwueller Looking good. Few more comments.

@jwueller
Copy link
Author

@trevnorris branch updated, one remaining question

@@ -527,7 +535,10 @@ Example:
console.log('The "data to append" was appended to file!');
});

## fs.appendFileSync(filename, data, [options])
Note that any specified file descriptor needs to be openend for appending.

Choose a reason for hiding this comment

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

Typo: openend should be opened.

@misterdjules
Copy link

@jwueller @trevnorris What do we need to do to move this PR forward?

@trevnorris
Copy link

@misterdjules probably a rebase. Last I looked it was fine. Once it's rebased I'll take another glance and sign off.

These changes affect the following functions and their synchronous
counterparts:

 * fs.readFile()
 * fs.writeFile()
 * fs.appendFile()

If the first parameter is a uint >= 0, it is treated as a file
descriptor. In all other cases, the original implementation is used to
ensure backwards compatibility. File descriptor ownership is never
taken from the user.

The documentation was adjusted to reflect these API changes. A note was
added to make the user aware of file descriptor ownership and the
conditions under which a file descriptor can be used by each of these
functions.

Tests were extended to test for file descriptor parameters under the
conditions noted in the relevant documentation.
@jwueller
Copy link
Author

@trevnorris Oh my, I just noticed that this PR is still open and pushed a rebase. Do we need anything else?

@jwueller
Copy link
Author

jwueller commented Jun 6, 2015

@trevnorris @misterdjules Should I add a test for fs.writeFileSync(fd, ...) in this PR, or do you want that one separately?

@jasnell
Copy link
Member

jasnell commented Aug 13, 2015

@jwueller ... well, thank you for being so patient on this one. At this point, given the API change, this is something that would need to be closed here then re-targeted at master with a new PR on http://github.com/nodejs/node.

@jasnell jasnell closed this Aug 27, 2015
@jwueller
Copy link
Author

@jasnell I'll do that, thank you for the update.

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

Successfully merging this pull request may close these issues.

7 participants