Coverage Report

Created: 2024-05-21 06:33

/src/util-linux/lib/sysfs.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * No copyright is claimed.  This code is in the public domain; do with
3
 * it what you wish.
4
 *
5
 * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
6
 */
7
#include <ctype.h>
8
#include <libgen.h>
9
#include <fcntl.h>
10
#include <sys/stat.h>
11
#include <unistd.h>
12
13
#include "c.h"
14
#include "pathnames.h"
15
#include "sysfs.h"
16
#include "fileutils.h"
17
#include "all-io.h"
18
#include "debug.h"
19
#include "strutils.h"
20
#include "buffer.h"
21
22
static void sysfs_blkdev_deinit_path(struct path_cxt *pc);
23
static int  sysfs_blkdev_enoent_redirect(struct path_cxt *pc, const char *path, int *dirfd);
24
25
/*
26
 * Debug stuff (based on include/debug.h)
27
 */
28
static UL_DEBUG_DEFINE_MASK(ulsysfs);
29
UL_DEBUG_DEFINE_MASKNAMES(ulsysfs) = UL_DEBUG_EMPTY_MASKNAMES;
30
31
0
#define ULSYSFS_DEBUG_INIT  (1 << 1)
32
0
#define ULSYSFS_DEBUG_CXT (1 << 2)
33
34
0
#define DBG(m, x)       __UL_DBG(ulsysfs, ULSYSFS_DEBUG_, m, x)
35
#define ON_DBG(m, x)    __UL_DBG_CALL(ulsysfs, ULSYSFS_DEBUG_, m, x)
36
37
0
#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(ulsysfs)
38
#include "debugobj.h"
39
40
void ul_sysfs_init_debug(void)
41
0
{
42
0
  if (ulsysfs_debug_mask)
43
0
    return;
44
0
  __UL_INIT_DEBUG_FROM_ENV(ulsysfs, ULSYSFS_DEBUG_, 0, ULSYSFS_DEBUG);
45
0
}
46
47
struct path_cxt *ul_new_sysfs_path(dev_t devno, struct path_cxt *parent, const char *prefix)
48
0
{
49
0
  struct path_cxt *pc = ul_new_path(NULL);
50
51
0
  if (!pc)
52
0
    return NULL;
53
0
  if (prefix)
54
0
    ul_path_set_prefix(pc, prefix);
55
56
0
  if (sysfs_blkdev_init_path(pc, devno, parent) != 0) {
57
0
    ul_unref_path(pc);
58
0
    return NULL;
59
0
  }
60
61
0
  DBG(CXT, ul_debugobj(pc, "alloc"));
62
0
  return pc;
63
0
}
64
65
/*
66
 * sysfs_blkdev_* is sysfs extension to ul_path_* API for block devices.
67
 *
68
 * The function is possible to call in loop and without sysfs_blkdev_deinit_path().
69
 * The sysfs_blkdev_deinit_path() is automatically called by ul_unref_path().
70
 *
71
 */
72
int sysfs_blkdev_init_path(struct path_cxt *pc, dev_t devno, struct path_cxt *parent)
73
0
{
74
0
  struct sysfs_blkdev *blk;
75
0
  int rc;
76
0
  char buf[sizeof(_PATH_SYS_DEVBLOCK)
77
0
     + sizeof(stringify_value(UINT32_MAX)) * 2
78
0
     + 3];
79
80
  /* define path to devno stuff */
81
0
  snprintf(buf, sizeof(buf), _PATH_SYS_DEVBLOCK "/%d:%d", major(devno), minor(devno));
82
0
  rc = ul_path_set_dir(pc, buf);
83
0
  if (rc)
84
0
    return rc;
85
86
  /* make sure path exists */
87
0
  rc = ul_path_get_dirfd(pc);
88
0
  if (rc < 0)
89
0
    return rc;
90
91
  /* initialize sysfs blkdev specific stuff */
92
0
  blk = ul_path_get_dialect(pc);
93
0
  if (!blk) {
94
0
    DBG(CXT, ul_debugobj(pc, "alloc new sysfs handler"));
95
0
    blk = calloc(1, sizeof(struct sysfs_blkdev));
96
0
    if (!blk)
97
0
      return -ENOMEM;
98
99
0
    ul_path_set_dialect(pc, blk, sysfs_blkdev_deinit_path);
100
0
    ul_path_set_enoent_redirect(pc, sysfs_blkdev_enoent_redirect);
101
0
  }
102
103
0
  DBG(CXT, ul_debugobj(pc, "init sysfs stuff"));
104
105
0
  blk->devno = devno;
106
0
  sysfs_blkdev_set_parent(pc, parent);
107
108
0
  return 0;
109
0
}
110
111
static void sysfs_blkdev_deinit_path(struct path_cxt *pc)
112
0
{
113
0
  struct sysfs_blkdev *blk;
114
115
0
  if (!pc)
116
0
    return;
117
118
0
  DBG(CXT, ul_debugobj(pc, "deinit"));
119
120
0
  blk = ul_path_get_dialect(pc);
121
0
  if (!blk)
122
0
    return;
123
124
0
  ul_unref_path(blk->parent);
125
0
  free(blk);
126
127
0
  ul_path_set_dialect(pc, NULL, NULL);
128
0
}
129
130
int sysfs_blkdev_set_parent(struct path_cxt *pc, struct path_cxt *parent)
131
0
{
132
0
  struct sysfs_blkdev *blk = ul_path_get_dialect(pc);
133
134
0
  if (!pc || !blk)
135
0
    return -EINVAL;
136
137
0
  if (blk->parent) {
138
0
    ul_unref_path(blk->parent);
139
0
    blk->parent = NULL;
140
0
  }
141
142
0
  if (parent) {
143
0
    ul_ref_path(parent);
144
0
    blk->parent = parent;
145
0
  } else
146
0
    blk->parent = NULL;
147
148
0
  DBG(CXT, ul_debugobj(pc, "new parent"));
149
0
  return 0;
150
0
}
151
152
struct path_cxt *sysfs_blkdev_get_parent(struct path_cxt *pc)
153
0
{
154
0
  struct sysfs_blkdev *blk = ul_path_get_dialect(pc);
155
0
  return blk ? blk->parent : NULL;
156
0
}
157
158
/*
159
 * Redirects ENOENT errors to the parent.
160
 * For example
161
 *
162
 *  /sys/dev/block/8:1/queue/logical_block_size redirects to
163
 *  /sys/dev/block/8:0/queue/logical_block_size
164
 */
