mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Fix #825706 (Private bug)
This commit is contained in:
parent
ec9a05923a
commit
a61f1387b0
@ -7,6 +7,7 @@ __docformat__ = 'restructuredtext en'
|
||||
import os, shutil, time
|
||||
|
||||
from calibre.devices.errors import PathError
|
||||
from calibre.utils.filenames import case_preserving_open_file
|
||||
|
||||
class File(object):
|
||||
|
||||
@ -46,10 +47,8 @@ class CLI(object):
|
||||
path = os.path.join(path, infile.name)
|
||||
if not replace_file and os.path.exists(path):
|
||||
raise PathError('File already exists: ' + path)
|
||||
d = os.path.dirname(path)
|
||||
if not os.path.exists(d):
|
||||
os.makedirs(d)
|
||||
with open(path, 'w+b') as dest:
|
||||
dest, actual_path = case_preserving_open_file(path)
|
||||
with dest:
|
||||
try:
|
||||
shutil.copyfileobj(infile, dest)
|
||||
except IOError:
|
||||
@ -62,6 +61,7 @@ class CLI(object):
|
||||
#if not check_transfer(infile, dest): raise Exception('Transfer failed')
|
||||
if close:
|
||||
infile.close()
|
||||
return actual_path
|
||||
|
||||
def munge_path(self, path):
|
||||
if path.startswith('/') and not (path.startswith(self._main_prefix) or \
|
||||
|
@ -258,10 +258,10 @@ class USBMS(CLI, Device):
|
||||
for i, infile in enumerate(files):
|
||||
mdata, fname = metadata.next(), names.next()
|
||||
filepath = self.normalize_path(self.create_upload_path(path, mdata, fname))
|
||||
paths.append(filepath)
|
||||
if not hasattr(infile, 'read'):
|
||||
infile = self.normalize_path(infile)
|
||||
self.put_file(infile, filepath, replace_file=True)
|
||||
filepath = self.put_file(infile, filepath, replace_file=True)
|
||||
paths.append(filepath)
|
||||
try:
|
||||
self.upload_cover(os.path.dirname(filepath),
|
||||
os.path.splitext(os.path.basename(filepath))[0],
|
||||
|
@ -3,11 +3,12 @@ Make strings safe for use as ASCII filenames, while trying to preserve as much
|
||||
meaning as possible.
|
||||
'''
|
||||
|
||||
import os
|
||||
import os, errno
|
||||
from math import ceil
|
||||
|
||||
from calibre import sanitize_file_name
|
||||
from calibre.constants import preferred_encoding, iswindows
|
||||
from calibre import sanitize_file_name, isbytestring, force_unicode
|
||||
from calibre.constants import (preferred_encoding, iswindows,
|
||||
filesystem_encoding)
|
||||
from calibre.utils.localization import get_udc
|
||||
|
||||
def ascii_text(orig):
|
||||
@ -114,3 +115,81 @@ def is_case_sensitive(path):
|
||||
os.remove(f1)
|
||||
return is_case_sensitive
|
||||
|
||||
def case_preserving_open_file(path, mode='wb', mkdir_mode=0777):
|
||||
'''
|
||||
Open the file pointed to by path with the specified mode. If any
|
||||
directories in path do not exist, they are created. Returns the
|
||||
opened file object and the path to the opened file object. This path is
|
||||
guaranteed to have the same case as the on disk path. For case insensitive
|
||||
filesystems, the returned path may be different from the passed in path.
|
||||
The returned path is always unicode and always an absolute path.
|
||||
|
||||
If mode is None, then this function assumes that path points to a directory
|
||||
and return the path to the directory as the file object.
|
||||
|
||||
mkdir_mode specifies the mode with which any missing directories in path
|
||||
are created.
|
||||
'''
|
||||
if isbytestring(path):
|
||||
path = path.decode(filesystem_encoding)
|
||||
|
||||
path = os.path.abspath(path)
|
||||
|
||||
sep = force_unicode(os.sep, 'ascii')
|
||||
|
||||
if path.endswith(sep):
|
||||
path = path[:-1]
|
||||
if not path:
|
||||
raise ValueError('Path must not point to root')
|
||||
|
||||
components = path.split(sep)
|
||||
if not components:
|
||||
raise ValueError('Invalid path: %r'%path)
|
||||
|
||||
cpath = sep
|
||||
if iswindows:
|
||||
# Always upper case the drive letter and add a trailing slash so that
|
||||
# the first os.listdir works correctly
|
||||
cpath = components[0].upper() + sep
|
||||
|
||||
# Create all the directories in path, putting the on disk case version of
|
||||
# the directory into cpath
|
||||
dirs = components[1:] if mode is None else components[1:-1]
|
||||
for comp in dirs:
|
||||
cdir = os.path.join(cpath, comp)
|
||||
try:
|
||||
os.mkdir(cdir, mkdir_mode)
|
||||
except OSError as e:
|
||||
if e.errno != errno.EEXIST:
|
||||
if not os.path.exists(cdir):
|
||||
# Check for exists again, as we could have got a permission
|
||||
# denied error
|
||||
raise
|
||||
# This component already exists, ensure the case is correct
|
||||
cl = comp.lower()
|
||||
candidates = [c for c in os.listdir(cpath) if c.lower() == cl]
|
||||
if len(candidates) == 1:
|
||||
cdir = os.path.join(cpath, candidates[0])
|
||||
# else: We are on a case sensitive file system so cdir must already
|
||||
# be correct
|
||||
cpath = cdir
|
||||
|
||||
if mode is None:
|
||||
ans = fpath = cpath
|
||||
else:
|
||||
fname = components[-1]
|
||||
ans = open(os.path.join(cpath, fname), mode)
|
||||
# Ensure file and all its metadata is written to disk so that subsequent
|
||||
# listdir() has file name in it. I don't know if this is actually
|
||||
# necessary, but given the diversity of platforms, best to be safe.
|
||||
ans.flush()
|
||||
os.fsync(ans.fileno())
|
||||
|
||||
cl = fname.lower()
|
||||
candidates = [c for c in os.listdir(cpath) if c.lower() == cl]
|
||||
if len(candidates) == 1:
|
||||
fpath = os.path.join(cpath, candidates[0])
|
||||
else:
|
||||
fpath = ans.name
|
||||
return ans, fpath
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user