8

Given two absolute or relative paths, A and B, I want to find out whether B is "inside of" the directory A—not just in the directory itself, but potentially in a subdirectory. I'd like to do this without a potentially huge number of fs.readdir calls.

For instance, if A is / and B is /foo/bar/baz, it should be pretty obvious that B is within A; the recursive readdir approach would be extremely inefficient.

One obvious idea is to convert both paths to absolute form, then check if the string form of B's absolute path starts with the string form of A's. However, there are two problems:

  1. How do you convert the relative paths to absolute paths?
  2. What about symbolic links and such?

I'll accept answers that make calls to Linux utilities (other than rm -rf... which technically could be used to solve the problem) or third-party Node libraries.

Trevor Burnham
  • 76,828
  • 33
  • 160
  • 196

2 Answers2

10
var fs = require('fs');

var a = fs.realpathSync('/home/mak/www'); // /var/www
var b = fs.realpathSync('/var/www/test/index.html');

var b_in_a = b.indexOf(a) == 0;

var a_is_dir = fs.statSync(a).isDirectory();

fs.*Sync also have asynchronous versions, see fs module.

fs.realpathSync and fs.statSyncwill throw if the path does not exist.

mak
  • 13,267
  • 5
  • 41
  • 47
  • This may be working code but I don't really consider this an answer because it's not explained at all. – Slbox Feb 12 '21 at 00:47
  • 2
    This can give the wrong answer for a file with the same prefix as the folder. If a = `/var/www` and b = `/var/wwwtwo/index.html`, then it returns true. – jgawrych Mar 17 '22 at 00:24
4

I suggest this:

const path = require('path')

function isWithin(outer, inner) {
    const rel = path.relative(outer, inner);
    return !rel.startsWith('../') && rel !== '..';
}

It uses path.relative to compute the path of inner relative to outer. If it's not contained, the first component of the resulting path will be .., so that's what we check for.

DS.
  • 22,632
  • 6
  • 47
  • 54