question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

bug(fs): fs.exists/fs.existsSync throws when querying subpath of existing file.

See original GitHub issue

Describe the bug A clear and concise description of what the bug is.

fs.exists/fs.existsSync throws when querying subpath of existing file.

To Reproduce Steps to reproduce the behavior:

  1. In any directory,
  2. touch a.txt
  3. deno --unstable
  4. > import * as fs from "https://deno.land/std@0.106.0/fs/mod.ts";
  5. > await fs.exists("a.txt/b");
  6. Error is thrown. See error

Expected behavior A clear and concise description of what you expected to happen.

Return false.

Screenshots If applicable, add screenshots to help explain your problem.

image

Desktop (please complete the following information):

  • OS: Ubuntu 20.04 with WSL on Windows11
  • Version
deno 1.13.2 (release, x86_64-unknown-linux-gnu)
v8 9.3.345.11
typescript 4.3.5

Additional context Add any other context about the problem here.

Other implementations:

  • Python3 os.path.exists: Return false.
  • Node.js v14 require("fs").exists: Resolves false.
  • Ruby 2.7 File.exist?: Return false.

Simple alternative implementation suggest:

const exists = async (filePath: string): Promise<boolean> => {
  try {
    await Deno.lstat(filePath);
    return true;
  } catch (_e: unknown) {
    return false;
  }
};

fs/exists_test.ts doesn’t include any throws/rejects test.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:29 (13 by maintainers)

github_iconTop GitHub Comments

4reactions
lambdalisuecommented, Apr 11, 2022

I would like to know: Can you confirm exists being a pitfall for people that want to check if a file exists, who end up checking for its existence before calling lstat to verify it’s a file and not a folder? How would you approach this problem?

Of course, people write problematic code from time to time, myself included, but I don’t understand why exists is the only victim. For example, I don’t think anyone would deprecate delay() even if the following code cause TOCTOU issues.

const info = await Deno.lstat("hello.txt");

// Something...
await delay(1000);

if (info.isFile) {
  // Maybe `hello.txt` become a directory at this point.
  await Deno.run(["rm", "-rf", "hello.txt"]).output();
}

Do you also think the deprecation was wrong in node.js for the same reasons you are clarifying?

I think fs.exists() in node.js is deprecated because of inconsistencies in the callback design, not because of TOCTOU issues, according to the node.js documentation

The parameters for this callback are not consistent with other Node.js callbacks. Normally, the first parameter to a Node.js callback is an err parameter, optionally followed by other parameters. The fs.exists() callback has only one boolean parameter. This is one reason fs.access() is recommended instead of fs.exists().

In addition, the above link also stated the following

Using fs.exists() to check for the existence of a file before calling fs.open(), fs.readFile() or fs.writeFile() is not recommended. Doing so introduces a race condition, since other processes may change the file’s state between the two calls. Instead, user code should open/read/write the file directly and handle the error raised if the file does not exist.

I think this paragraph is the correct way to alert users that improper use of the function can cause TOCTOU issues. In my opinion, there should be a distinction between documentation and deprecation.

If it comes back, it should be named access instead of exists, because it will return false, if there are no permissions to read the file/folder.

I totally agree with this.

Just saying exists and lstat is the same won’t get this anywhere, because it is not.

If lstat is used to check whether a file or directory exists, then I think it is the same.

Btw, your final MERGE_HEAD example would require you to use lstat, because in that case it is relevant if you have a file or folder, again. Unless you love the wild west of course.

Since git itself does not care whether it is a file or a directory, in order to mimic it’s behavior, it must not care wheter it is a file or a directory.

(I know it’s a little picky, who would touch the .git contents, right?)

People like me would touch the contents in .git directory to make git related tools.

3reactions
lambdalisuecommented, Apr 7, 2022

Hi.

I feel that “making fs.exists() deprecated” is too aggressive. You all talking about file operations after fs.exists() and some race condition caused by that but sometime there is a situation that just want to know if a file or directory exist. In that case, I anyway need to use fs.exists() and deal deprecation warning messages always.

For example, I need to check presence of a .git directory to roughly found a git working tree root directory. I will spawn git rev-parse or whatever after that so the first detection can be rough. This rough step is required because spawning a process on Windows is cost a bit. In this case, I don’t perform any further file operations on Deno itself so “race condition” is not good reason to me for making fs.exists() deprecated (because git process would handle that situation outside of Deno).

So please re-consider this deprecation again.

P.S.

I totally agree that documenting “file operations after fs.exists() may cause some race condition” on documentation of fs.exists() but I believe that documentation and deprecation is different and should be distinctive.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Issue - GitHub
After noticing in the node docs that fs.exists and fs. ... But I only want to allow the file to successfully open if...
Read more >
NodeJS - fs.exists() throws error but file is there?
Try adding __dirname infront of you path: var pathname = url.parse(req.url).pathname; fs.exists(__dirname + pathname, function (exist) ...
Read more >
File system | Node.js v19.3.0 Documentation
An error will be thrown if this method is called more than once or is called after the FileHandle is closed or closing....
Read more >
xfun source: R/paths.R - Rdrr.io
Find file paths that exist #' #' This is a shorthand of \code{x[file.exists(x)]}, and optionally returns the #' first existing file path.
Read more >
Releases.md | deno@v1.9.1
File (#10130)- feat(runtime): stabilize Deno.fstat and Deno. ... fs: ensure exists file/dir must be the same type or it will throw error (deno_std#294)### ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found