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):
75
75
super(LocalTransport, self).__init__(base)
76
76
self._local_base = urlutils.local_path_from_url(base)
77
if self._local_base[-1] != '/':
78
self._local_base = self._local_base + '/'
78
80
def clone(self, offset=None):
79
81
"""Return a new LocalTransport with root at self.base + offset
99
101
- relative_reference is url escaped.
101
103
if relative_reference in ('.', ''):
102
return self._local_base
104
# _local_base normally has a trailing slash; strip it so that stat
105
# on a transport pointing to a symlink reads the link not the
106
# referent but be careful of / and c:\
107
return osutils.split(self._local_base)[0]
103
108
return self._local_base + urlutils.unescape(relative_reference)
105
110
def abspath(self, relpath):
143
148
if abspath is None:
146
return urlutils.file_relpath(
147
urlutils.strip_trailing_slash(self.base),
148
urlutils.strip_trailing_slash(abspath))
151
return urlutils.file_relpath(self.base, abspath)
150
153
def has(self, relpath):
151
154
return os.access(self._abspath(relpath), os.F_OK)
160
163
transport._file_streams[canonical_url].flush()
162
165
path = self._abspath(relpath)
163
return open(path, 'rb')
166
return osutils.open_file(path, 'rb')
164
167
except (IOError, OSError),e:
165
168
if e.errno == errno.EISDIR:
166
169
return LateReadError(relpath)
254
257
if mode is not None and mode != S_IMODE(st.st_mode):
255
258
# Because of umask, we may still need to chmod the file.
256
259
# But in the general case, we won't have to
257
os.chmod(abspath, mode)
260
osutils.chmod_if_possible(abspath, mode)
313
316
local_mode = mode
315
318
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
319
except (IOError, OSError),e:
321
320
self._translate_error(e, abspath)
323
osutils.chmod_if_possible(abspath, mode)
324
except (IOError, OSError), e:
325
self._translate_error(e, abspath)
323
327
def mkdir(self, relpath, mode=None):
324
328
"""Create a directory at the given path."""
327
331
def open_write_stream(self, relpath, mode=None):
328
332
"""See Transport.open_write_stream."""
329
# initialise the file
330
self.put_bytes_non_atomic(relpath, "", mode=mode)
331
333
abspath = self._abspath(relpath)
332
handle = open(abspath, 'wb')
335
handle = osutils.open_file(abspath, 'wb')
336
except (IOError, OSError),e:
337
self._translate_error(e, abspath)
333
339
if mode is not None:
334
340
self._check_mode_and_size(abspath, handle.fileno(), mode)
335
341
transport._file_streams[self.abspath(relpath)] = handle
354
360
if mode is not None and mode != S_IMODE(st.st_mode):
355
361
# Because of umask, we may still need to chmod the file.
356
362
# But in the general case, we won't have to
357
os.chmod(file_abspath, mode)
363
osutils.chmod_if_possible(file_abspath, mode)
358
364
return st.st_size
360
366
def append_file(self, relpath, f, mode=None):
400
406
def rename(self, rel_from, rel_to):
401
407
path_from = self._abspath(rel_from)
408
path_to = self._abspath(rel_to)
403
410
# *don't* call bzrlib.osutils.rename, because we want to
404
# detect errors on rename
405
os.rename(path_from, self._abspath(rel_to))
411
# detect conflicting names on rename, and osutils.rename tries to
412
# mask cross-platform differences there
413
os.rename(path_from, path_to)
406
414
except (IOError, OSError),e:
407
415
# TODO: What about path_to?
408
416
self._translate_error(e, path_from)
451
459
otherpath = other._abspath(path)
452
460
shutil.copy(mypath, otherpath)
453
461
if mode is not None:
454
os.chmod(otherpath, mode)
462
osutils.chmod_if_possible(otherpath, mode)
455
463
except (IOError, OSError),e:
456
464
self._translate_error(e, path)
515
523
except (IOError, OSError),e:
516
524
self._translate_error(e, path)
526
if osutils.host_os_dereferences_symlinks():
527
def readlink(self, relpath):
528
"""See Transport.readlink."""
529
return osutils.readlink(self._abspath(relpath))
531
if osutils.hardlinks_good():
532
def hardlink(self, source, link_name):
533
"""See Transport.link."""
535
os.link(self._abspath(source), self._abspath(link_name))
536
except (IOError, OSError), e:
537
self._translate_error(e, source)
539
if osutils.has_symlinks():
540
def symlink(self, source, link_name):
541
"""See Transport.symlink."""
542
abs_link_dirpath = urlutils.dirname(self.abspath(link_name))
543
source_rel = urlutils.file_relpath(
544
abs_link_dirpath, self.abspath(source))
547
os.symlink(source_rel, self._abspath(link_name))
548
except (IOError, OSError), e:
549
self._translate_error(e, source_rel)
518
551
def _can_roundtrip_unix_modebits(self):
519
552
if sys.platform == 'win32':
533
566
self._local_base = urlutils._win32_local_path_from_url(base)
535
568
def abspath(self, relpath):
536
path = osutils.normpath(osutils.pathjoin(
569
path = osutils._win32_normpath(osutils.pathjoin(
537
570
self._local_base, urlutils.unescape(relpath)))
538
571
return urlutils._win32_local_path_to_url(path)
554
587
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('')
575
590
def get_test_permutations():
576
591
"""Return the permutations to be used in testing."""
578
(LocalTransport, LocalURLServer),
592
from bzrlib.tests import test_server
593
return [(LocalTransport, test_server.LocalURLServer),]