1
# Copyright (C) 2005, 2006 Canonical Ltd
1
# Copyright (C) 2005, 2006, 2008 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
24
25
import bzrlib.errors as errors
25
26
from bzrlib.errors import BzrError
26
27
from bzrlib.osutils import file_iterator, safe_unicode
27
from bzrlib.symbol_versioning import (deprecated_method,
28
from bzrlib.symbol_versioning import (deprecated_method,
29
30
from bzrlib.trace import mutter, note
30
31
import bzrlib.transactions as transactions
31
32
import bzrlib.urlutils as urlutils
105
106
def __del__(self):
106
107
if self.is_locked():
107
# XXX: This should show something every time, and be suitable for
108
# headless operation and embedding
109
from warnings import warn
110
warn("file group %r was not explicitly unlocked" % self)
108
# do not automatically unlock; there should have been a
109
# try/finally to unlock this.
110
warn("%r was gc'd while locked" % self)
113
112
def break_lock(self):
114
113
"""Break the lock of this lockable files group if it is held.
132
131
self._dir_mode = 0755
133
132
self._file_mode = 0644
135
self._dir_mode = st.st_mode & 07777
134
# Check the directory mode, but also make sure the created
135
# directories and files are read-write for this user. This is
136
# mostly a workaround for filesystems which lie about being able to
137
# write to a directory (cygwin & win32)
138
self._dir_mode = (st.st_mode & 07777) | 00700
136
139
# Remove the sticky and execute bits for files
137
140
self._file_mode = self._dir_mode & ~07111
138
141
if not self._set_dir_mode:
144
147
"""Return location relative to branch."""
145
148
return self._transport.abspath(self._escape(file_or_path))
147
@deprecated_method(zero_eight)
148
def controlfile(self, file_or_path, mode='r'):
149
"""Open a control file for this branch.
151
There are two classes of file in a lockable directory: text
152
and binary. binary files are untranslated byte streams. Text
153
control files are stored with Unix newlines and in UTF-8, even
154
if the platform or locale defaults are different.
156
Such files are not openable in write mode : they are managed via
157
put and put_utf8 which atomically replace old versions using
161
relpath = self._escape(file_or_path)
162
# TODO: codecs.open() buffers linewise, so it was overloaded with
163
# a much larger buffer, do we need to do the same for getreader/getwriter?
165
return self.get(relpath)
167
raise BzrError("Branch.controlfile(mode='wb') is not supported, use put[_utf8]")
169
return self.get_utf8(relpath)
171
raise BzrError("Branch.controlfile(mode='w') is not supported, use put[_utf8]")
173
raise BzrError("invalid controlfile mode %r" % mode)
176
151
def get(self, relpath):
177
152
"""Get a file as a bytestream."""
222
197
raise errors.BzrBadParameterNotString(a_string)
223
198
self.put_bytes(path, a_string.encode('utf-8'))
225
def lock_write(self):
200
def leave_in_place(self):
201
"""Set this LockableFiles to not clear the physical lock on unlock."""
202
self._lock.leave_in_place()
204
def dont_leave_in_place(self):
205
"""Set this LockableFiles to clear the physical lock on unlock."""
206
self._lock.dont_leave_in_place()
208
def lock_write(self, token=None):
209
"""Lock this group of files for writing.
211
:param token: if this is already locked, then lock_write will fail
212
unless the token matches the existing lock.
213
:returns: a token if this instance supports tokens, otherwise None.
214
:raises TokenLockingNotSupported: when a token is given but this
215
instance doesn't support using token locks.
216
:raises MismatchedToken: if the specified token doesn't match the token
217
of the existing lock.
219
A token should be passed in if you know that you have locked the object
220
some other way, and need to synchronise this object's state with that
226
223
# mutter("lock write: %s (%s)", self, self._lock_count)
227
224
# TODO: Upgrade locking to support using a Transport,
228
225
# and potentially a remote locking protocol
229
226
if self._lock_mode:
230
227
if self._lock_mode != 'w' or not self.get_transaction().writeable():
231
228
raise errors.ReadOnlyError(self)
229
self._lock.validate_token(token)
232
230
self._lock_count += 1
231
return self._token_from_lock
234
self._lock.lock_write()
233
token_from_lock = self._lock.lock_write(token=token)
235
234
#note('write locking %s', self)
236
235
#traceback.print_stack()
237
236
self._lock_mode = 'w'
238
237
self._lock_count = 1
239
238
self._set_transaction(transactions.WriteTransaction())
239
self._token_from_lock = token_from_lock
240
return token_from_lock
241
242
def lock_read(self):
242
243
# mutter("lock read: %s (%s)", self, self._lock_count)
333
334
def break_lock(self):
334
335
raise NotImplementedError(self.break_lock)
336
def lock_write(self):
337
def leave_in_place(self):
338
raise NotImplementedError(self.leave_in_place)
340
def dont_leave_in_place(self):
341
raise NotImplementedError(self.dont_leave_in_place)
343
def lock_write(self, token=None):
344
if token is not None:
345
raise errors.TokenLockingNotSupported(self)
337
346
self._lock = self._transport.lock_write(self._escaped_name)
339
348
def lock_read(self):
351
360
# for old-style locks, create the file now
352
361
self._transport.put_bytes(self._escaped_name, '',
353
362
mode=self._file_modebits)
364
def validate_token(self, token):
365
if token is not None:
366
raise errors.TokenLockingNotSupported(self)