Coverage Report

Created: 2025-12-14 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/libblkid/src/topology/topology.c
Line
Count
Source
1
/*
2
 * topology - gathers information about device topology
3
 *
4
 * Copyright 2009 Red Hat, Inc.  All rights reserved.
5
 *
6
 * This file may be redistributed under the terms of the
7
 * GNU Lesser General Public License.
8
 */
9
10
#include <stdio.h>
11
#include <string.h>
12
#include <stdlib.h>
13
#include <stddef.h>
14
#include <inttypes.h>
15
16
#include "topology.h"
17
18
/**
19
 * SECTION:topology
20
 * @title: Topology information
21
 * @short_description: block device topology information.
22
 *
23
 * The topology chain provides details about Linux block devices, for more
24
 * information see:
25
 *
26
 *      Linux kernel Documentation/ABI/testing/sysfs-block
27
 *
28
 * NAME=value (tags) interface is enabled by blkid_probe_enable_topology(),
29
 * and provides:
30
 *
31
 * @LOGICAL_SECTOR_SIZE: this is the smallest unit the storage device can
32
 *                       address. It is typically 512 bytes.
33
 *
34
 * @PHYSICAL_SECTOR_SIZE: this is the smallest unit a physical storage device
35
 *                        can write atomically. It is usually the same as the
36
 *                        logical sector size but may be bigger.
37
 *
38
 * @MINIMUM_IO_SIZE: minimum size which is the device's preferred unit of I/O.
39
 *                   For RAID arrays it is often the stripe chunk size.
40
 *
41
 * @OPTIMAL_IO_SIZE: usually the stripe width for RAID or zero. For RAID arrays
42
 *                   it is usually the stripe width or the internal track size.
43
 *
44
 * @ALIGNMENT_OFFSET: indicates how many bytes the beginning of the device is
45
 *                    offset from the disk's natural alignment.
46
 *
47
 * The NAME=value tags are not defined when the corresponding topology value
48
 * is zero. The MINIMUM_IO_SIZE should be always defined if kernel provides
49
 * topology information.
50
 *
51
 * Binary interface:
52
 *
53
 * blkid_probe_get_topology()
54
 *
55
 * blkid_topology_get_'VALUENAME'()
56
 */
57
static int topology_probe(blkid_probe pr, struct blkid_chain *chn);
58
static void topology_free(blkid_probe pr, void *data);
59
static int topology_is_complete(blkid_probe pr);
60
static int topology_set_logical_sector_size(blkid_probe pr);
61
62
/*
63
 * Binary interface
64
 */
65
struct blkid_struct_topology {
66
  unsigned long alignment_offset;
67
  unsigned long minimum_io_size;
68
  unsigned long optimal_io_size;
69
  unsigned long logical_sector_size;
70
  unsigned long physical_sector_size;
71
  unsigned long   dax;
72
  uint64_t  diskseq;
73
};
74
75
/*
76
 * Topology chain probing functions
77
 */
78
static const struct blkid_idinfo *idinfos[] =
79
{
80
#ifdef __linux__
81
  &sysfs_tp_idinfo,
82
  &ioctl_tp_idinfo,
83
  &md_tp_idinfo,
84
  &dm_tp_idinfo,
85
  &lvm_tp_idinfo,
86
  &evms_tp_idinfo
87
#endif
88
};
89
90
91
/*
92
 * Driver definition
93
 */
94
const struct blkid_chaindrv topology_drv = {
95
  .id           = BLKID_CHAIN_TOPLGY,
96
  .name         = "topology",
97
  .dflt_enabled = FALSE,
98
  .idinfos      = idinfos,
99
  .nidinfos     = ARRAY_SIZE(idinfos),
100
  .probe        = topology_probe,
101
  .safeprobe    = topology_probe,
102
  .free_data    = topology_free
103
};
104
105
/**
106
 * blkid_probe_enable_topology:
107
 * @pr: probe
108
 * @enable: TRUE/FALSE
109
 *
110
 * Enables/disables the topology probing for non-binary interface.
111
 *
112
 * Returns: 0 on success, or -1 in case of error.
113
 */
114
int blkid_probe_enable_topology(blkid_probe pr, int enable)
115
0
{
116
0
  pr->chains[BLKID_CHAIN_TOPLGY].enabled = enable;
117
0
  return 0;
118
0
}
119
120
/**
121
 * blkid_probe_get_topology:
122
 * @pr: probe
123
 *
124
 * This is a binary interface for topology values. See also blkid_topology_*
125
 * functions.
126
 *
127
 * This function is independent on blkid_do_[safe,full]probe() and
128
 * blkid_probe_enable_topology() calls.
129
 *
130
 * WARNING: the returned object will be overwritten by the next
131
 *          blkid_probe_get_topology() call for the same @pr. If you want to
132
 *          use more blkid_topology objects in the same time you have to create
133
 *          more blkid_probe handlers (see blkid_new_probe()).
134
 *
135
 * Returns: blkid_topology, or NULL in case of error.
136
 */
