]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/sysfs.c
lib/tt: don't ignore "extreme" columns if an free space is available
[thirdparty/util-linux.git] / lib / sysfs.c
CommitLineData
7fe16fda
KZ
1/*
2 * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
3 */
4
093b20ba
KZ
5#include <ctype.h>
6
7fe16fda
KZ
7#include "c.h"
8#include "at.h"
9#include "pathnames.h"
10#include "sysfs.h"
11
12char *sysfs_devno_attribute_path(dev_t devno, char *buf,
413993fc 13 size_t bufsiz, const char *attr)
7fe16fda
KZ
14{
15 int len;
16
17 if (attr)
413993fc 18 len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d/%s",
7fe16fda
KZ
19 major(devno), minor(devno), attr);
20 else
413993fc 21 len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d",
7fe16fda
KZ
22 major(devno), minor(devno));
23
6b9166ce 24 return (len < 0 || (size_t) len + 1 > bufsiz) ? NULL : buf;
7fe16fda
KZ
25}
26
27int sysfs_devno_has_attribute(dev_t devno, const char *attr)
28{
29 char path[PATH_MAX];
30 struct stat info;
31
32 if (!sysfs_devno_attribute_path(devno, path, sizeof(path), attr))
33 return 0;
34 if (stat(path, &info) == 0)
35 return 1;
36 return 0;
37}
38
413993fc 39char *sysfs_devno_path(dev_t devno, char *buf, size_t bufsiz)
7fe16fda 40{
413993fc 41 return sysfs_devno_attribute_path(devno, buf, bufsiz, NULL);
7fe16fda
KZ
42}
43
44dev_t sysfs_devname_to_devno(const char *name, const char *parent)
45{
46 char buf[PATH_MAX], *path = NULL;
47 dev_t dev = 0;
48
49 if (strncmp("/dev/", name, 5) == 0) {
50 /*
51 * Read from /dev
52 */
53 struct stat st;
54
55 if (stat(name, &st) == 0)
56 dev = st.st_rdev;
57 else
58 name += 5; /* unaccesible, or not node in /dev */
59 }
60
857db7f5 61 if (!dev && parent && strncmp("dm-", name, 3)) {
7fe16fda
KZ
62 /*
63 * Create path to /sys/block/<parent>/<name>/dev
64 */
65 int len = snprintf(buf, sizeof(buf),
66 _PATH_SYS_BLOCK "/%s/%s/dev", parent, name);
6b9166ce 67 if (len < 0 || (size_t) len + 1 > sizeof(buf))
7fe16fda
KZ
68 return 0;
69 path = buf;
70
71 } else if (!dev) {
72 /*
73 * Create path to /sys/block/<name>/dev
74 */
75 int len = snprintf(buf, sizeof(buf),
76 _PATH_SYS_BLOCK "/%s/dev", name);
6b9166ce 77 if (len < 0 || (size_t) len + 1 > sizeof(buf))
7fe16fda
KZ
78 return 0;
79 path = buf;
80 }
81
82 if (path) {
83 /*
84 * read devno from sysfs
85 */
86 FILE *f;
87 int maj = 0, min = 0;
88
89 f = fopen(path, "r");
90 if (!f)
91 return 0;
92
9d72cbc1 93 if (fscanf(f, "%d:%d", &maj, &min) == 2)
7fe16fda
KZ
94 dev = makedev(maj, min);
95 fclose(f);
96 }
97 return dev;
98}
99
413993fc
KZ
100/*
101 * Returns devname (e.g. "/dev/sda1") for the given devno.
102 *
103 * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
104 * symlinks.
105 *
106 * Please, use more robust blkid_devno_to_devname() in your applications.
107 */
108char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz)
109{
110 struct sysfs_cxt cxt;
111 char *name;
112 size_t sz;
113 struct stat st;
114
115 if (sysfs_init(&cxt, devno, NULL))
116 return NULL;
117
118 name = sysfs_get_devname(&cxt, buf, bufsiz);
119 sysfs_deinit(&cxt);
120
121 if (!name)
122 return NULL;
123
124 sz = strlen(name);
125
126 if (sz + sizeof("/dev/") > bufsiz)
127 return NULL;
128
129 /* create the final "/dev/<name>" string */
130 memmove(buf + 5, name, sz + 1);
131 memcpy(buf, "/dev/", 5);
132
133 if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == devno)
134 return buf;
135
136 return NULL;
137}
138
7fe16fda
KZ
139int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, struct sysfs_cxt *parent)
140{
141 char path[PATH_MAX];
4c5bbc5d 142 int fd, rc;
7fe16fda 143
413993fc 144 memset(cxt, 0, sizeof(*cxt));
3a18db62 145 cxt->dir_fd = -1;
413993fc 146
7fe16fda
KZ
147 if (!sysfs_devno_path(devno, path, sizeof(path)))
148 goto err;
149
150 fd = open(path, O_RDONLY);
151 if (fd < 0)
152 goto err;
06ae069b
KZ
153 cxt->dir_fd = fd;
154
7fe16fda
KZ
155 cxt->dir_path = strdup(path);
156 if (!cxt->dir_path)
157 goto err;
7fe16fda 158 cxt->devno = devno;
7fe16fda
KZ
159 cxt->parent = parent;
160 return 0;
161err:
4c5bbc5d 162 rc = errno > 0 ? -errno : -1;
7fe16fda
KZ
163 sysfs_deinit(cxt);
164 return rc;
165}
166
167void sysfs_deinit(struct sysfs_cxt *cxt)
168{
169 if (!cxt)
170 return;
171
172 if (cxt->dir_fd >= 0)
173 close(cxt->dir_fd);
87e77645
KZ
174 free(cxt->dir_path);
175
7fe16fda
KZ
176 cxt->devno = 0;
177 cxt->dir_fd = -1;
178 cxt->parent = NULL;
87e77645 179 cxt->dir_path = NULL;
7fe16fda
KZ
180}
181
182int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st)
183{
184 int rc = fstat_at(cxt->dir_fd, cxt->dir_path, attr, st, 0);
185
186 if (rc != 0 && errno == ENOENT &&
187 strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
188
189 /* Exception for "queue/<attr>". These attributes are available
190 * for parental devices only
191 */
192 return fstat_at(cxt->parent->dir_fd,
193 cxt->parent->dir_path, attr, st, 0);
194 }
195 return rc;
196}
197
d8a84552
KZ
198int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr)
199{
200 struct stat st;
201
202 return sysfs_stat(cxt, attr, &st) == 0;
203}
204
7fe16fda
KZ
205static int sysfs_open(struct sysfs_cxt *cxt, const char *attr)
206{
207 int fd = open_at(cxt->dir_fd, cxt->dir_path, attr, O_RDONLY);
208
209 if (fd == -1 && errno == ENOENT &&
210 strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
211
212 /* Exception for "queue/<attr>". These attributes are available
213 * for parental devices only
214 */
215 fd = open_at(cxt->parent->dir_fd, cxt->dir_path, attr, O_RDONLY);
216 }
217 return fd;
218}
219
413993fc
KZ
220ssize_t sysfs_readlink(struct sysfs_cxt *cxt, const char *attr,
221 char *buf, size_t bufsiz)
222{
cffee0de
CW
223 if (!cxt->dir_path)
224 return -1;
225
413993fc
KZ
226 if (attr)
227 return readlink_at(cxt->dir_fd, cxt->dir_path, attr, buf, bufsiz);
228
229 /* read /sys/dev/block/<maj:min> link */
230 return readlink(cxt->dir_path, buf, bufsiz);
231}
232
7fe16fda
KZ
233DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr)
234{
235 DIR *dir;
fee9431f 236 int fd = -1;
7fe16fda
KZ
237
238 if (attr)
239 fd = sysfs_open(cxt, attr);
fee9431f
KZ
240
241 else if (cxt->dir_fd >= 0)
7fe16fda
KZ
242 /* request to open root of device in sysfs (/sys/block/<dev>)
243 * -- we cannot use cxt->sysfs_fd directly, because closedir()
244 * will close this our persistent file descriptor.
245 */
246 fd = dup(cxt->dir_fd);
7fe16fda
KZ
247
248 if (fd < 0)
249 return NULL;
250
251 dir = fdopendir(fd);
252 if (!dir) {
253 close(fd);
254 return NULL;
255 }
256 if (!attr)
257 rewinddir(dir);
258 return dir;
259}
260
261
262static FILE *sysfs_fopen(struct sysfs_cxt *cxt, const char *attr)
263{
264 int fd = sysfs_open(cxt, attr);
265
266 return fd < 0 ? NULL : fdopen(fd, "r");
267}
268
269
270static struct dirent *xreaddir(DIR *dp)
271{
272 struct dirent *d;
273
274 while ((d = readdir(dp))) {
275 if (!strcmp(d->d_name, ".") ||
276 !strcmp(d->d_name, ".."))
277 continue;
278
279 /* blacklist here? */
280 break;
281 }
282 return d;
283}
284
285int sysfs_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name)
286{
287 char path[256];
288
289#ifdef _DIRENT_HAVE_D_TYPE
09a71aa1
PR
290 if (d->d_type != DT_DIR &&
291 d->d_type != DT_LNK)
7fe16fda
KZ
292 return 0;
293#endif
093b20ba
KZ
294 if (parent_name) {
295 const char *p = parent_name;
296 size_t len;
297
298 /* /dev/sda --> "sda" */
299 if (*parent_name == '/') {
300 p = strrchr(parent_name, '/');
301 if (!p)
302 return 0;
303 p++;
304 }
305
306 len = strlen(p);
307 if (strlen(d->d_name) <= len)
308 return 0;
309
a3b78053
KZ
310 /* partitions subdir name is
311 * "<parent>[:digit:]" or "<parent>p[:digit:]"
312 */
313 return strncmp(p, d->d_name, len) == 0 &&
314 ((*(d->d_name + len) == 'p' && isdigit(*(d->d_name + len + 1)))
315 || isdigit(*(d->d_name + len)));
093b20ba 316 }
7fe16fda
KZ
317
318 /* Cannot use /partition file, not supported on old sysfs */
319 snprintf(path, sizeof(path), "%s/start", d->d_name);
320
321 return faccessat(dirfd(dir), path, R_OK, 0) == 0;
322}
323
778ad369 324/*
f31322a2
BV
325 * Converts @partno (partition number) to devno of the partition.
326 * The @cxt handles wholedisk device.
778ad369
KZ
327 *
328 * Note that this code does not expect any special format of the
329 * partitions devnames.
330 */
331dev_t sysfs_partno_to_devno(struct sysfs_cxt *cxt, int partno)
332{
333 DIR *dir;
334 struct dirent *d;
335 char path[256];
336 dev_t devno = 0;
337
338 dir = sysfs_opendir(cxt, NULL);
339 if (!dir)
340 return 0;
341
342 while ((d = xreaddir(dir))) {
343 int n, maj, min;
344
345 if (!sysfs_is_partition_dirent(dir, d, NULL))
346 continue;
347
348 snprintf(path, sizeof(path), "%s/partition", d->d_name);
f31322a2 349 if (sysfs_read_int(cxt, path, &n))
778ad369
KZ
350 continue;
351
f31322a2
BV
352 if (n == partno) {
353 snprintf(path, sizeof(path), "%s/dev", d->d_name);
354 if (sysfs_scanf(cxt, path, "%d:%d", &maj, &min) == 2)
355 devno = makedev(maj, min);
356 break;
357 }
778ad369
KZ
358 }
359
360 closedir(dir);
361 return devno;
362}
363
364
7fe16fda
KZ
365int sysfs_scanf(struct sysfs_cxt *cxt, const char *attr, const char *fmt, ...)
366{
367 FILE *f = sysfs_fopen(cxt, attr);
368 va_list ap;
369 int rc;
370
371 if (!f)
372 return -EINVAL;
373 va_start(ap, fmt);
374 rc = vfscanf(f, fmt, ap);
375 va_end(ap);
376
377 fclose(f);
378 return rc;
379}
380
413993fc 381
90e9fcda 382int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res)
7fe16fda 383{
90e9fcda
KZ
384 int64_t x = 0;
385
386 if (sysfs_scanf(cxt, attr, "%"SCNd64, &x) == 1) {
387 if (res)
388 *res = x;
389 return 0;
390 }
391 return -1;
7fe16fda
KZ
392}
393
90e9fcda 394int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res)
7fe16fda 395{
90e9fcda
KZ
396 uint64_t x = 0;
397
398 if (sysfs_scanf(cxt, attr, "%"SCNu64, &x) == 1) {
399 if (res)
400 *res = x;
401 return 0;
402 }
403 return -1;
7fe16fda
KZ
404}
405
90e9fcda 406int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res)
7fe16fda 407{
90e9fcda
KZ
408 int x = 0;
409
410 if (sysfs_scanf(cxt, attr, "%d", &x) == 1) {
411 if (res)
412 *res = x;
413 return 0;
414 }
415 return -1;
7fe16fda
KZ
416}
417
418char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr)
419{
420 char buf[1024];
657d9adb 421 return sysfs_scanf(cxt, attr, "%1023[^\n]", buf) == 1 ?
7fe16fda
KZ
422 strdup(buf) : NULL;
423}
424
425int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr)
426{
427 DIR *dir;
428 int r = 0;
429
430 if (!(dir = sysfs_opendir(cxt, attr)))
431 return 0;
432
433 while (xreaddir(dir)) r++;
434
435 closedir(dir);
436 return r;
437}
438
439int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname)
440{
441 DIR *dir;
442 struct dirent *d;
443 int r = 0;
444
445 if (!(dir = sysfs_opendir(cxt, NULL)))
446 return 0;
447
448 while ((d = xreaddir(dir))) {
449 if (sysfs_is_partition_dirent(dir, d, devname))
450 r++;
451 }
452
453 closedir(dir);
454 return r;
455}
456
413993fc
KZ
457/*
458 * Returns slave name if there is only one slave, otherwise returns NULL.
459 * The result should be deallocated by free().
460 */
461char *sysfs_get_slave(struct sysfs_cxt *cxt)
462{
463 DIR *dir;
464 struct dirent *d;
465 char *name = NULL;
466
467 if (!(dir = sysfs_opendir(cxt, "slaves")))
468 return NULL;
469
470 while ((d = xreaddir(dir))) {
471 if (name)
472 goto err; /* more slaves */
473
474 name = strdup(d->d_name);
475 }
476
477 closedir(dir);
478 return name;
479err:
480 free(name);
5415374d 481 closedir(dir);
413993fc
KZ
482 return NULL;
483}
484
485/*
486 * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
487 * symlinks.
488 */
489char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz)
490{
491 char *name = NULL;
492 ssize_t sz;
493
494 sz = sysfs_readlink(cxt, NULL, buf, bufsiz - 1);
495 if (sz < 0)
496 return NULL;
497
498 buf[sz] = '\0';
499 name = strrchr(buf, '/');
500 if (!name)
501 return NULL;
502
503 name++;
504 sz = strlen(name);
505
506 memmove(buf, name, sz + 1);
507 return buf;
508}
7fe16fda 509
3b66f48e
ML
510/* returns basename and keeps dirname in the @path */
511static char *stripoff_last_component(char *path)
512{
513 char *p = strrchr(path, '/');
514
515 if (!p)
516 return NULL;
517 *p = '\0';
518 return ++p;
519}
520
521static int get_dm_wholedisk(struct sysfs_cxt *cxt, char *diskname,
522 size_t len, dev_t *diskdevno)
523{
524 int rc = 0;
525 char *name;
526
527 /* Note, sysfs_get_slave() returns the first slave only,
528 * if there is more slaves, then return NULL
529 */
530 name = sysfs_get_slave(cxt);
531 if (!name)
532 return -1;
533
534 if (diskname && len) {
535 strncpy(diskname, name, len);
536 diskname[len - 1] = '\0';
537 }
538
539 if (diskdevno) {
540 *diskdevno = sysfs_devname_to_devno(name, NULL);
541 if (!*diskdevno)
542 rc = -1;
543 }
544
545 free(name);
546 return rc;
547}
548
549int sysfs_devno_to_wholedisk(dev_t dev, char *diskname,
550 size_t len, dev_t *diskdevno)
551{
552 struct sysfs_cxt cxt;
553 int is_part = 0;
554
555 if (!dev || sysfs_init(&cxt, dev, NULL) != 0)
556 return -1;
557
558 is_part = sysfs_has_attribute(&cxt, "partition");
559 if (!is_part) {
560 /*
561 * Extra case for partitions mapped by device-mapper.
562 *
563 * All regualar partitions (added by BLKPG ioctl or kernel PT
564 * parser) have the /sys/.../partition file. The partitions
565 * mapped by DM don't have such file, but they have "part"
566 * prefix in DM UUID.
567 */
568 char *uuid = sysfs_strdup(&cxt, "dm/uuid");
569 char *tmp = uuid;
570 char *prefix = uuid ? strsep(&tmp, "-") : NULL;
571
572 if (prefix && strncasecmp(prefix, "part", 4) == 0)
573 is_part = 1;
574 free(uuid);
575
576 if (is_part &&
577 get_dm_wholedisk(&cxt, diskname, len, diskdevno) == 0)
578 /*
579 * partitioned device, mapped by DM
580 */
581 goto done;
582
583 is_part = 0;
584 }
585
586 if (!is_part) {
587 /*
588 * unpartitioned device
589 */
590 if (diskname && len) {
591 if (!sysfs_get_devname(&cxt, diskname, len))
592 goto err;
593 }
594 if (diskdevno)
595 *diskdevno = dev;
596
597 } else {
598 /*
599 * partitioned device
600 * - readlink /sys/dev/block/8:1 = ../../block/sda/sda1
601 * - dirname ../../block/sda/sda1 = ../../block/sda
602 * - basename ../../block/sda = sda
603 */
604 char linkpath[PATH_MAX];
605 char *name;
606 int linklen;
607
608 linklen = sysfs_readlink(&cxt, NULL,
609 linkpath, sizeof(linkpath) - 1);
610 if (linklen < 0)
611 goto err;
612 linkpath[linklen] = '\0';
613
614 stripoff_last_component(linkpath); /* dirname */
615 name = stripoff_last_component(linkpath); /* basename */
616 if (!name)
617 goto err;
618
619 if (diskname && len) {
620 strncpy(diskname, name, len);
621 diskname[len - 1] = '\0';
622 }
623
624 if (diskdevno) {
625 *diskdevno = sysfs_devname_to_devno(name, NULL);
626 if (!*diskdevno)
627 goto err;
628 }
629 }
630
631done:
632 sysfs_deinit(&cxt);
633 return 0;
634err:
635 sysfs_deinit(&cxt);
636 return -1;
637}
638
e918cca5 639#ifdef TEST_PROGRAM_SYSFS
7fe16fda
KZ
640#include <errno.h>
641#include <err.h>
642#include <stdlib.h>
643
644int main(int argc, char *argv[])
645{
4c5bbc5d 646 struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
7fe16fda
KZ
647 char *devname;
648 dev_t devno;
649 char path[PATH_MAX];
778ad369 650 int i, is_part;
90e9fcda 651 uint64_t u64;
413993fc 652 ssize_t len;
7fe16fda
KZ
653
654 if (argc != 2)
655 errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]);
656
657 devname = argv[1];
658 devno = sysfs_devname_to_devno(devname, NULL);
659
660 if (!devno)
661 err(EXIT_FAILURE, "failed to read devno");
662
778ad369
KZ
663 is_part = sysfs_devno_has_attribute(devno, "partition");
664
7fe16fda 665 printf("NAME: %s\n", devname);
778ad369 666 printf("DEVNO: %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno));
7fe16fda 667 printf("DEVNOPATH: %s\n", sysfs_devno_path(devno, path, sizeof(path)));
413993fc 668 printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path)));
778ad369 669 printf("PARTITION: %s\n", is_part ? "YES" : "NOT");
7fe16fda 670
1aae31c0
KZ
671 if (sysfs_init(&cxt, devno, NULL))
672 return EXIT_FAILURE;
7fe16fda 673
413993fc
KZ
674 len = sysfs_readlink(&cxt, NULL, path, sizeof(path) - 1);
675 if (len > 0) {
676 path[len] = '\0';
677 printf("DEVNOLINK: %s\n", path);
678 }
679
778ad369
KZ
680 if (!is_part) {
681 printf("First 5 partitions:\n");
682 for (i = 1; i <= 5; i++) {
683 dev_t dev = sysfs_partno_to_devno(&cxt, i);
684 if (dev)
685 printf("\t#%d %d:%d\n", i, major(dev), minor(dev));
686 }
687 }
688
7fe16fda 689 printf("SLAVES: %d\n", sysfs_count_dirents(&cxt, "slaves"));
90e9fcda
KZ
690
691 if (sysfs_read_u64(&cxt, "size", &u64))
413993fc 692 printf("read SIZE failed\n");
90e9fcda
KZ
693 else
694 printf("SIZE: %jd\n", u64);
695
696 if (sysfs_read_int(&cxt, "queue/hw_sector_size", &i))
413993fc 697 printf("read SECTOR failed\n");
90e9fcda
KZ
698 else
699 printf("SECTOR: %d\n", i);
7fe16fda 700
413993fc
KZ
701 printf("DEVNAME: %s\n", sysfs_get_devname(&cxt, path, sizeof(path)));
702
fb4a9e54 703 sysfs_deinit(&cxt);
7fe16fda
KZ
704 return EXIT_SUCCESS;
705}
706#endif