~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_walkdirs_win32.pyx

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2007-03-28 06:58:22 UTC
  • mfrom: (2379.2.3 hpss-chroot)
  • Revision ID: pqm@pqm.ubuntu.com-20070328065822-999550a858a3ced3
(robertc) Fix chroot urls to not expose the url of the transport they are protecting, allowing regular url operations to work on them. (Robert Collins, Andrew Bennetts)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2008-2012 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
 
    property st_dev:
99
 
        def __get__(self):
100
 
            return 0
101
 
    property st_ino:
102
 
        def __get__(self):
103
 
            return 0
104
 
    # st_uid and st_gid required for some external tools like bzr-git & dulwich
105
 
    property st_uid:
106
 
        def __get__(self):
107
 
            return 0
108
 
    property st_gid:
109
 
        def __get__(self):
110
 
            return 0
111
 
 
112
 
    def __repr__(self):
113
 
        """Repr is the same as a Stat object.
114
 
 
115
 
        (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)
116
 
        """
117
 
        return repr((self.st_mode, 0, 0, 0, 0, 0, self.st_size, self.st_atime,
118
 
                     self.st_mtime, self.st_ctime))
119
 
 
120
 
 
121
 
cdef object _get_name(WIN32_FIND_DATAW *data):
122
 
    """Extract the Unicode name for this file/dir."""
123
 
    return PyUnicode_FromUnicode(data.cFileName,
124
 
                                 wcslen(data.cFileName))
125
 
 
126
 
 
127
 
cdef int _get_mode_bits(WIN32_FIND_DATAW *data): # cannot_raise
128
 
    cdef int mode_bits
129
 
 
130
 
    mode_bits = 0100666 # writeable file, the most common
131
 
    if data.dwFileAttributes & FILE_ATTRIBUTE_READONLY == FILE_ATTRIBUTE_READONLY:
132
 
        mode_bits = mode_bits ^ 0222 # remove the write bits
133
 
    if data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY == FILE_ATTRIBUTE_DIRECTORY:
134
 
        # Remove the FILE bit, set the DIR bit, and set the EXEC bits
135
 
        mode_bits = mode_bits ^ 0140111
136
 
    return mode_bits
137
 
 
138
 
 
139
 
cdef __int64 _get_size(WIN32_FIND_DATAW *data): # cannot_raise
140
 
    # Pyrex casts a DWORD into a PyLong anyway, so it is safe to do << 32
141
 
    # on a DWORD
142
 
    return ((<__int64>data.nFileSizeHigh) << 32) + data.nFileSizeLow
143
 
 
144
 
 
145
 
cdef double _ftime_to_timestamp(FILETIME *ft): # cannot_raise
146
 
    """Convert from a FILETIME struct into a floating point timestamp.
147
 
 
148
 
    The fields of a FILETIME structure are the hi and lo part
149
 
    of a 64-bit value expressed in 100 nanosecond units.
150
 
    1e7 is one second in such units; 1e-7 the inverse.
151
 
    429.4967296 is 2**32 / 1e7 or 2**32 * 1e-7.
152
 
    It also uses the epoch 1601-01-01 rather than 1970-01-01
153
 
    (taken from posixmodule.c)
154
 
    """
155
 
    cdef __int64 val
156
 
    # NB: This gives slightly different results versus casting to a 64-bit
157
 
    #     integer and doing integer math before casting into a floating
158
 
    #     point number. But the difference is in the sub millisecond range,
159
 
    #     which doesn't seem critical here.
160
 
    # secs between epochs: 11,644,473,600
161
 
    val = ((<__int64>ft.dwHighDateTime) << 32) + ft.dwLowDateTime
162
 
    return (val * 1.0e-7) - 11644473600.0
163
 
 
164
 
 
165
 
cdef int _should_skip(WIN32_FIND_DATAW *data): # cannot_raise
166
 
    """Is this '.' or '..' so we should skip it?"""
167
 
    if (data.cFileName[0] != c'.'):
168
 
        return 0
169
 
    if data.cFileName[1] == c'\0':
170
 
        return 1
171
 
    if data.cFileName[1] == c'.' and data.cFileName[2] == c'\0':
172
 
        return 1
173
 
    return 0
174
 
 
175
 
 
176
 
cdef class Win32ReadDir:
177
 
    """Read directories on win32."""
178
 
 
179
 
    cdef object _directory_kind
180
 
    cdef object _file_kind
181
 
 
182
 
    def __init__(self):
183
 
        self._directory_kind = _readdir_py._directory
184
 
        self._file_kind = _readdir_py._file
185
 
 
186
 
    def top_prefix_to_starting_dir(self, top, prefix=""):
187
 
        """See DirReader.top_prefix_to_starting_dir."""
188
 
        global osutils
189
 
        if osutils is None:
190
 
            from bzrlib import osutils
191
 
        return (osutils.safe_utf8(prefix), None, None, None,
192
 
                osutils.safe_unicode(top))
