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
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
"""Transport for the local filesystem.
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
from bzrlib.transport import Transport, Server
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
49
class LocalTransport(Transport):
40
from bzrlib import transport
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
47
class LocalTransport(transport.Transport):
50
48
"""This is the transport agent for local filesystem access."""
52
50
def __init__(self, base):
71
69
self._local_base = ''
72
70
super(LocalTransport, self).__init__(base)
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
80
Because the local filesystem does not require a connection,
80
Because the local filesystem does not require a connection,
81
81
we can just return a new object.
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):
160
163
transport._file_streams[canonical_url].flush()
162
165
path = self._abspath(relpath)
163
return open(path, 'rb')
166
return osutils.open_file(path, 'rb')
164
167
except (IOError, OSError),e:
165
168
if e.errno == errno.EISDIR:
166
169
return LateReadError(relpath)
172
175
:param relpath: Location to put the contents, relative to base.
173
176
:param f: File-like object.
174
:param mode: The mode for the newly created file,
177
:param mode: The mode for the newly created file,
175
178
None means just use the default
285
289
def put_bytes_non_atomic(self, relpath, bytes, mode=None,
286
290
create_parent_dir=False, dir_mode=None):
289
294
self._put_non_atomic_helper(relpath, writer, mode=mode,
290
295
create_parent_dir=create_parent_dir,
291
296
dir_mode=dir_mode)
325
330
def open_write_stream(self, relpath, mode=None):
326
331
"""See Transport.open_write_stream."""
327
# initialise the file
328
self.put_bytes_non_atomic(relpath, "", mode=mode)
329
332
abspath = self._abspath(relpath)
330
handle = open(abspath, 'wb')
334
handle = osutils.open_file(abspath, 'wb')
335
except (IOError, OSError),e:
336
self._translate_error(e, abspath)
331
338
if mode is not None:
332
339
self._check_mode_and_size(abspath, handle.fileno(), mode)
333
340
transport._file_streams[self.abspath(relpath)] = handle
397
405
def rename(self, rel_from, rel_to):
398
406
path_from = self._abspath(rel_from)
407
path_to = self._abspath(rel_to)
400
# *don't* call bzrlib.osutils.rename, because we want to
401
# detect errors on rename
402
os.rename(path_from, self._abspath(rel_to))
409
# *don't* call bzrlib.osutils.rename, because we want to
410
# detect conflicting names on rename, and osutils.rename tries to
411
# mask cross-platform differences there
412
os.rename(path_from, path_to)
403
413
except (IOError, OSError),e:
404
414
# TODO: What about path_to?
405
415
self._translate_error(e, path_from)
512
522
except (IOError, OSError),e:
513
523
self._translate_error(e, path)
525
if osutils.host_os_dereferences_symlinks():
526
def readlink(self, relpath):
527
"""See Transport.readlink."""
528
return osutils.readlink(self._abspath(relpath))
530
if osutils.hardlinks_good():
531
def hardlink(self, source, link_name):
532
"""See Transport.link."""
534
os.link(self._abspath(source), self._abspath(link_name))
535
except (IOError, OSError), e:
536
self._translate_error(e, source)
538
if osutils.has_symlinks():
539
def symlink(self, source, link_name):
540
"""See Transport.symlink."""
541
abs_link_dirpath = urlutils.dirname(self.abspath(link_name))
542
source_rel = urlutils.file_relpath(
543
urlutils.strip_trailing_slash(abs_link_dirpath),
544
urlutils.strip_trailing_slash(self.abspath(source))
548
os.symlink(source_rel, self._abspath(link_name))
549
except (IOError, OSError), e:
550
self._translate_error(e, source_rel)
515
552
def _can_roundtrip_unix_modebits(self):
516
553
if sys.platform == 'win32':
537
574
def clone(self, offset=None):
538
575
"""Return a new LocalTransport with root at self.base + offset
539
Because the local filesystem does not require a connection,
576
Because the local filesystem does not require a connection,
540
577
we can just return a new object.
542
579
if offset is None:
551
588
return EmulatedWin32LocalTransport(abspath)
554
class LocalURLServer(Server):
555
"""A pretend server for local transports, using file:// urls.
557
Of course no actual server is required to access the local filesystem, so
558
this just exists to tell the test code how to get to it.
562
"""Setup the server to service requests.
564
:param decorated_transport: ignored by this implementation.
568
"""See Transport.Server.get_url."""
569
return urlutils.local_path_to_url('')
572
591
def get_test_permutations():
573
592
"""Return the permutations to be used in testing."""
575
(LocalTransport, LocalURLServer),
593
from bzrlib.tests import test_server
594
return [(LocalTransport, test_server.LocalURLServer),]