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