165
static int sysfs_blkdev_enoent_redirect(struct path_cxt *pc, const char *path, int *dirfd)
166
0
{
167
0
  struct sysfs_blkdev *blk = ul_path_get_dialect(pc);
168
169
0
  if (blk && blk->parent && path) {
170
0
    *dirfd = ul_path_get_dirfd(blk->parent);
171
0
    if (*dirfd >= 0) {
172
0
      DBG(CXT, ul_debugobj(pc, "%s redirected to parent", path));
173
0
      return 0;
174
0
    }
175
0
  }
176
0
  return 1; /* no redirect */
177
0
}
178
179
char *sysfs_blkdev_get_name(struct path_cxt *pc, char *buf, size_t bufsiz)
180
0
{
181
0
  char link[PATH_MAX];
182
0
  char *name;
183
0
  ssize_t sz;
184
185
        /* read /sys/dev/block/<maj:min> link */
186
0
  sz = ul_path_readlink(pc, link, sizeof(link), NULL);
187
0
  if (sz < 0)
188
0
    return NULL;
189
190
0
  name = strrchr(link, '/');
191
0
  if (!name)
192
0
    return NULL;
193
194
0
  name++;
195
0
  sz = strlen(name);
196
0
  if ((size_t) sz + 1 > bufsiz)
197
0
    return NULL;
198
199
0
  memcpy(buf, name, sz + 1);
200
0
  sysfs_devname_sys_to_dev(buf);
201
0
  return buf;
202
0
}
203
204
int sysfs_blkdev_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name)
205
0
{
206
0
  char path[NAME_MAX + 6 + 1];
207
208
0
#ifdef _DIRENT_HAVE_D_TYPE
209
0
  if (d->d_type != DT_DIR &&
210
0
      d->d_type != DT_LNK &&
211
0
      d->d_type != DT_UNKNOWN)
212
0
    return 0;
213
0
#endif
214
0
  size_t len = 0;
215
216
0
  if (parent_name) {
217
0
    const char *p = parent_name;
218
219
    /* /dev/sda --> "sda" */
220
0
    if (*parent_name == '/') {
221
0
      p = strrchr(parent_name, '/');
222
0
      if (!p)
223
0
        return 0;
224
0
      p++;
225
0
    }
226
227
0
    len = strlen(p);
228
0
    if ((strlen(d->d_name) <= len) || (strncmp(p, d->d_name, len) != 0))
229
0
      len = 0;
230
0
  }
231
232
0
  if (len > 0) {
233
    /* partitions subdir name is
234
     *  "<parent>[:digit:]" or "<parent>p[:digit:]"
235
     */
236
0
    return ((*(d->d_name + len) == 'p' && isdigit(*(d->d_name + len + 1)))
237
0
      || isdigit(*(d->d_name + len)));
238
0
  }
239
240
  /* Cannot use /partition file, not supported on old sysfs */
241
0
  snprintf(path, sizeof(path), "%s/start", d->d_name);
242
243
0
  return faccessat(dirfd(dir), path, R_OK, 0) == 0;
244
0
}
245
246
int sysfs_blkdev_count_partitions(struct path_cxt *pc, const char *devname)
247
0
{
248
0
  DIR *dir;
249
0
  struct dirent *d;
250
0
  int r = 0;
251
252
0
  dir = ul_path_opendir(pc, NULL);
253
0
  if (!dir)
254
0
    return 0;
255
256
0
  while ((d = xreaddir(dir))) {
257
0
    if (sysfs_blkdev_is_partition_dirent(dir, d, devname))
258
0
      r++;
259
0
  }
260
261
0
  closedir(dir);
262
0
  return r;
263
0
}
264
265
/*
266
 * Converts @partno (partition number) to devno of the partition.
267
 * The @pc handles wholedisk device.
268
 *
269
 * Note that this code does not expect any special format of the
270
 * partitions devnames.
271
 */
272
dev_t sysfs_blkdev_partno_to_devno(struct path_cxt *pc, int partno)
273
0
{
274
0
  DIR *dir;
275
0
  struct dirent *d;
276
0
  dev_t devno = 0;
277
278
0
  dir = ul_path_opendir(pc, NULL);
279
0
  if (!dir)
280
0
    return 0;
281
282
0
  while ((d = xreaddir(dir))) {
283
0
    int n;
284
285
0
    if (!sysfs_blkdev_is_partition_dirent(dir, d, NULL))
286
0
      continue;
287
288
0
    if (ul_path_readf_s32(pc, &n, "%s/partition", d->d_name))
289
0
      continue;
290
291
0
    if (n == partno) {
292
0
      if (ul_path_readf_majmin(pc, &devno, "%s/dev", d->d_name) == 0)
293
0
        break;
294
0
    }
295
0
  }
296
297
0
  closedir(dir);
298
0
  DBG(CXT, ul_debugobj(pc, "partno (%d) -> devno (%d)", (int) partno, (int) devno));
299
0
  return devno;
300
0
}
301
302
303
/*
304
 * Returns slave name if there is only one slave, otherwise returns NULL.
305
 * The result should be deallocated by free().
306
 */
