~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/local.py

Merge from bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
This is a fairly thin wrapper on regular file IO.
20
20
"""
21
21
 
 
22
import errno
22
23
import os
23
24
import shutil
24
25
import sys
26
27
import tempfile
27
28
 
28
29
from bzrlib import (
 
30
    atomicfile,
29
31
    osutils,
30
32
    urlutils,
31
33
    )
37
39
 
38
40
 
39
41
_append_flags = os.O_CREAT | os.O_APPEND | os.O_WRONLY | osutils.O_BINARY
 
42
_put_non_atomic_flags = os.O_CREAT | os.O_TRUNC | os.O_WRONLY | osutils.O_BINARY
40
43
 
41
44
 
42
45
class LocalTransport(Transport):
130
133
        except (IOError, OSError),e:
131
134
            self._translate_error(e, path)
132
135
 
133
 
    def put(self, relpath, f, mode=None):
134
 
        """Copy the file-like or string object into the location.
 
136
    def put_file(self, relpath, f, mode=None):
 
137
        """Copy the file-like object into the location.
135
138
 
136
139
        :param relpath: Location to put the contents, relative to base.
137
 
        :param f:       File-like or string object.
 
140
        :param f:       File-like object.
 
141
        :param mode: The mode for the newly created file, 
 
142
                     None means just use the default
138
143
        """
139
 
        from bzrlib.atomicfile import AtomicFile
140
144
 
141
145
        path = relpath
142
146
        try:
143
147
            path = self._abspath(relpath)
144
148
            check_legal_path(path)
145
 
            fp = AtomicFile(path, 'wb', new_mode=mode)
 
149
            fp = atomicfile.AtomicFile(path, 'wb', new_mode=mode)
146
150
        except (IOError, OSError),e:
147
151
            self._translate_error(e, path)
148
152
        try:
151
155
        finally:
152
156
            fp.close()
153
157
 
 
158
    def put_bytes(self, relpath, bytes, mode=None):
 
159
        """Copy the string into the location.
 
160
 
 
161
        :param relpath: Location to put the contents, relative to base.
 
162
        :param bytes:   String
 
163
        """
 
164
 
 
165
        path = relpath
 
166
        try:
 
167
            path = self._abspath(relpath)
 
168
            check_legal_path(path)
 
169
            fp = atomicfile.AtomicFile(path, 'wb', new_mode=mode)
 
170
        except (IOError, OSError),e:
 
171
            self._translate_error(e, path)
 
172
        try:
 
173
            fp.write(bytes)
 
174
            fp.commit()
 
175
        finally:
 
176
            fp.close()
 
177
 
 
178
    def _put_non_atomic_helper(self, relpath, writer,
 
179
                               mode=None,
 
180
                               create_parent_dir=False,
 
181
                               dir_mode=None):
 
182
        """Common functionality information for the put_*_non_atomic.
 
183
 
 
184
        This tracks all the create_parent_dir stuff.
 
185
 
 
186
        :param relpath: the path we are putting to.
 
187
        :param writer: A function that takes an os level file descriptor
 
188
            and writes whatever data it needs to write there.
 
189
        :param mode: The final file mode.
 
190
        :param create_parent_dir: Should we be creating the parent directory
 
191
            if it doesn't exist?
 
192
        """
 
193
        abspath = self._abspath(relpath)
 
194
        if mode is None:
 
195
            # os.open() will automatically use the umask
 
196
            local_mode = 0666
 
197
        else:
 
198
            local_mode = mode
 
199
        try:
 
200
            fd = os.open(abspath, _put_non_atomic_flags, local_mode)
 
201
        except (IOError, OSError),e:
 
202
            # We couldn't create the file, maybe we need to create
 
203
            # the parent directory, and try again
 
204
            if (not create_parent_dir
 
205
                or e.errno not in (errno.ENOENT,errno.ENOTDIR)):
 
206
                self._translate_error(e, relpath)
 
207
            parent_dir = os.path.dirname(abspath)
 
208
            if not parent_dir:
 
209
                self._translate_error(e, relpath)
 
210
            self._mkdir(parent_dir, mode=dir_mode)
 
211
            # We created the parent directory, lets try to open the
 
212
            # file again
 
213
            try:
 
214
                fd = os.open(abspath, _put_non_atomic_flags, local_mode)
 
215
            except (IOError, OSError), e:
 
216
                self._translate_error(e, relpath)
 
217
        try:
 
218
            st = os.fstat(fd)
 
219
            if mode is not None and mode != S_IMODE(st.st_mode):
 
220
                # Because of umask, we may still need to chmod the file.
 
221
                # But in the general case, we won't have to
 
222
                os.chmod(abspath, mode)
 
223
            writer(fd)
 
224
        finally:
 
225
            os.close(fd)
 
226
 
 
227
    def put_file_non_atomic(self, relpath, f, mode=None,
 
228
                            create_parent_dir=False,
 
229
                            dir_mode=None):
 
230
        """Copy the file-like object into the target location.
 
231
 
 
232
        This function is not strictly safe to use. It is only meant to
 
233
        be used when you already know that the target does not exist.
 
234
        It is not safe, because it will open and truncate the remote
 
235
        file. So there may be a time when the file has invalid contents.
 
236
 
 
237
        :param relpath: The remote location to put the contents.
 
238
        :param f:       File-like object.
 
239
        :param mode:    Possible access permissions for new file.
 
240
                        None means do not set remote permissions.
 
241
        :param create_parent_dir: If we cannot create the target file because
 
242
                        the parent directory does not exist, go ahead and
 
243
                        create it, and then try again.
 
244
        """
 
