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