307
char *sysfs_blkdev_get_slave(struct path_cxt *pc)
308
0
{
309
0
  DIR *dir;
310
0
  struct dirent *d;
311
0
  char *name = NULL;
312
313
0
  dir = ul_path_opendir(pc, "slaves");
314
0
  if (!dir)
315
0
    return NULL;
316
317
0
  while ((d = xreaddir(dir))) {
318
0
    if (name)
319
0
      goto err; /* more slaves */
320
0
    name = strdup(d->d_name);
321
0
  }
322
323
0
  closedir(dir);
324
0
  return name;
325
0
err:
326
0
  free(name);
327
0
  closedir(dir);
328
0
  return NULL;
329
0
}
330
331
332
0
#define SUBSYSTEM_LINKNAME  "/subsystem"
333
334
/*
335
 * For example:
336
 *
337
 * chain: /sys/dev/block/../../devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/ \
338
 *                           1-1.2:1.0/host65/target65:0:0/65:0:0:0/block/sdb
339
 *
340
 * The function check if <chain>/subsystem symlink exists, if yes then returns
341
 * basename of the readlink result, and remove the last subdirectory from the
342
 * <chain> path.
343
 */
344
static char *get_subsystem(char *chain, char *buf, size_t bufsz)
345
0
{
346
0
  size_t len;
347
0
  char *p;
348
349
0
  if (!chain || !*chain)
350
0
    return NULL;
351
352
0
  len = strlen(chain);
353
0
  if (len + sizeof(SUBSYSTEM_LINKNAME) > PATH_MAX)
354
0
    return NULL;
355
356
0
  do {
357
0
    ssize_t sz;
358
359
    /* append "/subsystem" to the path */
360
0
    memcpy(chain + len, SUBSYSTEM_LINKNAME, sizeof(SUBSYSTEM_LINKNAME));
361
362
    /* try if subsystem symlink exists */
363
0
    sz = readlink(chain, buf, bufsz - 1);
364
365
    /* remove last subsystem from chain */
366
0
    chain[len] = '\0';
367
0
    p = strrchr(chain, '/');
368
0
    if (p) {
369
0
      *p = '\0';
370
0
      len = p - chain;
371
0
    }
372
373
0
    if (sz > 0) {
374
      /* we found symlink to subsystem, return basename */
375
0
      buf[sz] = '\0';
376
0
      return basename(buf);
377
0
    }
378
379
0
  } while (p);
380
381
0
  return NULL;
382
0
}
383
384
/*
385
 * Returns complete path to the device, the path contains all subsystems
386
 * used for the device.
387
 */
388
char *sysfs_blkdev_get_devchain(struct path_cxt *pc, char *buf, size_t bufsz)
389
0
{
390
  /* read /sys/dev/block/<maj>:<min> symlink */
391
0
  ssize_t ssz;
392
0
  size_t sz = 0;
393
0
  struct ul_buffer tmp = UL_INIT_BUFFER;
394
0
  const char *p;
395
0
  char *res = NULL;
396
397
0
  ssz = ul_path_readlink(pc, buf, bufsz, NULL);
398
0
  if (ssz <= 0)
399
0
    return NULL;
400
401
0
  if ((p = ul_path_get_prefix(pc)))
402
0
    ul_buffer_append_string(&tmp, p);
403
404
0
  ul_buffer_append_string(&tmp, _PATH_SYS_DEVBLOCK "/");
405
0
  ul_buffer_append_data(&tmp, buf, ssz);
406
407
0
  p = ul_buffer_get_data(&tmp, &sz, NULL);
408
0
  if (p && sz < bufsz) {
409
0
    memcpy(buf, p, sz);
410
0
    res = buf;
411
0
  }
412
0
  ul_buffer_free_data(&tmp);
413
0
  return res;
414
0
}
415
416
/*
417
 * The @subsys returns the next subsystem in the chain. Function modifies
418
 * @devchain string.
419
 *
420
 * Returns: 0 in success, <0 on error, 1 on end of chain
421
 */
422
int sysfs_blkdev_next_subsystem(struct path_cxt *pc __attribute__((unused)),
423
       char *devchain, char **subsys)
424
0
{
425
0
  char subbuf[PATH_MAX];
426
0
  char *sub;
427
428
0
  if (!subsys || !devchain)
429
0
    return -EINVAL;
430
431
0
  *subsys = NULL;
432
433
0
  while ((sub = get_subsystem(devchain, subbuf, sizeof(subbuf)))) {
434
0
    *subsys = strdup(sub);
435
0
    if (!*subsys)
436
0
      return -ENOMEM;
437
0
    return 0;
438
0
  }
439
440
0
  return 1;
441
0
}
442
443
0
#define REMOVABLE_FILENAME  "/removable"
444
445
/*
446
 * For example:
447
 *
448
 * chain: /sys/dev/block/../../devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/ \
449
 *                           1-1.2:1.0/host65/target65:0:0/65:0:0:0/block/sdb
450
 */
