1
# Copyright (C) 2005-2011 Canonical Ltd
1
# Copyright (C) 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
"""Transport for the local filesystem.
23
from stat import ST_MODE, S_ISDIR, S_IMODE
23
from stat import ST_MODE, S_ISDIR, ST_SIZE, S_IMODE
26
26
from bzrlib.lazy_import import lazy_import
38
from bzrlib.trace import mutter
37
39
from bzrlib.transport import LateReadError
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):
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):
48
50
"""This is the transport agent for local filesystem access."""
50
52
def __init__(self, base):
61
63
base = urlutils.local_path_to_url(base)
62
64
if base[-1] != '/':
65
# Special case : windows has no "root", but does have
66
# multiple lettered drives inside it. #240910
67
if sys.platform == 'win32' and base == 'file:///':
70
super(LocalTransport, self).__init__(base)
73
66
super(LocalTransport, self).__init__(base)
74
67
self._local_base = urlutils.local_path_from_url(base)
75
if self._local_base[-1] != '/':
76
self._local_base = self._local_base + '/'
78
69
def clone(self, offset=None):
79
70
"""Return a new LocalTransport with root at self.base + offset
80
Because the local filesystem does not require a connection,
71
Because the local filesystem does not require a connection,
81
72
we can just return a new object.
99
90
- relative_reference is url escaped.
101
92
if relative_reference in ('.', ''):
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]
93
return self._local_base
106
94
return self._local_base + urlutils.unescape(relative_reference)
108
96
def abspath(self, relpath):
109
97
"""Return the full url to the given relative URL."""
110
98
# TODO: url escape the result. RBC 20060523.
99
assert isinstance(relpath, basestring), (type(relpath), relpath)
111
100
# jam 20060426 Using normpath on the real path, because that ensures
112
101
# proper handling of stuff like
113
102
path = osutils.normpath(osutils.pathjoin(
114
103
self._local_base, urlutils.unescape(relpath)))
115
# on windows, our _local_base may or may not have a drive specified
116
# (ie, it may be "/" or "c:/foo").
117
# If 'relpath' is '/' we *always* get back an abspath without
118
# the drive letter - but if our transport already has a drive letter,
119
# we want our abspaths to have a drive letter too - so handle that
121
if (sys.platform == "win32" and self._local_base[1:2] == ":"
123
path = self._local_base[:3]
125
104
return urlutils.local_path_to_url(path)
127
106
def local_abspath(self, relpath):
130
109
This function only exists for the LocalTransport, since it is
131
110
the only one that has direct local access.
132
111
This is mostly for stuff like WorkingTree which needs to know
133
the local working directory. The returned path will always contain
134
forward slashes as the path separator, regardless of the platform.
112
the local working directory.
136
114
This function is quite expensive: it calls realpath which resolves
163
141
transport._file_streams[canonical_url].flush()
165
143
path = self._abspath(relpath)
166
return osutils.open_file(path, 'rb')
144
return open(path, 'rb')
167
145
except (IOError, OSError),e:
168
146
if e.errno == errno.EISDIR:
169
147
return LateReadError(relpath)
175
153
:param relpath: Location to put the contents, relative to base.
176
154
:param f: File-like object.
177
:param mode: The mode for the newly created file,
155
:param mode: The mode for the newly created file,
178
156
None means just use the default
289
266
def put_bytes_non_atomic(self, relpath, bytes, mode=None,
290
267
create_parent_dir=False, dir_mode=None):
294
270
self._put_non_atomic_helper(relpath, writer, mode=mode,
295
271
create_parent_dir=create_parent_dir,
296
272
dir_mode=dir_mode)
330
306
def open_write_stream(self, relpath, mode=None):
331
307
"""See Transport.open_write_stream."""
308
# initialise the file
309
self.put_bytes_non_atomic(relpath, "", mode=mode)
332
310
abspath = self._abspath(relpath)
334
handle = osutils.open_file(abspath, 'wb')
335
except (IOError, OSError),e:
336
self._translate_error(e, abspath)
311
handle = open(abspath, 'wb')
338
312
if mode is not None:
339
313
self._check_mode_and_size(abspath, handle.fileno(), mode)
340
314
transport._file_streams[self.abspath(relpath)] = handle
405
378
def rename(self, rel_from, rel_to):
406
379
path_from = self._abspath(rel_from)
407
path_to = 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)
381
# *don't* call bzrlib.osutils.rename, because we want to
382
# detect errors on rename
383
os.rename(path_from, self._abspath(rel_to))
413
384
except (IOError, OSError),e:
414
385
# TODO: What about path_to?
415
386
self._translate_error(e, path_from)
522
493
except (IOError, OSError),e:
523
494
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)
552
496
def _can_roundtrip_unix_modebits(self):
553
497
if sys.platform == 'win32':
567
511
self._local_base = urlutils._win32_local_path_from_url(base)
569
513
def abspath(self, relpath):
570
path = osutils._win32_normpath(osutils.pathjoin(
514
assert isinstance(relpath, basestring), (type(relpath), relpath)
515
path = osutils.normpath(osutils.pathjoin(
571
516
self._local_base, urlutils.unescape(relpath)))
572
517
return urlutils._win32_local_path_to_url(path)
574
519
def clone(self, offset=None):
575
520
"""Return a new LocalTransport with root at self.base + offset
576
Because the local filesystem does not require a connection,
521
Because the local filesystem does not require a connection,
577
522
we can just return a new object.
579
524
if offset is None:
588
533
return EmulatedWin32LocalTransport(abspath)
536
class LocalURLServer(Server):
537
"""A pretend server for local transports, using file:// urls.
539
Of course no actual server is required to access the local filesystem, so
540
this just exists to tell the test code how to get to it.
544
"""Setup the server to service requests.
546
:param decorated_transport: ignored by this implementation.
550
"""See Transport.Server.get_url."""
551
return urlutils.local_path_to_url('')
591
554
def get_test_permutations():
592
555
"""Return the permutations to be used in testing."""
593
from bzrlib.tests import test_server
594
return [(LocalTransport, test_server.LocalURLServer),]
557
(LocalTransport, LocalURLServer),