file_manager: remove restriction that file paths must be in HOME

Instead make sure that the supplied path is not the system root and check that moonraker has the appropriate privileges to access the directory.

Also track visited directories when retreiving a full file list to prevent infinite recursion.

Signed-off-by: Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
Arksine 2020-12-31 18:25:05 -05:00
parent 77c483ea0f
commit 1814a11a8d
1 changed files with 27 additions and 9 deletions

View File

@ -15,7 +15,6 @@ from tornado.locks import Event
VALID_GCODE_EXTS = ['.gcode', '.g', '.gco'] VALID_GCODE_EXTS = ['.gcode', '.g', '.gco']
FULL_ACCESS_ROOTS = ["gcodes", "config"] FULL_ACCESS_ROOTS = ["gcodes", "config"]
ETC_DIR = "/etc/moonraker"
METADATA_SCRIPT = os.path.normpath(os.path.join( METADATA_SCRIPT = os.path.normpath(os.path.join(
os.path.dirname(__file__), "../../scripts/extract_metadata.py")) os.path.dirname(__file__), "../../scripts/extract_metadata.py"))
@ -84,14 +83,21 @@ class FileManager:
def register_directory(self, root, path): def register_directory(self, root, path):
if path is None: if path is None:
return False return False
home = os.path.expanduser('~')
path = os.path.normpath(os.path.expanduser(path)) path = os.path.normpath(os.path.expanduser(path))
if not os.path.isdir(path) or path == home or \ if os.path.islink(path):
not (path.startswith(home) or path.startswith(ETC_DIR)): path = os.path.realpath(path)
if not os.path.isdir(path) or path == "/":
logging.info( logging.info(
f"\nSupplied path ({path}) for ({root}) not valid. Please\n" f"\nSupplied path ({path}) for ({root}) a valid. Make sure\n"
"check that the path exists and is a subfolder in the HOME\n" "that the path exists and is not the file system root.")
"directory. Note that the path may not BE the home directory.") return False
permissions = os.R_OK
if root in FULL_ACCESS_ROOTS:
permissions |= os.W_OK
if not os.access(path, permissions):
logging.info(
f"\nMoonraker does not have permission to access path "
f"({path}) for ({root}).")
return False return False
if path != self.file_paths.get(root, ""): if path != self.file_paths.get(root, ""):
self.file_paths[root] = path self.file_paths[root] = path
@ -448,12 +454,24 @@ class FileManager:
logging.info(msg) logging.info(msg)
raise self.server.error(msg) raise self.server.error(msg)
logging.info(f"Updating File List <{root}>...") logging.info(f"Updating File List <{root}>...")
for root_path, dirs, files in os.walk(path, followlinks=True): st = os.stat(path)
visited_dirs = {(st.st_dev, st.st_ino)}
for dir_path, dir_names, files in os.walk(path, followlinks=True):
scan_dirs = []
# Filter out directories that have already been visted. This
# prevents infinite recrusion "followlinks" is set to True
for dname in dir_names:
st = os.stat(os.path.join(dir_path, dname))
key = (st.st_dev, st.st_ino)
if key not in visited_dirs:
visited_dirs.add(key)
scan_dirs.append(dname)
dir_names[:] = scan_dirs
for name in files: for name in files:
ext = os.path.splitext(name)[-1].lower() ext = os.path.splitext(name)[-1].lower()
if root == 'gcodes' and ext not in VALID_GCODE_EXTS: if root == 'gcodes' and ext not in VALID_GCODE_EXTS:
continue continue
full_path = os.path.join(root_path, name) full_path = os.path.join(dir_path, name)
fname = full_path[len(path) + 1:] fname = full_path[len(path) + 1:]
finfo = self._get_path_info(full_path) finfo = self._get_path_info(full_path)
filelist[fname] = finfo filelist[fname] = finfo