1# -*- coding: utf-8 -*-
2from __future__ import unicode_literals
3
4from ctypes import *
5import struct
6import os
7import datetime
8import uuid
9
10from .utils import *
11
12libc = cdll.LoadLibrary('/usr/lib/libc.dylib')
13
14# Constants
15FSOPT_NOFOLLOW         = 0x00000001
16FSOPT_NOINMEMUPDATE    = 0x00000002
17FSOPT_REPORT_FULLSIZE  = 0x00000004
18FSOPT_PACK_INVAL_ATTRS = 0x00000008
19
20VOL_CAPABILITIES_FORMAT     = 0
21VOL_CAPABILITIES_INTERFACES = 1
22
23VOL_CAP_FMT_PERSISTENTOBJECTIDS       = 0x00000001
24VOL_CAP_FMT_SYMBOLICLINKS             = 0x00000002
25VOL_CAP_FMT_HARDLINKS                 = 0x00000004
26VOL_CAP_FMT_JOURNAL                   = 0x00000008
27VOL_CAP_FMT_JOURNAL_ACTIVE            = 0x00000010
28VOL_CAP_FMT_NO_ROOT_TIMES             = 0x00000020
29VOL_CAP_FMT_SPARSE_FILES              = 0x00000040
30VOL_CAP_FMT_ZERO_RUNS                 = 0x00000080
31VOL_CAP_FMT_CASE_SENSITIVE            = 0x00000100
32VOL_CAP_FMT_CASE_PRESERVING           = 0x00000200
33VOL_CAP_FMT_FAST_STATFS               = 0x00000400
34VOL_CAP_FMT_2TB_FILESIZE              = 0x00000800
35VOL_CAP_FMT_OPENDENYMODES             = 0x00001000
36VOL_CAP_FMT_HIDDEN_FILES              = 0x00002000
37VOL_CAP_FMT_PATH_FROM_ID              = 0x00004000
38VOL_CAP_FMT_NO_VOLUME_SIZES           = 0x00008000
39VOL_CAP_FMT_DECMPFS_COMPRESSION       = 0x00010000
40VOL_CAP_FMT_64BIT_OBJECT_IDS          = 0x00020000
41
42VOL_CAP_INT_SEARCHFS                  = 0x00000001
43VOL_CAP_INT_ATTRLIST                  = 0x00000002
44VOL_CAP_INT_NFSEXPORT                 = 0x00000004
45VOL_CAP_INT_READDIRATTR               = 0x00000008
46VOL_CAP_INT_EXCHANGEDATA              = 0x00000010
47VOL_CAP_INT_COPYFILE                  = 0x00000020
48VOL_CAP_INT_ALLOCATE                  = 0x00000040
49VOL_CAP_INT_VOL_RENAME                = 0x00000080
50VOL_CAP_INT_ADVLOCK                   = 0x00000100
51VOL_CAP_INT_FLOCK                     = 0x00000200
52VOL_CAP_INT_EXTENDED_SECURITY         = 0x00000400
53VOL_CAP_INT_USERACCESS                = 0x00000800
54VOL_CAP_INT_MANLOCK                   = 0x00001000
55VOL_CAP_INT_NAMEDSTREAMS              = 0x00002000
56VOL_CAP_INT_EXTENDED_ATTR             = 0x00004000
57
58ATTR_CMN_NAME                         = 0x00000001
59ATTR_CMN_DEVID                        = 0x00000002
60ATTR_CMN_FSID                         = 0x00000004
61ATTR_CMN_OBJTYPE                      = 0x00000008
62ATTR_CMN_OBJTAG                       = 0x00000010
63ATTR_CMN_OBJID                        = 0x00000020
64ATTR_CMN_OBJPERMANENTID               = 0x00000040
65ATTR_CMN_PAROBJID                     = 0x00000080
66ATTR_CMN_SCRIPT                       = 0x00000100
67ATTR_CMN_CRTIME                       = 0x00000200
68ATTR_CMN_MODTIME                      = 0x00000400
69ATTR_CMN_CHGTIME                      = 0x00000800
70ATTR_CMN_ACCTIME                      = 0x00001000
71ATTR_CMN_BKUPTIME                     = 0x00002000
72ATTR_CMN_FNDRINFO                     = 0x00004000
73ATTR_CMN_OWNERID                      = 0x00008000
74ATTR_CMN_GRPID                        = 0x00010000
75ATTR_CMN_ACCESSMASK                   = 0x00020000
76ATTR_CMN_FLAGS                        = 0x00040000
77ATTR_CMN_USERACCESS                   = 0x00200000
78ATTR_CMN_EXTENDED_SECURITY            = 0x00400000
79ATTR_CMN_UUID                         = 0x00800000
80ATTR_CMN_GRPUUID                      = 0x01000000
81ATTR_CMN_FILEID                       = 0x02000000
82ATTR_CMN_PARENTID                     = 0x04000000
83ATTR_CMN_FULLPATH                     = 0x08000000
84ATTR_CMN_ADDEDTIME                    = 0x10000000
85ATTR_CMN_RETURNED_ATTRS               = 0x80000000
86ATTR_CMN_ALL_ATTRS                    = 0x9fe7ffff
87
88ATTR_VOL_FSTYPE				          = 0x00000001
89ATTR_VOL_SIGNATURE			          = 0x00000002
90ATTR_VOL_SIZE				          = 0x00000004
91ATTR_VOL_SPACEFREE			          = 0x00000008
92ATTR_VOL_SPACEAVAIL			          = 0x00000010
93ATTR_VOL_MINALLOCATION			      = 0x00000020
94ATTR_VOL_ALLOCATIONCLUMP		      = 0x00000040
95ATTR_VOL_IOBLOCKSIZE			      = 0x00000080
96ATTR_VOL_OBJCOUNT			          = 0x00000100
97ATTR_VOL_FILECOUNT			          = 0x00000200
98ATTR_VOL_DIRCOUNT			          = 0x00000400
99ATTR_VOL_MAXOBJCOUNT			      = 0x00000800
100ATTR_VOL_MOUNTPOINT			          = 0x00001000
101ATTR_VOL_NAME				          = 0x00002000
102ATTR_VOL_MOUNTFLAGS			          = 0x00004000
103ATTR_VOL_MOUNTEDDEVICE			      = 0x00008000
104ATTR_VOL_ENCODINGSUSED			      = 0x00010000
105ATTR_VOL_CAPABILITIES			      = 0x00020000
106ATTR_VOL_UUID				          = 0x00040000
107ATTR_VOL_ATTRIBUTES			          = 0x40000000
108ATTR_VOL_INFO				          = 0x80000000
109ATTR_VOL_ALL_ATTRS                    = 0xc007ffff
110
111ATTR_DIR_LINKCOUNT			          = 0x00000001
112ATTR_DIR_ENTRYCOUNT			          = 0x00000002
113ATTR_DIR_MOUNTSTATUS			      = 0x00000004
114DIR_MNTSTATUS_MNTPOINT		          = 0x00000001
115DIR_MNTSTATUS_TRIGGER			      = 0x00000002
116ATTR_DIR_ALL_ATTRS                    = 0x00000007
117
118ATTR_FILE_LINKCOUNT			          = 0x00000001
119ATTR_FILE_TOTALSIZE			          = 0x00000002
120ATTR_FILE_ALLOCSIZE			          = 0x00000004
121ATTR_FILE_IOBLOCKSIZE			      = 0x00000008
122ATTR_FILE_DEVTYPE			          = 0x00000020
123ATTR_FILE_DATALENGTH			      = 0x00000200
124ATTR_FILE_DATAALLOCSIZE			      = 0x00000400
125ATTR_FILE_RSRCLENGTH			      = 0x00001000
126ATTR_FILE_RSRCALLOCSIZE			      = 0x00002000
127
128ATTR_FILE_ALL_ATTRS                   = 0x0000362f
129
130ATTR_FORK_TOTALSIZE			          = 0x00000001
131ATTR_FORK_ALLOCSIZE			          = 0x00000002
132ATTR_FORK_ALL_ATTRS                   = 0x00000003
133
134# These can't be used
135ATTR_FILE_FORKCOUNT			          = 0x00000080
136ATTR_FILE_FORKLIST			          = 0x00000100
137ATTR_CMN_NAMEDATTRCOUNT			      = 0x00080000
138ATTR_CMN_NAMEDATTRLIST			      = 0x00100000
139ATTR_FILE_DATAEXTENTS			      = 0x00000800
140ATTR_FILE_RSRCEXTENTS			      = 0x00004000
141ATTR_FILE_CLUMPSIZE			          = 0x00000010
142ATTR_FILE_FILETYPE			          = 0x00000040
143
144class attrlist(Structure):
145    _fields_ = [('bitmapcount', c_ushort),
146                ('reserved', c_ushort),
147                ('commonattr', c_uint),
148                ('volattr', c_uint),
149                ('dirattr', c_uint),
150                ('fileattr', c_uint),
151                ('forkattr', c_uint)]
152
153class attribute_set_t(Structure):
154    _fields_ = [('commonattr', c_uint),
155                ('volattr', c_uint),
156                ('dirattr', c_uint),
157                ('fileattr', c_uint),
158                ('forkattr', c_uint)]
159
160class fsobj_id_t(Structure):
161    _fields_ = [('fid_objno', c_uint),
162                ('fid_generation', c_uint)]
163
164class timespec(Structure):
165    _fields_ = [('tv_sec', c_long),
166                ('tv_nsec', c_long)]
167
168class attrreference_t(Structure):
169    _fields_ = [('attr_dataoffset', c_int),
170                ('attr_length', c_uint)]
171
172class fsid_t(Structure):
173    _fields_ = [('val', c_uint * 2)]
174
175class guid_t(Structure):
176    _fields_ = [('g_guid', c_byte*16)]
177
178class kauth_ace(Structure):
179    _fields_ = [('ace_applicable', guid_t),
180                ('ace_flags', c_uint)]
181
182class kauth_acl(Structure):
183    _fields_ = [('acl_entrycount', c_uint),
184                ('acl_flags', c_uint),
185                ('acl_ace', kauth_ace * 128)]
186
187class kauth_filesec(Structure):
188    _fields_ = [('fsec_magic', c_uint),
189                ('fsec_owner', guid_t),
190                ('fsec_group', guid_t),
191                ('fsec_acl', kauth_acl)]
192
193class diskextent(Structure):
194    _fields_ = [('startblock', c_uint),
195                ('blockcount', c_uint)]
196
197OSType = c_uint
198UInt16 = c_ushort
199SInt16 = c_short
200SInt32 = c_int
201
202class Point(Structure):
203    _fields_ = [('x', SInt16),
204                ('y', SInt16)]
205class Rect(Structure):
206    _fields_ = [('x', SInt16),
207                ('y', SInt16),
208                ('w', SInt16),
209                ('h', SInt16)]
210class FileInfo(Structure):
211    _fields_ = [('fileType', OSType),
212                ('fileCreator', OSType),
213                ('finderFlags', UInt16),
214                ('location', Point),
215                ('reservedField', UInt16),
216                ('reserved1', SInt16 * 4),
217                ('extendedFinderFlags', UInt16),
218                ('reserved2', SInt16),
219                ('putAwayFolderID', SInt32)]
220class FolderInfo(Structure):
221    _fields_ = [('windowBounds', Rect),
222                ('finderFlags', UInt16),
223                ('location', Point),
224                ('reservedField', UInt16),
225                ('scrollPosition', Point),
226                ('reserved1', SInt32),
227                ('extendedFinderFlags', UInt16),
228                ('reserved2', SInt16),
229                ('putAwayFolderID', SInt32)]
230class FinderInfo(Union):
231    _fields_ = [('fileInfo', FileInfo),
232                ('folderInfo', FolderInfo)]
233
234extentrecord = diskextent * 8
235
236vol_capabilities_set_t = c_uint * 4
237
238class vol_capabilities_attr_t(Structure):
239    _fields_ = [('capabilities', vol_capabilities_set_t),
240                ('valid', vol_capabilities_set_t)]
241
242class vol_attributes_attr_t(Structure):
243    _fields_ = [('validattr', attribute_set_t),
244                ('nativeattr', attribute_set_t)]
245
246dev_t = c_uint
247
248fsobj_type_t = c_uint
249
250VNON = 0
251VREG = 1
252VDIR = 2
253VBLK = 3
254VCHR = 4
255VLNK = 5
256VSOCK = 6
257VFIFO = 7
258VBAD = 8
259VSTR = 9
260VCPLX = 10
261
262fsobj_tag_t = c_uint
263
264VT_NON = 0
265VT_UFS = 1
266VT_NFS = 2
267VT_MFS = 3
268VT_MSDOSFS = 4
269VT_LFS = 5
270VT_LOFS = 6
271VT_FDESC = 7
272VT_PORTAL = 8
273VT_NULL = 9
274VT_UMAP = 10
275VT_KERNFS = 11
276VT_PROCFS = 12
277VT_AFS = 13
278VT_ISOFS = 14
279VT_UNION = 15
280VT_HFS = 16
281VT_ZFS = 17
282VT_DEVFS = 18
283VT_WEBDAV = 19
284VT_UDF = 20
285VT_AFP = 21
286VT_CDDA = 22
287VT_CIFS = 23
288VT_OTHER = 24
289
290fsfile_type_t = c_uint
291fsvolid_t = c_uint
292text_encoding_t = c_uint
293uid_t = c_uint
294gid_t = c_uint
295int32_t = c_int
296uint32_t = c_uint
297int64_t = c_longlong
298uint64_t = c_ulonglong
299off_t = c_long
300size_t = c_ulong
301uuid_t = c_byte*16
302
303NAME_MAX = 255
304PATH_MAX = 1024
305
306class struct_statfs(Structure):
307    _fields_ = [('f_bsize', uint32_t),
308                ('f_iosize', int32_t),
309                ('f_blocks', uint64_t),
310                ('f_bfree', uint64_t),
311                ('f_bavail', uint64_t),
312                ('f_files', uint64_t),
313                ('f_ffree', uint64_t),
314                ('f_fsid', fsid_t),
315                ('f_owner', uid_t),
316                ('f_type', uint32_t),
317                ('f_flags', uint32_t),
318                ('f_fssubtype', uint32_t),
319                ('f_fstypename', c_char * 16),
320                ('f_mntonname', c_char * PATH_MAX),
321                ('f_mntfromname', c_char * PATH_MAX),
322                ('f_reserved', uint32_t * 8)]
323
324# Calculate the maximum number of bytes required for the attribute buffer
325_attr_info = (
326    # Common attributes
327    (0, ATTR_CMN_RETURNED_ATTRS, sizeof(attribute_set_t)),
328    (0, ATTR_CMN_NAME, sizeof(attrreference_t) + NAME_MAX * 3 + 1),
329    (0, ATTR_CMN_DEVID, sizeof(dev_t)),
330    (0, ATTR_CMN_FSID, sizeof(fsid_t)),
331    (0, ATTR_CMN_OBJTYPE, sizeof(fsobj_type_t)),
332    (0, ATTR_CMN_OBJTAG, sizeof(fsobj_tag_t)),
333    (0, ATTR_CMN_OBJPERMANENTID, sizeof(fsobj_id_t)),
334    (0, ATTR_CMN_PAROBJID, sizeof(fsobj_id_t)),
335    (0, ATTR_CMN_SCRIPT, sizeof(text_encoding_t)),
336    (0, ATTR_CMN_CRTIME, sizeof(timespec)),
337    (0, ATTR_CMN_MODTIME, sizeof(timespec)),
338    (0, ATTR_CMN_CHGTIME, sizeof(timespec)),
339    (0, ATTR_CMN_ACCTIME, sizeof(timespec)),
340    (0, ATTR_CMN_BKUPTIME, sizeof(timespec)),
341    (0, ATTR_CMN_FNDRINFO, sizeof(FinderInfo)),
342    (0, ATTR_CMN_OWNERID, sizeof(uid_t)),
343    (0, ATTR_CMN_GRPID, sizeof(gid_t)),
344    (0, ATTR_CMN_ACCESSMASK, sizeof(uint32_t)),
345    (0, ATTR_CMN_NAMEDATTRCOUNT, None),
346    (0, ATTR_CMN_NAMEDATTRLIST, None),
347    (0, ATTR_CMN_FLAGS, sizeof(uint32_t)),
348    (0, ATTR_CMN_USERACCESS, sizeof(uint32_t)),
349    (0, ATTR_CMN_EXTENDED_SECURITY, sizeof(attrreference_t) + sizeof(kauth_filesec)),
350    (0, ATTR_CMN_UUID, sizeof(guid_t)),
351    (0, ATTR_CMN_GRPUUID, sizeof(guid_t)),
352    (0, ATTR_CMN_FILEID, sizeof(uint64_t)),
353    (0, ATTR_CMN_PARENTID, sizeof(uint64_t)),
354    (0, ATTR_CMN_FULLPATH, sizeof(attrreference_t) + PATH_MAX),
355    (0, ATTR_CMN_ADDEDTIME, sizeof(timespec)),
356
357    # Volume attributes
358    (1, ATTR_VOL_FSTYPE, sizeof(uint32_t)),
359    (1, ATTR_VOL_SIGNATURE, sizeof(uint32_t)),
360    (1, ATTR_VOL_SIZE, sizeof(off_t)),
361    (1, ATTR_VOL_SPACEFREE, sizeof(off_t)),
362    (1, ATTR_VOL_SPACEAVAIL, sizeof(off_t)),
363    (1, ATTR_VOL_MINALLOCATION, sizeof(off_t)),
364    (1, ATTR_VOL_ALLOCATIONCLUMP, sizeof(off_t)),
365    (1, ATTR_VOL_IOBLOCKSIZE, sizeof(uint32_t)),
366    (1, ATTR_VOL_OBJCOUNT, sizeof(uint32_t)),
367    (1, ATTR_VOL_FILECOUNT, sizeof(uint32_t)),
368    (1, ATTR_VOL_DIRCOUNT, sizeof(uint32_t)),
369    (1, ATTR_VOL_MAXOBJCOUNT, sizeof(uint32_t)),
370    (1, ATTR_VOL_MOUNTPOINT, sizeof(attrreference_t) + PATH_MAX),
371    (1, ATTR_VOL_NAME, sizeof(attrreference_t) + NAME_MAX + 1),
372    (1, ATTR_VOL_MOUNTFLAGS, sizeof(uint32_t)),
373    (1, ATTR_VOL_MOUNTEDDEVICE, sizeof(attrreference_t) + PATH_MAX),
374    (1, ATTR_VOL_ENCODINGSUSED, sizeof(c_ulonglong)),
375    (1, ATTR_VOL_CAPABILITIES, sizeof(vol_capabilities_attr_t)),
376    (1, ATTR_VOL_UUID, sizeof(uuid_t)),
377    (1, ATTR_VOL_ATTRIBUTES, sizeof(vol_attributes_attr_t)),
378
379    # Directory attributes
380    (2, ATTR_DIR_LINKCOUNT, sizeof(uint32_t)),
381    (2, ATTR_DIR_ENTRYCOUNT, sizeof(uint32_t)),
382    (2, ATTR_DIR_MOUNTSTATUS, sizeof(uint32_t)),
383
384    # File attributes
385    (3, ATTR_FILE_LINKCOUNT, sizeof(uint32_t)),
386    (3, ATTR_FILE_TOTALSIZE, sizeof(off_t)),
387    (3, ATTR_FILE_ALLOCSIZE, sizeof(off_t)),
388    (3, ATTR_FILE_IOBLOCKSIZE, sizeof(uint32_t)),
389    (3, ATTR_FILE_CLUMPSIZE, sizeof(uint32_t)),
390    (3, ATTR_FILE_DEVTYPE, sizeof(uint32_t)),
391    (3, ATTR_FILE_FILETYPE, sizeof(uint32_t)),
392    (3, ATTR_FILE_FORKCOUNT, sizeof(uint32_t)),
393    (3, ATTR_FILE_FORKLIST, None),
394    (3, ATTR_FILE_DATALENGTH, sizeof(off_t)),
395    (3, ATTR_FILE_DATAALLOCSIZE, sizeof(off_t)),
396    (3, ATTR_FILE_DATAEXTENTS, sizeof(extentrecord)),
397    (3, ATTR_FILE_RSRCLENGTH, sizeof(off_t)),
398    (3, ATTR_FILE_RSRCALLOCSIZE, sizeof(off_t)),
399    (3, ATTR_FILE_RSRCEXTENTS, sizeof(extentrecord)),
400
401    # Fork attributes
402    (4, ATTR_FORK_TOTALSIZE, sizeof(off_t)),
403    (4, ATTR_FORK_ALLOCSIZE, sizeof(off_t))
404    )
405
406def _attrbuf_size(attrs):
407    size = 4
408    for entry in _attr_info:
409        if attrs[entry[0]] & entry[1]:
410            if entry[2] is None:
411                raise ValueError('Unsupported attribute (%u, %x)'
412                                 % (entry[0], entry[1]))
413            size += entry[2]
414    return size
415
416_getattrlist = libc.getattrlist
417_getattrlist.argtypes = [c_char_p, POINTER(attrlist), c_void_p, c_ulong, c_ulong]
418_getattrlist.restype = c_int
419
420_fgetattrlist = libc.fgetattrlist
421_fgetattrlist.argtypes = [c_int, POINTER(attrlist), c_void_p, c_ulong, c_ulong]
422_fgetattrlist.restype = c_int
423
424_statfs = libc['statfs$INODE64']
425_statfs.argtypes = [c_char_p, POINTER(struct_statfs)]
426_statfs.restype = c_int
427
428_fstatfs = libc['fstatfs$INODE64']
429_fstatfs.argtypes = [c_int, POINTER(struct_statfs)]
430_fstatfs.restype = c_int
431
432def _datetime_from_timespec(ts):
433    td = datetime.timedelta(seconds=ts.tv_sec + 1.0e-9 * ts.tv_nsec)
434    return unix_epoch + td
435
436def _decode_utf8_nul(sz):
437    nul = sz.find(b'\0')
438    if nul > -1:
439        sz = sz[:nul]
440    return sz.decode('utf-8')
441
442def _decode_attrlist_result(buf, attrs, options):
443    result = []
444
445    assert len(buf) >= 4
446    total_size = uint32_t.from_buffer(buf, 0).value
447    assert total_size <= len(buf)
448
449    offset = 4
450
451    # Common attributes
452    if attrs[0] & ATTR_CMN_RETURNED_ATTRS:
453        a = attribute_set_t.from_buffer(buf, offset)
454        result.append(a)
455        offset += sizeof (attribute_set_t)
456        if not (options & FSOPT_PACK_INVAL_ATTRS):
457            attrs = [a.commonattr, a.volattr, a.dirattr, a.fileattr, a.forkattr]
458    if attrs[0] & ATTR_CMN_NAME:
459        a = attrreference_t.from_buffer(buf, offset)
460        ofs = offset + a.attr_dataoffset
461        name = _decode_utf8_nul(buf[ofs:ofs+a.attr_length])
462        offset += sizeof (attrreference_t)
463        result.append(name)
464    if attrs[0] & ATTR_CMN_DEVID:
465        a = dev_t.from_buffer(buf, offset)
466        offset += sizeof(dev_t)
467        result.append(a.value)
468    if attrs[0] & ATTR_CMN_FSID:
469        a = fsid_t.from_buffer(buf, offset)
470        offset += sizeof(fsid_t)
471        result.append(a)
472    if attrs[0] & ATTR_CMN_OBJTYPE:
473        a = fsobj_type_t.from_buffer(buf, offset)
474        offset += sizeof(fsobj_type_t)
475        result.append(a.value)
476    if attrs[0] & ATTR_CMN_OBJTAG:
477        a = fsobj_tag_t.from_buffer(buf, offset)
478        offset += sizeof(fsobj_tag_t)
479        result.append(a.value)
480    if attrs[0] & ATTR_CMN_OBJID:
481        a = fsobj_id_t.from_buffer(buf, offset)
482        offset += sizeof(fsobj_id_t)
483        result.append(a)
484    if attrs[0] & ATTR_CMN_OBJPERMANENTID:
485        a = fsobj_id_t.from_buffer(buf, offset)
486        offset += sizeof(fsobj_id_t)
487        result.append(a)
488    if attrs[0] & ATTR_CMN_PAROBJID:
489        a = fsobj_id_t.from_buffer(buf, offset)
490        offset += sizeof(fsobj_id_t)
491        result.append(a)
492    if attrs[0] & ATTR_CMN_SCRIPT:
493        a = text_encoding_t.from_buffer(buf, offset)
494        offset += sizeof(text_encoding_t)
495        result.append(a.value)
496    if attrs[0] & ATTR_CMN_CRTIME:
497        a = timespec.from_buffer(buf, offset)
498        offset += sizeof(timespec)
499        result.append(_datetime_from_timespec(a))
500    if attrs[0] & ATTR_CMN_MODTIME:
501        a = timespec.from_buffer(buf, offset)
502        offset += sizeof(timespec)
503        result.append(_datetime_from_timespec(a))
504    if attrs[0] & ATTR_CMN_CHGTIME:
505        a = timespec.from_buffer(buf, offset)
506        offset += sizeof(timespec)
507        result.append(_datetime_from_timespec(a))
508    if attrs[0] & ATTR_CMN_ACCTIME:
509        a = timespec.from_buffer(buf, offset)
510        offset += sizeof(timespec)
511        result.append(_datetime_from_timespec(a))
512    if attrs[0] & ATTR_CMN_BKUPTIME:
513        a = timespec.from_buffer(buf, offset)
514        offset += sizeof(timespec)
515        result.append(_datetime_from_timespec(a))
516    if attrs[0] & ATTR_CMN_FNDRINFO:
517        a = FinderInfo.from_buffer(buf, offset)
518        offset += sizeof(FinderInfo)
519        result.append(a)
520    if attrs[0] & ATTR_CMN_OWNERID:
521        a = uid_t.from_buffer(buf, offset)
522        offset += sizeof(uid_t)
523        result.append(a.value)
524    if attrs[0] & ATTR_CMN_GRPID:
525        a = gid_t.from_buffer(buf, offset)
526        offset += sizeof(gid_t)
527        result.append(a.value)
528    if attrs[0] & ATTR_CMN_ACCESSMASK:
529        a = uint32_t.from_buffer(buf, offset)
530        offset += sizeof(uint32_t)
531        result.append(a.value)
532    if attrs[0] & ATTR_CMN_FLAGS:
533        a = uint32_t.from_buffer(buf, offset)
534        offset += sizeof(uint32_t)
535        result.append(a.value)
536    if attrs[0] & ATTR_CMN_USERACCESS:
537        a = uint32_t.from_buffer(buf, offset)
538        offset += sizeof(uint32_t)
539        result.append(a.value)
540    if attrs[0] & ATTR_CMN_EXTENDED_SECURITY:
541        a = attrreference_t.from_buffer(buf, offset)
542        ofs = offset + a.attr_dataoffset
543        offset += sizeof(attrreference_t)
544        ec = uint32_t.from_buffer(buf, ofs + 36).value
545        class kauth_acl(Structure):
546            _fields_ = [('acl_entrycount', c_uint),
547                        ('acl_flags', c_uint),
548                        ('acl_ace', kauth_ace * ec)]
549        class kauth_filesec(Structure):
550            _fields_ = [('fsec_magic', c_uint),
551                        ('fsec_owner', guid_t),
552                        ('fsec_group', guid_t),
553                        ('fsec_acl', kauth_acl)]
554        a = kauth_filesec.from_buffer(buf, ofs)
555        result.append(a)
556    if attrs[0] & ATTR_CMN_UUID:
557        result.append(uuid.UUID(bytes=buf[offset:offset+16]))
558        offset += sizeof(guid_t)
559    if attrs[0] & ATTR_CMN_GRPUUID:
560        result.append(uuid.UUID(bytes=buf[offset:offset+16]))
561        offset += sizeof(guid_t)
562    if attrs[0] & ATTR_CMN_FILEID:
563        a = uint64_t.from_buffer(buf, offset)
564        offset += sizeof(uint64_t)
565        result.append(a.value)
566    if attrs[0] & ATTR_CMN_PARENTID:
567        a = uint64_t.from_buffer(buf, offset)
568        offset += sizeof(uint64_t)
569        result.append(a.value)
570    if attrs[0] & ATTR_CMN_FULLPATH:
571        a = attrreference_t.from_buffer(buf, offset)
572        ofs = offset + a.attr_dataoffset
573        path = _decode_utf8_nul(buf[ofs:ofs+a.attr_length])
574        offset += sizeof (attrreference_t)
575        result.append(path)
576    if attrs[0] & ATTR_CMN_ADDEDTIME:
577        a = timespec.from_buffer(buf, offset)
578        offset += sizeof(timespec)
579        result.append(_datetime_from_timespec(a))
580
581    # Volume attributes
582    if attrs[1] & ATTR_VOL_FSTYPE:
583        a = uint32_t.from_buffer(buf, offset)
584        offset += sizeof(uint32_t)
585        result.append(a.value)
586    if attrs[1] & ATTR_VOL_SIGNATURE:
587        a = uint32_t.from_buffer(buf, offset)
588        offset += sizeof(uint32_t)
589        result.append(a.value)
590    if attrs[1] & ATTR_VOL_SIZE:
591        a = off_t.from_buffer(buf, offset)
592        offset += sizeof(off_t)
593        result.append(a.value)
594    if attrs[1] & ATTR_VOL_SPACEFREE:
595        a = off_t.from_buffer(buf, offset)
596        offset += sizeof(off_t)
597        result.append(a.value)
598    if attrs[1] & ATTR_VOL_SPACEAVAIL:
599        a = off_t.from_buffer(buf, offset)
600        offset += sizeof(off_t)
601        result.append(a.value)
602    if attrs[1] & ATTR_VOL_MINALLOCATION:
603        a = off_t.from_buffer(buf, offset)
604        offset += sizeof(off_t)
605        result.append(a.value)
606    if attrs[1] & ATTR_VOL_ALLOCATIONCLUMP:
607        a = off_t.from_buffer(buf, offset)
608        offset += sizeof(off_t)
609        result.append(a.value)
610    if attrs[1] & ATTR_VOL_IOBLOCKSIZE:
611        a = uint32_t.from_buffer(buf, offset)
612        offset += sizeof(uint32_t)
613        result.append(a.value)
614    if attrs[1] & ATTR_VOL_OBJCOUNT:
615        a = uint32_t.from_buffer(buf, offset)
616        offset += sizeof(uint32_t)
617        result.append(a.value)
618    if attrs[1] & ATTR_VOL_FILECOUNT:
619        a = uint32_t.from_buffer(buf, offset)
620        offset += sizeof(uint32_t)
621        result.append(a.value)
622    if attrs[1] & ATTR_VOL_DIRCOUNT:
623        a = uint32_t.from_buffer(buf, offset)
624        offset += sizeof(uint32_t)
625        result.append(a.value)
626    if attrs[1] & ATTR_VOL_MAXOBJCOUNT:
627        a = uint32_t.from_buffer(buf, offset)
628        offset += sizeof(uint32_t)
629        result.append(a.value)
630    if attrs[1] & ATTR_VOL_MOUNTPOINT:
631        a = attrreference_t.from_buffer(buf, offset)
632        ofs = offset + a.attr_dataoffset
633        path = _decode_utf8_nul(buf[ofs:ofs+a.attr_length])
634        offset += sizeof (attrreference_t)
635        result.append(path)
636    if attrs[1] & ATTR_VOL_NAME:
637        a = attrreference_t.from_buffer(buf, offset)
638        ofs = offset + a.attr_dataoffset
639        name = _decode_utf8_nul(buf[ofs:ofs+a.attr_length])
640        offset += sizeof (attrreference_t)
641        result.append(name)
642    if attrs[1] & ATTR_VOL_MOUNTFLAGS:
643        a = uint32_t.from_buffer(buf, offset)
644        offset += sizeof(uint32_t)
645        result.append(a.value)
646    if attrs[1] & ATTR_VOL_MOUNTEDDEVICE:
647        a = attrreference_t.from_buffer(buf, offset)
648        ofs = offset + a.attr_dataoffset
649        path = _decode_utf8_nul(buf[ofs:ofs+a.attr_length])
650        offset += sizeof (attrreference_t)
651        result.append(path)
652    if attrs[1] & ATTR_VOL_ENCODINGSUSED:
653        a = c_ulonglong.from_buffer(buf, offset)
654        offset += sizeof(c_ulonglong)
655        result.append(a.value)
656    if attrs[1] & ATTR_VOL_CAPABILITIES:
657        a = vol_capabilities_attr_t.from_buffer(buf, offset)
658        offset += sizeof(vol_capabilities_attr_t)
659        result.append(a)
660    if attrs[1] & ATTR_VOL_UUID:
661        result.append(uuid.UUID(bytes=buf[offset:offset+16]))
662        offset += sizeof(uuid_t)
663    if attrs[1] & ATTR_VOL_ATTRIBUTES:
664        a = vol_attributes_attr_t.from_buffer(buf, offset)
665        offset += sizeof(vol_attributes_attr_t)
666        result.append(a)
667
668    # Directory attributes
669    if attrs[2] & ATTR_DIR_LINKCOUNT:
670        a = uint32_t.from_buffer(buf, offset)
671        offset += sizeof(uint32_t)
672        result.append(a.value)
673    if attrs[2] & ATTR_DIR_ENTRYCOUNT:
674        a = uint32_t.from_buffer(buf, offset)
675        offset += sizeof(uint32_t)
676        result.append(a.value)
677    if attrs[2] & ATTR_DIR_MOUNTSTATUS:
678        a = uint32_t.from_buffer(buf, offset)
679        offset += sizeof(uint32_t)
680        result.append(a.value)
681
682    # File attributes
683    if attrs[3] & ATTR_FILE_LINKCOUNT:
684        a = uint32_t.from_buffer(buf, offset)
685        offset += sizeof(uint32_t)
686        result.append(a.value)
687    if attrs[3] & ATTR_FILE_TOTALSIZE:
688        a = off_t.from_buffer(buf, offset)
689        offset += sizeof(off_t)
690        result.append(a.value)
691    if attrs[3] & ATTR_FILE_ALLOCSIZE:
692        a = off_t.from_buffer(buf, offset)
693        offset += sizeof(off_t)
694        result.append(a.value)
695    if attrs[3] & ATTR_FILE_IOBLOCKSIZE:
696        a = uint32_t.from_buffer(buf, offset)
697        offset += sizeof(uint32_t)
698        result.append(a.value)
699    if attrs[3] & ATTR_FILE_CLUMPSIZE:
700        a = uint32_t.from_buffer(buf, offset)
701        offset += sizeof(uint32_t)
702        result.append(a.value)
703    if attrs[3] & ATTR_FILE_DEVTYPE:
704        a = uint32_t.from_buffer(buf, offset)
705        offset += sizeof(uint32_t)
706        result.append(a.value)
707    if attrs[3] & ATTR_FILE_FILETYPE:
708        a = uint32_t.from_buffer(buf, offset)
709        offset += sizeof(uint32_t)
710        result.append(a.value)
711    if attrs[3] & ATTR_FILE_FORKCOUNT:
712        a = uint32_t.from_buffer(buf, offset)
713        offset += sizeof(uint32_t)
714        result.append(a.value)
715    if attrs[3] & ATTR_FILE_DATALENGTH:
716        a = off_t.from_buffer(buf, offset)
717        offset += sizeof(off_t)
718        result.append(a.value)
719    if attrs[3] & ATTR_FILE_DATAALLOCSIZE:
720        a = off_t.from_buffer(buf, offset)
721        offset += sizeof(off_t)
722        result.append(a.value)
723    if attrs[3] & ATTR_FILE_DATAEXTENTS:
724        a = extentrecord.from_buffer(buf, offset)
725        offset += sizeof(extentrecord)
726        result.append(a.value)
727    if attrs[3] & ATTR_FILE_RSRCLENGTH:
728        a = off_t.from_buffer(buf, offset)
729        offset += sizeof(off_t)
730        result.append(a.value)
731    if attrs[3] & ATTR_FILE_RSRCALLOCSIZE:
732        a = off_t.from_buffer(buf, offset)
733        offset += sizeof(off_t)
734        result.append(a.value)
735    if attrs[3] & ATTR_FILE_RSRCEXTENTS:
736        a = extentrecord.from_buffer(buf, offset)
737        offset += sizeof(extentrecord)
738        result.append(a.value)
739
740    # Fork attributes
741    if attrs[4] & ATTR_FORK_TOTALSIZE:
742        a = off_t.from_buffer(buf, offset)
743        offset += sizeof(off_t)
744        result.append(a.value)
745    if attrs[4] & ATTR_FORK_ALLOCSIZE:
746        a = off_t.from_buffer(buf, offset)
747        offset += sizeof(off_t)
748        result.append(a.value)
749
750    return result
751
752# Sadly, ctypes.get_errno() seems not to work
753__error = libc.__error
754__error.restype = POINTER(c_int)
755
756def _get_errno():
757    return __error().contents.value
758
759def getattrlist(path, attrs, options):
760    if not isinstance(path, bytes):
761        path = path.encode('utf-8')
762    attrs = list(attrs)
763    if attrs[1]:
764        attrs[1] |= ATTR_VOL_INFO
765    alist = attrlist(bitmapcount=5,
766                     commonattr=attrs[0],
767                     volattr=attrs[1],
768                     dirattr=attrs[2],
769                     fileattr=attrs[3],
770                     forkattr=attrs[4])
771
772    bufsize = _attrbuf_size(attrs)
773    buf = create_string_buffer(bufsize)
774
775    ret = _getattrlist(path, byref(alist), buf, bufsize,
776                       options | FSOPT_REPORT_FULLSIZE)
777
778    if ret < 0:
779        err = _get_errno()
780        raise OSError(err, os.strerror(err), path)
781
782    return _decode_attrlist_result(buf, attrs, options)
783
784def fgetattrlist(fd, attrs, options):
785    if hasattr(fd, 'fileno'):
786        fd = fd.fileno()
787    attrs = list(attrs)
788    if attrs[1]:
789        attrs[1] |= ATTR_VOL_INFO
790    alist = attrlist(bitmapcount=5,
791                     commonattr=attrs[0],
792                     volattr=attrs[1],
793                     dirattr=attrs[2],
794                     fileattr=attrs[3],
795                     forkattr=attrs[4])
796
797    bufsize = _attrbuf_size(attrs)
798    buf = create_string_buffer(bufsize)
799
800    ret = _fgetattrlist(fd, byref(alist), buf, bufsize,
801                        options | FSOPT_REPORT_FULLSIZE)
802
803    if ret < 0:
804        err = _get_errno()
805        raise OSError(err, os.strerror(err))
806
807    return _decode_attrlist_result(buf, attrs, options)
808
809def statfs(path):
810    if not isinstance(path, bytes):
811        path = path.encode('utf-8')
812    result = struct_statfs()
813    ret = _statfs(path, byref(result))
814    if ret < 0:
815        err = _get_errno()
816        raise OSError(err, os.strerror(err), path)
817    return result
818
819def fstatfs(fd):
820    if hasattr(fd, 'fileno'):
821        fd = fd.fileno()
822    result = struct_statfs()
823    ret = _fstatfs(fd, byref(result))
824    if ret < 0:
825        err = _get_errno()
826        raise OSError(err, os.strerror(err))
827    return result
828