/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/_walkdirs_win32.pyx

  • Committer: Vincent Ladeuil
  • Date: 2012-01-18 14:09:19 UTC
  • mto: This revision was merged to the branch mainline in revision 6468.
  • Revision ID: v.ladeuil+lp@free.fr-20120118140919-rlvdrhpc0nq1lbwi
Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2008-2011 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Helper functions for Walkdirs on win32."""
 
18
 
 
19
 
 
20
cdef extern from "python-compat.h":
 
21
    struct _HANDLE:
 
22
        pass
 
23
    ctypedef _HANDLE *HANDLE
 
24
    ctypedef unsigned long DWORD
 
25
    ctypedef long long __int64
 
26
    ctypedef unsigned short WCHAR
 
27
    struct _FILETIME:
 
28
        DWORD dwHighDateTime
 
29
        DWORD dwLowDateTime
 
30
    ctypedef _FILETIME FILETIME
 
31
 
 
32
    struct _WIN32_FIND_DATAW:
 
33
        DWORD dwFileAttributes
 
34
        FILETIME ftCreationTime
 
35
        FILETIME ftLastAccessTime
 
36
        FILETIME ftLastWriteTime
 
37
        DWORD nFileSizeHigh
 
38
        DWORD nFileSizeLow
 
39
        # Some reserved stuff here
 
40
        WCHAR cFileName[260] # MAX_PATH
 
41
        WCHAR cAlternateFilename[14]
 
42
 
 
43
    # We have to use the typedef trick, otherwise pyrex uses:
 
44
    #  struct WIN32_FIND_DATAW
 
45
    # which fails due to 'incomplete type'
 
46
    ctypedef _WIN32_FIND_DATAW WIN32_FIND_DATAW
 
47
 
 
48
    HANDLE INVALID_HANDLE_VALUE
 
49
    HANDLE FindFirstFileW(WCHAR *path, WIN32_FIND_DATAW *data)
 
50
    int FindNextFileW(HANDLE search, WIN32_FIND_DATAW *data)
 
51
    int FindClose(HANDLE search)
 
52
 
 
53
    DWORD FILE_ATTRIBUTE_READONLY
 
54
    DWORD FILE_ATTRIBUTE_DIRECTORY
 
55
    int ERROR_NO_MORE_FILES
 
56
 
 
57
    int GetLastError()
 
58
 
 
59
    # Wide character functions
 
60
    DWORD wcslen(WCHAR *)
 
61
 
 
62
 
 
63
cdef extern from "Python.h":
 
64
    WCHAR *PyUnicode_AS_UNICODE(object)
 
65
    Py_ssize_t PyUnicode_GET_SIZE(object)
 
66
    object PyUnicode_FromUnicode(WCHAR *, Py_ssize_t)
 
67
    int PyList_Append(object, object) except -1
 
68
    object PyUnicode_AsUTF8String(object)
 
69
 
 
70
 
 
71
import operator
 
72
import os
 
73
import stat
 
74
 
 
75
from bzrlib import _readdir_py
 
76
 
 
77
cdef object osutils
 
78
osutils = None
 
79
 
 
80
 
 
81
cdef class _Win32Stat:
 
82
    """Represent a 'stat' result generated from WIN32_FIND_DATA"""
 
83
 
 
84
    cdef readonly int st_mode
 
85
    cdef readonly double st_ctime
 
86
    cdef readonly double st_mtime
 
87
    cdef readonly double st_atime
 
88
    # We can't just declare this as 'readonly' because python2.4 doesn't define
 
89
    # T_LONGLONG as a structure member. So instead we just use a property that
 
90
    # will convert it correctly anyway.
 
91
    cdef __int64 _st_size
 
92
 
 
93
    property st_size:
 
94
        def __get__(self):
 
95
            return self._st_size
 
96
 
 
97
    # os.stat always returns 0, so we hard code it here
 
98
    cdef readonly int st_dev
 
99
    cdef readonly int st_ino
 
100
 
 
101
    def __repr__(self):
 
102
        """Repr is the same as a Stat object.
 
103
 
 
104
        (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)
 
