/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 | | * 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 is more slaves, then return 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 | |
|
603 | 0 | } else { |
604 | | /* |
605 | | * partitioned device |
606 | | * - readlink /sys/dev/block/8:1 = ../../block/sda/sda1 |
607 | | * - dirname ../../block/sda/sda1 = ../../block/sda |
608 | | * - basename ../../block/sda = sda |
609 | | */ |
610 | 0 | char linkpath[PATH_MAX]; |
611 | 0 | char *name; |
612 | 0 | ssize_t linklen; |
613 | |
|
614 | 0 | linklen = ul_path_readlink(pc, linkpath, sizeof(linkpath), NULL); |
615 | 0 | if (linklen < 0) |
616 | 0 | goto err; |
617 | | |
618 | 0 | stripoff_last_component(linkpath); /* dirname */ |
619 | 0 | name = stripoff_last_component(linkpath); /* basename */ |
620 | 0 | if (!name) |
621 | 0 | goto err; |
622 | | |
623 | 0 | sysfs_devname_sys_to_dev(name); |
624 | 0 | if (diskname && len) |
625 | 0 | xstrncpy(diskname, name, len); |
626 | |
|
627 | 0 | if (diskdevno) { |
628 | 0 | *diskdevno = __sysfs_devname_to_devno(ul_path_get_prefix(pc), name, NULL); |
629 | 0 | if (!*diskdevno) |
630 | 0 | goto err; |
631 | 0 | } |
632 | 0 | } |
633 | | |
634 | 0 | done: |
635 | 0 | return 0; |
636 | 0 | err: |
637 | 0 | return -1; |
638 | 0 | } |
639 | | |
640 | | int sysfs_devno_to_wholedisk(dev_t devno, char *diskname, |
641 | | size_t len, dev_t *diskdevno) |
642 | 0 | { |
643 | 0 | struct path_cxt *pc; |
644 | 0 | int rc = 0; |
645 | |
|
646 | 0 | if (!devno) |
647 | 0 | return -EINVAL; |
648 | 0 | pc = ul_new_sysfs_path(devno, NULL, NULL); |
649 | 0 | if (!pc) |
650 | 0 | return -ENOMEM; |
651 | | |
652 | 0 | rc = sysfs_blkdev_get_wholedisk(pc, diskname, len, diskdevno); |
653 | 0 | ul_unref_path(pc); |
654 | 0 | return rc; |
655 | 0 | } |
656 | | |
657 | | /* |
658 | | * Returns 1 if the device is private device mapper device. The @uuid |
659 | | * (if not NULL) returns DM device UUID, use free() to deallocate. |
660 | | */ |
661 | | int sysfs_devno_is_dm_private(dev_t devno, char **uuid) |
662 | 0 | { |
663 | 0 | struct path_cxt *pc = NULL; |
664 | 0 | char *id = NULL; |
665 | 0 | int rc = 0; |
666 | |
|
667 | 0 | pc = ul_new_sysfs_path(devno, NULL, NULL); |
668 | 0 | if (!pc) |
669 | 0 | goto done; |
670 | 0 | if (ul_path_read_string(pc, &id, "dm/uuid") <= 0 || !id) |
671 | 0 | goto done; |
672 | | |
673 | | /* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important |
674 | | * is the "LVM" prefix and "-<name>" postfix). |
675 | | */ |
676 | 0 | if (strncmp(id, "LVM-", 4) == 0) { |
677 | 0 | char *p = strrchr(id + 4, '-'); |
678 | |
|
679 | 0 | if (p && *(p + 1)) |
680 | 0 | rc = 1; |
681 | | |
682 | | /* Private Stratis devices prefix the UUID with "stratis-1-private" |
683 | | */ |
684 | 0 | } else if (strncmp(id, "stratis-1-private", 17) == 0) { |
685 | 0 | rc = 1; |
686 | 0 | } |
687 | 0 | done: |
688 | 0 | ul_unref_path(pc); |
689 | 0 | if (uuid) |
690 | 0 | *uuid = id; |
691 | 0 | else |
692 | 0 | free(id); |
693 | 0 | return rc; |
694 | 0 | } |
695 | | |
696 | | /* |
697 | | * Return 0 or 1, or < 0 in case of error |
698 | | */ |
699 | | int sysfs_devno_is_wholedisk(dev_t devno) |
700 | 0 | { |
701 | 0 | dev_t disk; |
702 | |
|
703 | 0 | if (sysfs_devno_to_wholedisk(devno, NULL, 0, &disk) != 0) |
704 | 0 | return -1; |
705 | | |
706 | 0 | return devno == disk; |
707 | 0 | } |
708 | | |
709 | | |
710 | | int sysfs_blkdev_scsi_get_hctl(struct path_cxt *pc, int *h, int *c, int *t, int *l) |
711 | 0 | { |
712 | 0 | char buf[PATH_MAX], *hctl; |
713 | 0 | struct sysfs_blkdev *blk; |
714 | 0 | ssize_t len; |
715 | |
|
716 | 0 | blk = ul_path_get_dialect(pc); |
717 | |
|
718 | 0 | if (!blk || blk->hctl_error) |
719 | 0 | return -EINVAL; |
720 | 0 | if (blk->has_hctl) |
721 | 0 | goto done; |
722 | | |
723 | 0 | blk->hctl_error = 1; |
724 | 0 | len = ul_path_readlink(pc, buf, sizeof(buf), "device"); |
725 | 0 | if (len < 0) |
726 | 0 | return len; |
727 | | |
728 | 0 | hctl = strrchr(buf, '/'); |
729 | 0 | if (!hctl) |
730 | 0 | return -1; |
731 | 0 | hctl++; |
732 | |
|
733 | 0 | if (sscanf(hctl, "%u:%u:%u:%u", &blk->scsi_host, &blk->scsi_channel, |
734 | 0 | &blk->scsi_target, &blk->scsi_lun) != 4) |
735 | 0 | return -1; |
736 | | |
737 | 0 | blk->has_hctl = 1; |
738 | 0 | done: |
739 | 0 | if (h) |
740 | 0 | *h = blk->scsi_host; |
741 | 0 | if (c) |
742 | 0 | *c = blk->scsi_channel; |
743 | 0 | if (t) |
744 | 0 | *t = blk->scsi_target; |
745 | 0 | if (l) |
746 | 0 | *l = blk->scsi_lun; |
747 | |
|
748 | 0 | blk->hctl_error = 0; |
749 | 0 | return 0; |
750 | 0 | } |
751 | | |
752 | | |
753 | | static char *scsi_host_attribute_path( |
754 | | struct path_cxt *pc, |
755 | | const char *type, |
756 | | char *buf, |
757 | | size_t bufsz, |
758 | | const char *attr) |
759 | 0 | { |
760 | 0 | int len; |
761 | 0 | int host; |
762 | 0 | const char *prefix; |
763 | |
|
764 | 0 | if (sysfs_blkdev_scsi_get_hctl(pc, &host, NULL, NULL, NULL)) |
765 | 0 | return NULL; |
766 | | |
767 | 0 | prefix = ul_path_get_prefix(pc); |
768 | 0 | if (!prefix) |
769 | 0 | prefix = ""; |
770 | |
|
771 | 0 | if (attr) |
772 | 0 | len = snprintf(buf, bufsz, "%s%s/%s_host/host%d/%s", |
773 | 0 | prefix, _PATH_SYS_CLASS, type, host, attr); |
774 | 0 | else |
775 | 0 | len = snprintf(buf, bufsz, "%s%s/%s_host/host%d", |
776 | 0 | prefix, _PATH_SYS_CLASS, type, host); |
777 | |
|
778 | 0 | return (len < 0 || (size_t) len >= bufsz) ? NULL : buf; |
779 | 0 | } |
780 | | |
781 | | char *sysfs_blkdev_scsi_host_strdup_attribute(struct path_cxt *pc, |
782 | | const char *type, const char *attr) |
783 | 0 | { |
784 | 0 | char buf[1024]; |
785 | 0 | int rc; |
786 | 0 | FILE *f; |
787 | |
|
788 | 0 | if (!attr || !type || |
789 | 0 | !scsi_host_attribute_path(pc, type, buf, sizeof(buf), attr)) |
790 | 0 | return NULL; |
791 | | |
792 | 0 | if (!(f = fopen(buf, "r" UL_CLOEXECSTR))) |
793 | 0 | return NULL; |
794 | | |
795 | 0 | rc = fscanf(f, "%1023[^\n]", buf); |
796 | 0 | fclose(f); |
797 | |
|
798 | 0 | return rc == 1 ? strdup(buf) : NULL; |
799 | 0 | } |
800 | | |
801 | | int sysfs_blkdev_scsi_host_is(struct path_cxt *pc, const char *type) |
802 | 0 | { |
803 | 0 | char buf[PATH_MAX]; |
804 | 0 | struct stat st; |
805 | |
|
806 | 0 | if (!type || !scsi_host_attribute_path(pc, type, |
807 | 0 | buf, sizeof(buf), NULL)) |
808 | 0 | return 0; |
809 | | |
810 | 0 | return stat(buf, &st) == 0 && S_ISDIR(st.st_mode); |
811 | 0 | } |
812 | | |
813 | | static char *scsi_attribute_path(struct path_cxt *pc, |
814 | | char *buf, size_t bufsz, const char *attr) |
815 | 0 | { |
816 | 0 | int len, h, c, t, l; |
817 | 0 | const char *prefix; |
818 | |
|
819 | 0 | if (sysfs_blkdev_scsi_get_hctl(pc, &h, &c, &t, &l) != 0) |
820 | 0 | return NULL; |
821 | | |
822 | 0 | prefix = ul_path_get_prefix(pc); |
823 | 0 | if (!prefix) |
824 | 0 | prefix = ""; |
825 | |
|
826 | 0 | if (attr) |
827 | 0 | len = snprintf(buf, bufsz, "%s%s/devices/%d:%d:%d:%d/%s", |
828 | 0 | prefix, _PATH_SYS_SCSI, |
829 | 0 | h,c,t,l, attr); |
830 | 0 | else |
831 | 0 | len = snprintf(buf, bufsz, "%s%s/devices/%d:%d:%d:%d", |
832 | 0 | prefix, _PATH_SYS_SCSI, |
833 | 0 | h,c,t,l); |
834 | 0 | return (len < 0 || (size_t) len >= bufsz) ? NULL : buf; |
835 | 0 | } |
836 | | |
837 | | int sysfs_blkdev_scsi_has_attribute(struct path_cxt *pc, const char *attr) |
838 | 0 | { |
839 | 0 | char path[PATH_MAX]; |
840 | 0 | struct stat st; |
841 | |
|
842 | 0 | if (!scsi_attribute_path(pc, path, sizeof(path), attr)) |
843 | 0 | return 0; |
844 | | |
845 | 0 | return stat(path, &st) == 0; |
846 | 0 | } |
847 | | |
848 | | int sysfs_blkdev_scsi_path_contains(struct path_cxt *pc, const char *pattern) |
849 | 0 | { |
850 | 0 | char path[PATH_MAX], linkc[PATH_MAX]; |
851 | 0 | struct stat st; |
852 | 0 | ssize_t len; |
853 | |
|
854 | 0 | if (!scsi_attribute_path(pc, path, sizeof(path), NULL)) |
855 | 0 | return 0; |
856 | | |
857 | 0 | if (stat(path, &st) != 0) |
858 | 0 | return 0; |
859 | | |
860 | 0 | len = readlink(path, linkc, sizeof(linkc) - 1); |
861 | 0 | if (len < 0) |
862 | 0 | return 0; |
863 | | |
864 | 0 | linkc[len] = '\0'; |
865 | 0 | return strstr(linkc, pattern) != NULL; |
866 | 0 | } |
867 | | |
868 | | static dev_t read_devno(const char *path) |
869 | 0 | { |
870 | 0 | FILE *f; |
871 | 0 | int maj = 0, min = 0; |
872 | 0 | dev_t dev = 0; |
873 | |
|
874 | 0 | f = fopen(path, "r" UL_CLOEXECSTR); |
875 | 0 | if (!f) |
876 | 0 | return 0; |
877 | | |
878 | 0 | if (fscanf(f, "%d:%d", &maj, &min) == 2) |
879 | 0 | dev = makedev(maj, min); |
880 | 0 | fclose(f); |
881 | 0 | return dev; |
882 | 0 | } |
883 | | |
884 | | int sysfs_devname_is_hidden(const char *prefix, const char *name) |
885 | 0 | { |
886 | 0 | char buf[PATH_MAX]; |
887 | 0 | int rc = 0, hidden = 0, len; |
888 | 0 | FILE *f; |
889 | |
|
890 | 0 | if (strncmp("/dev/", name, 5) == 0) |
891 | 0 | return 0; |
892 | | |
893 | 0 | if (!prefix) |
894 | 0 | prefix = ""; |
895 | | /* |
896 | | * Create path to /sys/block/<name>/hidden |
897 | | */ |
898 | 0 | len = snprintf(buf, sizeof(buf), |
899 | 0 | "%s" _PATH_SYS_BLOCK "/%s/hidden", |
900 | 0 | prefix, name); |
901 | |
|
902 | 0 | if (len < 0 || (size_t) len + 1 > sizeof(buf)) |
903 | 0 | return 0; |
904 | | |
905 | 0 | f = fopen(buf, "r" UL_CLOEXECSTR); |
906 | 0 | if (!f) |
907 | 0 | return 0; |
908 | | |
909 | 0 | rc = fscanf(f, "%d", &hidden); |
910 | 0 | fclose(f); |
911 | |
|
912 | 0 | return rc == 1 ? hidden : 0; |
913 | 0 | } |
914 | | |
915 | | |
916 | | dev_t __sysfs_devname_to_devno(const char *prefix, const char *name, const char *parent) |
917 | 0 | { |
918 | 0 | char buf[PATH_MAX]; |
919 | 0 | char *_name = NULL, *_parent = NULL; /* name as encoded in sysfs */ |
920 | 0 | dev_t dev = 0; |
921 | 0 | int len; |
922 | |
|
923 | 0 | if (!prefix) |
924 | 0 | prefix = ""; |
925 | |
|
926 | 0 | assert(name); |
927 | | |
928 | 0 | if (strncmp("/dev/", name, 5) == 0) { |
929 | | /* |
930 | | * Read from /dev |
931 | | */ |
932 | 0 | struct stat st; |
933 | |
|
934 | 0 | if (stat(name, &st) == 0) { |
935 | 0 | dev = st.st_rdev; |
936 | 0 | goto done; |
937 | 0 | } |
938 | 0 | name += 5; /* unaccessible, or not node in /dev */ |
939 | 0 | } |
940 | | |
941 | 0 | _name = strdup(name); |
942 | 0 | if (!_name) |
943 | 0 | goto done; |
944 | 0 | sysfs_devname_dev_to_sys(_name); |
945 | |
|
946 | 0 | if (parent) { |
947 | 0 | _parent = strdup(parent); |
948 | 0 | if (!_parent) |
949 | 0 | goto done; |
950 | 0 | } |
951 | | |
952 | 0 | if (parent && strncmp("dm-", name, 3) != 0) { |
953 | | /* |
954 | | * Create path to /sys/block/<parent>/<name>/dev |
955 | | */ |
956 | 0 | sysfs_devname_dev_to_sys(_parent); |
957 | 0 | len = snprintf(buf, sizeof(buf), |
958 | 0 | "%s" _PATH_SYS_BLOCK "/%s/%s/dev", |
959 | 0 | prefix, _parent, _name); |
960 | 0 | if (len < 0 || (size_t) len >= sizeof(buf)) |
961 | 0 | goto done; |
962 | | |
963 | | /* don't try anything else for dm-* */ |
964 | 0 | dev = read_devno(buf); |
965 | 0 | goto done; |
966 | 0 | } |
967 | | |
968 | | /* |
969 | | * Read from /sys/block/<sysname>/dev |
970 | | */ |
971 | 0 | len = snprintf(buf, sizeof(buf), |
972 | 0 | "%s" _PATH_SYS_BLOCK "/%s/dev", |
973 | 0 | prefix, _name); |
974 | 0 | if (len < 0 || (size_t) len >= sizeof(buf)) |
975 | 0 | goto done; |
976 | 0 | dev = read_devno(buf); |
977 | | |
978 | | /* |
979 | | * Read from /sys/block/<parent>/<partition>/dev |
980 | | */ |
981 | 0 | if (!dev && parent && startswith(name, parent)) { |
982 | 0 | len = snprintf(buf, sizeof(buf), |
983 | 0 | "%s" _PATH_SYS_BLOCK "/%s/%s/dev", |
984 | 0 | prefix, _parent, _name); |
985 | 0 | if (len < 0 || (size_t) len >= sizeof(buf)) |
986 | 0 | goto done; |
987 | 0 | dev = read_devno(buf); |
988 | 0 | } |
989 | | |
990 | | /* |
991 | | * Read from /sys/block/<sysname>/device/dev |
992 | | */ |
993 | 0 | if (!dev) { |
994 | 0 | len = snprintf(buf, sizeof(buf), |
995 | 0 | "%s" _PATH_SYS_BLOCK "/%s/device/dev", |
996 | 0 | prefix, _name); |
997 | 0 | if (len < 0 || (size_t) len >= sizeof(buf)) |
998 | 0 | goto done; |
999 | 0 | dev = read_devno(buf); |
1000 | 0 | } |
1001 | 0 | done: |
1002 | 0 | free(_name); |
1003 | 0 | free(_parent); |
1004 | 0 | return dev; |
1005 | 0 | } |
1006 | | |
1007 | | dev_t sysfs_devname_to_devno(const char *name) |
1008 | 0 | { |
1009 | 0 | return __sysfs_devname_to_devno(NULL, name, NULL); |
1010 | 0 | } |
1011 | | |
1012 | | char *sysfs_blkdev_get_path(struct path_cxt *pc, char *buf, size_t bufsiz) |
1013 | 0 | { |
1014 | 0 | const char *name = sysfs_blkdev_get_name(pc, buf, bufsiz); |
1015 | 0 | char *res = NULL; |
1016 | 0 | size_t sz; |
1017 | 0 | struct stat st; |
1018 | |
|
1019 | 0 | if (!name) |
1020 | 0 | goto done; |
1021 | | |
1022 | 0 | sz = strlen(name); |
1023 | 0 | if (sz + sizeof("/dev/") > bufsiz) |
1024 | 0 | goto done; |
1025 | | |
1026 | | /* create the final "/dev/<name>" string */ |
1027 | 0 | memmove(buf + 5, name, sz + 1); |
1028 | 0 | memcpy(buf, "/dev/", 5); |
1029 | |
|
1030 | 0 | if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == sysfs_blkdev_get_devno(pc)) |
1031 | 0 | res = buf; |
1032 | 0 | done: |
1033 | 0 | return res; |
1034 | 0 | } |
1035 | | |
1036 | | dev_t sysfs_blkdev_get_devno(struct path_cxt *pc) |
1037 | 0 | { |
1038 | 0 | return ((struct sysfs_blkdev *) ul_path_get_dialect(pc))->devno; |
1039 | 0 | } |
1040 | | |
1041 | | /* |
1042 | | * Returns devname (e.g. "/dev/sda1") for the given devno. |
1043 | | * |
1044 | | * Please, use more robust blkid_devno_to_devname() in your applications. |
1045 | | */ |
1046 | | char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz) |
1047 | 0 | { |
1048 | 0 | struct path_cxt *pc = ul_new_sysfs_path(devno, NULL, NULL); |
1049 | 0 | char *res = NULL; |
1050 | |
|
1051 | 0 | if (pc) { |
1052 | 0 | res = sysfs_blkdev_get_path(pc, buf, bufsiz); |
1053 | 0 | ul_unref_path(pc); |
1054 | 0 | } |
1055 | 0 | return res; |
1056 | 0 | } |
1057 | | |
1058 | | char *sysfs_devno_to_devname(dev_t devno, char *buf, size_t bufsiz) |
1059 | 0 | { |
1060 | 0 | struct path_cxt *pc = ul_new_sysfs_path(devno, NULL, NULL); |
1061 | 0 | char *res = NULL; |
1062 | |
|
1063 | 0 | if (pc) { |
1064 | 0 | res = sysfs_blkdev_get_name(pc, buf, bufsiz); |
1065 | 0 | ul_unref_path(pc); |
1066 | 0 | } |
1067 | 0 | return res; |
1068 | 0 | } |
1069 | | |
1070 | | int sysfs_devno_count_partitions(dev_t devno) |
1071 | 0 | { |
1072 | 0 | struct path_cxt *pc = ul_new_sysfs_path(devno, NULL, NULL); |
1073 | 0 | int n = 0; |
1074 | |
|
1075 | 0 | if (pc) { |
1076 | 0 | char buf[PATH_MAX + 1]; |
1077 | 0 | char *name = sysfs_blkdev_get_name(pc, buf, sizeof(buf)); |
1078 | |
|
1079 | 0 | n = sysfs_blkdev_count_partitions(pc, name); |
1080 | 0 | ul_unref_path(pc); |
1081 | 0 | } |
1082 | 0 | return n; |
1083 | 0 | } |
1084 | | |
1085 | | char *sysfs_chrdev_devno_to_devname(dev_t devno, char *buf, size_t bufsiz) |
1086 | 0 | { |
1087 | 0 | char link[PATH_MAX]; |
1088 | 0 | struct path_cxt *pc; |
1089 | 0 | char *name; |
1090 | 0 | ssize_t sz; |
1091 | |
|
1092 | 0 | pc = ul_new_path(_PATH_SYS_DEVCHAR "/%u:%u", major(devno), minor(devno)); |
1093 | 0 | if (!pc) |
1094 | 0 | return NULL; |
1095 | | |
1096 | | /* read /sys/dev/char/<maj:min> link */ |
1097 | 0 | sz = ul_path_readlink(pc, link, sizeof(link), NULL); |
1098 | 0 | ul_unref_path(pc); |
1099 | |
|
1100 | 0 | if (sz < 0) |
1101 | 0 | return NULL; |
1102 | | |
1103 | 0 | name = strrchr(link, '/'); |
1104 | 0 | if (!name) |
1105 | 0 | return NULL; |
1106 | | |
1107 | 0 | name++; |
1108 | 0 | sz = strlen(name); |
1109 | 0 | if ((size_t) sz + 1 > bufsiz) |
1110 | 0 | return NULL; |
1111 | | |
1112 | 0 | memcpy(buf, name, sz + 1); |
1113 | 0 | sysfs_devname_sys_to_dev(buf); |
1114 | 0 | return buf; |
1115 | |
|
1116 | 0 | } |
1117 | | |
1118 | | enum sysfs_byteorder sysfs_get_byteorder(struct path_cxt *pc) |
1119 | 0 | { |
1120 | 0 | int rc; |
1121 | 0 | char buf[BUFSIZ]; |
1122 | 0 | enum sysfs_byteorder ret; |
1123 | |
|
1124 | 0 | rc = ul_path_read_buffer(pc, buf, sizeof(buf), _PATH_SYS_CPU_BYTEORDER); |
1125 | 0 | if (rc < 0) |
1126 | 0 | goto unknown; |
1127 | | |
1128 | 0 | if (strncmp(buf, "little", sizeof(buf)) == 0) { |
1129 | 0 | ret = SYSFS_BYTEORDER_LITTLE; |
1130 | 0 | goto out; |
1131 | 0 | } else if (strncmp(buf, "big", sizeof(buf)) == 0) { |
1132 | 0 | ret = SYSFS_BYTEORDER_BIG; |
1133 | 0 | goto out; |
1134 | 0 | } |
1135 | | |
1136 | 0 | unknown: |
1137 | 0 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
1138 | 0 | ret = SYSFS_BYTEORDER_LITTLE; |
1139 | | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
1140 | | ret = SYSFS_BYTEORDER_BIG; |
1141 | | #else |
1142 | | #error Unknown byte order |
1143 | | #endif |
1144 | |
|
1145 | 0 | out: |
1146 | 0 | return ret; |
1147 | 0 | } |
1148 | | |
1149 | | int sysfs_get_address_bits(struct path_cxt *pc) |
1150 | 0 | { |
1151 | 0 | int rc; |
1152 | 0 | int address_bits; |
1153 | |
|
1154 | 0 | rc = ul_path_scanf(pc, _PATH_SYS_ADDRESS_BITS, "%d", &address_bits); |
1155 | 0 | if (rc < 0) |
1156 | 0 | return rc; |
1157 | 0 | if (address_bits < 0) |
1158 | 0 | return -EINVAL; |
1159 | 0 | return address_bits; |
1160 | 0 | } |
1161 | | |
1162 | | |
1163 | | #ifdef TEST_PROGRAM_SYSFS |
1164 | | #include <errno.h> |
1165 | | #include <err.h> |
1166 | | #include <stdlib.h> |
1167 | | |
1168 | | int main(int argc, char *argv[]) |
1169 | | { |
1170 | | struct path_cxt *pc; |
1171 | | char *devname; |
1172 | | dev_t devno, disk_devno; |
1173 | | char path[PATH_MAX], *sub, *chain; |
1174 | | char diskname[32]; |
1175 | | int i, is_part, rc = EXIT_SUCCESS; |
1176 | | uint64_t u64; |
1177 | | |
1178 | | if (argc != 2) |
1179 | | errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]); |
1180 | | |
1181 | | ul_sysfs_init_debug(); |
1182 | | |
1183 | | devname = argv[1]; |
1184 | | devno = sysfs_devname_to_devno(devname); |
1185 | | |
1186 | | if (!devno) |
1187 | | err(EXIT_FAILURE, "failed to read devno"); |
1188 | | |
1189 | | printf("non-context:\n"); |
1190 | | printf(" DEVNO: %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno)); |
1191 | | printf(" DEVNAME: %s\n", sysfs_devno_to_devname(devno, path, sizeof(path))); |
1192 | | printf(" DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path))); |
1193 | | |
1194 | | sysfs_devno_to_wholedisk(devno, diskname, sizeof(diskname), &disk_devno); |
1195 | | printf(" WHOLEDISK-DEVNO: %u (%d:%d)\n", (unsigned int) disk_devno, major(disk_devno), minor(disk_devno)); |
1196 | | printf(" WHOLEDISK-DEVNAME: %s\n", diskname); |
1197 | | |
1198 | | pc = ul_new_sysfs_path(devno, NULL, NULL); |
1199 | | if (!pc) |
1200 | | goto done; |
1201 | | |
1202 | | printf("context based:\n"); |
1203 | | devno = sysfs_blkdev_get_devno(pc); |
1204 | | printf(" DEVNO: %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno)); |
1205 | | printf(" DEVNAME: %s\n", sysfs_blkdev_get_name(pc, path, sizeof(path))); |
1206 | | printf(" DEVPATH: %s\n", sysfs_blkdev_get_path(pc, path, sizeof(path))); |
1207 | | |
1208 | | sysfs_devno_to_wholedisk(devno, diskname, sizeof(diskname), &disk_devno); |
1209 | | printf(" WHOLEDISK-DEVNO: %u (%d:%d)\n", (unsigned int) disk_devno, major(disk_devno), minor(disk_devno)); |
1210 | | printf(" WHOLEDISK-DEVNAME: %s\n", diskname); |
1211 | | |
1212 | | is_part = ul_path_access(pc, F_OK, "partition") == 0; |
1213 | | printf(" PARTITION: %s\n", is_part ? "YES" : "NOT"); |
1214 | | |
1215 | | if (is_part && disk_devno) { |
1216 | | struct path_cxt *disk_pc = ul_new_sysfs_path(disk_devno, NULL, NULL); |
1217 | | sysfs_blkdev_set_parent(pc, disk_pc); |
1218 | | |
1219 | | ul_unref_path(disk_pc); |
1220 | | } |
1221 | | |
1222 | | printf(" HOTPLUG: %s\n", sysfs_blkdev_is_hotpluggable(pc) ? "yes" : "no"); |
1223 | | printf(" REMOVABLE: %s\n", sysfs_blkdev_is_removable(pc) ? "yes" : "no"); |
1224 | | printf(" SLAVES: %d\n", ul_path_count_dirents(pc, "slaves")); |
1225 | | |
1226 | | if (!is_part) { |
1227 | | printf("First 5 partitions:\n"); |
1228 | | for (i = 1; i <= 5; i++) { |
1229 | | dev_t dev = sysfs_blkdev_partno_to_devno(pc, i); |
1230 | | if (dev) |
1231 | | printf("\t#%d %d:%d\n", i, major(dev), minor(dev)); |
1232 | | } |
1233 | | } |
1234 | | |
1235 | | if (ul_path_read_u64(pc, &u64, "size") != 0) |
1236 | | printf(" (!) read SIZE failed\n"); |
1237 | | else |
1238 | | printf(" SIZE: %jd\n", u64); |
1239 | | |
1240 | | if (ul_path_read_s32(pc, &i, "queue/hw_sector_size")) |
1241 | | printf(" (!) read SECTOR failed\n"); |
1242 | | else |
1243 | | printf(" SECTOR: %d\n", i); |
1244 | | |
1245 | | |
1246 | | chain = sysfs_blkdev_get_devchain(pc, path, sizeof(path)); |
1247 | | printf(" SUBSYSTEMS:\n"); |
1248 | | |
1249 | | while (chain && sysfs_blkdev_next_subsystem(pc, chain, &sub) == 0) { |
1250 | | printf("\t%s\n", sub); |
1251 | | free(sub); |
1252 | | } |
1253 | | |
1254 | | rc = EXIT_SUCCESS; |
1255 | | done: |
1256 | | ul_unref_path(pc); |
1257 | | return rc; |
1258 | | } |
1259 | | #endif /* TEST_PROGRAM_SYSFS */ |