]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/lsblk-devtree.c
script: report also timing file, do it only once
[thirdparty/util-linux.git] / misc-utils / lsblk-devtree.c
CommitLineData
0986f29a
KZ
1/*
2 * These functions implement tree of block devices. The devtree struct contains
3 * two basic lists:
4 *
5 * 1) devtree->devices -- This is simple list without any hierarchy. We use
6 * reference counting here.
7 *
8 * 2) devtree->roots -- The root nodes of the trees. The code does not use
9 * reference counting here due to complexity and it's unnecessary.
10 *
11 * Note that the same device maybe have more parents and more children. The
12 * device is allocated only once and shared within the tree. The dependence
13 * (devdep struct) contains reference to child as well as to parent and the
14 * dependence is reference by ls_childs from parent device and by ls_parents
15 * from child. (Yes, "childs" is used for children ;-)
16 *
17 * Copyright (C) 2018 Karel Zak <kzak@redhat.com>
18 */
5bb395f4
KZ
19#include "lsblk.h"
20#include "sysfs.h"
21
22
23void lsblk_reset_iter(struct lsblk_iter *itr, int direction)
24{
25 if (direction == -1)
26 direction = itr->direction;
27
28 memset(itr, 0, sizeof(*itr));
29 itr->direction = direction;
30}
31
10501add 32struct lsblk_device *lsblk_new_device()
5bb395f4
KZ
33{
34 struct lsblk_device *dev;
35
36 dev = calloc(1, sizeof(*dev));
37 if (!dev)
38 return NULL;
39
40 dev->refcount = 1;
81a8936c 41 dev->removable = -1;
8a2f175c 42 dev->discard_granularity = (uint64_t) -1;
5bb395f4 43
0986f29a
KZ
44 INIT_LIST_HEAD(&dev->childs);
45 INIT_LIST_HEAD(&dev->parents);
5bb395f4
KZ
46 INIT_LIST_HEAD(&dev->ls_roots);
47 INIT_LIST_HEAD(&dev->ls_devices);
48
49 DBG(DEV, ul_debugobj(dev, "alloc"));
50 return dev;
51}
52
53void lsblk_ref_device(struct lsblk_device *dev)
54{
55 if (dev)
56 dev->refcount++;
57}
58
0986f29a
KZ
59/* removes dependence from child as well as from parent */
60static int remove_dependence(struct lsblk_devdep *dep)
5bb395f4 61{
0986f29a 62 if (!dep)
5bb395f4
KZ
63 return -EINVAL;
64
0986f29a
KZ
65 DBG(DEP, ul_debugobj(dep, " dealloc"));
66
67 list_del_init(&dep->ls_childs);
68 list_del_init(&dep->ls_parents);
69
5bb395f4
KZ
70 free(dep);
71 return 0;
72}
73
74static int device_remove_dependences(struct lsblk_device *dev)
75{
76 if (!dev)
77 return -EINVAL;
78
0986f29a
KZ
79 if (!list_empty(&dev->childs))
80 DBG(DEV, ul_debugobj(dev, " %s: remove all children deps", dev->name));
81 while (!list_empty(&dev->childs)) {
82 struct lsblk_devdep *dp = list_entry(dev->childs.next,
83 struct lsblk_devdep, ls_childs);
84 remove_dependence(dp);
85 }
86
87 if (!list_empty(&dev->parents))
88 DBG(DEV, ul_debugobj(dev, " %s: remove all parents deps", dev->name));
89 while (!list_empty(&dev->parents)) {
90 struct lsblk_devdep *dp = list_entry(dev->parents.next,
91 struct lsblk_devdep, ls_parents);
92 remove_dependence(dp);
5bb395f4 93 }
0986f29a 94
5bb395f4
KZ
95 return 0;
96}
97
98void lsblk_unref_device(struct lsblk_device *dev)
99{
57502dd3 100 if (!dev)
5bb395f4
KZ
101 return;
102
103 if (--dev->refcount <= 0) {
0986f29a 104 DBG(DEV, ul_debugobj(dev, " freeing [%s] <<", dev->name));
5bb395f4
KZ
105
106 device_remove_dependences(dev);
107 lsblk_device_free_properties(dev->properties);
108
10501add
KZ
109 lsblk_unref_device(dev->wholedisk);
110
5bb395f4
KZ
111 free(dev->dm_name);
112 free(dev->filename);
113 free(dev->mountpoint);
dc4662f0 114 free(dev->dedupkey);
5bb395f4
KZ
115
116 ul_unref_path(dev->sysfs);
5bb395f4 117
0986f29a
KZ
118 DBG(DEV, ul_debugobj(dev, " >> dealloc [%s]", dev->name));
119 free(dev->name);
5bb395f4
KZ
120 free(dev);
121 }
122}
123
0986f29a 124int lsblk_device_has_child(struct lsblk_device *dev, struct lsblk_device *child)
5bb395f4 125{
e2d7870b
KZ
126 struct lsblk_device *x = NULL;
127 struct lsblk_iter itr;
5bb395f4 128
e2d7870b
KZ
129 lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
130
131 while (lsblk_device_next_child(dev, &itr, &x) == 0) {
132 if (x == child)
133 return 1;
5bb395f4
KZ
134 }
135
e2d7870b
KZ
136 return 0;
137}
138
139int lsblk_device_new_dependence(struct lsblk_device *parent, struct lsblk_device *child)
140{
141 struct lsblk_devdep *dp;
142
143 if (!parent || !child)
144 return -EINVAL;
145
0986f29a 146 if (lsblk_device_has_child(parent, child))
e2d7870b
KZ
147 return 1;
148
5bb395f4
KZ
149 dp = calloc(1, sizeof(*dp));
150 if (!dp)
e2d7870b 151 return -ENOMEM;
5bb395f4 152
0986f29a
KZ
153 INIT_LIST_HEAD(&dp->ls_childs);
154 INIT_LIST_HEAD(&dp->ls_parents);
5bb395f4 155
5bb395f4 156 dp->child = child;
0986f29a
KZ
157 list_add_tail(&dp->ls_childs, &parent->childs);
158
159 dp->parent = parent;
0bd05f5e 160 list_add_tail(&dp->ls_parents, &child->parents);
5bb395f4
KZ
161
162 DBG(DEV, ul_debugobj(parent, "add dependence 0x%p [%s->%s]", dp, parent->name, child->name));
5bb395f4 163
e2d7870b 164 return 0;
5bb395f4
KZ
165}
166
0986f29a 167static int device_next_child(struct lsblk_device *dev,
5bb395f4 168 struct lsblk_iter *itr,
dc4662f0 169 struct lsblk_devdep **dp)
5bb395f4
KZ
170{
171 int rc = 1;
172
dc4662f0 173 if (!dev || !itr || !dp)
5bb395f4 174 return -EINVAL;
dc4662f0 175 *dp = NULL;
5bb395f4
KZ
176
177 if (!itr->head)
0986f29a 178 LSBLK_ITER_INIT(itr, &dev->childs);
5bb395f4 179 if (itr->p != itr->head) {
0986f29a 180 LSBLK_ITER_ITERATE(itr, *dp, struct lsblk_devdep, ls_childs);
5bb395f4
KZ
181 rc = 0;
182 }
183
184 return rc;
185}
186
dc4662f0
KZ
187int lsblk_device_next_child(struct lsblk_device *dev,
188 struct lsblk_iter *itr,
189 struct lsblk_device **child)
190{
191 struct lsblk_devdep *dp = NULL;
0986f29a 192 int rc = device_next_child(dev, itr, &dp);
dc4662f0
KZ
193
194 if (!child)
195 return -EINVAL;
196
197 *child = rc == 0 ? dp->child : NULL;
198 return rc;
199}
200
0bd05f5e
KZ
201int lsblk_device_is_last_parent(struct lsblk_device *dev, struct lsblk_device *parent)
202{
203 struct lsblk_devdep *dp = list_last_entry(
204 &dev->parents,
205 struct lsblk_devdep, ls_parents);
e361253e
SK
206 if (!dp)
207 return 0;
0bd05f5e
KZ
208 return dp->parent == parent;
209}
210
211int lsblk_device_next_parent(
212 struct lsblk_device *dev,
213 struct lsblk_iter *itr,
214 struct lsblk_device **parent)
215{
216 int rc = 1;
217
218 if (!dev || !itr || !parent)
219 return -EINVAL;
220 *parent = NULL;
221
222 if (!itr->head)
223 LSBLK_ITER_INIT(itr, &dev->parents);
224 if (itr->p != itr->head) {
225 struct lsblk_devdep *dp = NULL;
226 LSBLK_ITER_ITERATE(itr, dp, struct lsblk_devdep, ls_parents);
227 if (dp)
228 *parent = dp->parent;
229 rc = 0;
230 }
231
232 return rc;
233}
e2d7870b 234
5bb395f4
KZ
235struct lsblk_devtree *lsblk_new_devtree()
236{
237 struct lsblk_devtree *tr;
238
239 tr = calloc(1, sizeof(*tr));
240 if (!tr)
241 return NULL;
242
243 tr->refcount = 1;
244
245 INIT_LIST_HEAD(&tr->roots);
246 INIT_LIST_HEAD(&tr->devices);
247
248 DBG(TREE, ul_debugobj(tr, "alloc"));
249 return tr;
250}
251
252void lsblk_ref_devtree(struct lsblk_devtree *tr)
253{
254 if (tr)
255 tr->refcount++;
256}
257
258void lsblk_unref_devtree(struct lsblk_devtree *tr)
259{
57502dd3 260 if (!tr)
5bb395f4
KZ
261 return;
262
263 if (--tr->refcount <= 0) {
264 DBG(TREE, ul_debugobj(tr, "dealloc"));
265
5bb395f4
KZ
266 while (!list_empty(&tr->devices)) {
267 struct lsblk_device *dev = list_entry(tr->devices.next,
268 struct lsblk_device, ls_devices);
57502dd3 269 lsblk_devtree_remove_device(tr, dev);
5bb395f4
KZ
270 }
271 free(tr);
272 }
273}
274
275int lsblk_devtree_add_root(struct lsblk_devtree *tr, struct lsblk_device *dev)
276{
21d4a48c
KZ
277 if (!lsblk_devtree_has_device(tr, dev))
278 lsblk_devtree_add_device(tr, dev);
279
280 /* We don't increment reference counter for tr->roots list. The primary
281 * reference is tr->devices */
5bb395f4
KZ
282
283 DBG(TREE, ul_debugobj(tr, "add root device 0x%p [%s]", dev, dev->name));
284 list_add_tail(&dev->ls_roots, &tr->roots);
285 return 0;
286}
287
288int lsblk_devtree_next_root(struct lsblk_devtree *tr,
289 struct lsblk_iter *itr,
290 struct lsblk_device **dev)
291{
292 int rc = 1;
293
294 if (!tr || !itr || !dev)
295 return -EINVAL;
296 *dev = NULL;
297 if (!itr->head)
298 LSBLK_ITER_INIT(itr, &tr->roots);
299 if (itr->p != itr->head) {
300 LSBLK_ITER_ITERATE(itr, *dev, struct lsblk_device, ls_roots);
301 rc = 0;
302 }
303 return rc;
304}
305
306int lsblk_devtree_add_device(struct lsblk_devtree *tr, struct lsblk_device *dev)
307{
308 lsblk_ref_device(dev);
309
310 DBG(TREE, ul_debugobj(tr, "add device 0x%p [%s]", dev, dev->name));
311 list_add_tail(&dev->ls_devices, &tr->devices);
312 return 0;
313}
314
315int lsblk_devtree_next_device(struct lsblk_devtree *tr,
316 struct lsblk_iter *itr,
317 struct lsblk_device **dev)
318{
319 int rc = 1;
320
321 if (!tr || !itr || !dev)
322 return -EINVAL;
323 *dev = NULL;
324 if (!itr->head)
325 LSBLK_ITER_INIT(itr, &tr->devices);
326 if (itr->p != itr->head) {
327 LSBLK_ITER_ITERATE(itr, *dev, struct lsblk_device, ls_devices);
328 rc = 0;
329 }
330 return rc;
331}
332
21d4a48c
KZ
333int lsblk_devtree_has_device(struct lsblk_devtree *tr, struct lsblk_device *dev)
334{
335 struct lsblk_device *x = NULL;
336 struct lsblk_iter itr;
337
338 lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
339
340 while (lsblk_devtree_next_device(tr, &itr, &x) == 0) {
341 if (x == dev)
342 return 1;
343 }
344
345 return 0;
346}
347
5bb395f4
KZ
348struct lsblk_device *lsblk_devtree_get_device(struct lsblk_devtree *tr, const char *name)
349{
350 struct lsblk_device *dev = NULL;
351 struct lsblk_iter itr;
352
353 lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
354
355 while (lsblk_devtree_next_device(tr, &itr, &dev) == 0) {
356 if (strcmp(name, dev->name) == 0)
357 return dev;
358 }
359
360 return NULL;
361}
362
21d4a48c
KZ
363int lsblk_devtree_remove_device(struct lsblk_devtree *tr, struct lsblk_device *dev)
364{
365 DBG(TREE, ul_debugobj(tr, "remove device 0x%p [%s]", dev, dev->name));
366
367 if (!lsblk_devtree_has_device(tr, dev))
368 return 1;
369
370 list_del_init(&dev->ls_roots);
371 list_del_init(&dev->ls_devices);
372 lsblk_unref_device(dev);
373
374 return 0;
375}
5bb395f4 376
dc4662f0
KZ
377static int device_dedupkey_is_equal(
378 struct lsblk_device *dev,
379 struct lsblk_device *pattern)
380{
381 assert(pattern->dedupkey);
382
383 if (!dev->dedupkey || dev == pattern)
384 return 0;
385 if (strcmp(dev->dedupkey, pattern->dedupkey) == 0) {
386 if (!device_is_partition(dev) ||
9e677de1 387 !dev->wholedisk->dedupkey ||
dc4662f0
KZ
388 strcmp(dev->dedupkey, dev->wholedisk->dedupkey) != 0) {
389 DBG(DEV, ul_debugobj(dev, "%s: match deduplication pattern", dev->name));
390 return 1;
391 }
392 }
393 return 0;
394}
395
396static void device_dedup_dependencies(
397 struct lsblk_device *dev,
398 struct lsblk_device *pattern)
399{
400 struct lsblk_iter itr;
401 struct lsblk_devdep *dp;
402
403 lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
404
0986f29a 405 while (device_next_child(dev, &itr, &dp) == 0) {
dc4662f0
KZ
406 struct lsblk_device *child = dp->child;
407
408 if (device_dedupkey_is_equal(child, pattern)) {
409 DBG(DEV, ul_debugobj(dev, "remove duplicate dependence: 0x%p [%s]",
410 dp->child, dp->child->name));
0986f29a 411 remove_dependence(dp);
dc4662f0
KZ
412 } else
413 device_dedup_dependencies(child, pattern);
414 }
415}
416
417static void devtree_dedup(struct lsblk_devtree *tr, struct lsblk_device *pattern)
418{
419 struct lsblk_iter itr;
420 struct lsblk_device *dev = NULL;
421
422 lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
423
424 DBG(TREE, ul_debugobj(tr, "de-duplicate by key: %s", pattern->dedupkey));
425
426 while (lsblk_devtree_next_root(tr, &itr, &dev) == 0) {
427 if (device_dedupkey_is_equal(dev, pattern)) {
428 DBG(TREE, ul_debugobj(tr, "remove duplicate device: 0x%p [%s]",
429 dev, dev->name));
430 /* Note that root list does not use ref-counting; the
431 * primary reference is ls_devices */
432 list_del_init(&dev->ls_roots);
433 } else
434 device_dedup_dependencies(dev, pattern);
435 }
436}
437
438static int cmp_devices_devno(struct list_head *a, struct list_head *b,
439 __attribute__((__unused__)) void *data)
440{
441 struct lsblk_device *ax = list_entry(a, struct lsblk_device, ls_devices),
442 *bx = list_entry(b, struct lsblk_device, ls_devices);
443
444 return cmp_numbers(makedev(ax->maj, ax->min),
445 makedev(bx->maj, bx->min));
446}
447
448/* Note that dev->dedupkey has to be already set */
449int lsblk_devtree_deduplicate_devices(struct lsblk_devtree *tr)
450{
451 struct lsblk_device *pattern = NULL;
452 struct lsblk_iter itr;
453 char *last = NULL;
454
455 list_sort(&tr->devices, cmp_devices_devno, NULL);
456 lsblk_reset_iter(&itr, LSBLK_ITER_FORWARD);
457
458 while (lsblk_devtree_next_device(tr, &itr, &pattern) == 0) {
459 if (!pattern->dedupkey)
460 continue;
461 if (device_is_partition(pattern) &&
9e677de1 462 pattern->wholedisk->dedupkey &&
dc4662f0
KZ
463 strcmp(pattern->dedupkey, pattern->wholedisk->dedupkey) == 0)
464 continue;
465 if (last && strcmp(pattern->dedupkey, last) == 0)
466 continue;
467
468 devtree_dedup(tr, pattern);
469 last = pattern->dedupkey;
470 }
471 return 0;
472}