451
static int sysfs_devchain_is_removable(char *chain)
452
0
{
453
0
  size_t len;
454
0
  char buf[20];
455
0
  char *p;
456
457
0
  if (!chain || !*chain)
458
0
    return 0;
459
460
0
  len = strlen(chain);
461
0
  if (len + sizeof(REMOVABLE_FILENAME) > PATH_MAX)
462
0
    return 0;
463
464
0
  do {
465
0
    int fd, rc;
466
467
    /* append "/removable" to the path */
468
0
    memcpy(chain + len, REMOVABLE_FILENAME, sizeof(REMOVABLE_FILENAME));
469
470
    /* try to read it */
471
0
    fd = open(chain, O_RDONLY);
472
0
    if (fd != -1) {
473
0
      rc = read_all(fd, buf, sizeof(buf));
474
0
      close(fd);
475
476
0
      if (rc > 0) {
477
0
        if (strncmp(buf, "fixed", min(rc, 5)) == 0) {
478
0
          return 0;
479
0
        } else if (strncmp(buf, "removable", min(rc, 9)) == 0) {
480
0
          return 1;
481
0
        }
482
0
      }
483
0
    }
484
485
    /* remove last subsystem from chain */
486
0
    chain[len] = '\0';
487
0
    p = strrchr(chain, '/');
488
0
    if (p) {
489
0
      *p = '\0';
490
0
      len = p - chain;
491
0
    }
492
493
0
  } while (p);
494
495
0
  return 0;
496
0
}
497
498
int sysfs_blkdev_is_hotpluggable(struct path_cxt *pc)
499
0
{
500
0
  char buf[PATH_MAX], *chain;
501
502
0
  chain = sysfs_blkdev_get_devchain(pc, buf, sizeof(buf));
503
0
  return sysfs_devchain_is_removable(chain);
504
0
}
505
506
int sysfs_blkdev_is_removable(struct path_cxt *pc)
507
0
{
508
0
  int rc = 0;
509
510
  // FIXME usb is not actually removable
511
512
  /* check /sys/dev/block/<maj>:<min>/removable attribute */
513
0
  if (ul_path_read_s32(pc, &rc, "removable") == 0)
514
0
    return rc;
515
516
0
  return 0;
517
0
}
518
519
static int get_dm_wholedisk(struct path_cxt *pc, char *diskname,
520
                size_t len, dev_t *diskdevno)
521
0
{
522
0
    int rc = 0;
523
0
    char *name;
524
525
    /* Note, sysfs_blkdev_get_slave() returns the first slave only,
526
     * if there is more slaves, then return NULL
527
     */
528
0
    name = sysfs_blkdev_get_slave(pc);
529
0
    if (!name)
530
0
        return -1;
531
532
0
    if (diskname && len)
533
0
        xstrncpy(diskname, name, len);
534
535
0
    if (diskdevno) {
536
0
        *diskdevno = __sysfs_devname_to_devno(ul_path_get_prefix(pc), name, NULL);
537
0
        if (!*diskdevno)
538
0
            rc = -1;
539
0
    }
540
541
0
    free(name);
542
0
    return rc;
543
0
}
544
545
/*
546
 * Returns by @diskdevno whole disk device devno and (optionally) by
547
 * @diskname the whole disk device name.
548
 */
549
int sysfs_blkdev_get_wholedisk( struct path_cxt *pc,
550
        char *diskname,
551
        size_t len,
552
        dev_t *diskdevno)
553
0
{
554
0
    int is_part = 0;
555
556
0
    if (!pc)
557
0
        return -1;
558
559
0
    is_part = ul_path_access(pc, F_OK, "partition") == 0;
560
0
    if (!is_part) {
561
        /*
562
         * Extra case for partitions mapped by device-mapper.
563
         *
564
         * All regular partitions (added by BLKPG ioctl or kernel PT
565
         * parser) have the /sys/.../partition file. The partitions
566
         * mapped by DM don't have such file, but they have "part"
567
         * prefix in DM UUID.
568
         */
569
0
        char *uuid = NULL, *tmp, *prefix;
570
571
0
  ul_path_read_string(pc, &uuid, "dm/uuid");
572
0
  tmp = uuid;
573
0
  prefix = uuid ? strsep(&tmp, "-") : NULL;
574
575
0
        if (prefix && strncasecmp(prefix, "part", 4) == 0)
576
0
            is_part = 1;
577
0
        free(uuid);
578
579
0
        if (is_part &&
580
0
            get_dm_wholedisk(pc, diskname, len, diskdevno) == 0)
581
            /*
582
             * partitioned device, mapped by DM
583
             */
584
0
            goto done;
585
586
0
        is_part = 0;
587
0
    }
588
589
0
    if (!is_part) {
590
        /*
591
         * unpartitioned device
592
         */
593
0
        if (diskname && !sysfs_blkdev_get_name(pc, diskname, len))
594
0
            goto err;
595
0
        if (diskdevno)
596
0
            *diskdevno = sysfs_blkdev_get_devno(pc);
597
598
0
    } else {
599
        /*
600
         * partitioned device
601
         *  - readlink /sys/dev/block/8:1   = ../../block/sda/sda1
602
         *  - dirname  ../../block/sda/sda1 = ../../block/sda
603
         *  - basename ../../block/sda      = sda
604
         */
605
0
        char linkpath[PATH_MAX];
606
0
        char *name;
607
0
  ssize_t linklen;
608
609
0
  linklen = ul_path_readlink(pc, linkpath, sizeof(linkpath), NULL);
610
0
        if (linklen < 0)
611
0
            goto err;
612
613
0
        stripoff_last_component(linkpath);      /* dirname */
614
0
        name = stripoff_last_component(linkpath);   /* basename */
615
0
        if (!name)
616
0
            goto err;
617
618
0
  sysfs_devname_sys_to_dev(name);
619
0
        if (diskname && len)
620
0
            xstrncpy(diskname, name, len);
621
622
0
        if (diskdevno) {
623
0
            *diskdevno = __sysfs_devname_to_devno(ul_path_get_prefix(pc), name, NULL);
624
0
            if (!*diskdevno)
625
0
                goto err;
626
0
        }
627
0
    }
628
629
0
done:
630
0
    return 0;
631
0
err:
632
0
    return -1;
633
0
}
634
635
int sysfs_devno_to_wholedisk(dev_t devno, char *diskname,
636
                 size_t len, dev_t *diskdevno)
