From 396cbb7b64546785aeedac404d7c28a2ccf9d3ee Mon Sep 17 00:00:00 2001 From: Bas Stottelaar Date: Thu, 13 Nov 2014 02:32:23 +0100 Subject: [PATCH] Prevent infinite recursion while scanning library. --- headphones/helpers.py | 37 +++++++++++++++++++++++++++++++++++++ headphones/librarysync.py | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/headphones/helpers.py b/headphones/helpers.py index 8c1be7e7..7823bd59 100644 --- a/headphones/helpers.py +++ b/headphones/helpers.py @@ -604,6 +604,43 @@ def smartMove(src, dest, delete=True): except Exception as e: logger.warn('Error moving file %s: %s', filename.decode(headphones.SYS_ENCODING, 'replace'), e) +def walk_directory(basedir, followlinks=True): + """ + Enhanced version of 'os.walk' where symlink directores are traversed, but + with care. In case a folder is already processed, don't traverse it again. + """ + + import logger + + # Add the base path, because symlinks poiting to the basedir should not be + # traversed again. + traversed = [os.path.abspath(basedir)] + + def _inner(root, directories, files): + for directory in directories: + path = os.path.join(root, directory) + + if followlinks and os.path.islink(path): + real_path = os.path.abspath(os.readlink(path)) + + if real_path in traversed: + logger.debug("Skipping '%s' since it is a symlink to "\ + "'%s', which is already visited.", path, real_path) + else: + traversed.append(real_path) + + for args in os.walk(real_path): + for result in _inner(*args): + yield result + + # Pass on actual result + yield root, directories, files + + # Start traversing + for args in os.walk(basedir): + for result in _inner(*args): + yield result + ######################### #Sab renaming functions # ######################### diff --git a/headphones/librarysync.py b/headphones/librarysync.py index 0b49f563..3644dbc5 100644 --- a/headphones/librarysync.py +++ b/headphones/librarysync.py @@ -78,7 +78,7 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal latest_subdirectory = [] - for r, d, f in os.walk(dir, followlinks=True): + for r, d, f in helpers.walk_directory(dir): # Need to abuse slicing to get a copy of the list, doing it directly # will skip the element after a deleted one using a list comprehension # will not work correctly for nested subdirectories (os.walk keeps its