137
blkid_topology blkid_probe_get_topology(blkid_probe pr)
138
0
{
139
0
  return (blkid_topology) blkid_probe_get_binary_data(pr,
140
0
      &pr->chains[BLKID_CHAIN_TOPLGY]);
141
0
}
142
143
/*
144
 * The blkid_do_probe() backend.
145
 */
146
static int topology_probe(blkid_probe pr, struct blkid_chain *chn)
147
0
{
148
0
  size_t i;
149
0
  int rc;
150
151
0
  if (chn->idx < -1)
152
0
    return -1;
153
154
0
  if (!S_ISBLK(pr->mode))
155
0
    return -EINVAL; /* nothing, works with block devices only */
156
157
0
  if (chn->binary) {
158
0
    DBG(LOWPROBE, ul_debug("initialize topology binary data"));
159
160
0
    if (chn->data)
161
      /* reset binary data */
162
0
      memset(chn->data, 0,
163
0
          sizeof(struct blkid_struct_topology));
164
0
    else {
165
0
      chn->data = calloc(1,
166
0
          sizeof(struct blkid_struct_topology));
167
0
      if (!chn->data)
168
0
        return -ENOMEM;
169
0
    }
170
0
  }
171
172
0
  blkid_probe_chain_reset_values(pr, chn);
173
174
0
  DBG(LOWPROBE, ul_debug("--> starting probing loop [TOPOLOGY idx=%d]",
175
0
    chn->idx));
176
177
0
  i = chn->idx < 0 ? 0 : chn->idx + 1U;
178
179
0
  for ( ; i < ARRAY_SIZE(idinfos); i++) {
180
0
    const struct blkid_idinfo *id = idinfos[i];
181
182
0
    chn->idx = i;
183
184
0
    if (id->probefunc) {
185
0
      DBG(LOWPROBE, ul_debug("%s: call probefunc()", id->name));
186
0
      errno = 0;
187
0
      rc = id->probefunc(pr, NULL);
188
0
      blkid_probe_prune_buffers(pr);
189
0
      if (rc != 0)
190
0
        continue;
191
0
    }
192
193
0
    if (!topology_is_complete(pr))
194
0
      continue;
195
196
    /* generic for all probing drivers */
197
0
    topology_set_logical_sector_size(pr);
198
199
0
    DBG(LOWPROBE, ul_debug("<-- leaving probing loop (type=%s) [TOPOLOGY idx=%d]",
200
0
      id->name, chn->idx));
201
0
    return BLKID_PROBE_OK;
202
0
  }
203
204
0
  DBG(LOWPROBE, ul_debug("<-- leaving probing loop (failed) [TOPOLOGY idx=%d]",
205
0
    chn->idx));
206
0
  return BLKID_PROBE_NONE;
207
0
}
208
209
static void topology_free(blkid_probe pr __attribute__((__unused__)),
210
        void *data)
211
5.74k
{
212
5.74k
  free(data);
213
5.74k
}
214
215
static int topology_set_value(blkid_probe pr, const char *name,
216
        size_t structoff, unsigned long data)
217
0
{
218
0
  struct blkid_chain *chn = blkid_probe_get_chain(pr);
219
220
0
  if (!chn)
221
0
    return -1;
222
0
  if (!data)
223
0
    return 0; /* ignore zeros */
224
225
0
  if (chn->binary) {
226
0
    memcpy((char *) chn->data + structoff, &data, sizeof(data));
227
0
    return 0;
228
0
  }
229
0
  return blkid_probe_sprintf_value(pr, name, "%lu", data);
230
0
}
231
232
static int topology_set_value64(blkid_probe pr, const char *name,
233
        size_t structoff, uint64_t data)
234
0
{
235
0
  struct blkid_chain *chn = blkid_probe_get_chain(pr);
236
237
0
  if (!chn)
238
0
    return -1;
239
0
  if (!data)
240
0
    return 0; /* ignore zeros */
241
242
0
  if (chn->binary) {
243
0
    memcpy((char *) chn->data + structoff, &data, sizeof(data));
244
0
    return 0;
245
0
  }
246
0
  return blkid_probe_sprintf_value(pr, name, "%"PRIu64, data);
247
0
}
248
249
250
/* the topology info is complete when we have at least "minimum_io_size" which
251
 * is provided by all blkid topology drivers */