105
        """
 
106
        return repr((self.st_mode, 0, 0, 0, 0, 0, self.st_size, self.st_atime,
 
107
                     self.st_mtime, self.st_ctime))
 
108
 
 
109
 
 
110
cdef object _get_name(WIN32_FIND_DATAW *data):
 
111
    """Extract the Unicode name for this file/dir."""
 
112
    return PyUnicode_FromUnicode(data.cFileName,
 
113
                                 wcslen(data.cFileName))
 
114
 
 
115
 
 
116
cdef int _get_mode_bits(WIN32_FIND_DATAW *data): # cannot_raise
 
117
    cdef int mode_bits
 
118
 
 
119
    mode_bits = 0100666 # writeable file, the most common
 
120
    if data.dwFileAttributes & FILE_ATTRIBUTE_READONLY == FILE_ATTRIBUTE_READONLY:
 
121
        mode_bits = mode_bits ^ 0222 # remove the write bits
 
122
    if data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY:
 
123
        # Remove the FILE bit, set the DIR bit, and set the EXEC bits
 
124
        mode_bits = mode_bits ^ 0140111
 
125
    return mode_bits
 
126
 
 
127
 
 
128
cdef __int64 _get_size(WIN32_FIND_DATAW *data): # cannot_raise
 
129
    # Pyrex casts a DWORD into a PyLong anyway, so it is safe to do << 32
 
130
    # on a DWORD
 
131
    return ((<__int64>data.nFileSizeHigh) << 32) + data.nFileSizeLow
 
132
 
 
133
 
 
134
cdef double _ftime_to_timestamp(FILETIME *ft): # cannot_raise
 
135
    """Convert from a FILETIME struct into a floating point timestamp.
 
136
 
 
137
    The fields of a FILETIME structure are the hi and lo part
 
138
    of a 64-bit value expressed in 100 nanosecond units.
 
139
    1e7 is one second in such units; 1e-7 the inverse.
 
140
    429.4967296 is 2**32 / 1e7 or 2**32 * 1e-7.
 
141
    It also uses the epoch 1601-01-01 rather than 1970-01-01
 
142
    (taken from posixmodule.c)
 
143
    """
 
144
    cdef __int64 val
 
145
    # NB: This gives slightly different results versus casting to a 64-bit
 
146
    #     integer and doing integer math before casting into a floating
 
147
    #     point number. But the difference is in the sub millisecond range,
 
148
    #     which doesn't seem critical here.
 
149
    # secs between epochs: 11,644,473,600
 
150
    val = ((<__int64>ft.dwHighDateTime) << 32) + ft.dwLowDateTime
 
151
    return (val * 1.0e-7) - 11644473600.0
 
152
 
 
153
 
 
154
cdef int _should_skip(WIN32_FIND_DATAW *data): # cannot_raise
 
155
    """Is this '.' or '..' so we should skip it?"""
 
156
    if (data.cFileName[0] != c'.'):
 
157
        return 0
 
158
    if data.cFileName[1] == c'\0':
 
159
        return 1
 
160
    if data.cFileName[1] == c'.' and data.cFileName[2] == c'\0':
 
161
        return 1
 
162
    return 0
 
163
 
 
164
 
 
165
cdef class Win32ReadDir:
 
166
    """Read directories on win32."""
 
167
 
 
168
    cdef object _directory_kind
 
169
    cdef object _file_kind
 
170
 
 
171
    def __init__(self):
 
172
        self._directory_kind = _readdir_py._directory
 
173
        self._file_kind = _readdir_py._file
 
174
 
 
175
    def top_prefix_to_starting_dir(self, top, prefix=""):
 
176
        """See DirReader.top_prefix_to_starting_dir."""
 
177
        global osutils
 
178
        if osutils is None:
 
179
            from bzrlib import osutils
 
180
        return (osutils.safe_utf8(prefix), None, None, None,
 
181
                osutils.safe_unicode(top))
 
182
 
 
183
    cdef object _get_kind(self, WIN32_FIND_DATAW *data):
 
184
        if data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY:
 
185
            return self._directory_kind
 
186
        return self._file_kind
 
187
 
 
188
    cdef _Win32Stat _get_stat_value(self, WIN32_FIND_DATAW *data):
 
189
        """Get the filename and the stat information."""
 
190
        cdef _Win32Stat statvalue
 
191
 
 
192
        statvalue = _Win32Stat()
 
193
        statvalue.st_mode = _get_mode_bits(data)
 
194
        statvalue.st_ctime = _ftime_to_timestamp(&data.ftCreationTime)
 
195
        statvalue.st_mtime = _ftime_to_timestamp(&data.ftLastWriteTime)
 
196
        statvalue.st_atime = _ftime_to_timestamp(&data.ftLastAccessTime)
 
197
        statvalue._st_size = _get_size(data)
 
198
        statvalue.st_ino = 0
 
199
        statvalue.st_dev = 0
 
200
        return statvalue
 
201
 
 
202
    def read_dir(self, prefix, top):
 
203
        """Win32 implementation of DirReader.read_dir.
 