245
        def writer(fd):
 
246
            self._pump_to_fd(f, fd)
 
247
        self._put_non_atomic_helper(relpath, writer, mode=mode,
 
248
                                    create_parent_dir=create_parent_dir,
 
249
                                    dir_mode=dir_mode)
 
250
 
 
251
    def put_bytes_non_atomic(self, relpath, bytes, mode=None,
 
252
                             create_parent_dir=False, dir_mode=None):
 
253
        def writer(fd):
 
254
            os.write(fd, bytes)
 
255
        self._put_non_atomic_helper(relpath, writer, mode=mode,
 
256
                                    create_parent_dir=create_parent_dir,
 
257
                                    dir_mode=dir_mode)
 
258
 
154
259
    def iter_files_recursive(self):
155
260
        """Iter the relative paths of files in the transports sub-tree."""
156
261
        queue = list(self.list_dir(u'.'))
163
268
            else:
164
269
                yield relpath
165
270
 
166
 
    def mkdir(self, relpath, mode=None):
167
 
        """Create a directory at the given path."""
168
 
        path = relpath
 
271
    def _mkdir(self, abspath, mode=None):
 
272
        """Create a real directory, filtering through mode"""
 
273
        if mode is None:
 
274
            # os.mkdir() will filter through umask
 
275
            local_mode = 0777
 
276
        else:
 
277
            local_mode = mode
169
278
        try:
170
 
            if mode is None:
171
 
                # os.mkdir() will filter through umask
172
 
                local_mode = 0777
173
 
            else:
174
 
                local_mode = mode
175
 
            path = self._abspath(relpath)
176
 
            os.mkdir(path, local_mode)
 
279
            os.mkdir(abspath, local_mode)
177
280
            if mode is not None:
178
281
                # It is probably faster to just do the chmod, rather than
179
282
                # doing a stat, and then trying to compare
180
 
                os.chmod(path, mode)
181
 
        except (IOError, OSError),e:
182
 
            self._translate_error(e, path)
183
 
 
184
 
    def append(self, relpath, f, mode=None):
 
283
                os.chmod(abspath, mode)
 
284
        except (IOError, OSError),e:
 
285
            self._translate_error(e, abspath)
 
286
 
 
287
    def mkdir(self, relpath, mode=None):
 
288
        """Create a directory at the given path."""
 
289
        self._mkdir(self._abspath(relpath), mode=mode)
 
290
 
 
291
    def _get_append_file(self, relpath, mode=None):
 
292
        """Call os.open() for the given relpath"""
 
293
        file_abspath = self._abspath(relpath)
 
294
        if mode is None:
 
295
            # os.open() will automatically use the umask
 
296
            local_mode = 0666
 
297
        else:
 
298
            local_mode = mode
 
299
        try:
 
300
            return file_abspath, os.open(file_abspath, _append_flags, local_mode)
 
301
        except (IOError, OSError),e:
 
302
            self._translate_error(e, relpath)
 
303
 
 
304
    def _check_mode_and_size(self, file_abspath, fd, mode=None):
 
305
        """Check the mode of the file, and return the current size"""
 
306
        st = os.fstat(fd)
 
307
        if mode is not None and mode != S_IMODE(st.st_mode):
 
308
            # Because of umask, we may still need to chmod the file.
 
309
            # But in the general case, we won't have to
 
310
            os.chmod(file_abspath, mode)
 
311
        return st.st_size
 
312
 
 
313
    def append_file(self, relpath, f, mode=None):
185
314
        """Append the text in the file-like object into the final location."""
186
 
        abspath = self._abspath(relpath)
187
 
        if mode is None:
188
 
            # os.open() will automatically use the umask
189
 
            local_mode = 0666
190
 
        else:
191
 
            local_mode = mode
192
 
        try:
193
 
            fd = os.open(abspath, _append_flags, local_mode)
194
 
        except (IOError, OSError),e:
195
 
            self._translate_error(e, relpath)
196
 
        try:
197
 
            st = os.fstat(fd)
198
 
            result = st.st_size
199
 
            if mode is not None and mode != S_IMODE(st.st_mode):
200
 
                # Because of umask, we may still need to chmod the file.
201
 
                # But in the general case, we won't have to
202
 
                os.chmod(abspath, mode)
 
315
        file_abspath, fd = self._get_append_file(relpath, mode=mode)
 
316
        try:
 
317
            result = self._check_mode_and_size(file_abspath, fd, mode=mode)
203
318
            self._pump_to_fd(f, fd)
204
319
        finally:
205
320
            os.close(fd)
206
321
        return result
207
322
 
 
323
    def append_bytes(self, relpath, bytes, mode=None):
 
324
        """Append the text in the string into the final location."""
 
325
        file_abspath, fd = self._get_append_file(relpath, mode=mode)
 
326
        try:
 
327
            result = self._check_mode_and_size(file_abspath, fd, mode=mode)
 
328
            os.write(fd, bytes)
 
329
        finally:
 
330
            os.close(fd)
 
331
        return result
 
332
 
208
333
    def _pump_to_fd(self, fromfile, to_fd):
209
334
        """Copy contents of one file to another."""
210
335
        BUFSIZE = 32768