1
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
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
11
# GNU General Public License for more details.
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
from bzrlib.decorators import *
22
from bzrlib.decorators import (needs_read_lock,
23
24
import bzrlib.errors as errors
24
25
from bzrlib.errors import BzrError
25
26
from bzrlib.osutils import file_iterator, safe_unicode
26
from bzrlib.symbol_versioning import *
27
from bzrlib.symbol_versioning import (deprecated_method,
27
29
from bzrlib.trace import mutter, note
28
30
import bzrlib.transactions as transactions
31
import bzrlib.urlutils as urlutils
30
34
# XXX: The tracking here of lock counts and whether the lock is held is
31
35
# somewhat redundant with what's done in LockDir; the main difference is that
73
77
:param lock_class: Class of lock strategy to use: typically
74
78
either LockDir or TransportLock.
77
80
self._transport = transport
78
81
self.lock_name = lock_name
79
82
self._transaction = None
81
83
self._lock_mode = None
82
84
self._lock_count = 0
83
86
esc_name = self._escape(lock_name)
84
87
self._lock = lock_class(transport, esc_name,
85
88
file_modebits=self._file_mode,
129
132
self._dir_mode = 0755
130
133
self._file_mode = 0644
132
self._dir_mode = st.st_mode & 07777
135
# Check the directory mode, but also make sure the created
136
# directories and files are read-write for this user. This is
137
# mostly a workaround for filesystems which lie about being able to
138
# write to a directory (cygwin & win32)
139
self._dir_mode = (st.st_mode & 07777) | 00700
133
140
# Remove the sticky and execute bits for files
134
141
self._file_mode = self._dir_mode & ~07111
135
142
if not self._set_dir_mode:
141
148
"""Return location relative to branch."""
142
149
return self._transport.abspath(self._escape(file_or_path))
144
@deprecated_method(zero_eight)
145
def controlfile(self, file_or_path, mode='r'):
146
"""Open a control file for this branch.
148
There are two classes of file in a lockable directory: text
149
and binary. binary files are untranslated byte streams. Text
150
control files are stored with Unix newlines and in UTF-8, even
151
if the platform or locale defaults are different.
153
Such files are not openable in write mode : they are managed via
154
put and put_utf8 which atomically replace old versions using
158
relpath = self._escape(file_or_path)
159
# TODO: codecs.open() buffers linewise, so it was overloaded with
160
# a much larger buffer, do we need to do the same for getreader/getwriter?
162
return self.get(relpath)
164
raise BzrError("Branch.controlfile(mode='wb') is not supported, use put[_utf8]")
166
return self.get_utf8(relpath)
168
raise BzrError("Branch.controlfile(mode='w') is not supported, use put[_utf8]")
170
raise BzrError("invalid controlfile mode %r" % mode)
173
152
def get(self, relpath):
174
153
"""Get a file as a bytestream."""
191
170
:param f: A file-like or string object whose contents should be copied.
193
self._transport.put(self._escape(path), file, mode=self._file_mode)
172
self._transport.put_file(self._escape(path), file, mode=self._file_mode)
175
def put_bytes(self, path, a_string):
176
"""Write a string of bytes.
178
:param path: The path to put the bytes, relative to the transport root.
179
:param string: A string object, whose exact bytes are to be copied.
181
self._transport.put_bytes(self._escape(path), a_string,
182
mode=self._file_mode)
195
184
@needs_write_lock
196
185
def put_utf8(self, path, a_string):
197
186
"""Write a string, encoding as utf-8.
199
188
:param path: The path to put the string, relative to the transport root.
200
:param string: A file-like or string object whose contents should be copied.
189
:param string: A string or unicode object whose contents should be copied.
202
191
# IterableFile would not be needed if Transport.put took iterables
203
192
# instead of files. ADHB 2005-12-25
207
196
# these are valuable files which should have exact contents.
208
197
if not isinstance(a_string, basestring):
209
198
raise errors.BzrBadParameterNotString(a_string)
210
self.put(path, StringIO(a_string.encode('utf-8')))
212
def lock_write(self):
199
self.put_bytes(path, a_string.encode('utf-8'))
201
def leave_in_place(self):
202
"""Set this LockableFiles to not clear the physical lock on unlock."""
203
self._lock.leave_in_place()
205
def dont_leave_in_place(self):
206
"""Set this LockableFiles to clear the physical lock on unlock."""
207
self._lock.dont_leave_in_place()
209
def lock_write(self, token=None):
210
"""Lock this group of files for writing.
212
:param token: if this is already locked, then lock_write will fail
213
unless the token matches the existing lock.
214
:returns: a token if this instance supports tokens, otherwise None.
215
:raises TokenLockingNotSupported: when a token is given but this
216
instance doesn't support using token locks.
217
:raises MismatchedToken: if the specified token doesn't match the token
218
of the existing lock.
220
A token should be passed in if you know that you have locked the object
221
some other way, and need to synchronise this object's state with that
213
224
# mutter("lock write: %s (%s)", self, self._lock_count)
214
225
# TODO: Upgrade locking to support using a Transport,
215
226
# and potentially a remote locking protocol
216
227
if self._lock_mode:
217
228
if self._lock_mode != 'w' or not self.get_transaction().writeable():
218
229
raise errors.ReadOnlyError(self)
230
self._lock.validate_token(token)
219
231
self._lock_count += 1
232
return self._token_from_lock
221
self._lock.lock_write()
234
token_from_lock = self._lock.lock_write(token=token)
222
235
#note('write locking %s', self)
223
236
#traceback.print_stack()
224
237
self._lock_mode = 'w'
225
238
self._lock_count = 1
226
239
self._set_transaction(transactions.WriteTransaction())
240
self._token_from_lock = token_from_lock
241
return token_from_lock
228
243
def lock_read(self):
229
244
# mutter("lock read: %s (%s)", self, self._lock_count)
320
335
def break_lock(self):
321
336
raise NotImplementedError(self.break_lock)
323
def lock_write(self):
338
def leave_in_place(self):
339
raise NotImplementedError(self.leave_in_place)
341
def dont_leave_in_place(self):
342
raise NotImplementedError(self.dont_leave_in_place)
344
def lock_write(self, token=None):
345
if token is not None:
346
raise errors.TokenLockingNotSupported(self)
324
347
self._lock = self._transport.lock_write(self._escaped_name)
326
349
def lock_read(self):
336
359
def create(self, mode=None):
337
360
"""Create lock mechanism"""
338
361
# for old-style locks, create the file now
339
self._transport.put(self._escaped_name, StringIO(),
362
self._transport.put_bytes(self._escaped_name, '',
340
363
mode=self._file_modebits)
365
def validate_token(self, token):
366
if token is not None:
367
raise errors.TokenLockingNotSupported(self)