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