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)
76
69
def clone(self, offset=None):
77
70
"""Return a new LocalTransport with root at self.base + offset
78
Because the local filesystem does not require a connection,
71
Because the local filesystem does not require a connection,
79
72
we can just return a new object.
97
90
- relative_reference is url escaped.
99
92
if relative_reference in ('.', ''):
100
# _local_base normally has a trailing slash; strip it so that stat
101
# on a transport pointing to a symlink reads the link not the
102
# referent but be careful of / and c:\
103
return osutils.split(self._local_base)[0]
93
return self._local_base
104
94
return self._local_base + urlutils.unescape(relative_reference)
106
96
def abspath(self, relpath):
107
97
"""Return the full url to the given relative URL."""
108
98
# TODO: url escape the result. RBC 20060523.
99
assert isinstance(relpath, basestring), (type(relpath), relpath)
109
100
# jam 20060426 Using normpath on the real path, because that ensures
110
101
# proper handling of stuff like
111
102
path = osutils.normpath(osutils.pathjoin(
112
103
self._local_base, urlutils.unescape(relpath)))
113
# on windows, our _local_base may or may not have a drive specified
114
# (ie, it may be "/" or "c:/foo").
115
# If 'relpath' is '/' we *always* get back an abspath without
116
# the drive letter - but if our transport already has a drive letter,
117
# we want our abspaths to have a drive letter too - so handle that
119
if (sys.platform == "win32" and self._local_base[1:2] == ":"
121
path = self._local_base[:3]
123
104
return urlutils.local_path_to_url(path)
125
106
def local_abspath(self, relpath):
128
109
This function only exists for the LocalTransport, since it is
129
110
the only one that has direct local access.
130
111
This is mostly for stuff like WorkingTree which needs to know
131
the local working directory. The returned path will always contain
132
forward slashes as the path separator, regardless of the platform.
112
the local working directory.
134
114
This function is quite expensive: it calls realpath which resolves
161
141
transport._file_streams[canonical_url].flush()
163
143
path = self._abspath(relpath)
164
return osutils.open_file(path, 'rb')
144
return open(path, 'rb')
165
145
except (IOError, OSError),e:
166
146
if e.errno == errno.EISDIR:
167
147
return LateReadError(relpath)
173
153
:param relpath: Location to put the contents, relative to base.
174
154
:param f: File-like object.
175
:param mode: The mode for the newly created file,
155
:param mode: The mode for the newly created file,
176
156
None means just use the default
287
266
def put_bytes_non_atomic(self, relpath, bytes, mode=None,
288
267
create_parent_dir=False, dir_mode=None):
292
270
self._put_non_atomic_helper(relpath, writer, mode=mode,
293
271
create_parent_dir=create_parent_dir,
294
272
dir_mode=dir_mode)
330
308
# initialise the file
331
309
self.put_bytes_non_atomic(relpath, "", mode=mode)
332
310
abspath = self._abspath(relpath)
333
handle = osutils.open_file(abspath, 'wb')
311
handle = open(abspath, 'wb')
334
312
if mode is not None:
335
313
self._check_mode_and_size(abspath, handle.fileno(), mode)
336
314
transport._file_streams[self.abspath(relpath)] = handle
401
378
def rename(self, rel_from, rel_to):
402
379
path_from = self._abspath(rel_from)
403
path_to = self._abspath(rel_to)
405
# *don't* call bzrlib.osutils.rename, because we want to
406
# detect conflicting names on rename, and osutils.rename tries to
407
# mask cross-platform differences there
408
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))
409
384
except (IOError, OSError),e:
410
385
# TODO: What about path_to?
411
386
self._translate_error(e, path_from)
518
493
except (IOError, OSError),e:
519
494
self._translate_error(e, path)
521
if osutils.host_os_dereferences_symlinks():
522
def readlink(self, relpath):
523
"""See Transport.readlink."""
524
return osutils.readlink(self._abspath(relpath))
526
if osutils.hardlinks_good():
527
def hardlink(self, source, link_name):
528
"""See Transport.link."""
530
os.link(self._abspath(source), self._abspath(link_name))
531
except (IOError, OSError), e:
532
self._translate_error(e, source)
534
if osutils.has_symlinks():
535
def symlink(self, source, link_name):
536
"""See Transport.symlink."""
537
abs_link_dirpath = urlutils.dirname(self.abspath(link_name))
538
source_rel = urlutils.file_relpath(
539
urlutils.strip_trailing_slash(abs_link_dirpath),
540
urlutils.strip_trailing_slash(self.abspath(source))
544
os.symlink(source_rel, self._abspath(link_name))
545
except (IOError, OSError), e:
546
self._translate_error(e, source_rel)
548
496
def _can_roundtrip_unix_modebits(self):
549
497
if sys.platform == 'win32':
563
511
self._local_base = urlutils._win32_local_path_from_url(base)
565
513
def abspath(self, relpath):
514
assert isinstance(relpath, basestring), (type(relpath), relpath)
566
515
path = osutils.normpath(osutils.pathjoin(
567
516
self._local_base, urlutils.unescape(relpath)))
568
517
return urlutils._win32_local_path_to_url(path)
570
519
def clone(self, offset=None):
571
520
"""Return a new LocalTransport with root at self.base + offset
572
Because the local filesystem does not require a connection,
521
Because the local filesystem does not require a connection,
573
522
we can just return a new object.
575
524
if offset is None:
584
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('')
587
554
def get_test_permutations():
588
555
"""Return the permutations to be used in testing."""
589
from bzrlib.tests import test_server
590
return [(LocalTransport, test_server.LocalURLServer),]
557
(LocalTransport, LocalURLServer),