Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 336 lines (275 sloc) 9.576 kb
e5ea23a @g2p Split into the main entry point and a btrfs module.
authored
1 # vim: set fileencoding=utf-8 sw=4 ts=4 et :
2
3 import cffi
4 import fcntl
5
6
7 from os import getcwd # XXX
8
9
10 ffi = cffi.FFI()
11
12 ffi.cdef("""
13 /* ioctl.h */
14
15 #define BTRFS_IOC_TREE_SEARCH ...
16
17 struct btrfs_ioctl_search_key {
18 /* possibly the root of the search
19 * though the ioctl fd seems to be used as well */
20 uint64_t tree_id;
21
22 /* keys returned will be >= min and <= max */
23 uint64_t min_objectid;
24 uint64_t max_objectid;
25
26 /* keys returned will be >= min and <= max */
27 uint64_t min_offset;
28 uint64_t max_offset;
29
30 /* max and min transids to search for */
31 uint64_t min_transid;
32 uint64_t max_transid;
33
34 /* keys returned will be >= min and <= max */
35 uint32_t min_type;
36 uint32_t max_type;
37
38 /*
39 * how many items did userland ask for, and how many are we
40 * returning
41 */
42 uint32_t nr_items;
43
44 ...;
45 };
46
47
48 /* ctree.h */
49
50 #define BTRFS_EXTENT_DATA_KEY ...
51 #define BTRFS_INODE_REF_KEY ...
52 #define BTRFS_INODE_ITEM_KEY ...
53 #define BTRFS_DIR_ITEM_KEY ...
54 #define BTRFS_DIR_INDEX_KEY ...
55
56 struct btrfs_ioctl_search_header {
57 uint64_t transid;
58 uint64_t objectid;
59 uint64_t offset;
60 uint32_t type;
61 uint32_t len;
62 };
63
64 struct btrfs_ioctl_search_args {
65 /* search parameters and state */
66 struct btrfs_ioctl_search_key key;
67 /* found items */
68 char buf[];
69 };
70
71 struct btrfs_file_extent_item {
72 /*
73 * transaction id that created this extent
74 */
75 uint64_t generation;
76 /*
77 * max number of bytes to hold this extent in ram
78 * when we split a compressed extent we can't know how big
79 * each of the resulting pieces will be. So, this is
80 * an upper limit on the size of the extent in ram instead of
81 * an exact limit.
82 */
83 uint64_t ram_bytes;
84
85 /*
86 * 32 bits for the various ways we might encode the data,
87 * including compression and encryption. If any of these
88 * are set to something a given disk format doesn't understand
89 * it is treated like an incompat flag for reading and writing,
90 * but not for stat.
91 */
92 uint8_t compression;
93 uint8_t encryption;
94 uint16_t other_encoding; /* spare for later use */
95
96 /* are we inline data or a real extent? */
97 uint8_t type;
98
99 /*
100 * disk space consumed by the extent, checksum blocks are included
101 * in these numbers
102 */
103 uint64_t disk_bytenr;
104 uint64_t disk_num_bytes;
105 /*
106 * the logical offset in file blocks (no csums)
107 * this extent record is for. This allows a file extent to point
108 * into the middle of an existing extent on disk, sharing it
109 * between two snapshots (useful if some bytes in the middle of the
110 * extent have changed
111 */
112 uint64_t offset;
113 /*
114 * the logical number of file blocks (no csums included)
115 */
116 uint64_t num_bytes;
117 ...;
118 };
119
120 struct btrfs_timespec {
121 uint64_t sec;
122 uint32_t nsec;
123 ...;
124 };
125
126 struct btrfs_inode_item {
127 /* nfs style generation number */
128 uint64_t generation;
129 /* transid that last touched this inode */
130 uint64_t transid;
131 uint64_t size;
132 uint64_t nbytes;
133 uint64_t block_group;
134 uint32_t nlink;
135 uint32_t uid;
136 uint32_t gid;
137 uint32_t mode;
138 uint64_t rdev;
139 uint64_t flags;
140
141 /* modification sequence number for NFS */
142 uint64_t sequence;
143
144 /*
145 * a little future expansion, for more than this we can
146 * just grow the inode item and version it
147 */
148 uint64_t reserved[4];
149 struct btrfs_timespec atime;
150 struct btrfs_timespec ctime;
151 struct btrfs_timespec mtime;
152 struct btrfs_timespec otime;
153 ...;
154 };
155
156 struct btrfs_inode_ref {
157 uint64_t index;
158 uint16_t name_len;
159 /* name goes here */
160 ...;
161 };
162
163 struct btrfs_disk_key {
164 uint64_t objectid;
165 uint8_t type;
166 uint64_t offset;
167 ...;
168 };
169
170 struct btrfs_dir_item {
171 struct btrfs_disk_key location;
172 uint64_t transid;
173 uint16_t data_len;
174 uint16_t name_len;
175 uint8_t type;
176 ...;
177 };
178
179 uint64_t btrfs_stack_file_extent_generation(struct btrfs_file_extent_item *s);
180 uint64_t btrfs_stack_inode_generation(struct btrfs_inode_item *s);
181 uint64_t btrfs_stack_inode_ref_name_len(struct btrfs_inode_ref *s);
182 uint64_t btrfs_stack_dir_name_len(struct btrfs_dir_item *s);
183 """)
184
185
186 # Also accessible as v
187 v = ffi.verify('''
188 #include <btrfs-progs/ioctl.h>
189 #include <btrfs-progs/ctree.h>
190 ''',
191 include_dirs=[getcwd()])
192
193
194 u64_max = ffi.cast('uint64_t', -1)
195
196
197 def ino_resolve(volume_fd, ino):
198 """
199 A straightforward port of ino_resolve in btrfs-progs find-new.
200
201 Requires a search.
202 Conceptually broken because of files with multiple hardlinks.
203 btrfs does keep track of a preferred name for inodes though.
204 """
205
206 args = ffi.new('struct btrfs_ioctl_search_args *')
207 args_buffer = ffi.buffer(args)
208 sk = args.key
209
210 sk.min_objectid = sk.max_objectid = ino
211 sk.min_type = sk.max_type = v.BTRFS_INODE_REF_KEY
212 sk.max_offset = u64_max
213 sk.max_transid = u64_max
214 sk.nr_items = 1
215
216 fcntl.ioctl(volume_fd, v.BTRFS_IOC_TREE_SEARCH, args_buffer)
217 if sk.nr_items == 0:
218 return
219
220 sh = ffi.cast(
221 'struct btrfs_ioctl_search_header *', args.buf)
222 assert sh.type == v.BTRFS_INODE_REF_KEY
223 ref = ffi.cast(
224 'struct btrfs_inode_ref *', sh + 1)
225 return name_of_inode_ref(ref)
226
227
228 def name_of_inode_ref(ref):
229 namelen = v.btrfs_stack_inode_ref_name_len(ref)
230 return ffi.string(ffi.cast('char*', ref + 1), namelen)
231
232
233 def name_of_dir_item(item):
234 namelen = v.btrfs_stack_dir_name_len(item)
235 return ffi.string(ffi.cast('char*', item + 1), namelen)
236
237
8bf4582 @g2p Make find_new more library friendly
authored
238 class FindError(Exception):
239 pass
240
241
242 def find_new(volume_fd, min_generation, results_file):
e5ea23a @g2p Split into the main entry point and a btrfs module.
authored
243 args = ffi.new('struct btrfs_ioctl_search_args *')
244 args_buffer = ffi.buffer(args)
245 sk = args.key
246
247 # Not a valid objectid that I know.
248 # But find-new uses that and it seems to work.
249 sk.tree_id = 0
250
251 sk.min_transid = min_generation
252
253 sk.max_objectid = u64_max
254 sk.max_offset = u64_max
255 sk.max_transid = u64_max
256 sk.max_type = v.BTRFS_EXTENT_DATA_KEY
257
258 while True:
259 sk.nr_items = 4096
260
261 try:
262 fcntl.ioctl(
263 volume_fd, v.BTRFS_IOC_TREE_SEARCH, args_buffer)
264 except IOError as e:
8bf4582 @g2p Make find_new more library friendly
authored
265 raise FindError(e)
e5ea23a @g2p Split into the main entry point and a btrfs module.
authored
266
267 if sk.nr_items == 0:
268 break
269
270 offset = 0
271 for item_id in xrange(sk.nr_items):
272 sh = ffi.cast(
273 'struct btrfs_ioctl_search_header *', args.buf + offset)
274 offset += ffi.sizeof('struct btrfs_ioctl_search_header') + sh.len
275
276 # XXX The classic btrfs find-new looks only at extents,
277 # and doesn't find empty files or directories.
278 # Need to look at other types.
279 if sh.type == v.BTRFS_EXTENT_DATA_KEY:
280 item = ffi.cast(
281 'struct btrfs_file_extent_item *', sh + 1)
282 found_gen = v.btrfs_stack_file_extent_generation(
283 item)
284 # XXX How do we name a hardlinked file?
285 #name = ino_resolve(volume_fd, sh.objectid)
8bf4582 @g2p Make find_new more library friendly
authored
286 results_file.write(
e5ea23a @g2p Split into the main entry point and a btrfs module.
authored
287 'item type %d ino %d len %d gen0 %d gen1 %d\n' % (
288 sh.type, sh.objectid, sh.len, sh.transid, found_gen))
289 if found_gen < min_generation:
290 continue
291 elif sh.type == v.BTRFS_INODE_ITEM_KEY:
292 item = ffi.cast(
293 'struct btrfs_inode_item *', sh + 1)
294 found_gen = v.btrfs_stack_inode_generation(item)
295 #name = ino_resolve(volume_fd, sh.objectid)
8bf4582 @g2p Make find_new more library friendly
authored
296 results_file.write(
e5ea23a @g2p Split into the main entry point and a btrfs module.
authored
297 'item type %d ino %d len %d gen0 %d gen1 %d\n' % (
298 sh.type, sh.objectid, sh.len, sh.transid, found_gen))
299 if found_gen < min_generation:
300 continue
301 elif sh.type == v.BTRFS_INODE_REF_KEY:
302 ref = ffi.cast(
303 'struct btrfs_inode_ref *', sh + 1)
304 name = name_of_inode_ref(ref)
8bf4582 @g2p Make find_new more library friendly
authored
305 results_file.write(
e5ea23a @g2p Split into the main entry point and a btrfs module.
authored
306 'item type %d ino %d len %d gen0 %d name %s\n' % (
307 sh.type, sh.objectid, sh.len, sh.transid, name))
308 elif (sh.type == v.BTRFS_DIR_ITEM_KEY
309 or sh.type == v.BTRFS_DIR_INDEX_KEY):
310 item = ffi.cast(
311 'struct btrfs_dir_item *', sh + 1)
312 name = name_of_dir_item(item)
8bf4582 @g2p Make find_new more library friendly
authored
313 results_file.write(
e5ea23a @g2p Split into the main entry point and a btrfs module.
authored
314 'item type %d dir ino %d len %d'
315 ' gen0 %d gen1 %d type1 %d name %s\n' % (
316 sh.type, sh.objectid, sh.len,
317 sh.transid, item.transid, item.type, name))
318 else:
8bf4582 @g2p Make find_new more library friendly
authored
319 results_file.write(
e5ea23a @g2p Split into the main entry point and a btrfs module.
authored
320 'item type %d oid %d len %d gen0 %d\n' % (
321 sh.type, sh.objectid, sh.len, sh.transid))
322 sk.min_objectid = sh.objectid
323 sk.min_type = sh.type
324 sk.min_offset = sh.offset
325
326 # CFFI 0.3 raises an OverflowError if necessary, no need to assert
327 #assert sk.min_offset < u64_max
328 # If the OverflowError actually happens in practice,
329 # we'll need to increase min_type resetting min_objectid to zero,
330 # then increase min_objectid resetting min_type and min_offset to zero.
331 # See
332 # https://btrfs.wiki.kernel.org/index.php/Btrfs_design#Btree_Data_structures
333 # and btrfs_key for the btree iteration order.
334 sk.min_offset += 1
335
Something went wrong with that request. Please try again.