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
19
19
This is a fairly thin wrapper on regular file IO.
22
from __future__ import absolute_import
25
from stat import ST_MODE, S_ISDIR, S_IMODE
23
from stat import ST_MODE, S_ISDIR, ST_SIZE, S_IMODE
28
26
from bzrlib.lazy_import import lazy_import
38
from bzrlib.trace import mutter
39
39
from bzrlib.transport import LateReadError
42
from bzrlib import transport
45
_append_flags = os.O_CREAT | os.O_APPEND | os.O_WRONLY | osutils.O_BINARY | osutils.O_NOINHERIT
46
_put_non_atomic_flags = os.O_CREAT | os.O_TRUNC | os.O_WRONLY | osutils.O_BINARY | osutils.O_NOINHERIT
49
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):
50
50
"""This is the transport agent for local filesystem access."""
52
52
def __init__(self, base):
53
53
"""Set the base path where files will be stored."""
54
54
if not base.startswith('file://'):
55
raise AssertionError("not a file:// url: %r" % base)
55
symbol_versioning.warn(
56
"Instantiating LocalTransport with a filesystem path"
57
" is deprecated as of bzr 0.8."
58
" Please use bzrlib.transport.get_transport()"
59
" or pass in a file:// url.",
63
base = urlutils.local_path_to_url(base)
56
64
if base[-1] != '/':
67
75
super(LocalTransport, self).__init__(base)
68
76
self._local_base = urlutils.local_path_from_url(base)
69
if self._local_base[-1] != '/':
70
self._local_base = self._local_base + '/'
72
78
def clone(self, offset=None):
73
79
"""Return a new LocalTransport with root at self.base + offset
93
99
- relative_reference is url escaped.
95
101
if relative_reference in ('.', ''):
96
# _local_base normally has a trailing slash; strip it so that stat
97
# on a transport pointing to a symlink reads the link not the
98
# referent but be careful of / and c:\
99
return osutils.split(self._local_base)[0]
102
return self._local_base
100
103
return self._local_base + urlutils.unescape(relative_reference)
102
105
def abspath(self, relpath):
140
143
if abspath is None:
143
return urlutils.file_relpath(self.base, abspath)
146
return urlutils.file_relpath(
147
urlutils.strip_trailing_slash(self.base),
148
urlutils.strip_trailing_slash(abspath))
145
150
def has(self, relpath):
146
151
return os.access(self._abspath(relpath), os.F_OK)
155
160
transport._file_streams[canonical_url].flush()
157
162
path = self._abspath(relpath)
158
return osutils.open_file(path, 'rb')
163
return open(path, 'rb')
159
164
except (IOError, OSError),e:
160
165
if e.errno == errno.EISDIR:
161
166
return LateReadError(relpath)
249
254
if mode is not None and mode != S_IMODE(st.st_mode):
250
255
# Because of umask, we may still need to chmod the file.
251
256
# But in the general case, we won't have to
252
osutils.chmod_if_possible(abspath, mode)
257
os.chmod(abspath, mode)
308
313
local_mode = mode
310
315
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)
311
320
except (IOError, OSError),e:
312
321
self._translate_error(e, abspath)
315
osutils.chmod_if_possible(abspath, mode)
316
except (IOError, OSError), e:
317
self._translate_error(e, abspath)
319
323
def mkdir(self, relpath, mode=None):
320
324
"""Create a directory at the given path."""
323
327
def open_write_stream(self, relpath, mode=None):
324
328
"""See Transport.open_write_stream."""
329
# initialise the file
330
self.put_bytes_non_atomic(relpath, "", mode=mode)
325
331
abspath = self._abspath(relpath)
327
handle = osutils.open_file(abspath, 'wb')
328
except (IOError, OSError),e:
329
self._translate_error(e, abspath)
332
handle = open(abspath, 'wb')
331
333
if mode is not None:
332
334
self._check_mode_and_size(abspath, handle.fileno(), mode)
333
335
transport._file_streams[self.abspath(relpath)] = handle
352
354
if mode is not None and mode != S_IMODE(st.st_mode):
353
355
# Because of umask, we may still need to chmod the file.
354
356
# But in the general case, we won't have to
355
osutils.chmod_if_possible(file_abspath, mode)
357
os.chmod(file_abspath, mode)
356
358
return st.st_size
358
360
def append_file(self, relpath, f, mode=None):
398
400
def rename(self, rel_from, rel_to):
399
401
path_from = self._abspath(rel_from)
400
path_to = self._abspath(rel_to)
402
403
# *don't* call bzrlib.osutils.rename, because we want to
403
# detect conflicting names on rename, and osutils.rename tries to
404
# mask cross-platform differences there
405
os.rename(path_from, path_to)
404
# detect errors on rename
405
os.rename(path_from, self._abspath(rel_to))
406
406
except (IOError, OSError),e:
407
407
# TODO: What about path_to?
408
408
self._translate_error(e, path_from)
451
451
otherpath = other._abspath(path)
452
452
shutil.copy(mypath, otherpath)
453
453
if mode is not None:
454
osutils.chmod_if_possible(otherpath, mode)
454
os.chmod(otherpath, mode)
455
455
except (IOError, OSError),e:
456
456
self._translate_error(e, path)
515
515
except (IOError, OSError),e:
516
516
self._translate_error(e, path)
518
if osutils.host_os_dereferences_symlinks():
519
def readlink(self, relpath):
520
"""See Transport.readlink."""
521
return osutils.readlink(self._abspath(relpath))
523
if osutils.hardlinks_good():
524
def hardlink(self, source, link_name):
525
"""See Transport.link."""
527
os.link(self._abspath(source), self._abspath(link_name))
528
except (IOError, OSError), e:
529
self._translate_error(e, source)
531
if osutils.has_symlinks():
532
def symlink(self, source, link_name):
533
"""See Transport.symlink."""
534
abs_link_dirpath = urlutils.dirname(self.abspath(link_name))
535
source_rel = urlutils.file_relpath(
536
abs_link_dirpath, self.abspath(source))
539
os.symlink(source_rel, self._abspath(link_name))
540
except (IOError, OSError), e:
541
self._translate_error(e, source_rel)
543
518
def _can_roundtrip_unix_modebits(self):
544
519
if sys.platform == 'win32':
558
533
self._local_base = urlutils._win32_local_path_from_url(base)
560
535
def abspath(self, relpath):
561
path = osutils._win32_normpath(osutils.pathjoin(
536
path = osutils.normpath(osutils.pathjoin(
562
537
self._local_base, urlutils.unescape(relpath)))
563
538
return urlutils._win32_local_path_to_url(path)
579
554
return EmulatedWin32LocalTransport(abspath)
557
class LocalURLServer(Server):
558
"""A pretend server for local transports, using file:// urls.
560
Of course no actual server is required to access the local filesystem, so
561
this just exists to tell the test code how to get to it.
565
"""Setup the server to service requests.
567
:param decorated_transport: ignored by this implementation.
571
"""See Transport.Server.get_url."""
572
return urlutils.local_path_to_url('')
582
575
def get_test_permutations():
583
576
"""Return the permutations to be used in testing."""
584
from bzrlib.tests import test_server
585
return [(LocalTransport, test_server.LocalURLServer),]
578
(LocalTransport, LocalURLServer),