From 02fd9620948597d2a8d32683b1efb86e624bdd28 Mon Sep 17 00:00:00 2001 From: JoeLametta Date: Mon, 22 Oct 2018 20:51:14 +0200 Subject: [PATCH] Limit length of filenames (#311) * Limit length of filenames If whipper generated filenames are longer thant the maximum value supported by the filesystem, the I/O operations are going to fail. With this commit filenames which may be too long are truncated to the maximum allowable length. Fixes #197. --- whipper/common/common.py | 15 +++++++++++++++ whipper/common/program.py | 6 +++--- whipper/program/cdparanoia.py | 4 ++-- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/whipper/common/common.py b/whipper/common/common.py index c8aefd4..853204b 100644 --- a/whipper/common/common.py +++ b/whipper/common/common.py @@ -23,6 +23,7 @@ import os import os.path import math import subprocess +import unicodedata from whipper.extern import asyncsub @@ -153,6 +154,20 @@ class MissingFrames(Exception): pass +def truncate_filename(path): + """ + Truncate filename to the max. len. allowed by the path's filesystem + Hopefully it handles Unicode strings correctly + """ + p, f = os.path.split(os.path.normpath(path)) + f, e = os.path.splitext(f) + fn_lim = os.pathconf(p, 'PC_NAME_MAX') # max filenmae length in bytes + max = fn_lim - len(e.encode('utf-8')) + f = unicodedata.normalize('NFC', f) + f_trunc = unicode(f.encode('utf-8')[:max], 'utf-8', errors='ignore') + return os.path.join(p, f_trunc + e) + + def shrinkPath(path): """ Shrink a full path to a shorter version. diff --git a/whipper/common/program.py b/whipper/common/program.py index 5a1daaa..78790b4 100644 --- a/whipper/common/program.py +++ b/whipper/common/program.py @@ -574,7 +574,7 @@ class Program: return accurip.verify_result(self.result, responses, checksums) def write_m3u(self, discname): - m3uPath = u'%s.m3u' % discname + m3uPath = common.truncate_filename(discname + '.m3u') with open(m3uPath, 'w') as f: f.write(u'#EXTM3U\n'.encode('utf-8')) for track in self.result.tracks: @@ -596,7 +596,7 @@ class Program: def writeCue(self, discName): assert self.result.table.canCue() - cuePath = '%s.cue' % discName + cuePath = common.truncate_filename(discName + '.cue') logger.debug('write .cue file to %s', cuePath) handle = open(cuePath, 'w') # FIXME: do we always want utf-8 ? @@ -608,7 +608,7 @@ class Program: return cuePath def writeLog(self, discName, logger): - logPath = '%s.log' % discName + logPath = common.truncate_filename(discName + '.log') handle = open(logPath, 'w') log = logger.log(self.result) handle.write(log.encode('utf-8')) diff --git a/whipper/program/cdparanoia.py b/whipper/program/cdparanoia.py index 3163dc7..07f2e69 100644 --- a/whipper/program/cdparanoia.py +++ b/whipper/program/cdparanoia.py @@ -481,8 +481,8 @@ class ReadVerifyTrackTask(task.MultiSeparateTask): except IOError as e: if errno.ENAMETOOLONG != e.errno: raise - path = common.shrinkPath(path) - tmpoutpath = path + u'.part' + path = common.truncate_filename(common.shrinkPath(path)) + tmpoutpath = common.truncate_filename(path + u'.part') open(tmpoutpath, 'wb').close() self._tmppath = tmpoutpath self.path = path