From cde45dc7c19bf0c14336a084ed188c160de4251e Mon Sep 17 00:00:00 2001 From: Philipp Wolfer Date: Tue, 8 Mar 2022 08:12:13 +0100 Subject: [PATCH] PICARD-2076: path node length limit on Windows is 255 --- picard/util/__init__.py | 13 +++++++------ picard/util/filenaming.py | 12 ++++++++---- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/picard/util/__init__.py b/picard/util/__init__.py index 57752c392..ba863cdfb 100644 --- a/picard/util/__init__.py +++ b/picard/util/__init__.py @@ -75,13 +75,14 @@ from picard.const.sys import ( if IS_WIN: import winreg -# Windows path length constraints: -# the entire path's length +# Windows path length constraints +# See https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation +# the entire path's length (260 - 1 null character) WIN_MAX_FILEPATH_LEN = 259 -# the entire parent directory path's length, *excluding* the final separator -WIN_MAX_DIRPATH_LEN = 247 -# a single node's length (this seems to be the case for older NTFS) -WIN_MAX_NODE_LEN = 226 +# the entire parent directory path's length must leave room for a 8.3 filename +WIN_MAX_DIRPATH_LEN = WIN_MAX_FILEPATH_LEN - 12 +# a single node's (directory or file) length +WIN_MAX_NODE_LEN = 255 # Prefix for long paths in Windows API WIN_LONGPATH_PREFIX = '\\\\?\\' diff --git a/picard/util/filenaming.py b/picard/util/filenaming.py index a35ff4d4b..b149b7eb1 100644 --- a/picard/util/filenaming.py +++ b/picard/util/filenaming.py @@ -213,7 +213,11 @@ def _make_win_short_filename(relpath, reserved=0): # http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx # # The MAX_PATH is 260 characters, with this possible format for a file: - # "X:\<244-char dir path>\<11-char filename>". + # "X:\<244-char dir path>\<12-char filename>". + + # Use a shorter max node length then the theoretically allowed 255 characters + # to leave room for longer file names + MAX_NODE_LENGTH = WIN_MAX_NODE_LEN - 29 # to make predictable directory paths we need to fit the directories in # WIN_MAX_DIRPATH_LEN, and truncate the filename to whatever's left @@ -224,8 +228,8 @@ def _make_win_short_filename(relpath, reserved=0): return shorten_path(path, length, mode=ShortenMode.UTF16) xlength = _get_utf16_length - # shorten to WIN_MAX_NODE_LEN from the beginning - relpath = shorten(relpath, WIN_MAX_NODE_LEN) + # shorten to MAX_NODE_LENGTH from the beginning + relpath = shorten(relpath, MAX_NODE_LENGTH) dirpath, filename = os.path.split(relpath) # what if dirpath is already the right size? dplen = xlength(dirpath) @@ -366,7 +370,7 @@ def make_short_filename(basedir, relpath, win_shorten_path=False, relative_to="" reserved += 1 return _make_win_short_filename(relpath, reserved) else: - return shorten_path(relpath, 255, mode=ShortenMode.UTF16) + return shorten_path(relpath, WIN_MAX_NODE_LEN, mode=ShortenMode.UTF16) # if we're being windows compatible, figure out how much # needs to be reserved for the basedir part elif win_shorten_path: