|
1 # osutil.py - CFFI version of osutil.c |
|
2 # |
|
3 # Copyright 2016 Maciej Fijalkowski <fijall@gmail.com> |
|
4 # |
|
5 # This software may be used and distributed according to the terms of the |
|
6 # GNU General Public License version 2 or any later version. |
|
7 |
1 from __future__ import absolute_import |
8 from __future__ import absolute_import |
2 |
9 |
3 import cffi |
10 import os |
|
11 import stat as statmod |
4 |
12 |
5 ffi = cffi.FFI() |
13 from ..pure.osutil import * |
6 ffi.set_source("_osutil_cffi", """ |
|
7 #include <sys/attr.h> |
|
8 #include <sys/vnode.h> |
|
9 #include <unistd.h> |
|
10 #include <fcntl.h> |
|
11 #include <time.h> |
|
12 |
14 |
13 typedef struct val_attrs { |
15 from .. import ( |
14 uint32_t length; |
16 pycompat, |
15 attribute_set_t returned; |
17 ) |
16 attrreference_t name_info; |
|
17 fsobj_type_t obj_type; |
|
18 struct timespec mtime; |
|
19 uint32_t accessmask; |
|
20 off_t datalength; |
|
21 } __attribute__((aligned(4), packed)) val_attrs_t; |
|
22 """, include_dirs=['mercurial']) |
|
23 ffi.cdef(''' |
|
24 |
18 |
25 typedef uint32_t attrgroup_t; |
19 if pycompat.sysplatform == 'darwin': |
|
20 from . import _osutil |
26 |
21 |
27 typedef struct attrlist { |
22 ffi = _osutil.ffi |
28 uint16_t bitmapcount; /* number of attr. bit sets in list */ |
23 lib = _osutil.lib |
29 uint16_t reserved; /* (to maintain 4-byte alignment) */ |
|
30 attrgroup_t commonattr; /* common attribute group */ |
|
31 attrgroup_t volattr; /* volume attribute group */ |
|
32 attrgroup_t dirattr; /* directory attribute group */ |
|
33 attrgroup_t fileattr; /* file attribute group */ |
|
34 attrgroup_t forkattr; /* fork attribute group */ |
|
35 ...; |
|
36 }; |
|
37 |
24 |
38 typedef struct attribute_set { |
25 listdir_batch_size = 4096 |
39 ...; |
26 # tweakable number, only affects performance, which chunks |
40 } attribute_set_t; |
27 # of bytes do we get back from getattrlistbulk |
41 |
28 |
42 typedef struct attrreference { |
29 attrkinds = [None] * 20 # we need the max no for enum VXXX, 20 is plenty |
43 int attr_dataoffset; |
|
44 int attr_length; |
|
45 ...; |
|
46 } attrreference_t; |
|
47 |
30 |
48 typedef int ... off_t; |
31 attrkinds[lib.VREG] = statmod.S_IFREG |
|
32 attrkinds[lib.VDIR] = statmod.S_IFDIR |
|
33 attrkinds[lib.VLNK] = statmod.S_IFLNK |
|
34 attrkinds[lib.VBLK] = statmod.S_IFBLK |
|
35 attrkinds[lib.VCHR] = statmod.S_IFCHR |
|
36 attrkinds[lib.VFIFO] = statmod.S_IFIFO |
|
37 attrkinds[lib.VSOCK] = statmod.S_IFSOCK |
49 |
38 |
50 typedef struct val_attrs { |
39 class stat_res(object): |
51 uint32_t length; |
40 def __init__(self, st_mode, st_mtime, st_size): |
52 attribute_set_t returned; |
41 self.st_mode = st_mode |
53 attrreference_t name_info; |
42 self.st_mtime = st_mtime |
54 uint32_t obj_type; |
43 self.st_size = st_size |
55 struct timespec mtime; |
|
56 uint32_t accessmask; |
|
57 off_t datalength; |
|
58 ...; |
|
59 } val_attrs_t; |
|
60 |
44 |
61 /* the exact layout of the above struct will be figured out during build time */ |
45 tv_sec_ofs = ffi.offsetof("struct timespec", "tv_sec") |
|
46 buf = ffi.new("char[]", listdir_batch_size) |
62 |
47 |
63 typedef int ... time_t; |
48 def listdirinternal(dfd, req, stat, skip): |
|
49 ret = [] |
|
50 while True: |
|
51 r = lib.getattrlistbulk(dfd, req, buf, listdir_batch_size, 0) |
|
52 if r == 0: |
|
53 break |
|
54 if r == -1: |
|
55 raise OSError(ffi.errno, os.strerror(ffi.errno)) |
|
56 cur = ffi.cast("val_attrs_t*", buf) |
|
57 for i in range(r): |
|
58 lgt = cur.length |
|
59 assert lgt == ffi.cast('uint32_t*', cur)[0] |
|
60 ofs = cur.name_info.attr_dataoffset |
|
61 str_lgt = cur.name_info.attr_length |
|
62 base_ofs = ffi.offsetof('val_attrs_t', 'name_info') |
|
63 name = str(ffi.buffer(ffi.cast("char*", cur) + base_ofs + ofs, |
|
64 str_lgt - 1)) |
|
65 tp = attrkinds[cur.obj_type] |
|
66 if name == "." or name == "..": |
|
67 continue |
|
68 if skip == name and tp == statmod.S_ISDIR: |
|
69 return [] |
|
70 if stat: |
|
71 mtime = cur.mtime.tv_sec |
|
72 mode = (cur.accessmask & ~lib.S_IFMT)| tp |
|
73 ret.append((name, tp, stat_res(st_mode=mode, st_mtime=mtime, |
|
74 st_size=cur.datalength))) |
|
75 else: |
|
76 ret.append((name, tp)) |
|
77 cur = ffi.cast("val_attrs_t*", int(ffi.cast("intptr_t", cur)) |
|
78 + lgt) |
|
79 return ret |
64 |
80 |
65 typedef struct timespec { |
81 def listdir(path, stat=False, skip=None): |
66 time_t tv_sec; |
82 req = ffi.new("struct attrlist*") |
67 ...; |
83 req.bitmapcount = lib.ATTR_BIT_MAP_COUNT |
68 }; |
84 req.commonattr = (lib.ATTR_CMN_RETURNED_ATTRS | |
|
85 lib.ATTR_CMN_NAME | |
|
86 lib.ATTR_CMN_OBJTYPE | |
|
87 lib.ATTR_CMN_ACCESSMASK | |
|
88 lib.ATTR_CMN_MODTIME) |
|
89 req.fileattr = lib.ATTR_FILE_DATALENGTH |
|
90 dfd = lib.open(path, lib.O_RDONLY, 0) |
|
91 if dfd == -1: |
|
92 raise OSError(ffi.errno, os.strerror(ffi.errno)) |
69 |
93 |
70 int getattrlist(const char* path, struct attrlist * attrList, void * attrBuf, |
94 try: |
71 size_t attrBufSize, unsigned int options); |
95 ret = listdirinternal(dfd, req, stat, skip) |
72 |
96 finally: |
73 int getattrlistbulk(int dirfd, struct attrlist * attrList, void * attrBuf, |
97 try: |
74 size_t attrBufSize, uint64_t options); |
98 lib.close(dfd) |
75 |
99 except BaseException: |
76 #define ATTR_BIT_MAP_COUNT ... |
100 pass # we ignore all the errors from closing, not |
77 #define ATTR_CMN_NAME ... |
101 # much we can do about that |
78 #define ATTR_CMN_OBJTYPE ... |
102 return ret |
79 #define ATTR_CMN_MODTIME ... |
|
80 #define ATTR_CMN_ACCESSMASK ... |
|
81 #define ATTR_CMN_ERROR ... |
|
82 #define ATTR_CMN_RETURNED_ATTRS ... |
|
83 #define ATTR_FILE_DATALENGTH ... |
|
84 |
|
85 #define VREG ... |
|
86 #define VDIR ... |
|
87 #define VLNK ... |
|
88 #define VBLK ... |
|
89 #define VCHR ... |
|
90 #define VFIFO ... |
|
91 #define VSOCK ... |
|
92 |
|
93 #define S_IFMT ... |
|
94 |
|
95 int open(const char *path, int oflag, int perm); |
|
96 int close(int); |
|
97 |
|
98 #define O_RDONLY ... |
|
99 ''') |
|
100 |
|
101 if __name__ == '__main__': |
|
102 ffi.compile() |
|