252
static int topology_is_complete(blkid_probe pr)
253
0
{
254
0
  struct blkid_chain *chn = blkid_probe_get_chain(pr);
255
256
0
  if (!chn)
257
0
    return FALSE;
258
259
0
  if (chn->binary && chn->data) {
260
0
    blkid_topology tp = (blkid_topology) chn->data;
261
0
    if (tp->minimum_io_size)
262
0
      return TRUE;
263
0
  }
264
265
0
  return __blkid_probe_lookup_value(pr, "MINIMUM_IO_SIZE") ? TRUE : FALSE;
266
0
}
267
268
int blkid_topology_set_alignment_offset(blkid_probe pr, int val)
269
0
{
270
0
  unsigned long xval;
271
272
  /* Welcome to Hell. The kernel is able to return -1 as an
273
   * alignment_offset if no compatible sizes and alignments
274
   * exist for stacked devices.
275
   *
276
   * There is no way how libblkid caller can respond to the value -1, so
277
   * we will hide this corner case...
278
   *
279
   * (TODO: maybe we can export an extra boolean value 'misaligned' rather
280
   *  then complete hide this problem.)
281
   */
282
0
  xval = val < 0 ? 0 : val;
283
284
0
  return topology_set_value(pr,
285
0
      "ALIGNMENT_OFFSET",
286
0
      offsetof(struct blkid_struct_topology, alignment_offset),
287
0
      xval);
288
0
}
289
290
int blkid_topology_set_minimum_io_size(blkid_probe pr, unsigned long val)
291
0
{
292
0
  return topology_set_value(pr,
293
0
      "MINIMUM_IO_SIZE",
294
0
      offsetof(struct blkid_struct_topology, minimum_io_size),
295
0
      val);
296
0
}
297
298
int blkid_topology_set_optimal_io_size(blkid_probe pr, unsigned long val)
299
0
{
300
0
  return topology_set_value(pr,
301
0
      "OPTIMAL_IO_SIZE",
302
0
      offsetof(struct blkid_struct_topology, optimal_io_size),
303
0
      val);
304
0
}
305
306
/* BLKSSZGET is provided on all systems since 2.3.3 -- so we don't have to
307
 * waste time with sysfs.
308
 */
309
static int topology_set_logical_sector_size(blkid_probe pr)
310
0
{
311
0
  unsigned long val = blkid_probe_get_sectorsize(pr);
312
313
0
  if (!val)
314
0
    return -1;
315
316
0
  return topology_set_value(pr,
317
0
      "LOGICAL_SECTOR_SIZE",
318
0
      offsetof(struct blkid_struct_topology, logical_sector_size),
319
0
      val);
320
0
}
321
322
int blkid_topology_set_physical_sector_size(blkid_probe pr, unsigned long val)
323
0
{
324
0
  return topology_set_value(pr,
325
0
      "PHYSICAL_SECTOR_SIZE",
326
0
      offsetof(struct blkid_struct_topology, physical_sector_size),
327
0
      val);
328
0
}
329
330
int blkid_topology_set_dax(blkid_probe pr, unsigned long val)
331
0
{
332
0
  return topology_set_value(pr,
333
0
      "DAX",
334
0
      offsetof(struct blkid_struct_topology, dax),
335
0
      val);
336
0
}
337
338
int blkid_topology_set_diskseq(blkid_probe pr, uint64_t val)
339
0
{
340
0
  return topology_set_value64(pr,
341
0
      "DISKSEQ",
342
0
      offsetof(struct blkid_struct_topology, diskseq),
343
0
      val);
344
0
}
345
346
/**
347
 * blkid_topology_get_alignment_offset:
348
 * @tp: topology
349
 *
350
 * Returns: alignment offset in bytes or 0.
351
 */
352
unsigned long blkid_topology_get_alignment_offset(blkid_topology tp)
353
0
{
354
0
  return tp->alignment_offset;
355
0
}
356
357
/**
358
 * blkid_topology_get_minimum_io_size:
359
 * @tp: topology
360
 *
361
 * Returns: minimum io size in bytes or 0.
362
 */
363
unsigned long blkid_topology_get_minimum_io_size(blkid_topology tp)
364
0
{
365
0
  return tp->minimum_io_size;
366
0
}
367
368
/**
369
 * blkid_topology_get_optimal_io_size
370
 * @tp: topology
371
 *
372
 * Returns: optimal io size in bytes or 0.
373
 */
374
unsigned long blkid_topology_get_optimal_io_size(blkid_topology tp)
375
0
{
376
0
  return tp->optimal_io_size;
377
0
}
378
379
/**
380
 * blkid_topology_get_logical_sector_size
381
 * @tp: topology
382
 *
383
 * Returns: logical sector size (BLKSSZGET ioctl) in bytes or 0.
384
 */
385
unsigned long blkid_topology_get_logical_sector_size(blkid_topology tp)
386
0
{
387
0
  return tp->logical_sector_size;
388
0
}
389
390
/**
391
 * blkid_topology_get_physical_sector_size
392
 * @tp: topology
393
 *
394
 * Returns: logical sector size (BLKSSZGET ioctl) in bytes or 0.
395
 */
396
unsigned long blkid_topology_get_physical_sector_size(blkid_topology tp)
397
0
{
398
0
  return tp->physical_sector_size;
399
0
}
400
401
/**
402
 * blkid_topology_get_dax
403
 * @tp: topology
404
 *
405
 * Returns: 1 if dax is supported, 0 otherwise.
406
 *
407
 * Since: 2.36
408
 */
409
unsigned long blkid_topology_get_dax(blkid_topology tp)
410
0
{
411
0
  return tp->dax;
412
0
}
413
414
/**
415
 * blkid_topology_get_diskseq
416
 * @tp: topology
417
 *
418
 * Returns: disk sequence number
419
 *
420
 * Since: 2.39
421
 */
422
uint64_t blkid_topology_get_diskseq(blkid_topology tp)
423
0
{
424
0
  return tp->diskseq;
425
0
}