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