]>
Commit | Line | Data |
---|---|---|
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 | ||
12 | char *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 | ||
27 | int 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 | 39 | char *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 | ||
44 | dev_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 | */ | |
108 | char *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 |
139 | int 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; | |
161 | err: | |
4c5bbc5d | 162 | rc = errno > 0 ? -errno : -1; |
7fe16fda KZ |
163 | sysfs_deinit(cxt); |
164 | return rc; | |
165 | } | |
166 | ||
167 | void 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 | ||
182 | int 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 |
198 | int 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 |
205 | static 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 |
220 | ssize_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 |
233 | DIR *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 | ||
262 | static 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 | ||
270 | static 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 | ||
285 | int 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 | */ | |
331 | dev_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 |
365 | int 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 | 382 | int 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 | 394 | int 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 | 406 | int 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 | ||
418 | char *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 | ||
425 | int 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 | ||
439 | int 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 | */ | |
461 | char *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; | |
479 | err: | |
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 | */ | |
489 | char *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 */ |
511 | static 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 | ||
521 | static 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 | ||
549 | int 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 | ||
631 | done: | |
632 | sysfs_deinit(&cxt); | |
633 | return 0; | |
634 | err: | |
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 | ||
644 | int 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 |