23
from stat import ST_MODE, S_ISDIR, ST_SIZE, S_IMODE
23
from stat import ST_MODE, S_ISDIR, S_IMODE
26
from bzrlib.lazy_import import lazy_import
26
from ..lazy_import import lazy_import
27
27
lazy_import(globals(), """
38
from bzrlib.trace import mutter
39
from bzrlib.transport import LateReadError
36
from breezy.transport import LateReadError
42
from bzrlib import transport
39
from .. import transport
45
42
_append_flags = os.O_CREAT | os.O_APPEND | os.O_WRONLY | osutils.O_BINARY | osutils.O_NOINHERIT
52
49
def __init__(self, base):
53
50
"""Set the base path where files will be stored."""
54
51
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)
52
raise AssertionError("not a file:// url: %r" % base)
64
53
if base[-1] != '/':
75
64
super(LocalTransport, self).__init__(base)
76
65
self._local_base = urlutils.local_path_from_url(base)
66
if self._local_base[-1] != '/':
67
self._local_base = self._local_base + '/'
78
69
def clone(self, offset=None):
79
70
"""Return a new LocalTransport with root at self.base + offset
99
90
- relative_reference is url escaped.
101
92
if relative_reference in ('.', ''):
102
return self._local_base
93
# _local_base normally has a trailing slash; strip it so that stat
94
# on a transport pointing to a symlink reads the link not the
95
# referent but be careful of / and c:\
96
return osutils.split(self._local_base)[0]
103
97
return self._local_base + urlutils.unescape(relative_reference)
105
99
def abspath(self, relpath):
108
102
# jam 20060426 Using normpath on the real path, because that ensures
109
103
# proper handling of stuff like
110
104
path = osutils.normpath(osutils.pathjoin(
111
self._local_base, urlutils.unescape(relpath)))
105
self._local_base, urlutils.unescape(relpath)))
112
106
# on windows, our _local_base may or may not have a drive specified
113
107
# (ie, it may be "/" or "c:/foo").
114
108
# If 'relpath' is '/' we *always* get back an abspath without
143
137
if abspath is None:
146
return urlutils.file_relpath(
147
urlutils.strip_trailing_slash(self.base),
148
urlutils.strip_trailing_slash(abspath))
140
return urlutils.file_relpath(self.base, abspath)
150
142
def has(self, relpath):
151
143
return os.access(self._abspath(relpath), os.F_OK)
162
154
path = self._abspath(relpath)
163
155
return osutils.open_file(path, 'rb')
164
except (IOError, OSError),e:
156
except (IOError, OSError) as e:
165
157
if e.errno == errno.EISDIR:
166
158
return LateReadError(relpath)
167
159
self._translate_error(e, path)
180
172
path = self._abspath(relpath)
181
173
osutils.check_legal_path(path)
182
174
fp = atomicfile.AtomicFile(path, 'wb', new_mode=mode)
183
except (IOError, OSError),e:
175
except (IOError, OSError) as e:
184
176
self._translate_error(e, path)
186
178
length = self._pump(f, fp)
192
def put_bytes(self, relpath, bytes, mode=None):
184
def put_bytes(self, relpath, raw_bytes, mode=None):
193
185
"""Copy the string into the location.
195
187
:param relpath: Location to put the contents, relative to base.
188
:param raw_bytes: String
190
if not isinstance(raw_bytes, bytes):
192
'raw_bytes must be bytes, not %s' % type(raw_bytes))
201
195
path = self._abspath(relpath)
202
196
osutils.check_legal_path(path)
203
197
fp = atomicfile.AtomicFile(path, 'wb', new_mode=mode)
204
except (IOError, OSError),e:
198
except (IOError, OSError) as e:
205
199
self._translate_error(e, path)
228
222
abspath = self._abspath(relpath)
230
224
# os.open() will automatically use the umask
233
227
local_mode = mode
235
229
fd = os.open(abspath, _put_non_atomic_flags, local_mode)
236
except (IOError, OSError),e:
230
except (IOError, OSError) as e:
237
231
# We couldn't create the file, maybe we need to create
238
232
# the parent directory, and try again
239
233
if (not create_parent_dir
240
or e.errno not in (errno.ENOENT,errno.ENOTDIR)):
234
or e.errno not in (errno.ENOENT, errno.ENOTDIR)):
241
235
self._translate_error(e, relpath)
242
236
parent_dir = os.path.dirname(abspath)
243
237
if not parent_dir:
249
243
fd = os.open(abspath, _put_non_atomic_flags, local_mode)
250
except (IOError, OSError), e:
244
except (IOError, OSError) as e:
251
245
self._translate_error(e, relpath)
253
247
st = os.fstat(fd)
254
248
if mode is not None and mode != S_IMODE(st.st_mode):
255
249
# Because of umask, we may still need to chmod the file.
256
250
# But in the general case, we won't have to
257
os.chmod(abspath, mode)
251
osutils.chmod_if_possible(abspath, mode)
308
302
"""Create a real directory, filtering through mode"""
310
304
# os.mkdir() will filter through umask
313
307
local_mode = mode
315
309
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
except (IOError, OSError),e:
310
except (IOError, OSError) as e:
321
311
self._translate_error(e, abspath)
314
osutils.chmod_if_possible(abspath, mode)
315
except (IOError, OSError) as e:
316
self._translate_error(e, abspath)
323
318
def mkdir(self, relpath, mode=None):
324
319
"""Create a directory at the given path."""
327
322
def open_write_stream(self, relpath, mode=None):
328
323
"""See Transport.open_write_stream."""
329
# initialise the file
330
self.put_bytes_non_atomic(relpath, "", mode=mode)
331
324
abspath = self._abspath(relpath)
332
handle = osutils.open_file(abspath, 'wb')
326
handle = osutils.open_file(abspath, 'wb')
327
except (IOError, OSError) as e:
328
self._translate_error(e, abspath)
333
330
if mode is not None:
334
331
self._check_mode_and_size(abspath, handle.fileno(), mode)
335
332
transport._file_streams[self.abspath(relpath)] = handle
340
337
file_abspath = self._abspath(relpath)
342
339
# os.open() will automatically use the umask
345
342
local_mode = mode
347
344
return file_abspath, os.open(file_abspath, _append_flags, local_mode)
348
except (IOError, OSError),e:
345
except (IOError, OSError) as e:
349
346
self._translate_error(e, relpath)
351
348
def _check_mode_and_size(self, file_abspath, fd, mode=None):
354
351
if mode is not None and mode != S_IMODE(st.st_mode):
355
352
# Because of umask, we may still need to chmod the file.
356
353
# But in the general case, we won't have to
357
os.chmod(file_abspath, mode)
354
osutils.chmod_if_possible(file_abspath, mode)
358
355
return st.st_size
360
357
def append_file(self, relpath, f, mode=None):
393
390
path_to = self._abspath(rel_to)
395
392
shutil.copy(path_from, path_to)
396
except (IOError, OSError),e:
393
except (IOError, OSError) as e:
397
394
# TODO: What about path_to?
398
395
self._translate_error(e, path_from)
401
398
path_from = self._abspath(rel_from)
402
399
path_to = self._abspath(rel_to)
404
# *don't* call bzrlib.osutils.rename, because we want to
401
# *don't* call breezy.osutils.rename, because we want to
405
402
# detect conflicting names on rename, and osutils.rename tries to
406
# mask cross-platform differences there; however we do update the
407
# exception to include the filenames
403
# mask cross-platform differences there
408
404
os.rename(path_from, path_to)
409
except (IOError, OSError),e:
405
except (IOError, OSError) as e:
410
406
# TODO: What about path_to?
411
self._translate_error(
412
osutils._add_rename_error_details(e, path_from, path_to),
407
self._translate_error(e, path_from)
415
409
def move(self, rel_from, rel_to):
416
410
"""Move the item at rel_from to the location at rel_to"""
421
415
# this version will delete the destination if necessary
422
416
osutils.rename(path_from, path_to)
423
except (IOError, OSError),e:
417
except (IOError, OSError) as e:
424
418
# TODO: What about path_to?
425
419
self._translate_error(e, path_from)
431
425
path = self._abspath(relpath)
433
except (IOError, OSError),e:
427
except (IOError, OSError) as e:
434
428
self._translate_error(e, path)
436
430
def external_url(self):
437
"""See bzrlib.transport.Transport.external_url."""
431
"""See breezy.transport.Transport.external_url."""
438
432
# File URL's are externally usable.
456
450
otherpath = other._abspath(path)
457
451
shutil.copy(mypath, otherpath)
458
452
if mode is not None:
459
os.chmod(otherpath, mode)
460
except (IOError, OSError),e:
453
osutils.chmod_if_possible(otherpath, mode)
454
except (IOError, OSError) as e:
461
455
self._translate_error(e, path)
476
470
path = self._abspath(relpath)
478
472
entries = os.listdir(path)
479
except (IOError, OSError), e:
473
except (IOError, OSError) as e:
480
474
self._translate_error(e, path)
481
475
return [urlutils.escape(entry) for entry in entries]
488
482
path = self._abspath(relpath)
489
483
return os.lstat(path)
490
except (IOError, OSError),e:
484
except (IOError, OSError) as e:
491
485
self._translate_error(e, path)
493
487
def lock_read(self, relpath):
494
488
"""Lock the given file for shared (read) access.
495
489
:return: A lock object, which should be passed to Transport.unlock()
497
from bzrlib.lock import ReadLock
491
from breezy.lock import ReadLock
500
494
path = self._abspath(relpath)
501
495
return ReadLock(path)
502
except (IOError, OSError), e:
496
except (IOError, OSError) as e:
503
497
self._translate_error(e, path)
505
499
def lock_write(self, relpath):
509
503
:return: A lock object, which should be passed to Transport.unlock()
511
from bzrlib.lock import WriteLock
505
from breezy.lock import WriteLock
512
506
return WriteLock(self._abspath(relpath))
514
508
def rmdir(self, relpath):
518
512
path = self._abspath(relpath)
520
except (IOError, OSError),e:
514
except (IOError, OSError) as e:
521
515
self._translate_error(e, path)
523
517
if osutils.host_os_dereferences_symlinks():
524
518
def readlink(self, relpath):
525
519
"""See Transport.readlink."""
526
return osutils.readlink(self._abspath(relpath))
521
return osutils.readlink(self._abspath(relpath))
522
except (IOError, OSError) as e:
523
self._translate_error(e, relpath)
528
525
if osutils.hardlinks_good():
529
526
def hardlink(self, source, link_name):
530
527
"""See Transport.link."""
532
529
os.link(self._abspath(source), self._abspath(link_name))
533
except (IOError, OSError), e:
530
except (IOError, OSError) as e:
534
531
self._translate_error(e, source)
536
if osutils.has_symlinks():
533
if getattr(os, 'symlink', None) is not None:
537
534
def symlink(self, source, link_name):
538
535
"""See Transport.symlink."""
539
536
abs_link_dirpath = urlutils.dirname(self.abspath(link_name))
540
537
source_rel = urlutils.file_relpath(
541
urlutils.strip_trailing_slash(abs_link_dirpath),
542
urlutils.strip_trailing_slash(self.abspath(source))
538
abs_link_dirpath, self.abspath(source))
546
541
os.symlink(source_rel, self._abspath(link_name))
547
except (IOError, OSError), e:
542
except (IOError, OSError) as e:
548
543
self._translate_error(e, source_rel)
550
545
def _can_roundtrip_unix_modebits(self):
565
560
self._local_base = urlutils._win32_local_path_from_url(base)
567
562
def abspath(self, relpath):
568
path = osutils.normpath(osutils.pathjoin(
569
self._local_base, urlutils.unescape(relpath)))
563
path = osutils._win32_normpath(osutils.pathjoin(
564
self._local_base, urlutils.unescape(relpath)))
570
565
return urlutils._win32_local_path_to_url(path)
572
567
def clone(self, offset=None):
589
584
def get_test_permutations():
590
585
"""Return the permutations to be used in testing."""
591
from bzrlib.tests import test_server
592
return [(LocalTransport, test_server.LocalURLServer),]
586
from ..tests import test_server
587
return [(LocalTransport, test_server.LocalURLServer), ]