637
0
{
638
0
  struct path_cxt *pc;
639
0
  int rc = 0;
640
641
0
  if (!devno)
642
0
    return -EINVAL;
643
0
  pc = ul_new_sysfs_path(devno, NULL, NULL);
644
0
  if (!pc)
645
0
    return -ENOMEM;
646
647
0
  rc = sysfs_blkdev_get_wholedisk(pc, diskname, len, diskdevno);
648
0
  ul_unref_path(pc);
649
0
  return rc;
650
0
}
651
652
/*
653
 * Returns 1 if the device is private device mapper device. The @uuid
654
 * (if not NULL) returns DM device UUID, use free() to deallocate.
655
 */
656
int sysfs_devno_is_dm_private(dev_t devno, char **uuid)
657
0
{
658
0
  struct path_cxt *pc = NULL;
659
0
  char *id = NULL;
660
0
  int rc = 0;
661
662
0
  pc = ul_new_sysfs_path(devno, NULL, NULL);
663
0
  if (!pc)
664
0
    goto done;
665
0
  if (ul_path_read_string(pc, &id, "dm/uuid") <= 0 || !id)
666
0
    goto done;
667
668
  /* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important
669
   * is the "LVM" prefix and "-<name>" postfix).
670
   */
671
0
  if (strncmp(id, "LVM-", 4) == 0) {
672
0
    char *p = strrchr(id + 4, '-');
673
674
0
    if (p && *(p + 1))
675
0
      rc = 1;
676
677
  /* Private Stratis devices prefix the UUID with "stratis-1-private"
678
   */
679
0
  } else if (strncmp(id, "stratis-1-private", 17) == 0) {
680
0
    rc = 1;
681
0
  }
682
0
done:
683
0
  ul_unref_path(pc);
684
0
  if (uuid)
685
0
    *uuid = id;
686
0
  else
687
0
    free(id);
688
0
  return rc;
689
0
}
690
691
/*
692
 * Return 0 or 1, or < 0 in case of error
693
 */
694
int sysfs_devno_is_wholedisk(dev_t devno)
695
0
{
696
0
  dev_t disk;
697
698
0
  if (sysfs_devno_to_wholedisk(devno, NULL, 0, &disk) != 0)
699
0
    return -1;
700
701
0
  return devno == disk;
702
0
}
703
704
705
int sysfs_blkdev_scsi_get_hctl(struct path_cxt *pc, int *h, int *c, int *t, int *l)
706
0
{
707
0
  char buf[PATH_MAX], *hctl;
708
0
  struct sysfs_blkdev *blk;
709
0
  ssize_t len;
710
711
0
  blk = ul_path_get_dialect(pc);
712
713
0
  if (!blk || blk->hctl_error)
714
0
    return -EINVAL;
715
0
  if (blk->has_hctl)
716
0
    goto done;
717
718
0
  blk->hctl_error = 1;
719
0
  len = ul_path_readlink(pc, buf, sizeof(buf), "device");
720
0
  if (len < 0)
721
0
    return len;
722
723
0
  hctl = strrchr(buf, '/');
724
0
  if (!hctl)
725
0
    return -1;
726
0
  hctl++;
727
728
0
  if (sscanf(hctl, "%u:%u:%u:%u", &blk->scsi_host, &blk->scsi_channel,
729
0
        &blk->scsi_target, &blk->scsi_lun) != 4)
730
0
    return -1;
731
732
0
  blk->has_hctl = 1;
733
0
done:
734
0
  if (h)
735
0
    *h = blk->scsi_host;
736
0
  if (c)
737
0
    *c = blk->scsi_channel;
738
0
  if (t)
739
0
    *t = blk->scsi_target;
740
0
  if (l)
741
0
    *l = blk->scsi_lun;
742
743
0
  blk->hctl_error = 0;
744
0
  return 0;
745
0
}
746
747
748
static char *scsi_host_attribute_path(
749
      struct path_cxt *pc,
750
      const char *type,
751
      char *buf,
752
      size_t bufsz,
753
      const char *attr)
754
0
{
755
0
  int len;
756
0
  int host;
757
0
  const char *prefix;
758
759
0
  if (sysfs_blkdev_scsi_get_hctl(pc, &host, NULL, NULL, NULL))
760
0
    return NULL;
761
762
0
  prefix = ul_path_get_prefix(pc);
763
0
  if (!prefix)
764
0
    prefix = "";
765
766
0
  if (attr)
767
0
    len = snprintf(buf, bufsz, "%s%s/%s_host/host%d/%s",
768
0
        prefix, _PATH_SYS_CLASS, type, host, attr);
769
0
  else
770
0
    len = snprintf(buf, bufsz, "%s%s/%s_host/host%d",
771
0
        prefix, _PATH_SYS_CLASS, type, host);
772
773
0
  return (len < 0 || (size_t) len >= bufsz) ? NULL : buf;
774
0
}
775
776
char *sysfs_blkdev_scsi_host_strdup_attribute(struct path_cxt *pc,
777
      const char *type, const char *attr)
778
0
{
779
0
  char buf[1024];
780
0
  int rc;
781
0
  FILE *f;
782
783
0
  if (!attr || !type ||
784
0
      !scsi_host_attribute_path(pc, type, buf, sizeof(buf), attr))
785
0
    return NULL;
786
787
0
  if (!(f = fopen(buf, "r" UL_CLOEXECSTR)))
788
0
                return NULL;
789
790
0
  rc = fscanf(f, "%1023[^\n]", buf);
791
0
  fclose(f);
792
793
0
  return rc == 1 ? strdup(buf) : NULL;
794
0
}
795
796
int sysfs_blkdev_scsi_host_is(struct path_cxt *pc, const char *type)
797
0
{
798
0
  char buf[PATH_MAX];
799
0
  struct stat st;
800
801
0
  if (!type || !scsi_host_attribute_path(pc, type,
802
0
        buf, sizeof(buf), NULL))
803
0
    return 0;
804
805
0
  return stat(buf, &st) == 0 && S_ISDIR(st.st_mode);
806
0
}
807
808
static char *scsi_attribute_path(struct path_cxt *pc,
809
    char *buf, size_t bufsz, const char *attr)
