]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/path.c
lib/timeutils: print error if timestamp can't be parsed
[thirdparty/util-linux.git] / lib / path.c
1 /*
2 * No copyright is claimed. This code is in the public domain; do with
3 * it what you wish.
4 *
5 * Copyright (C) 2018 Karel Zak <kzak@redhat.com>
6 *
7 *
8 * Simple functions to access files. Paths can be globally prefixed to read
9 * data from an alternative source (e.g. a /proc dump for regression tests).
10 *
11 * The paths is possible to format by printf-like way for functions with "f"
12 * postfix in the name (e.g. readf, openf, ... ul_path_readf_u64()).
13 *
14 * The ul_path_read_* API is possible to use without path_cxt handler. In this
15 * case is not possible to use global prefix and printf-like formatting.
16 */
17 #include <stdarg.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include <stdio.h>
21 #include <inttypes.h>
22 #include <errno.h>
23
24 #include "c.h"
25 #include "fileutils.h"
26 #include "all-io.h"
27 #include "path.h"
28 #include "debug.h"
29 #include "strutils.h"
30
31 /*
32 * Debug stuff (based on include/debug.h)
33 */
34 static UL_DEBUG_DEFINE_MASK(ulpath);
35 UL_DEBUG_DEFINE_MASKNAMES(ulpath) = UL_DEBUG_EMPTY_MASKNAMES;
36
37 #define ULPATH_DEBUG_INIT (1 << 1)
38 #define ULPATH_DEBUG_CXT (1 << 2)
39
40 #define DBG(m, x) __UL_DBG(ulpath, ULPATH_DEBUG_, m, x)
41 #define ON_DBG(m, x) __UL_DBG_CALL(ulpath, ULPATH_DEBUG_, m, x)
42
43 #define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(ulpath)
44 #include "debugobj.h"
45
46 void ul_path_init_debug(void)
47 {
48 if (ulpath_debug_mask)
49 return;
50 __UL_INIT_DEBUG_FROM_ENV(ulpath, ULPATH_DEBUG_, 0, ULPATH_DEBUG);
51 }
52
53 struct path_cxt *ul_new_path(const char *dir, ...)
54 {
55 struct path_cxt *pc = calloc(1, sizeof(*pc));
56
57 if (!pc)
58 return NULL;
59
60 DBG(CXT, ul_debugobj(pc, "alloc"));
61
62 pc->refcount = 1;
63 pc->dir_fd = -1;
64
65 if (dir) {
66 int rc;
67 va_list ap;
68
69 va_start(ap, dir);
70 rc = vasprintf(&pc->dir_path, dir, ap);
71 va_end(ap);
72
73 if (rc < 0 || !pc->dir_path)
74 goto fail;
75 }
76 return pc;
77 fail:
78 ul_unref_path(pc);
79 return NULL;
80 }
81
82 void ul_ref_path(struct path_cxt *pc)
83 {
84 if (pc)
85 pc->refcount++;
86 }
87
88 void ul_unref_path(struct path_cxt *pc)
89 {
90 if (!pc)
91 return;
92
93 pc->refcount--;
94
95 if (pc->refcount <= 0) {
96 DBG(CXT, ul_debugobj(pc, "dealloc"));
97 if (pc->dialect)
98 pc->free_dialect(pc);
99 ul_path_close_dirfd(pc);
100 free(pc->dir_path);
101 free(pc->prefix);
102 free(pc);
103 }
104 }
105
106 int ul_path_set_prefix(struct path_cxt *pc, const char *prefix)
107 {
108 char *p = NULL;
109
110 assert(pc->dir_fd < 0);
111
112 if (prefix) {
113 p = strdup(prefix);
114 if (!p)
115 return -ENOMEM;
116 }
117
118 free(pc->prefix);
119 pc->prefix = p;
120 DBG(CXT, ul_debugobj(pc, "new prefix: '%s'", p));
121 return 0;
122 }
123
124 const char *ul_path_get_prefix(struct path_cxt *pc)
125 {
126 return pc ? pc->prefix : NULL;
127 }
128
129 int ul_path_set_dir(struct path_cxt *pc, const char *dir)
130 {
131 char *p = NULL;
132
133 if (dir) {
134 p = strdup(dir);
135 if (!p)
136 return -ENOMEM;
137 }
138
139 if (pc->dir_fd >= 0) {
140 close(pc->dir_fd);
141 pc->dir_fd = -1;
142 }
143
144 free(pc->dir_path);
145 pc->dir_path = p;
146 DBG(CXT, ul_debugobj(pc, "new dir: '%s'", p));
147 return 0;
148 }
149
150 const char *ul_path_get_dir(struct path_cxt *pc)
151 {
152 return pc ? pc->dir_path : NULL;
153 }
154
155 int ul_path_set_dialect(struct path_cxt *pc, void *data, void free_data(struct path_cxt *))
156 {
157 pc->dialect = data;
158 pc->free_dialect = free_data;
159 DBG(CXT, ul_debugobj(pc, "(re)set dialect"));
160 return 0;
161 }
162
163 void *ul_path_get_dialect(struct path_cxt *pc)
164 {
165 return pc ? pc->dialect : NULL;
166 }
167
168 int ul_path_set_enoent_redirect(struct path_cxt *pc, int (*func)(struct path_cxt *, const char *, int *))
169 {
170 pc->redirect_on_enoent = func;
171 return 0;
172 }
173
174 static const char *get_absdir(struct path_cxt *pc)
175 {
176 int rc;
177 const char *dirpath;
178
179 if (!pc->prefix)
180 return pc->dir_path;
181
182 dirpath = pc->dir_path;
183 if (!dirpath)
184 return pc->prefix;
185 if (*dirpath == '/')
186 dirpath++;
187
188 rc = snprintf(pc->path_buffer, sizeof(pc->path_buffer), "%s/%s", pc->prefix, dirpath);
189 if (rc < 0)
190 return NULL;
191 if ((size_t)rc >= sizeof(pc->path_buffer)) {
192 errno = ENAMETOOLONG;
193 return NULL;
194 }
195
196 return pc->path_buffer;
197 }
198
199 int ul_path_is_accessible(struct path_cxt *pc)
200 {
201 const char *path;
202 assert(pc);
203
204 if (pc->dir_fd >= 0)
205 return 1;
206
207 path = get_absdir(pc);
208 if (!path)
209 return 0;
210 return access(path, F_OK) == 0;
211 }
212
213 int ul_path_get_dirfd(struct path_cxt *pc)
214 {
215 assert(pc);
216 assert(pc->dir_path);
217
218 if (pc->dir_fd < 0) {
219 const char *path = get_absdir(pc);
220 if (!path)
221 return -errno;
222
223 DBG(CXT, ul_debugobj(pc, "opening dir: '%s'", path));
224 pc->dir_fd = open(path, O_RDONLY|O_CLOEXEC);
225 }
226
227 return pc->dir_fd;
228 }
229
230 /* Note that next ul_path_get_dirfd() will reopen the directory */
231 void ul_path_close_dirfd(struct path_cxt *pc)
232 {
233 assert(pc);
234
235 if (pc->dir_fd >= 0) {
236 DBG(CXT, ul_debugobj(pc, "closing dir"));
237 close(pc->dir_fd);
238 pc->dir_fd = -1;
239 }
240 }
241
242 int ul_path_isopen_dirfd(struct path_cxt *pc)
243 {
244 return pc && pc->dir_fd >= 0;
245 }
246
247 static const char *ul_path_mkpath(struct path_cxt *pc, const char *path, va_list ap)
248 {
249 int rc;
250
251 errno = 0;
252
253 rc = vsnprintf(pc->path_buffer, sizeof(pc->path_buffer), path, ap);
254 if (rc < 0) {
255 if (!errno)
256 errno = EINVAL;
257 return NULL;
258 }
259
260 if ((size_t)rc >= sizeof(pc->path_buffer)) {
261 errno = ENAMETOOLONG;
262 return NULL;
263 }
264
265 return pc->path_buffer;
266 }
267
268 char *ul_path_get_abspath(struct path_cxt *pc, char *buf, size_t bufsz, const char *path, ...)
269 {
270 if (path) {
271 int rc;
272 va_list ap;
273 const char *tail = NULL, *dirpath = pc->dir_path;
274
275 va_start(ap, path);
276 tail = ul_path_mkpath(pc, path, ap);
277 va_end(ap);
278
279 if (dirpath && *dirpath == '/')
280 dirpath++;
281 if (tail && *tail == '/')
282 tail++;
283
284 rc = snprintf(buf, bufsz, "%s/%s/%s",
285 pc->prefix ? pc->prefix : "",
286 dirpath ? dirpath : "",
287 tail ? tail : "");
288
289 if ((size_t)rc >= bufsz) {
290 errno = ENAMETOOLONG;
291 return NULL;
292 }
293 } else {
294 const char *tmp = get_absdir(pc);
295
296 if (!tmp)
297 return NULL;
298 xstrncpy(buf, tmp, bufsz);
299 }
300
301 return buf;
302 }
303
304
305 int ul_path_access(struct path_cxt *pc, int mode, const char *path)
306 {
307 int rc;
308
309 if (!pc) {
310 rc = access(path, mode);
311 DBG(CXT, ul_debug("access '%s' [no context, rc=%d]", path, rc));
312 } else {
313 int dir = ul_path_get_dirfd(pc);
314 if (dir < 0)
315 return dir;
316 if (*path == '/')
317 path++;
318
319 rc = faccessat(dir, path, mode, 0);
320
321 if (rc && errno == ENOENT
322 && pc->redirect_on_enoent
323 && pc->redirect_on_enoent(pc, path, &dir) == 0)
324 rc = faccessat(dir, path, mode, 0);
325
326 DBG(CXT, ul_debugobj(pc, "access: '%s' [rc=%d]", path, rc));
327 }
328 return rc;
329 }
330
331 int ul_path_accessf(struct path_cxt *pc, int mode, const char *path, ...)
332 {
333 va_list ap;
334 const char *p;
335
336 va_start(ap, path);
337 p = ul_path_mkpath(pc, path, ap);
338 va_end(ap);
339
340 return !p ? -errno : ul_path_access(pc, mode, p);
341 }
342
343 int ul_path_stat(struct path_cxt *pc, struct stat *sb, int flags, const char *path)
344 {
345 int rc;
346
347 if (!pc) {
348 rc = path ? stat(path, sb) : -EINVAL;
349 DBG(CXT, ul_debug("stat '%s' [no context, rc=%d]", path, rc));
350 } else {
351 int dir = ul_path_get_dirfd(pc);
352 if (dir < 0)
353 return dir;
354 if (path) {
355 if (*path == '/')
356 path++;
357 rc = fstatat(dir, path, sb, flags);
358
359 } else
360 rc = fstat(dir, sb); /* dir itself */
361
362 if (rc && errno == ENOENT
363 && path
364 && pc->redirect_on_enoent
365 && pc->redirect_on_enoent(pc, path, &dir) == 0)
366 rc = fstatat(dir, path, sb, 0);
367
368 DBG(CXT, ul_debugobj(pc, "stat '%s' [rc=%d]", path, rc));
369 }
370 return rc;
371 }
372
373 int ul_path_open(struct path_cxt *pc, int flags, const char *path)
374 {
375 int fd;
376
377 if (!path)
378 return -EINVAL;
379 if (!pc) {
380 fd = open(path, flags);
381 DBG(CXT, ul_debug("opening '%s' [no context]", path));
382 } else {
383 int fdx;
384 int dir = ul_path_get_dirfd(pc);
385 if (dir < 0)
386 return dir;
387
388 if (*path == '/')
389 path++;
390
391 fdx = fd = openat(dir, path, flags);
392
393 if (fd < 0 && errno == ENOENT
394 && pc->redirect_on_enoent
395 && pc->redirect_on_enoent(pc, path, &dir) == 0)
396 fd = openat(dir, path, flags);
397
398 DBG(CXT, ul_debugobj(pc, "opening '%s'%s", path, fdx != fd ? " [redirected]" : ""));
399 }
400 return fd;
401 }
402
403 int ul_path_vopenf(struct path_cxt *pc, int flags, const char *path, va_list ap)
404 {
405 const char *p = ul_path_mkpath(pc, path, ap);
406
407 return !p ? -errno : ul_path_open(pc, flags, p);
408 }
409
410 int ul_path_openf(struct path_cxt *pc, int flags, const char *path, ...)
411 {
412 va_list ap;
413 int rc;
414
415 va_start(ap, path);
416 rc = ul_path_vopenf(pc, flags, path, ap);
417 va_end(ap);
418
419 return rc;
420 }
421
422 /*
423 * Maybe stupid, but good enough ;-)
424 */
425 static int mode2flags(const char *mode)
426 {
427 int flags = 0;
428 const char *p;
429
430 for (p = mode; p && *p; p++) {
431 if (*p == 'r' && *(p + 1) == '+')
432 flags |= O_RDWR;
433 else if (*p == 'r')
434 flags |= O_RDONLY;
435
436 else if (*p == 'w' && *(p + 1) == '+')
437 flags |= O_RDWR | O_TRUNC;
438 else if (*p == 'w')
439 flags |= O_WRONLY | O_TRUNC;
440
441 else if (*p == 'a' && *(p + 1) == '+')
442 flags |= O_RDWR | O_APPEND;
443 else if (*p == 'a')
444 flags |= O_WRONLY | O_APPEND;
445 #ifdef O_CLOEXEC
446 else if (*p == *UL_CLOEXECSTR)
447 flags |= O_CLOEXEC;
448 #endif
449 }
450
451 return flags;
452 }
453
454 FILE *ul_path_fopen(struct path_cxt *pc, const char *mode, const char *path)
455 {
456 int flags = mode2flags(mode);
457 int fd = ul_path_open(pc, flags, path);
458
459 if (fd < 0)
460 return NULL;
461
462 return fdopen(fd, mode);
463 }
464
465
466 FILE *ul_path_vfopenf(struct path_cxt *pc, const char *mode, const char *path, va_list ap)
467 {
468 const char *p = ul_path_mkpath(pc, path, ap);
469
470 return !p ? NULL : ul_path_fopen(pc, mode, p);
471 }
472
473 FILE *ul_path_fopenf(struct path_cxt *pc, const char *mode, const char *path, ...)
474 {
475 FILE *f;
476 va_list ap;
477
478 va_start(ap, path);
479 f = ul_path_vfopenf(pc, mode, path, ap);
480 va_end(ap);
481
482 return f;
483 }
484
485 /*
486 * Open directory @path in read-onl mode. If the path is NULL then duplicate FD
487 * to the directory addressed by @pc.
488 */
489 DIR *ul_path_opendir(struct path_cxt *pc, const char *path)
490 {
491 DIR *dir;
492 int fd = -1;
493
494 if (path)
495 fd = ul_path_open(pc, O_RDONLY|O_CLOEXEC, path);
496 else if (pc->dir_path) {
497 int dirfd;
498
499 DBG(CXT, ul_debugobj(pc, "duplicate dir path"));
500 dirfd = ul_path_get_dirfd(pc);
501 if (dirfd >= 0)
502 fd = dup_fd_cloexec(dirfd, STDERR_FILENO + 1);
503 }
504
505 if (fd < 0)
506 return NULL;
507
508 dir = fdopendir(fd);
509 if (!dir) {
510 close(fd);
511 return NULL;
512 }
513 if (!path)
514 rewinddir(dir);
515 return dir;
516 }
517
518
519 /*
520 * Open directory @path in read-onl mode. If the path is NULL then duplicate FD
521 * to the directory addressed by @pc.
522 */
523 DIR *ul_path_vopendirf(struct path_cxt *pc, const char *path, va_list ap)
524 {
525 const char *p = ul_path_mkpath(pc, path, ap);
526
527 return !p ? NULL : ul_path_opendir(pc, p);
528 }
529
530 /*
531 * Open directory @path in read-onl mode. If the path is NULL then duplicate FD
532 * to the directory addressed by @pc.
533 */
534 DIR *ul_path_opendirf(struct path_cxt *pc, const char *path, ...)
535 {
536 va_list ap;
537 DIR *dir;
538
539 va_start(ap, path);
540 dir = ul_path_vopendirf(pc, path, ap);
541 va_end(ap);
542
543 return dir;
544 }
545
546 /*
547 * If @path is NULL then readlink is called on @pc directory.
548 */
549 ssize_t ul_path_readlink(struct path_cxt *pc, char *buf, size_t bufsiz, const char *path)
550 {
551 int dirfd;
552 ssize_t ssz;
553
554 if (!path) {
555 const char *p = get_absdir(pc);
556 if (!p)
557 return -errno;
558 ssz = readlink(p, buf, bufsiz - 1);
559 } else {
560 dirfd = ul_path_get_dirfd(pc);
561 if (dirfd < 0)
562 return dirfd;
563
564 if (*path == '/')
565 path++;
566
567 ssz = readlinkat(dirfd, path, buf, bufsiz - 1);
568 }
569
570 if (ssz >= 0)
571 buf[ssz] = '\0';
572 return ssz;
573 }
574
575 /*
576 * If @path is NULL then readlink is called on @pc directory.
577 */
578 ssize_t ul_path_readlinkf(struct path_cxt *pc, char *buf, size_t bufsiz, const char *path, ...)
579 {
580 const char *p;
581 va_list ap;
582
583 va_start(ap, path);
584 p = ul_path_mkpath(pc, path, ap);
585 va_end(ap);
586
587 return !p ? -errno : ul_path_readlink(pc, buf, bufsiz, p);
588 }
589
590 int ul_path_read(struct path_cxt *pc, char *buf, size_t len, const char *path)
591 {
592 int rc, errsv;
593 int fd;
594
595 fd = ul_path_open(pc, O_RDONLY|O_CLOEXEC, path);
596 if (fd < 0)
597 return -errno;
598
599 DBG(CXT, ul_debug(" reading '%s'", path));
600 rc = read_all(fd, buf, len);
601
602 errsv = errno;
603 close(fd);
604 errno = errsv;
605 return rc;
606 }
607
608 int ul_path_vreadf(struct path_cxt *pc, char *buf, size_t len, const char *path, va_list ap)
609 {
610 const char *p = ul_path_mkpath(pc, path, ap);
611
612 return !p ? -errno : ul_path_read(pc, buf, len, p);
613 }
614
615 int ul_path_readf(struct path_cxt *pc, char *buf, size_t len, const char *path, ...)
616 {
617 va_list ap;
618 int rc;
619
620 va_start(ap, path);
621 rc = ul_path_vreadf(pc, buf, len, path, ap);
622 va_end(ap);
623
624 return rc;
625 }
626
627
628 /*
629 * Returns newly allocated buffer with data from file. Maximal size is BUFSIZ
630 * (send patch if you need something bigger;-)
631 *
632 * Returns size of the string without \0, nothing is allocated if returns <= 0.
633 */
634 int ul_path_read_string(struct path_cxt *pc, char **str, const char *path)
635 {
636 char buf[BUFSIZ];
637 int rc;
638
639 if (!str)
640 return -EINVAL;
641
642 *str = NULL;
643 rc = ul_path_read(pc, buf, sizeof(buf) - 1, path);
644 if (rc < 0)
645 return rc;
646
647 /* Remove tailing newline (usual in sysfs) */
648 if (rc > 0 && *(buf + rc - 1) == '\n')
649 --rc;
650 if (rc == 0)
651 return 0;
652
653 buf[rc] = '\0';
654 *str = strdup(buf);
655 if (!*str)
656 rc = -ENOMEM;
657
658 return rc;
659 }
660
661 int ul_path_readf_string(struct path_cxt *pc, char **str, const char *path, ...)
662 {
663 const char *p;
664 va_list ap;
665
666 va_start(ap, path);
667 p = ul_path_mkpath(pc, path, ap);
668 va_end(ap);
669
670 return !p ? -errno : ul_path_read_string(pc, str, p);
671 }
672
673 int ul_path_read_buffer(struct path_cxt *pc, char *buf, size_t bufsz, const char *path)
674 {
675 int rc = ul_path_read(pc, buf, bufsz - 1, path);
676
677 if (rc == 0)
678 buf[0] = '\0';
679
680 else if (rc > 0) {
681 /* Remove tailing newline (usual in sysfs) */
682 if (*(buf + rc - 1) == '\n')
683 buf[--rc] = '\0';
684 else
685 buf[rc - 1] = '\0';
686 }
687
688 return rc;
689 }
690
691 int ul_path_readf_buffer(struct path_cxt *pc, char *buf, size_t bufsz, const char *path, ...)
692 {
693 const char *p;
694 va_list ap;
695
696 va_start(ap, path);
697 p = ul_path_mkpath(pc, path, ap);
698 va_end(ap);
699
700 return !p ? -errno : ul_path_read_buffer(pc, buf, bufsz, p);
701 }
702
703 int ul_path_scanf(struct path_cxt *pc, const char *path, const char *fmt, ...)
704 {
705 FILE *f;
706 va_list fmt_ap;
707 int rc;
708
709 f = ul_path_fopen(pc, "r" UL_CLOEXECSTR, path);
710 if (!f)
711 return -EINVAL;
712
713 DBG(CXT, ul_debug(" fscanf [%s] '%s'", fmt, path));
714
715 va_start(fmt_ap, fmt);
716 rc = vfscanf(f, fmt, fmt_ap);
717 va_end(fmt_ap);
718
719 fclose(f);
720 return rc;
721 }
722
723 int ul_path_scanff(struct path_cxt *pc, const char *path, va_list ap, const char *fmt, ...)
724 {
725 FILE *f;
726 va_list fmt_ap;
727 int rc;
728
729 f = ul_path_vfopenf(pc, "r" UL_CLOEXECSTR, path, ap);
730 if (!f)
731 return -EINVAL;
732
733 va_start(fmt_ap, fmt);
734 rc = vfscanf(f, fmt, fmt_ap);
735 va_end(fmt_ap);
736
737 fclose(f);
738 return rc;
739 }
740
741
742 int ul_path_read_s64(struct path_cxt *pc, int64_t *res, const char *path)
743 {
744 int64_t x = 0;
745 int rc;
746
747 rc = ul_path_scanf(pc, path, "%"SCNd64, &x);
748 if (rc != 1)
749 return -1;
750 if (res)
751 *res = x;
752 return 0;
753 }
754
755 int ul_path_readf_s64(struct path_cxt *pc, int64_t *res, const char *path, ...)
756 {
757 const char *p;
758 va_list ap;
759
760 va_start(ap, path);
761 p = ul_path_mkpath(pc, path, ap);
762 va_end(ap);
763
764 return !p ? -errno : ul_path_read_s64(pc, res, p);
765 }
766
767 int ul_path_read_u64(struct path_cxt *pc, uint64_t *res, const char *path)
768 {
769 uint64_t x = 0;
770 int rc;
771
772 rc = ul_path_scanf(pc, path, "%"SCNu64, &x);
773 if (rc != 1)
774 return -1;
775 if (res)
776 *res = x;
777 return 0;
778 }
779
780 int ul_path_readf_u64(struct path_cxt *pc, uint64_t *res, const char *path, ...)
781 {
782 const char *p;
783 va_list ap;
784
785 va_start(ap, path);
786 p = ul_path_mkpath(pc, path, ap);
787 va_end(ap);
788
789 return !p ? -errno : ul_path_read_u64(pc, res, p);
790 }
791
792 int ul_path_read_s32(struct path_cxt *pc, int *res, const char *path)
793 {
794 int rc, x = 0;
795
796 rc = ul_path_scanf(pc, path, "%d", &x);
797 if (rc != 1)
798 return -1;
799 if (res)
800 *res = x;
801 return 0;
802 }
803
804 int ul_path_readf_s32(struct path_cxt *pc, int *res, const char *path, ...)
805 {
806 const char *p;
807 va_list ap;
808
809 va_start(ap, path);
810 p = ul_path_mkpath(pc, path, ap);
811 va_end(ap);
812
813 return !p ? -errno : ul_path_read_s32(pc, res, p);
814 }
815
816 int ul_path_read_u32(struct path_cxt *pc, unsigned int *res, const char *path)
817 {
818 int rc;
819 unsigned int x = 0;
820
821 rc = ul_path_scanf(pc, path, "%u", &x);
822 if (rc != 1)
823 return -1;
824 if (res)
825 *res = x;
826 return 0;
827 }
828
829 int ul_path_readf_u32(struct path_cxt *pc, unsigned int *res, const char *path, ...)
830 {
831 const char *p;
832 va_list ap;
833
834 va_start(ap, path);
835 p = ul_path_mkpath(pc, path, ap);
836 va_end(ap);
837
838 return !p ? -errno : ul_path_read_u32(pc, res, p);
839 }
840
841 int ul_path_read_majmin(struct path_cxt *pc, dev_t *res, const char *path)
842 {
843 int rc, maj = 0, min = 0;
844
845 rc = ul_path_scanf(pc, path, "%d:%d", &maj, &min);
846 if (rc != 2)
847 return -1;
848 if (res)
849 *res = makedev(maj, min);
850 return 0;
851 }
852
853 int ul_path_readf_majmin(struct path_cxt *pc, dev_t *res, const char *path, ...)
854 {
855 const char *p;
856 va_list ap;
857
858 va_start(ap, path);
859 p = ul_path_mkpath(pc, path, ap);
860 va_end(ap);
861
862 return !p ? -errno : ul_path_read_majmin(pc, res, p);
863 }
864
865 int ul_path_write_string(struct path_cxt *pc, const char *str, const char *path)
866 {
867 int rc, errsv;
868 int fd;
869
870 fd = ul_path_open(pc, O_WRONLY|O_CLOEXEC, path);
871 if (fd < 0)
872 return -errno;
873
874 rc = write_all(fd, str, strlen(str));
875
876 errsv = errno;
877 close(fd);
878 errno = errsv;
879 return rc;
880 }
881
882 int ul_path_writef_string(struct path_cxt *pc, const char *str, const char *path, ...)
883 {
884 const char *p;
885 va_list ap;
886
887 va_start(ap, path);
888 p = ul_path_mkpath(pc, path, ap);
889 va_end(ap);
890
891 return !p ? -errno : ul_path_write_string(pc, str, p);
892 }
893
894 int ul_path_write_s64(struct path_cxt *pc, int64_t num, const char *path)
895 {
896 char buf[sizeof(stringify_value(LLONG_MAX))];
897 int rc, errsv;
898 int fd, len;
899
900 fd = ul_path_open(pc, O_WRONLY|O_CLOEXEC, path);
901 if (fd < 0)
902 return -errno;
903
904 len = snprintf(buf, sizeof(buf), "%" PRId64, num);
905 if (len < 0 || (size_t) len >= sizeof(buf))
906 rc = len < 0 ? -errno : -E2BIG;
907 else
908 rc = write_all(fd, buf, len);
909
910 errsv = errno;
911 close(fd);
912 errno = errsv;
913 return rc;
914 }
915
916 int ul_path_write_u64(struct path_cxt *pc, uint64_t num, const char *path)
917 {
918 char buf[sizeof(stringify_value(ULLONG_MAX))];
919 int rc, errsv;
920 int fd, len;
921
922 fd = ul_path_open(pc, O_WRONLY|O_CLOEXEC, path);
923 if (fd < 0)
924 return -errno;
925
926 len = snprintf(buf, sizeof(buf), "%" PRIu64, num);
927 if (len < 0 || (size_t) len >= sizeof(buf))
928 rc = len < 0 ? -errno : -E2BIG;
929 else
930 rc = write_all(fd, buf, len);
931
932 errsv = errno;
933 close(fd);
934 errno = errsv;
935 return rc;
936 }
937
938 int ul_path_writef_u64(struct path_cxt *pc, uint64_t num, const char *path, ...)
939 {
940 const char *p;
941 va_list ap;
942
943 va_start(ap, path);
944 p = ul_path_mkpath(pc, path, ap);
945 va_end(ap);
946
947 return !p ? -errno : ul_path_write_u64(pc, num, p);
948
949 }
950
951 int ul_path_count_dirents(struct path_cxt *pc, const char *path)
952 {
953 DIR *dir;
954 int r = 0;
955
956 dir = ul_path_opendir(pc, path);
957 if (!dir)
958 return 0;
959
960 while (xreaddir(dir)) r++;
961
962 closedir(dir);
963 return r;
964 }
965
966 int ul_path_countf_dirents(struct path_cxt *pc, const char *path, ...)
967 {
968 const char *p;
969 va_list ap;
970
971 va_start(ap, path);
972 p = ul_path_mkpath(pc, path, ap);
973 va_end(ap);
974
975 return !p ? -errno : ul_path_count_dirents(pc, p);
976 }
977
978 /* first call (when @sub is NULL) opens the directory, last call closes the diretory */
979 int ul_path_next_dirent(struct path_cxt *pc, DIR **sub, const char *dirname, struct dirent **d)
980 {
981 if (!pc || !sub || !d)
982 return -EINVAL;
983
984 if (!*sub) {
985 *sub = ul_path_opendir(pc, dirname);
986 if (!*sub)
987 return -errno;
988 }
989
990 *d = xreaddir(*sub);
991 if (*d)
992 return 0;
993
994 closedir(*sub);
995 *sub = NULL;
996 return 1;
997 }
998
999 /*
1000 * Like fopen() but, @path is always prefixed by @prefix. This function is
1001 * useful in case when ul_path_* API is overkill.
1002 */
1003 FILE *ul_prefix_fopen(const char *prefix, const char *path, const char *mode)
1004 {
1005 char buf[PATH_MAX];
1006
1007 if (!path)
1008 return NULL;
1009 if (!prefix)
1010 return fopen(path, mode);
1011 if (*path == '/')
1012 path++;
1013
1014 snprintf(buf, sizeof(buf), "%s/%s", prefix, path);
1015 return fopen(buf, mode);
1016 }
1017
1018 #ifdef HAVE_CPU_SET_T
1019 static int ul_path_cpuparse(struct path_cxt *pc, cpu_set_t **set, int maxcpus, int islist, const char *path, va_list ap)
1020 {
1021 FILE *f;
1022 size_t setsize, len = maxcpus * 7;
1023 char *buf;
1024 int rc;
1025
1026 *set = NULL;
1027
1028 buf = malloc(len);
1029 if (!buf)
1030 return -ENOMEM;
1031
1032 f = ul_path_vfopenf(pc, "r" UL_CLOEXECSTR, path, ap);
1033 if (!f) {
1034 rc = -errno;
1035 goto out;
1036 }
1037
1038 rc = fgets(buf, len, f) == NULL ? -EIO : 0;
1039 fclose(f);
1040
1041 if (rc)
1042 goto out;
1043
1044 len = strlen(buf);
1045 if (buf[len - 1] == '\n')
1046 buf[len - 1] = '\0';
1047
1048 *set = cpuset_alloc(maxcpus, &setsize, NULL);
1049 if (!*set) {
1050 rc = -EINVAL;
1051 goto out;
1052 }
1053
1054 if (islist) {
1055 if (cpulist_parse(buf, *set, setsize, 0)) {
1056 rc = -EINVAL;
1057 goto out;
1058 }
1059 } else {
1060 if (cpumask_parse(buf, *set, setsize)) {
1061 rc = -EINVAL;
1062 goto out;
1063 }
1064 }
1065 rc = 0;
1066
1067 out:
1068 if (rc)
1069 cpuset_free(*set);
1070 free(buf);
1071 return rc;
1072 }
1073
1074 int ul_path_readf_cpuset(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...)
1075 {
1076 va_list ap;
1077 int rc = 0;
1078
1079 va_start(ap, path);
1080 rc = ul_path_cpuparse(pc, set, maxcpus, 0, path, ap);
1081 va_end(ap);
1082
1083 return rc;
1084 }
1085
1086 int ul_path_readf_cpulist(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...)
1087 {
1088 va_list ap;
1089 int rc = 0;
1090
1091 va_start(ap, path);
1092 rc = ul_path_cpuparse(pc, set, maxcpus, 1, path, ap);
1093 va_end(ap);
1094
1095 return rc;
1096 }
1097
1098 #endif /* HAVE_CPU_SET_T */
1099
1100
1101 #ifdef TEST_PROGRAM_PATH
1102 #include <getopt.h>
1103
1104 static void __attribute__((__noreturn__)) usage(void)
1105 {
1106 fprintf(stdout, " %s [options] <dir> <command>\n\n", program_invocation_short_name);
1107 fputs(" -p, --prefix <dir> redirect hardcoded paths to <dir>\n", stdout);
1108
1109 fputs(" Commands:\n", stdout);
1110 fputs(" read-u64 <file> read uint64_t from file\n", stdout);
1111 fputs(" read-s64 <file> read int64_t from file\n", stdout);
1112 fputs(" read-u32 <file> read uint32_t from file\n", stdout);
1113 fputs(" read-s32 <file> read int32_t from file\n", stdout);
1114 fputs(" read-string <file> read string from file\n", stdout);
1115 fputs(" read-majmin <file> read devno from file\n", stdout);
1116 fputs(" read-link <file> read symlink\n", stdout);
1117 fputs(" write-string <file> <str> write string from file\n", stdout);
1118 fputs(" write-u64 <file> <str> write uint64_t from file\n", stdout);
1119
1120 exit(EXIT_SUCCESS);
1121 }
1122
1123 int main(int argc, char *argv[])
1124 {
1125 int c;
1126 const char *prefix = NULL, *dir, *file, *command;
1127 struct path_cxt *pc = NULL;
1128
1129 static const struct option longopts[] = {
1130 { "prefix", 1, NULL, 'p' },
1131 { "help", 0, NULL, 'h' },
1132 { NULL, 0, NULL, 0 },
1133 };
1134
1135 while((c = getopt_long(argc, argv, "p:h", longopts, NULL)) != -1) {
1136 switch(c) {
1137 case 'p':
1138 prefix = optarg;
1139 break;
1140 case 'h':
1141 usage();
1142 break;
1143 default:
1144 err(EXIT_FAILURE, "try --help");
1145 }
1146 }
1147
1148 if (optind == argc)
1149 errx(EXIT_FAILURE, "<dir> not defined");
1150 dir = argv[optind++];
1151
1152 ul_path_init_debug();
1153
1154 pc = ul_new_path("%s", dir);
1155 if (!pc)
1156 err(EXIT_FAILURE, "failed to initialize path context");
1157 if (prefix)
1158 ul_path_set_prefix(pc, prefix);
1159
1160 if (optind == argc)
1161 errx(EXIT_FAILURE, "<command> not defined");
1162 command = argv[optind++];
1163
1164 if (strcmp(command, "read-u32") == 0) {
1165 uint32_t res;
1166
1167 if (optind == argc)
1168 errx(EXIT_FAILURE, "<file> not defined");
1169 file = argv[optind++];
1170
1171 if (ul_path_read_u32(pc, &res, file) != 0)
1172 err(EXIT_FAILURE, "read u64 failed");
1173 printf("read: %s: %u\n", file, res);
1174
1175 if (ul_path_readf_u32(pc, &res, "%s", file) != 0)
1176 err(EXIT_FAILURE, "readf u64 failed");
1177 printf("readf: %s: %u\n", file, res);
1178
1179 } else if (strcmp(command, "read-s32") == 0) {
1180 int32_t res;
1181
1182 if (optind == argc)
1183 errx(EXIT_FAILURE, "<file> not defined");
1184 file = argv[optind++];
1185
1186 if (ul_path_read_s32(pc, &res, file) != 0)
1187 err(EXIT_FAILURE, "read u64 failed");
1188 printf("read: %s: %d\n", file, res);
1189
1190 if (ul_path_readf_s32(pc, &res, "%s", file) != 0)
1191 err(EXIT_FAILURE, "readf u64 failed");
1192 printf("readf: %s: %d\n", file, res);
1193
1194 } else if (strcmp(command, "read-u64") == 0) {
1195 uint64_t res;
1196
1197 if (optind == argc)
1198 errx(EXIT_FAILURE, "<file> not defined");
1199 file = argv[optind++];
1200
1201 if (ul_path_read_u64(pc, &res, file) != 0)
1202 err(EXIT_FAILURE, "read u64 failed");
1203 printf("read: %s: %" PRIu64 "\n", file, res);
1204
1205 if (ul_path_readf_u64(pc, &res, "%s", file) != 0)
1206 err(EXIT_FAILURE, "readf u64 failed");
1207 printf("readf: %s: %" PRIu64 "\n", file, res);
1208
1209 } else if (strcmp(command, "read-s64") == 0) {
1210 int64_t res;
1211
1212 if (optind == argc)
1213 errx(EXIT_FAILURE, "<file> not defined");
1214 file = argv[optind++];
1215
1216 if (ul_path_read_s64(pc, &res, file) != 0)
1217 err(EXIT_FAILURE, "read u64 failed");
1218 printf("read: %s: %" PRIu64 "\n", file, res);
1219
1220 if (ul_path_readf_s64(pc, &res, "%s", file) != 0)
1221 err(EXIT_FAILURE, "readf u64 failed");
1222 printf("readf: %s: %" PRIu64 "\n", file, res);
1223
1224 } else if (strcmp(command, "read-majmin") == 0) {
1225 dev_t res;
1226
1227 if (optind == argc)
1228 errx(EXIT_FAILURE, "<file> not defined");
1229 file = argv[optind++];
1230
1231 if (ul_path_read_majmin(pc, &res, file) != 0)
1232 err(EXIT_FAILURE, "read maj:min failed");
1233 printf("read: %s: %d\n", file, (int) res);
1234
1235 if (ul_path_readf_majmin(pc, &res, "%s", file) != 0)
1236 err(EXIT_FAILURE, "readf maj:min failed");
1237 printf("readf: %s: %d\n", file, (int) res);
1238
1239 } else if (strcmp(command, "read-string") == 0) {
1240 char *res;
1241
1242 if (optind == argc)
1243 errx(EXIT_FAILURE, "<file> not defined");
1244 file = argv[optind++];
1245
1246 if (ul_path_read_string(pc, &res, file) <= 0)
1247 err(EXIT_FAILURE, "read string failed");
1248 printf("read: %s: %s\n", file, res);
1249
1250 if (ul_path_readf_string(pc, &res, "%s", file) <= 0)
1251 err(EXIT_FAILURE, "readf string failed");
1252 printf("readf: %s: %s\n", file, res);
1253
1254 } else if (strcmp(command, "read-link") == 0) {
1255 char res[PATH_MAX];
1256
1257 if (optind == argc)
1258 errx(EXIT_FAILURE, "<file> not defined");
1259 file = argv[optind++];
1260
1261 if (ul_path_readlink(pc, res, sizeof(res), file) < 0)
1262 err(EXIT_FAILURE, "read symlink failed");
1263 printf("read: %s: %s\n", file, res);
1264
1265 if (ul_path_readlinkf(pc, res, sizeof(res), "%s", file) < 0)
1266 err(EXIT_FAILURE, "readf symlink failed");
1267 printf("readf: %s: %s\n", file, res);
1268
1269 } else if (strcmp(command, "write-string") == 0) {
1270 char *str;
1271
1272 if (optind + 1 == argc)
1273 errx(EXIT_FAILURE, "<file> <string> not defined");
1274 file = argv[optind++];
1275 str = argv[optind++];
1276
1277 if (ul_path_write_string(pc, str, file) != 0)
1278 err(EXIT_FAILURE, "write string failed");
1279 if (ul_path_writef_string(pc, str, "%s", file) != 0)
1280 err(EXIT_FAILURE, "writef string failed");
1281
1282 } else if (strcmp(command, "write-u64") == 0) {
1283 uint64_t num;
1284
1285 if (optind + 1 == argc)
1286 errx(EXIT_FAILURE, "<file> <num> not defined");
1287 file = argv[optind++];
1288 num = strtoumax(argv[optind++], NULL, 0);
1289
1290 if (ul_path_write_u64(pc, num, file) != 0)
1291 err(EXIT_FAILURE, "write u64 failed");
1292 if (ul_path_writef_u64(pc, num, "%s", file) != 0)
1293 err(EXIT_FAILURE, "writef u64 failed");
1294 }
1295
1296 ul_unref_path(pc);
1297 return EXIT_SUCCESS;
1298 }
1299 #endif /* TEST_PROGRAM_PATH */
1300