1
# Copyright (C) 2005, 2006 Canonical Ltd
1
# Copyright (C) 2005-2011 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
23
from stat import ST_MODE, S_ISDIR, ST_SIZE, S_IMODE
23
from stat import ST_MODE, S_ISDIR, S_IMODE
26
26
from bzrlib.lazy_import import lazy_import
38
from bzrlib.trace import mutter
39
37
from bzrlib.transport import LateReadError
42
40
from bzrlib import transport
45
_append_flags = os.O_CREAT | os.O_APPEND | os.O_WRONLY | osutils.O_BINARY
46
_put_non_atomic_flags = os.O_CREAT | os.O_TRUNC | os.O_WRONLY | osutils.O_BINARY
43
_append_flags = os.O_CREAT | os.O_APPEND | os.O_WRONLY | osutils.O_BINARY | osutils.O_NOINHERIT
44
_put_non_atomic_flags = os.O_CREAT | os.O_TRUNC | os.O_WRONLY | osutils.O_BINARY | osutils.O_NOINHERIT
49
47
class LocalTransport(transport.Transport):
75
73
super(LocalTransport, self).__init__(base)
76
74
self._local_base = urlutils.local_path_from_url(base)
75
if self._local_base[-1] != '/':
76
self._local_base = self._local_base + '/'
78
78
def clone(self, offset=None):
79
79
"""Return a new LocalTransport with root at self.base + offset
99
99
- relative_reference is url escaped.
101
101
if relative_reference in ('.', ''):
102
return self._local_base
102
# _local_base normally has a trailing slash; strip it so that stat
103
# on a transport pointing to a symlink reads the link not the
104
# referent but be careful of / and c:\
105
return osutils.split(self._local_base)[0]
103
106
return self._local_base + urlutils.unescape(relative_reference)
105
108
def abspath(self, relpath):
143
146
if abspath is None:
146
return urlutils.file_relpath(
147
urlutils.strip_trailing_slash(self.base),
148
urlutils.strip_trailing_slash(abspath))
149
return urlutils.file_relpath(self.base, abspath)
150
151
def has(self, relpath):
151
152
return os.access(self._abspath(relpath), os.F_OK)
160
161
transport._file_streams[canonical_url].flush()
162
163
path = self._abspath(relpath)
163
return open(path, 'rb')
164
return osutils.open_file(path, 'rb')
164
165
except (IOError, OSError),e:
165
166
if e.errno == errno.EISDIR:
166
167
return LateReadError(relpath)
254
255
if mode is not None and mode != S_IMODE(st.st_mode):
255
256
# Because of umask, we may still need to chmod the file.
256
257
# But in the general case, we won't have to
257
os.chmod(abspath, mode)
258
osutils.chmod_if_possible(abspath, mode)
313
314
local_mode = mode
315
316
os.mkdir(abspath, local_mode)
317
# It is probably faster to just do the chmod, rather than
318
# doing a stat, and then trying to compare
319
os.chmod(abspath, mode)
320
317
except (IOError, OSError),e:
321
318
self._translate_error(e, abspath)
321
osutils.chmod_if_possible(abspath, mode)
322
except (IOError, OSError), e:
323
self._translate_error(e, abspath)
323
325
def mkdir(self, relpath, mode=None):
324
326
"""Create a directory at the given path."""
327
329
def open_write_stream(self, relpath, mode=None):
328
330
"""See Transport.open_write_stream."""
329
# initialise the file
330
self.put_bytes_non_atomic(relpath, "", mode=mode)
331
331
abspath = self._abspath(relpath)
332
handle = open(abspath, 'wb')
333
handle = osutils.open_file(abspath, 'wb')
334
except (IOError, OSError),e:
335
self._translate_error(e, abspath)
333
337
if mode is not None:
334
338
self._check_mode_and_size(abspath, handle.fileno(), mode)
335
339
transport._file_streams[self.abspath(relpath)] = handle
354
358
if mode is not None and mode != S_IMODE(st.st_mode):
355
359
# Because of umask, we may still need to chmod the file.
356
360
# But in the general case, we won't have to
357
os.chmod(file_abspath, mode)
361
osutils.chmod_if_possible(file_abspath, mode)
358
362
return st.st_size
360
364
def append_file(self, relpath, f, mode=None):
400
404
def rename(self, rel_from, rel_to):
401
405
path_from = self._abspath(rel_from)
406
path_to = self._abspath(rel_to)
403
408
# *don't* call bzrlib.osutils.rename, because we want to
404
# detect errors on rename
405
os.rename(path_from, self._abspath(rel_to))
409
# detect conflicting names on rename, and osutils.rename tries to
410
# mask cross-platform differences there
411
os.rename(path_from, path_to)
406
412
except (IOError, OSError),e:
407
413
# TODO: What about path_to?
408
414
self._translate_error(e, path_from)
451
457
otherpath = other._abspath(path)
452
458
shutil.copy(mypath, otherpath)
453
459
if mode is not None:
454
os.chmod(otherpath, mode)
460
osutils.chmod_if_possible(otherpath, mode)
455
461
except (IOError, OSError),e:
456
462
self._translate_error(e, path)
515
521
except (IOError, OSError),e:
516
522
self._translate_error(e, path)
524
if osutils.host_os_dereferences_symlinks():
525
def readlink(self, relpath):
526
"""See Transport.readlink."""
527
return osutils.readlink(self._abspath(relpath))
529
if osutils.hardlinks_good():
530
def hardlink(self, source, link_name):
531
"""See Transport.link."""
533
os.link(self._abspath(source), self._abspath(link_name))
534
except (IOError, OSError), e:
535
self._translate_error(e, source)
537
if osutils.has_symlinks():
538
def symlink(self, source, link_name):
539
"""See Transport.symlink."""
540
abs_link_dirpath = urlutils.dirname(self.abspath(link_name))
541
source_rel = urlutils.file_relpath(
542
abs_link_dirpath, self.abspath(source))
545
os.symlink(source_rel, self._abspath(link_name))
546
except (IOError, OSError), e:
547
self._translate_error(e, source_rel)
518
549
def _can_roundtrip_unix_modebits(self):
519
550
if sys.platform == 'win32':
533
564
self._local_base = urlutils._win32_local_path_from_url(base)
535
566
def abspath(self, relpath):
536
path = osutils.normpath(osutils.pathjoin(
567
path = osutils._win32_normpath(osutils.pathjoin(
537
568
self._local_base, urlutils.unescape(relpath)))
538
569
return urlutils._win32_local_path_to_url(path)