204
 
 
205
        :seealso: DirReader.read_dir
 
206
        """
 
207
        cdef WIN32_FIND_DATAW search_data
 
208
        cdef HANDLE hFindFile
 
209
        cdef int last_err
 
210
        cdef WCHAR *query
 
211
        cdef int result
 
212
 
 
213
        if prefix:
 
214
            relprefix = prefix + '/'
 
215
        else:
 
216
            relprefix = ''
 
217
        top_slash = top + '/'
 
218
 
 
219
        top_star = top_slash + '*'
 
220
 
 
221
        dirblock = []
 
222
 
 
223
        query = PyUnicode_AS_UNICODE(top_star)
 
224
        hFindFile = FindFirstFileW(query, &search_data)
 
225
        if hFindFile == INVALID_HANDLE_VALUE:
 
226
            # Raise an exception? This path doesn't seem to exist
 
227
            raise WindowsError(GetLastError(), top_star)
 
228
 
 
229
        try:
 
230
            result = 1
 
231
            while result:
 
232
                # Skip '.' and '..'
 
233
                if _should_skip(&search_data):
 
234
                    result = FindNextFileW(hFindFile, &search_data)
 
235
                    continue
 
236
                name_unicode = _get_name(&search_data)
 
237
                name_utf8 = PyUnicode_AsUTF8String(name_unicode)
 
238
                PyList_Append(dirblock,
 
239
                    (relprefix + name_utf8, name_utf8,
 
240
                     self._get_kind(&search_data),
 
241
                     self._get_stat_value(&search_data),
 
242
                     top_slash + name_unicode))
 
243
 
 
244
                result = FindNextFileW(hFindFile, &search_data)
 
245
            # FindNextFileW sets GetLastError() == ERROR_NO_MORE_FILES when it
 
246
            # actually finishes. If we have anything else, then we have a
 
247
            # genuine problem
 
248
            last_err = GetLastError()
 
249
            if last_err != ERROR_NO_MORE_FILES:
 
250
                raise WindowsError(last_err)
 
251
        finally:
 
252
            result = FindClose(hFindFile)
 
253
            if result == 0:
 
254
                last_err = GetLastError()
 
255
                # TODO: We should probably raise an exception if FindClose
 
256
                #       returns an error, however, I don't want to supress an
 
257
                #       earlier Exception, so for now, I'm ignoring this
 
258
        dirblock.sort(key=operator.itemgetter(1))
 
259
        return dirblock
 
260
 
 
261
 
 
262
def lstat(path):
 
263
    """Equivalent to os.lstat, except match Win32ReadDir._get_stat_value.
 
264
    """
 
265
    return wrap_stat(os.lstat(path))
 
266
 
 
267
 
 
268
def fstat(fd):
 
269
    """Like os.fstat, except match Win32ReadDir._get_stat_value
 
270
 
 
271
    :seealso: wrap_stat
 
272
    """
 
273
    return wrap_stat(os.fstat(fd))
 
274
 
 
275
 
 
276
def wrap_stat(st):
 
277
    """Return a _Win32Stat object, based on the given stat result.
 
278
 
 
279
    On Windows, os.fstat(open(fname).fileno()) != os.lstat(fname). This is
 
280
    generally because os.lstat and os.fstat differ in what they put into st_ino
 
281
    and st_dev. What gets set where seems to also be dependent on the python
 
282
    version. So we always set it to 0 to avoid worrying about it.
 
283
    """
 
284
    cdef _Win32Stat statvalue
 
285
    statvalue = _Win32Stat()
 
286
    statvalue.st_mode = st.st_mode
 
287
    statvalue.st_ctime = st.st_ctime
 
288
    statvalue.st_mtime = st.st_mtime
 
289
    statvalue.st_atime = st.st_atime
 
290
    statvalue._st_size = st.st_size
 
291
    statvalue.st_ino = 0
 
292
    statvalue.st_dev = 0
 
293
    return statvalue