810
0
{
811
0
  int len, h, c, t, l;
812
0
  const char *prefix;
813
814
0
  if (sysfs_blkdev_scsi_get_hctl(pc, &h, &c, &t, &l) != 0)
815
0
    return NULL;
816
817
0
  prefix = ul_path_get_prefix(pc);
818
0
  if (!prefix)
819
0
    prefix = "";
820
821
0
  if (attr)
822
0
    len = snprintf(buf, bufsz, "%s%s/devices/%d:%d:%d:%d/%s",
823
0
        prefix, _PATH_SYS_SCSI,
824
0
        h,c,t,l, attr);
825
0
  else
826
0
    len = snprintf(buf, bufsz, "%s%s/devices/%d:%d:%d:%d",
827
0
        prefix, _PATH_SYS_SCSI,
828
0
        h,c,t,l);
829
0
  return (len < 0 || (size_t) len >= bufsz) ? NULL : buf;
830
0
}
831
832
int sysfs_blkdev_scsi_has_attribute(struct path_cxt *pc, const char *attr)
833
0
{
834
0
  char path[PATH_MAX];
835
0
  struct stat st;
836
837
0
  if (!scsi_attribute_path(pc, path, sizeof(path), attr))
838
0
    return 0;
839
840
0
  return stat(path, &st) == 0;
841
0
}
842
843
int sysfs_blkdev_scsi_path_contains(struct path_cxt *pc, const char *pattern)
844
0
{
845
0
  char path[PATH_MAX], linkc[PATH_MAX];
846
0
  struct stat st;
847
0
  ssize_t len;
848
849
0
  if (!scsi_attribute_path(pc, path, sizeof(path), NULL))
850
0
    return 0;
851
852
0
  if (stat(path, &st) != 0)
853
0
    return 0;
854
855
0
  len = readlink(path, linkc, sizeof(linkc) - 1);
856
0
  if (len < 0)
857
0
    return 0;
858
859
0
  linkc[len] = '\0';
860
0
  return strstr(linkc, pattern) != NULL;
861
0
}
862
863
static dev_t read_devno(const char *path)
864
0
{
865
0
  FILE *f;
866
0
  int maj = 0, min = 0;
867
0
  dev_t dev = 0;
868
869
0
  f = fopen(path, "r" UL_CLOEXECSTR);
870
0
  if (!f)
871
0
    return 0;
872
873
0
  if (fscanf(f, "%d:%d", &maj, &min) == 2)
874
0
    dev = makedev(maj, min);
875
0
  fclose(f);
876
0
  return dev;
877
0
}
878
879
int sysfs_devname_is_hidden(const char *prefix, const char *name)
880
0
{
881
0
  char buf[PATH_MAX];
882
0
  int rc = 0, hidden = 0, len;
883
0
  FILE *f;
884
885
0
  if (strncmp("/dev/", name, 5) == 0)
886
0
    return 0;
887
888
0
  if (!prefix)
889
0
    prefix = "";
890
  /*
891
   * Create path to /sys/block/<name>/hidden
892
   */
893
0
  len = snprintf(buf, sizeof(buf),
894
0
      "%s" _PATH_SYS_BLOCK "/%s/hidden",
895
0
      prefix, name);
896
897
0
  if (len < 0 || (size_t) len + 1 > sizeof(buf))
898
0
    return 0;
899
900
0
  f = fopen(buf, "r" UL_CLOEXECSTR);
901
0
  if (!f)
902
0
    return 0;
903
904
0
  rc = fscanf(f, "%d", &hidden);
905
0
  fclose(f);
906
907
0
  return rc == 1 ? hidden : 0;
908
0
}
909
910
911
dev_t __sysfs_devname_to_devno(const char *prefix, const char *name, const char *parent)
912
0
{
913
0
  char buf[PATH_MAX];
914
0
  char *_name = NULL, *_parent = NULL;  /* name as encoded in sysfs */
915
0
  dev_t dev = 0;
916
0
  int len;
917
918
0
  if (!prefix)
919
0
    prefix = "";
920
921
0
  assert(name);
922
923
0
  if (strncmp("/dev/", name, 5) == 0) {
924
    /*
925
     * Read from /dev
926
     */
927
0
    struct stat st;
928
929
0
    if (stat(name, &st) == 0) {
930
0
      dev = st.st_rdev;
931
0
      goto done;
932
0
    }
933
0
    name += 5;  /* unaccessible, or not node in /dev */
934
0
  }
935
936
0
  _name = strdup(name);
937
0
  if (!_name)
938
0
    goto done;
939
0
  sysfs_devname_dev_to_sys(_name);
940
941
0
  if (parent) {
942
0
    _parent = strdup(parent);
943
0
    if (!_parent)
944
0
      goto done;
945
0
  }
946
947
0
  if (parent && strncmp("dm-", name, 3) != 0) {
948
    /*
949
     * Create path to /sys/block/<parent>/<name>/dev
950
     */
951
0
    sysfs_devname_dev_to_sys(_parent);
952
0
    len = snprintf(buf, sizeof(buf),
953
0
        "%s" _PATH_SYS_BLOCK "/%s/%s/dev",
954
0
        prefix, _parent, _name);
955
0
    if (len < 0 || (size_t) len >= sizeof(buf))
956
0
      goto done;
957
958
    /* don't try anything else for dm-* */
959
0
    dev = read_devno(buf);
960
0
    goto done;
961
0
  }
962
963
  /*
964
   * Read from /sys/block/<sysname>/dev
965
   */
966
0
  len = snprintf(buf, sizeof(buf),
967
0
      "%s" _PATH_SYS_BLOCK "/%s/dev",
968
0
      prefix, _name);
969
0
  if (len < 0 || (size_t) len >= sizeof(buf))
970
0
    goto done;
971
0
  dev = read_devno(buf);
972
973
  /*
974
   * Read from /sys/block/<parent>/<partition>/dev
975
   */
976
0
  if (!dev && parent && startswith(name, parent)) {
977
0
    len = snprintf(buf, sizeof(buf),
978
0
        "%s" _PATH_SYS_BLOCK "/%s/%s/dev",
979
0
        prefix, _parent, _name);
980
0
    if (len < 0 || (size_t) len >= sizeof(buf))
981
0
      goto done;
982
0
    dev = read_devno(buf);
983
0
  }
984
985
  /*
986
   * Read from /sys/block/<sysname>/device/dev
987
   */
988
0
  if (!dev) {
989
0
    len = snprintf(buf, sizeof(buf),
990
0
        "%s" _PATH_SYS_BLOCK "/%s/device/dev",
991
0
        prefix, _name);
992
0
    if (len < 0 || (size_t) len >= sizeof(buf))
993
0
      goto done;
994
0
    dev = read_devno(buf);
995
0
  }
996
0
done:
997
0
  free(_name);
998
0
  free(_parent);
999
0
  return dev;
1000
0
}
1001
1002
dev_t sysfs_devname_to_devno(const char *name)
1003
0
{
1004
0
  return __sysfs_devname_to_devno(NULL, name, NULL);
1005
0
}
1006
1007
char *sysfs_blkdev_get_path(struct path_cxt *pc, char *buf, size_t bufsiz)
1008
0
{
1009
0
  const char *name = sysfs_blkdev_get_name(pc, buf, bufsiz);
1010
0
  char *res = NULL;
1011
0
  size_t sz;
1012
0
  struct stat st;
1013
1014
0
  if (!name)
1015
0
    goto done;
1016
1017
0
  sz = strlen(name);
1018
0
  if (sz + sizeof("/dev/") > bufsiz)
1019
0
    goto done;
1020
1021
  /* create the final "/dev/<name>" string */
1022
0
  memmove(buf + 5, name, sz + 1);
1023
0
  memcpy(buf, "/dev/", 5);
1024
1025
0
  if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == sysfs_blkdev_get_devno(pc))
