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):
69
71
self._local_base = ''
70
72
super(LocalTransport, self).__init__(base)
73
75
super(LocalTransport, self).__init__(base)
74
76
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
# _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]
102
return self._local_base
106
103
return self._local_base + urlutils.unescape(relative_reference)
108
105
def abspath(self, relpath):
112
109
# proper handling of stuff like
113
110
path = osutils.normpath(osutils.pathjoin(
114
111
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
112
return urlutils.local_path_to_url(path)
127
114
def local_abspath(self, relpath):
163
150
transport._file_streams[canonical_url].flush()
165
152
path = self._abspath(relpath)
166
return osutils.open_file(path, 'rb')
153
return open(path, 'rb')
167
154
except (IOError, OSError),e:
168
155
if e.errno == errno.EISDIR:
169
156
return LateReadError(relpath)
175
162
:param relpath: Location to put the contents, relative to base.
176
163
:param f: File-like object.
177
:param mode: The mode for the newly created file,
164
:param mode: The mode for the newly created file,
178
165
None means just use the default
289
275
def put_bytes_non_atomic(self, relpath, bytes, mode=None,
290
276
create_parent_dir=False, dir_mode=None):
294
279
self._put_non_atomic_helper(relpath, writer, mode=mode,
295
280
create_parent_dir=create_parent_dir,
296
281
dir_mode=dir_mode)
330
315
def open_write_stream(self, relpath, mode=None):
331
316
"""See Transport.open_write_stream."""
317
# initialise the file
318
self.put_bytes_non_atomic(relpath, "", mode=mode)
332
319
abspath = self._abspath(relpath)
333
handle = osutils.open_file(abspath, 'wb')
320
handle = open(abspath, 'wb')
335
321
if mode is not None:
336
322
self._check_mode_and_size(abspath, handle.fileno(), mode)
337
323
transport._file_streams[self.abspath(relpath)] = handle
402
387
def rename(self, rel_from, rel_to):
403
388
path_from = self._abspath(rel_from)
404
path_to = self._abspath(rel_to)
406
# *don't* call bzrlib.osutils.rename, because we want to
407
# detect conflicting names on rename, and osutils.rename tries to
408
# mask cross-platform differences there
409
os.rename(path_from, path_to)
390
# *don't* call bzrlib.osutils.rename, because we want to
391
# detect errors on rename
392
os.rename(path_from, self._abspath(rel_to))
410
393
except (IOError, OSError),e:
411
394
# TODO: What about path_to?
412
395
self._translate_error(e, path_from)
519
502
except (IOError, OSError),e:
520
503
self._translate_error(e, path)
522
if osutils.host_os_dereferences_symlinks():
523
def readlink(self, relpath):
524
"""See Transport.readlink."""
525
return osutils.readlink(self._abspath(relpath))
527
if osutils.hardlinks_good():
528
def hardlink(self, source, link_name):
529
"""See Transport.link."""
531
os.link(self._abspath(source), self._abspath(link_name))
532
except (IOError, OSError), e:
533
self._translate_error(e, source)
535
if osutils.has_symlinks():
536
def symlink(self, source, link_name):
537
"""See Transport.symlink."""
538
abs_link_dirpath = urlutils.dirname(self.abspath(link_name))
539
source_rel = urlutils.file_relpath(
540
urlutils.strip_trailing_slash(abs_link_dirpath),
541
urlutils.strip_trailing_slash(self.abspath(source))
545
os.symlink(source_rel, self._abspath(link_name))
546
except (IOError, OSError), e:
547
self._translate_error(e, source_rel)
549
505
def _can_roundtrip_unix_modebits(self):
550
506
if sys.platform == 'win32':
571
527
def clone(self, offset=None):
572
528
"""Return a new LocalTransport with root at self.base + offset
573
Because the local filesystem does not require a connection,
529
Because the local filesystem does not require a connection,
574
530
we can just return a new object.
576
532
if offset is None:
585
541
return EmulatedWin32LocalTransport(abspath)
544
class LocalURLServer(Server):
545
"""A pretend server for local transports, using file:// urls.
547
Of course no actual server is required to access the local filesystem, so
548
this just exists to tell the test code how to get to it.
552
"""Setup the server to service requests.
554
:param decorated_transport: ignored by this implementation.
558
"""See Transport.Server.get_url."""
559
return urlutils.local_path_to_url('')
588
562
def get_test_permutations():
589
563
"""Return the permutations to be used in testing."""
590
from bzrlib.tests import test_server
591
return [(LocalTransport, test_server.LocalURLServer),]
565
(LocalTransport, LocalURLServer),