Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions fs_storage/rooted_dir_file_system.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Copyright 2023 ACSONE SA/NV (https://www.acsone.eu).
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

import os
import posixpath
from pathlib import PurePosixPath

from fsspec.implementations.dirfs import DirFileSystem
from fsspec.implementations.local import make_path_posix
from fsspec.registry import register_implementation


Expand All @@ -20,18 +20,22 @@ class RootedDirFileSystem(DirFileSystem):
"""

def _join(self, path):
path = super()._join(path)
joined = super()._join(path)
# Ensure that the path is a subpath of the root path by resolving
# any relative paths.
# Since the path separator is not always the same on all systems,
# we need to normalize the path separator.
path_posix = os.path.normpath(make_path_posix(path))
root_posix = os.path.normpath(make_path_posix(self.path))
if not path_posix.startswith(root_posix):

root = PurePosixPath(self.path).as_posix()
rnorm = posixpath.normpath(root)

jnorm = posixpath.normpath(joined or ".")
Copy link
Member

Choose a reason for hiding this comment

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

I'm not convinced by this. Is it guaranteed that self.path will parse correctly as a PurePosixPath? Is it guaranteed that joined will be handled correctly by posixpath.normpath (Looking at the DirFileSystem implementation, it could be a dict or a list, and there is no guarantee the separator will be /).

Copy link
Contributor

Choose a reason for hiding this comment

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

indeed, ... this code lack of tests...
Considering the remarks mentioned here, the solution should take into account the separator to detect the kind of path (Posix or Windows)..... @sbidoul I may be wrong, but regardless of the type of the path argument, the result of the call to super will always be a string... We could add a check before the added logic to make sure, but in my opinion, it's useless.

def _join(self, path):
    joined = super()._join(path)
    if not isinstance(joined, str):
        return joined
    # Uses the rigth path abstraction according to the
    # path separator
    if self.sep == '/':
        PathClass = PurePosixPath
        normpath = posixpath.normpath
    elif self.sep == '\\':
        PathClass = PureWindowsPath
        normpath = ntpath.normpath
    else:
        raise ValueError(f"Unknown path separator: {self.sep!r}")

    root = PathClass(self.path)
    joined_path = PathClass(joined or '.')

    rnorm = normpath(str(root))
    jnorm = normpath(str(joined_path))

    if not (jnorm == rnorm or jnorm.startswith(rnorm + self.sep)):
        raise PermissionError(
            f"Path {path!r} resolves to {jnorm!r} which is outside "
            f"the root path {rnorm!r}"
        )

    return joined

Copy link
Member

Choose a reason for hiding this comment

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

Maybe. But since we see that normpath is involving os.getcwd, does it make sense at all to use it on paths that may have nothing to do with the local file system?

Copy link
Contributor

Choose a reason for hiding this comment

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

os.getcwd was called by the make_path_posix from fsspec.implementations.local AFAIK, it's not the case with the methods from python core.


if not (jnorm == rnorm or jnorm.startswith(rnorm + "/")):
raise PermissionError(
"Path %s is not a subpath of the root path %s" % (path, self.path)
f"Path {path!r} resolves to {jnorm!r} which is outside "
f"the root path {rnorm!r}"
)
return path

return joined


register_implementation("rooted_dir", RootedDirFileSystem)