1026
0
    res = buf;
1027
0
done:
1028
0
  return res;
1029
0
}
1030
1031
dev_t sysfs_blkdev_get_devno(struct path_cxt *pc)
1032
0
{
1033
0
  return ((struct sysfs_blkdev *) ul_path_get_dialect(pc))->devno;
1034
0
}
1035
1036
/*
1037
 * Returns devname (e.g. "/dev/sda1") for the given devno.
1038
 *
1039
 * Please, use more robust blkid_devno_to_devname() in your applications.
1040
 */
1041
char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz)
1042
0
{
1043
0
  struct path_cxt *pc = ul_new_sysfs_path(devno, NULL, NULL);
1044
0
  char *res = NULL;
1045
1046
0
  if (pc) {
1047
0
    res = sysfs_blkdev_get_path(pc, buf, bufsiz);
1048
0
    ul_unref_path(pc);
1049
0
  }
1050
0
  return res;
1051
0
}
1052
1053
char *sysfs_devno_to_devname(dev_t devno, char *buf, size_t bufsiz)
1054
0
{
1055
0
  struct path_cxt *pc = ul_new_sysfs_path(devno, NULL, NULL);
1056
0
  char *res = NULL;
1057
1058
0
  if (pc) {
1059
0
    res = sysfs_blkdev_get_name(pc, buf, bufsiz);
1060
0
    ul_unref_path(pc);
1061
0
  }
1062
0
  return res;
1063
0
}
1064
1065
int sysfs_devno_count_partitions(dev_t devno)
1066
0
{
1067
0
  struct path_cxt *pc = ul_new_sysfs_path(devno, NULL, NULL);
1068
0
  int n = 0;
1069
1070
0
  if (pc) {
1071
0
    char buf[PATH_MAX + 1];
1072
0
    char *name = sysfs_blkdev_get_name(pc, buf, sizeof(buf));
1073
1074
0
    n = sysfs_blkdev_count_partitions(pc, name);
1075
0
    ul_unref_path(pc);
1076
0
  }
1077
0
  return n;
1078
0
}
1079
1080
char *sysfs_chrdev_devno_to_devname(dev_t devno, char *buf, size_t bufsiz)
1081
0
{
1082
0
  char link[PATH_MAX];
1083
0
  struct path_cxt *pc;
1084
0
  char *name;
1085
0
  ssize_t sz;
1086
1087
0
  pc = ul_new_path(_PATH_SYS_DEVCHAR "/%u:%u", major(devno), minor(devno));
1088
0
  if (!pc)
1089
0
    return NULL;
1090
1091
        /* read /sys/dev/char/<maj:min> link */
1092
0
  sz = ul_path_readlink(pc, link, sizeof(link), NULL);
1093
0
  ul_unref_path(pc);
1094
1095
0
  if (sz < 0)
1096
0
    return NULL;
1097
1098
0
  name = strrchr(link, '/');
1099
0
  if (!name)
1100
0
    return NULL;
1101
1102
0
  name++;
1103
0
  sz = strlen(name);
1104
0
  if ((size_t) sz + 1 > bufsiz)
1105
0
    return NULL;
1106
1107
0
  memcpy(buf, name, sz + 1);
1108
0
  sysfs_devname_sys_to_dev(buf);
1109
0
  return buf;
1110
1111
0
}
1112
1113
enum sysfs_byteorder sysfs_get_byteorder(struct path_cxt *pc)
1114
0
{
1115
0
  int rc;
1116
0
  char buf[BUFSIZ];
1117
0
  enum sysfs_byteorder ret;
1118
1119
0
  rc = ul_path_read_buffer(pc, buf, sizeof(buf), _PATH_SYS_CPU_BYTEORDER);
1120
0
  if (rc < 0)
1121
0
    goto unknown;
1122
1123
0
  if (strncmp(buf, "little", sizeof(buf)) == 0) {
1124
0
    ret = SYSFS_BYTEORDER_LITTLE;
1125
0
    goto out;
1126
0
  } else if (strncmp(buf, "big", sizeof(buf)) == 0) {
1127
0
    ret = SYSFS_BYTEORDER_BIG;
1128
0
    goto out;
1129
0
  }
1130
1131
0
unknown:
1132
0
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1133
0
  ret = SYSFS_BYTEORDER_LITTLE;
1134
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
1135
  ret = SYSFS_BYTEORDER_BIG;
1136
#else
1137
#error Unknown byte order
1138
#endif
1139
1140
0
out:
1141
0
  return ret;
1142
0
}
1143
1144
int sysfs_get_address_bits(struct path_cxt *pc)
1145
0
{
1146
0
  int rc;
1147
0
  int address_bits;
1148
1149
0
  rc = ul_path_scanf(pc, _PATH_SYS_ADDRESS_BITS, "%d", &address_bits);
1150
0
  if (rc < 0)
1151
0
    return rc;
1152
0
  if (address_bits < 0)
1153
0
    return -EINVAL;
1154
0
  return address_bits;
1155
0
}
1156
1157
1158
#ifdef TEST_PROGRAM_SYSFS
1159
#include <errno.h>
1160
#include <err.h>
1161
#include <stdlib.h>
1162
1163
int main(int argc, char *argv[])
1164
{
1165
  struct path_cxt *pc;
1166
  char *devname;
1167
  dev_t devno, disk_devno;
1168
  char path[PATH_MAX], *sub, *chain;
1169
  char diskname[32];
1170
  int i, is_part, rc = EXIT_SUCCESS;
1171
  uint64_t u64;
1172
1173
  if (argc != 2)
1174
    errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]);
