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