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
19
19
This is a fairly thin wrapper on regular file IO.
22
from __future__ import absolute_import
23
from stat import ST_MODE, S_ISDIR, ST_SIZE, S_IMODE
25
from stat import ST_MODE, S_ISDIR, S_IMODE
26
28
from bzrlib.lazy_import import lazy_import
38
from bzrlib.trace import mutter
39
39
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):
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):
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
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)
55
raise AssertionError("not a file:// url: %r" % base)
64
56
if base[-1] != '/':
75
67
super(LocalTransport, self).__init__(base)
76
68
self._local_base = urlutils.local_path_from_url(base)
69
if self._local_base[-1] != '/':
70
self._local_base = self._local_base + '/'
78
72
def clone(self, offset=None):
79
73
"""Return a new LocalTransport with root at self.base + offset
99
93
- relative_reference is url escaped.
101
95
if relative_reference in ('.', ''):
102
return self._local_base
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]
103
100
return self._local_base + urlutils.unescape(relative_reference)
105
102
def abspath(self, relpath):
143
140
if abspath is None:
146
return urlutils.file_relpath(
147
urlutils.strip_trailing_slash(self.base),
148
urlutils.strip_trailing_slash(abspath))
143
return urlutils.file_relpath(self.base, abspath)
150
145
def has(self, relpath):
151
146
return os.access(self._abspath(relpath), os.F_OK)
160
155
transport._file_streams[canonical_url].flush()
162
157
path = self._abspath(relpath)
163
return open(path, 'rb')
158
return osutils.open_file(path, 'rb')
164
159
except (IOError, OSError),e:
165
160
if e.errno == errno.EISDIR:
166
161
return LateReadError(relpath)
254
249
if mode is not None and mode != S_IMODE(st.st_mode):
255
250
# Because of umask, we may still need to chmod the file.
256
251
# But in the general case, we won't have to
257
os.chmod(abspath, mode)
252
osutils.chmod_if_possible(abspath, mode)
313
308
local_mode = mode
315
310
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)
320
311
except (IOError, OSError),e:
321
312
self._translate_error(e, abspath)
315
osutils.chmod_if_possible(abspath, mode)
316
except (IOError, OSError), e:
317
self._translate_error(e, abspath)
323
319
def mkdir(self, relpath, mode=None):
324
320
"""Create a directory at the given path."""
327
323
def open_write_stream(self, relpath, mode=None):
328
324
"""See Transport.open_write_stream."""
329
# initialise the file
330
self.put_bytes_non_atomic(relpath, "", mode=mode)
331
325
abspath = self._abspath(relpath)
332
handle = open(abspath, 'wb')
327
handle = osutils.open_file(abspath, 'wb')
328
except (IOError, OSError),e:
329
self._translate_error(e, abspath)
333
331
if mode is not None:
334
332
self._check_mode_and_size(abspath, handle.fileno(), mode)
335
333
transport._file_streams[self.abspath(relpath)] = handle
354
352
if mode is not None and mode != S_IMODE(st.st_mode):
355
353
# Because of umask, we may still need to chmod the file.
356
354
# But in the general case, we won't have to
357
os.chmod(file_abspath, mode)
355
osutils.chmod_if_possible(file_abspath, mode)
358
356
return st.st_size
360
358
def append_file(self, relpath, f, mode=None):
400
398
def rename(self, rel_from, rel_to):
401
399
path_from = self._abspath(rel_from)
400
path_to = self._abspath(rel_to)
403
402
# *don't* call bzrlib.osutils.rename, because we want to
404
# detect errors on rename
405
os.rename(path_from, self._abspath(rel_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)
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
os.chmod(otherpath, mode)
454
osutils.chmod_if_possible(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)
518
543
def _can_roundtrip_unix_modebits(self):
519
544
if sys.platform == 'win32':
533
558
self._local_base = urlutils._win32_local_path_from_url(base)
535
560
def abspath(self, relpath):
536
path = osutils.normpath(osutils.pathjoin(
561
path = osutils._win32_normpath(osutils.pathjoin(
537
562
self._local_base, urlutils.unescape(relpath)))
538
563
return urlutils._win32_local_path_to_url(path)
554
579
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.
564
def start_server(self):
568
"""See Transport.Server.get_url."""
569
return urlutils.local_path_to_url('')
572
582
def get_test_permutations():
573
583
"""Return the permutations to be used in testing."""
575
(LocalTransport, LocalURLServer),
584
from bzrlib.tests import test_server
585
return [(LocalTransport, test_server.LocalURLServer),]