1175
1176
  ul_sysfs_init_debug();
1177
1178
  devname = argv[1];
1179
  devno = sysfs_devname_to_devno(devname);
1180
1181
  if (!devno)
1182
    err(EXIT_FAILURE, "failed to read devno");
1183
1184
  printf("non-context:\n");
1185
  printf(" DEVNO:   %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno));
1186
  printf(" DEVNAME: %s\n", sysfs_devno_to_devname(devno, path, sizeof(path)));
1187
  printf(" DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path)));
1188
1189
  sysfs_devno_to_wholedisk(devno, diskname, sizeof(diskname), &disk_devno);
1190
  printf(" WHOLEDISK-DEVNO:   %u (%d:%d)\n", (unsigned int) disk_devno, major(disk_devno), minor(disk_devno));
1191
  printf(" WHOLEDISK-DEVNAME: %s\n", diskname);
1192
1193
  pc = ul_new_sysfs_path(devno, NULL, NULL);
1194
  if (!pc)
1195
    goto done;
1196
1197
  printf("context based:\n");
1198
  devno = sysfs_blkdev_get_devno(pc);
1199
  printf(" DEVNO:   %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno));
1200
  printf(" DEVNAME: %s\n", sysfs_blkdev_get_name(pc, path, sizeof(path)));
1201
  printf(" DEVPATH: %s\n", sysfs_blkdev_get_path(pc, path, sizeof(path)));
1202
1203
  sysfs_devno_to_wholedisk(devno, diskname, sizeof(diskname), &disk_devno);
1204
  printf(" WHOLEDISK-DEVNO: %u (%d:%d)\n", (unsigned int) disk_devno, major(disk_devno), minor(disk_devno));
1205
  printf(" WHOLEDISK-DEVNAME: %s\n", diskname);
1206
1207
  is_part = ul_path_access(pc, F_OK, "partition") == 0;
1208
  printf(" PARTITION: %s\n", is_part ? "YES" : "NOT");
1209
1210
  if (is_part && disk_devno) {
1211
    struct path_cxt *disk_pc =  ul_new_sysfs_path(disk_devno, NULL, NULL);
1212
    sysfs_blkdev_set_parent(pc, disk_pc);
1213
1214
    ul_unref_path(disk_pc);
1215
  }
1216
1217
  printf(" HOTPLUG: %s\n", sysfs_blkdev_is_hotpluggable(pc) ? "yes" : "no");
1218
  printf(" REMOVABLE: %s\n", sysfs_blkdev_is_removable(pc) ? "yes" : "no");
1219
  printf(" SLAVES: %d\n", ul_path_count_dirents(pc, "slaves"));
1220
1221
  if (!is_part) {
1222
    printf("First 5 partitions:\n");
1223
    for (i = 1; i <= 5; i++) {
1224
      dev_t dev = sysfs_blkdev_partno_to_devno(pc, i);
1225
      if (dev)
1226
        printf("\t#%d %d:%d\n", i, major(dev), minor(dev));
1227
    }
1228
  }
1229
1230
  if (ul_path_read_u64(pc, &u64, "size") != 0)
1231
    printf(" (!) read SIZE failed\n");
1232
  else
1233
    printf(" SIZE: %jd\n", u64);
1234
1235
  if (ul_path_read_s32(pc, &i, "queue/hw_sector_size"))
1236
    printf(" (!) read SECTOR failed\n");
1237
  else
1238
    printf(" SECTOR: %d\n", i);
1239
1240
1241
  chain = sysfs_blkdev_get_devchain(pc, path, sizeof(path));
1242
  printf(" SUBSYSTEMS:\n");
1243
1244
  while (chain && sysfs_blkdev_next_subsystem(pc, chain, &sub) == 0) {
1245
    printf("\t%s\n", sub);
1246
    free(sub);
1247
  }
1248
1249
  rc = EXIT_SUCCESS;
1250
done:
1251
  ul_unref_path(pc);
1252
  return rc;
1253
}
1254
#endif /* TEST_PROGRAM_SYSFS */