193
 
 
194
 
    cdef object _get_kind(self, WIN32_FIND_DATAW *data):
195
 
        if data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY:
196
 
            return self._directory_kind
197
 
        return self._file_kind
198
 
 
199
 
    cdef _Win32Stat _get_stat_value(self, WIN32_FIND_DATAW *data):
200
 
        """Get the filename and the stat information."""
201
 
        cdef _Win32Stat statvalue
202
 
 
203
 
        statvalue = _Win32Stat()
204
 
        statvalue.st_mode = _get_mode_bits(data)
205
 
        statvalue.st_ctime = _ftime_to_timestamp(&data.ftCreationTime)
206
 
        statvalue.st_mtime = _ftime_to_timestamp(&data.ftLastWriteTime)
207
 
        statvalue.st_atime = _ftime_to_timestamp(&data.ftLastAccessTime)
208
 
        statvalue._st_size = _get_size(data)
209
 
        return statvalue
210
 
 
211
 
    def read_dir(self, prefix, top):
212
 
        """Win32 implementation of DirReader.read_dir.
213
 
 
214
 
        :seealso: DirReader.read_dir
215
 
        """
216
 
        cdef WIN32_FIND_DATAW search_data
217
 
        cdef HANDLE hFindFile
218
 
        cdef int last_err
219
 
        cdef WCHAR *query
220
 
        cdef int result
221
 
 
222
 
        if prefix:
223
 
            relprefix = prefix + '/'
224
 
        else:
225
 
            relprefix = ''
226
 
        top_slash = top + '/'
227
 
 
228
 
        top_star = top_slash + '*'
229
 
 
230
 
        dirblock = []
231
 
 
232
 
        query = PyUnicode_AS_UNICODE(top_star)
233
 
        hFindFile = FindFirstFileW(query, &search_data)
234
 
        if hFindFile == INVALID_HANDLE_VALUE:
235
 
            # Raise an exception? This path doesn't seem to exist
236
 
            raise WindowsError(GetLastError(), top_star)
237
 
 
238
 
        try:
239
 
            result = 1
240
 
            while result:
241
 
                # Skip '.' and '..'
242
 
                if _should_skip(&search_data):
243
 
                    result = FindNextFileW(hFindFile, &search_data)
244
 
                    continue
245
 
                name_unicode = _get_name(&search_data)
246
 
                name_utf8 = PyUnicode_AsUTF8String(name_unicode)
247
 
                PyList_Append(dirblock,
248
 
                    (relprefix + name_utf8, name_utf8,
249
 
                     self._get_kind(&search_data),
250
 
                     self._get_stat_value(&search_data),
251
 
                     top_slash + name_unicode))
252
 
 
253
 
                result = FindNextFileW(hFindFile, &search_data)
254
 
            # FindNextFileW sets GetLastError() == ERROR_NO_MORE_FILES when it
255
 
            # actually finishes. If we have anything else, then we have a
256
 
            # genuine problem
257
 
            last_err = GetLastError()
258
 
            if last_err != ERROR_NO_MORE_FILES:
259
 
                raise WindowsError(last_err)
260
 
        finally:
261
 
            result = FindClose(hFindFile)
262
 
            if result == 0:
263
 
                last_err = GetLastError()
264
 
                # TODO: We should probably raise an exception if FindClose
265
 
                #       returns an error, however, I don't want to supress an
266
 
                #       earlier Exception, so for now, I'm ignoring this
267
 
        dirblock.sort(key=operator.itemgetter(1))
268
 
        return dirblock
269
 
 
270
 
 
271
 
def lstat(path):
272
 
    """Equivalent to os.lstat, except match Win32ReadDir._get_stat_value.
273
 
    """
274
 
    return wrap_stat(os.lstat(path))
275
 
 
276
 
 
277
 
def fstat(fd):
278
 
    """Like os.fstat, except match Win32ReadDir._get_stat_value
279
 
 
280
 
    :seealso: wrap_stat
281
 
    """
282
 
    return wrap_stat(os.fstat(fd))
283
 
 
284
 
 
285
 
def wrap_stat(st):
286
 
    """Return a _Win32Stat object, based on the given stat result.
287
 
 
288
 
    On Windows, os.fstat(open(fname).fileno()) != os.lstat(fname). This is
289
 
    generally because os.lstat and os.fstat differ in what they put into st_ino
290
 
    and st_dev. What gets set where seems to also be dependent on the python
291
 
    version. So we always set it to 0 to avoid worrying about it.
292
 
    """
293
 
    cdef _Win32Stat statvalue
294
 
    statvalue = _Win32Stat()
295
 
    statvalue.st_mode = st.st_mode
296
 
    statvalue.st_ctime = st.st_ctime
297
 
    statvalue.st_mtime = st.st_mtime
298
 
    statvalue.st_atime = st.st_atime
299
 
    statvalue._st_size = st.st_size
300
 
    return statvalue