]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/path.c
a828aea493c05c207331e4489e2f8da788bcc678
[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 * Written by Karel Zak <kzak@redhat.com> [2018]
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_vstatf(struct path_cxt *pc, struct stat *sb, int flags, const char *path, va_list ap)
374 {
375 const char *p = ul_path_mkpath(pc, path, ap);
376
377 return !p ? -errno : ul_path_stat(pc, sb, flags, p);
378 }
379
380 int ul_path_statf(struct path_cxt *pc, struct stat *sb, int flags, const char *path, ...)
381 {
382 va_list ap;
383 int rc;
384
385 va_start(ap, path);
386 rc = ul_path_vstatf(pc, sb, flags, path, ap);
387 va_end(ap);
388
389 return rc;
390 }
391
392 int ul_path_open(struct path_cxt *pc, int flags, const char *path)
393 {
394 int fd;
395
396 if (!path)
397 return -EINVAL;
398 if (!pc) {
399 fd = open(path, flags);
400 DBG(CXT, ul_debug("opening '%s' [no context]", path));
401 } else {
402 int fdx;
403 int dir = ul_path_get_dirfd(pc);
404 if (dir < 0)
405 return dir;
406
407 if (*path == '/')
408 path++;
409
410 fdx = fd = openat(dir, path, flags);
411
412 if (fd < 0 && errno == ENOENT
413 && pc->redirect_on_enoent
414 && pc->redirect_on_enoent(pc, path, &dir) == 0)
415 fd = openat(dir, path, flags);
416
417 DBG(CXT, ul_debugobj(pc, "opening '%s'%s", path, fdx != fd ? " [redirected]" : ""));
418 }
419 return fd;
420 }
421
422 int ul_path_vopenf(struct path_cxt *pc, int flags, const char *path, va_list ap)
423 {
424 const char *p = ul_path_mkpath(pc, path, ap);
425
426 return !p ? -errno : ul_path_open(pc, flags, p);
427 }
428
429 int ul_path_openf(struct path_cxt *pc, int flags, const char *path, ...)
430 {
431 va_list ap;
432 int rc;
433
434 va_start(ap, path);
435 rc = ul_path_vopenf(pc, flags, path, ap);
436 va_end(ap);
437
438 return rc;
439 }
440
441 /*
442 * Maybe stupid, but good enough ;-)
443 */
444 static int mode2flags(const char *mode)
445 {
446 int flags = 0;
447 const char *p;
448
449 for (p = mode; p && *p; p++) {
450 if (*p == 'r' && *(p + 1) == '+')
451 flags |= O_RDWR;
452 else if (*p == 'r')
453 flags |= O_RDONLY;
454
455 else if (*p == 'w' && *(p + 1) == '+')
456 flags |= O_RDWR | O_TRUNC;
457 else if (*p == 'w')
458 flags |= O_WRONLY | O_TRUNC;
459
460 else if (*p == 'a' && *(p + 1) == '+')
461 flags |= O_RDWR | O_APPEND;
462 else if (*p == 'a')
463 flags |= O_WRONLY | O_APPEND;
464 #ifdef O_CLOEXEC
465 else if (*p == *UL_CLOEXECSTR)
466 flags |= O_CLOEXEC;
467 #endif
468 }
469
470 return flags;
471 }
472
473 FILE *ul_path_fopen(struct path_cxt *pc, const char *mode, const char *path)
474 {
475 int flags = mode2flags(mode);
476 int fd = ul_path_open(pc, flags, path);
477
478 if (fd < 0)
479 return NULL;
480
481 return fdopen(fd, mode);
482 }
483
484
485 FILE *ul_path_vfopenf(struct path_cxt *pc, const char *mode, const char *path, va_list ap)
486 {
487 const char *p = ul_path_mkpath(pc, path, ap);
488
489 return !p ? NULL : ul_path_fopen(pc, mode, p);
490 }
491
492 FILE *ul_path_fopenf(struct path_cxt *pc, const char *mode, const char *path, ...)
493 {
494 FILE *f;
495 va_list ap;
496
497 va_start(ap, path);
498 f = ul_path_vfopenf(pc, mode, path, ap);
499 va_end(ap);
500
501 return f;
502 }
503
504 /*
505 * Open directory @path in read-only mode. If the path is NULL then duplicate FD
506 * to the directory addressed by @pc.
507 */
508 DIR *ul_path_opendir(struct path_cxt *pc, const char *path)
509 {
510 DIR *dir;
511 int fd = -1;
512
513 if (path)
514 fd = ul_path_open(pc, O_RDONLY|O_CLOEXEC, path);
515 else if (pc->dir_path) {
516 int dirfd;
517
518 DBG(CXT, ul_debugobj(pc, "duplicate dir path"));
519 dirfd = ul_path_get_dirfd(pc);
520 if (dirfd >= 0)
521 fd = dup_fd_cloexec(dirfd, STDERR_FILENO + 1);
522 }
523
524 if (fd < 0)
525 return NULL;
526
527 dir = fdopendir(fd);
528 if (!dir) {
529 close(fd);
530 return NULL;
531 }
532 if (!path)
533 rewinddir(dir);
534 return dir;
535 }
536
537
538 /*
539 * Open directory @path in read-only mode. If the path is NULL then duplicate FD
540 * to the directory addressed by @pc.
541 */
542 DIR *ul_path_vopendirf(struct path_cxt *pc, const char *path, va_list ap)
543 {
544 const char *p = ul_path_mkpath(pc, path, ap);
545
546 return !p ? NULL : ul_path_opendir(pc, p);
547 }
548
549 /*
550 * Open directory @path in read-only mode. If the path is NULL then duplicate FD
551 * to the directory addressed by @pc.
552 */
553 DIR *ul_path_opendirf(struct path_cxt *pc, const char *path, ...)
554 {
555 va_list ap;
556 DIR *dir;
557
558 va_start(ap, path);
559 dir = ul_path_vopendirf(pc, path, ap);
560 va_end(ap);
561
562 return dir;
563 }
564
565 /*
566 * If @path is NULL then readlink is called on @pc directory.
567 */
568 ssize_t ul_path_readlink(struct path_cxt *pc, char *buf, size_t bufsiz, const char *path)
569 {
570 int dirfd;
571 ssize_t ssz;
572
573 if (!path) {
574 const char *p = get_absdir(pc);
575 if (!p)
576 return -errno;
577 ssz = readlink(p, buf, bufsiz - 1);
578 } else {
579 dirfd = ul_path_get_dirfd(pc);
580 if (dirfd < 0)
581 return dirfd;
582
583 if (*path == '/')
584 path++;
585
586 ssz = readlinkat(dirfd, path, buf, bufsiz - 1);
587 }
588
589 if (ssz >= 0)
590 buf[ssz] = '\0';
591 return ssz;
592 }
593
594 /*
595 * If @path is NULL then readlink is called on @pc directory.
596 */
597 ssize_t ul_path_readlinkf(struct path_cxt *pc, char *buf, size_t bufsiz, const char *path, ...)
598 {
599 const char *p;
600 va_list ap;
601
602 va_start(ap, path);
603 p = ul_path_mkpath(pc, path, ap);
604 va_end(ap);
605
606 return !p ? -errno : ul_path_readlink(pc, buf, bufsiz, p);
607 }
608
609 int ul_path_read(struct path_cxt *pc, char *buf, size_t len, const char *path)
610 {
611 int rc, errsv;
612 int fd;
613
614 fd = ul_path_open(pc, O_RDONLY|O_CLOEXEC, path);
615 if (fd < 0)
616 return -errno;
617
618 DBG(CXT, ul_debug(" reading '%s'", path));
619 rc = read_all(fd, buf, len);
620
621 errsv = errno;
622 close(fd);
623 errno = errsv;
624 return rc;
625 }
626
627 int ul_path_vreadf(struct path_cxt *pc, char *buf, size_t len, const char *path, va_list ap)
628 {
629 const char *p = ul_path_mkpath(pc, path, ap);
630
631 return !p ? -errno : ul_path_read(pc, buf, len, p);
632 }
633
634 int ul_path_readf(struct path_cxt *pc, char *buf, size_t len, const char *path, ...)
635 {
636 va_list ap;
637 int rc;
638
639 va_start(ap, path);
640 rc = ul_path_vreadf(pc, buf, len, path, ap);
641 va_end(ap);
642
643 return rc;
644 }
645
646
647 /*
648 * Returns newly allocated buffer with data from file. Maximal size is BUFSIZ
649 * (send patch if you need something bigger;-)
650 *
651 * Returns size of the string without \0, nothing is allocated if returns <= 0.
652 */
653 int ul_path_read_string(struct path_cxt *pc, char **str, const char *path)
654 {
655 char buf[BUFSIZ];
656 int rc;
657
658 if (!str)
659 return -EINVAL;
660
661 *str = NULL;
662
663 rc = ul_path_read_buffer(pc, buf, sizeof(buf), path);
664 if (rc < 0)
665 return rc;
666
667 *str = strdup(buf);
668 if (!*str)
669 rc = -ENOMEM;
670
671 return rc;
672 }
673
674 int ul_path_readf_string(struct path_cxt *pc, char **str, const char *path, ...)
675 {
676 const char *p;
677 va_list ap;
678
679 va_start(ap, path);
680 p = ul_path_mkpath(pc, path, ap);
681 va_end(ap);
682
683 return !p ? -errno : ul_path_read_string(pc, str, p);
684 }
685
686 int ul_path_read_buffer(struct path_cxt *pc, char *buf, size_t bufsz, const char *path)
687 {
688 int rc = ul_path_read(pc, buf, bufsz - 1, path);
689
690 if (rc == 0)
691 buf[0] = '\0';
692
693 else if (rc > 0) {
694 /* Remove trailing newline (usual in sysfs) */
695 if (*(buf + rc - 1) == '\n')
696 buf[--rc] = '\0';
697 else
698 buf[rc] = '\0';
699 }
700
701 return rc;
702 }
703
704 int ul_path_vreadf_buffer(struct path_cxt *pc, char *buf, size_t bufsz, const char *path, va_list ap)
705 {
706 const char *p;
707
708 p = ul_path_mkpath(pc, path, ap);
709
710 return !p ? -errno : ul_path_read_buffer(pc, buf, bufsz, p);
711 }
712
713 int ul_path_readf_buffer(struct path_cxt *pc, char *buf, size_t bufsz, const char *path, ...)
714 {
715 va_list ap;
716 int rc;
717
718 va_start(ap, path);
719 rc = ul_path_vreadf_buffer(pc, buf, bufsz, path, ap);
720 va_end(ap);
721
722 return rc;
723 }
724
725 int ul_path_scanf(struct path_cxt *pc, const char *path, const char *fmt, ...)
726 {
727 FILE *f;
728 va_list fmt_ap;
729 int rc;
730
731 f = ul_path_fopen(pc, "r" UL_CLOEXECSTR, path);
732 if (!f)
733 return -EINVAL;
734
735 DBG(CXT, ul_debug(" fscanf [%s] '%s'", fmt, path));
736
737 va_start(fmt_ap, fmt);
738 rc = vfscanf(f, fmt, fmt_ap);
739 va_end(fmt_ap);
740
741 fclose(f);
742 return rc;
743 }
744
745 int ul_path_scanff(struct path_cxt *pc, const char *path, va_list ap, const char *fmt, ...)
746 {
747 FILE *f;
748 va_list fmt_ap;
749 int rc;
750
751 f = ul_path_vfopenf(pc, "r" UL_CLOEXECSTR, path, ap);
752 if (!f)
753 return -EINVAL;
754
755 va_start(fmt_ap, fmt);
756 rc = vfscanf(f, fmt, fmt_ap);
757 va_end(fmt_ap);
758
759 fclose(f);
760 return rc;
761 }
762
763
764 int ul_path_read_s64(struct path_cxt *pc, int64_t *res, const char *path)
765 {
766 int64_t x = 0;
767 int rc;
768
769 rc = ul_path_scanf(pc, path, "%"SCNd64, &x);
770 if (rc != 1)
771 return -1;
772 if (res)
773 *res = x;
774 return 0;
775 }
776
777 int ul_path_readf_s64(struct path_cxt *pc, int64_t *res, const char *path, ...)
778 {
779 const char *p;
780 va_list ap;
781
782 va_start(ap, path);
783 p = ul_path_mkpath(pc, path, ap);
784 va_end(ap);
785
786 return !p ? -errno : ul_path_read_s64(pc, res, p);
787 }
788
789 int ul_path_read_u64(struct path_cxt *pc, uint64_t *res, const char *path)
790 {
791 uint64_t x = 0;
792 int rc;
793
794 rc = ul_path_scanf(pc, path, "%"SCNu64, &x);
795 if (rc != 1)
796 return -1;
797 if (res)
798 *res = x;
799 return 0;
800 }
801
802 int ul_path_readf_u64(struct path_cxt *pc, uint64_t *res, const char *path, ...)
803 {
804 const char *p;
805 va_list ap;
806
807 va_start(ap, path);
808 p = ul_path_mkpath(pc, path, ap);
809 va_end(ap);
810
811 return !p ? -errno : ul_path_read_u64(pc, res, p);
812 }
813
814 int ul_path_read_s32(struct path_cxt *pc, int *res, const char *path)
815 {
816 int rc, x = 0;
817
818 rc = ul_path_scanf(pc, path, "%d", &x);
819 if (rc != 1)
820 return -1;
821 if (res)
822 *res = x;
823 return 0;
824 }
825
826 int ul_path_readf_s32(struct path_cxt *pc, int *res, const char *path, ...)
827 {
828 const char *p;
829 va_list ap;
830
831 va_start(ap, path);
832 p = ul_path_mkpath(pc, path, ap);
833 va_end(ap);
834
835 return !p ? -errno : ul_path_read_s32(pc, res, p);
836 }
837
838 int ul_path_read_u32(struct path_cxt *pc, unsigned int *res, const char *path)
839 {
840 int rc;
841 unsigned int x = 0;
842
843 rc = ul_path_scanf(pc, path, "%u", &x);
844 if (rc != 1)
845 return -1;
846 if (res)
847 *res = x;
848 return 0;
849 }
850
851 int ul_path_readf_u32(struct path_cxt *pc, unsigned int *res, const char *path, ...)
852 {
853 const char *p;
854 va_list ap;
855
856 va_start(ap, path);
857 p = ul_path_mkpath(pc, path, ap);
858 va_end(ap);
859
860 return !p ? -errno : ul_path_read_u32(pc, res, p);
861 }
862
863 int ul_path_read_majmin(struct path_cxt *pc, dev_t *res, const char *path)
864 {
865 int rc, maj = 0, min = 0;
866
867 rc = ul_path_scanf(pc, path, "%d:%d", &maj, &min);
868 if (rc != 2)
869 return -1;
870 if (res)
871 *res = makedev(maj, min);
872 return 0;
873 }
874
875 int ul_path_readf_majmin(struct path_cxt *pc, dev_t *res, const char *path, ...)
876 {
877 const char *p;
878 va_list ap;
879
880 va_start(ap, path);
881 p = ul_path_mkpath(pc, path, ap);
882 va_end(ap);
883
884 return !p ? -errno : ul_path_read_majmin(pc, res, p);
885 }
886
887 int ul_path_write_string(struct path_cxt *pc, const char *str, const char *path)
888 {
889 int rc, errsv;
890 int fd;
891
892 fd = ul_path_open(pc, O_WRONLY|O_CLOEXEC, path);
893 if (fd < 0)
894 return -errno;
895
896 rc = write_all(fd, str, strlen(str));
897
898 errsv = errno;
899 close(fd);
900 errno = errsv;
901 return rc;
902 }
903
904 int ul_path_writef_string(struct path_cxt *pc, const char *str, const char *path, ...)
905 {
906 const char *p;
907 va_list ap;
908
909 va_start(ap, path);
910 p = ul_path_mkpath(pc, path, ap);
911 va_end(ap);
912
913 return !p ? -errno : ul_path_write_string(pc, str, p);
914 }
915
916 int ul_path_write_s64(struct path_cxt *pc, int64_t num, const char *path)
917 {
918 char buf[sizeof(stringify_value(LLONG_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), "%" PRId64, 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_write_u64(struct path_cxt *pc, uint64_t num, const char *path)
939 {
940 char buf[sizeof(stringify_value(ULLONG_MAX))];
941 int rc, errsv;
942 int fd, len;
943
944 fd = ul_path_open(pc, O_WRONLY|O_CLOEXEC, path);
945 if (fd < 0)
946 return -errno;
947
948 len = snprintf(buf, sizeof(buf), "%" PRIu64, num);
949 if (len < 0 || (size_t) len >= sizeof(buf))
950 rc = len < 0 ? -errno : -E2BIG;
951 else
952 rc = write_all(fd, buf, len);
953
954 errsv = errno;
955 close(fd);
956 errno = errsv;
957 return rc;
958 }
959
960 int ul_path_writef_u64(struct path_cxt *pc, uint64_t num, const char *path, ...)
961 {
962 const char *p;
963 va_list ap;
964
965 va_start(ap, path);
966 p = ul_path_mkpath(pc, path, ap);
967 va_end(ap);
968
969 return !p ? -errno : ul_path_write_u64(pc, num, p);
970
971 }
972
973 int ul_path_count_dirents(struct path_cxt *pc, const char *path)
974 {
975 DIR *dir;
976 int r = 0;
977
978 dir = ul_path_opendir(pc, path);
979 if (!dir)
980 return 0;
981
982 while (xreaddir(dir)) r++;
983
984 closedir(dir);
985 return r;
986 }
987
988 int ul_path_countf_dirents(struct path_cxt *pc, const char *path, ...)
989 {
990 const char *p;
991 va_list ap;
992
993 va_start(ap, path);
994 p = ul_path_mkpath(pc, path, ap);
995 va_end(ap);
996
997 return !p ? -errno : ul_path_count_dirents(pc, p);
998 }
999
1000 /* first call (when @sub is NULL) opens the directory, last call closes the directory */
1001 int ul_path_next_dirent(struct path_cxt *pc, DIR **sub, const char *dirname, struct dirent **d)
1002 {
1003 if (!pc || !sub || !d)
1004 return -EINVAL;
1005
1006 if (!*sub) {
1007 *sub = ul_path_opendir(pc, dirname);
1008 if (!*sub)
1009 return -errno;
1010 }
1011
1012 *d = xreaddir(*sub);
1013 if (*d)
1014 return 0;
1015
1016 closedir(*sub);
1017 *sub = NULL;
1018 return 1;
1019 }
1020
1021 #ifdef HAVE_CPU_SET_T
1022 static int ul_path_cpuparse(struct path_cxt *pc, cpu_set_t **set, int maxcpus, int islist, const char *path, va_list ap)
1023 {
1024 size_t setsize, len = maxcpus * 7;
1025 char *buf;
1026 int rc;
1027
1028 *set = NULL;
1029
1030 buf = malloc(len);
1031 if (!buf)
1032 return -ENOMEM;
1033
1034 rc = ul_path_vreadf_buffer(pc, buf, len, path, ap);
1035 if (rc < 0)
1036 goto out;
1037
1038 *set = cpuset_alloc(maxcpus, &setsize, NULL);
1039 if (!*set) {
1040 rc = -EINVAL;
1041 goto out;
1042 }
1043
1044 if (islist) {
1045 if (cpulist_parse(buf, *set, setsize, 0)) {
1046 errno = EINVAL;
1047 rc = -errno;
1048 goto out;
1049 }
1050 } else {
1051 if (cpumask_parse(buf, *set, setsize)) {
1052 errno = EINVAL;
1053 rc = -errno;
1054 goto out;
1055 }
1056 }
1057 rc = 0;
1058
1059 out:
1060 if (rc) {
1061 cpuset_free(*set);
1062 *set = NULL;
1063 }
1064 free(buf);
1065 return rc;
1066 }
1067
1068 int ul_path_readf_cpuset(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...)
1069 {
1070 va_list ap;
1071 int rc = 0;
1072
1073 va_start(ap, path);
1074 rc = ul_path_cpuparse(pc, set, maxcpus, 0, path, ap);
1075 va_end(ap);
1076
1077 return rc;
1078 }
1079
1080 int ul_path_readf_cpulist(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...)
1081 {
1082 va_list ap;
1083 int rc = 0;
1084
1085 va_start(ap, path);
1086 rc = ul_path_cpuparse(pc, set, maxcpus, 1, path, ap);
1087 va_end(ap);
1088
1089 return rc;
1090 }
1091
1092 #endif /* HAVE_CPU_SET_T */
1093
1094
1095 #ifdef TEST_PROGRAM_PATH
1096 #include <getopt.h>
1097
1098 static void __attribute__((__noreturn__)) usage(void)
1099 {
1100 fprintf(stdout, " %s [options] <dir> <command>\n\n", program_invocation_short_name);
1101 fputs(" -p, --prefix <dir> redirect hardcoded paths to <dir>\n", stdout);
1102
1103 fputs(" Commands:\n", stdout);
1104 fputs(" read-u64 <file> read uint64_t from file\n", stdout);
1105 fputs(" read-s64 <file> read int64_t from file\n", stdout);
1106 fputs(" read-u32 <file> read uint32_t from file\n", stdout);
1107 fputs(" read-s32 <file> read int32_t from file\n", stdout);
1108 fputs(" read-string <file> read string from file\n", stdout);
1109 fputs(" read-majmin <file> read devno from file\n", stdout);
1110 fputs(" read-link <file> read symlink\n", stdout);
1111 fputs(" write-string <file> <str> write string from file\n", stdout);
1112 fputs(" write-u64 <file> <str> write uint64_t from file\n", stdout);
1113
1114 exit(EXIT_SUCCESS);
1115 }
1116
1117 int main(int argc, char *argv[])
1118 {
1119 int c;
1120 const char *prefix = NULL, *dir, *file, *command;
1121 struct path_cxt *pc = NULL;
1122
1123 static const struct option longopts[] = {
1124 { "prefix", 1, NULL, 'p' },
1125 { "help", 0, NULL, 'h' },
1126 { NULL, 0, NULL, 0 },
1127 };
1128
1129 while((c = getopt_long(argc, argv, "p:h", longopts, NULL)) != -1) {
1130 switch(c) {
1131 case 'p':
1132 prefix = optarg;
1133 break;
1134 case 'h':
1135 usage();
1136 break;
1137 default:
1138 err(EXIT_FAILURE, "try --help");
1139 }
1140 }
1141
1142 if (optind == argc)
1143 errx(EXIT_FAILURE, "<dir> not defined");
1144 dir = argv[optind++];
1145
1146 ul_path_init_debug();
1147
1148 pc = ul_new_path("%s", dir);
1149 if (!pc)
1150 err(EXIT_FAILURE, "failed to initialize path context");
1151 if (prefix)
1152 ul_path_set_prefix(pc, prefix);
1153
1154 if (optind == argc)
1155 errx(EXIT_FAILURE, "<command> not defined");
1156 command = argv[optind++];
1157
1158 if (strcmp(command, "read-u32") == 0) {
1159 uint32_t res;
1160
1161 if (optind == argc)
1162 errx(EXIT_FAILURE, "<file> not defined");
1163 file = argv[optind++];
1164
1165 if (ul_path_read_u32(pc, &res, file) != 0)
1166 err(EXIT_FAILURE, "read u64 failed");
1167 printf("read: %s: %u\n", file, res);
1168
1169 if (ul_path_readf_u32(pc, &res, "%s", file) != 0)
1170 err(EXIT_FAILURE, "readf u64 failed");
1171 printf("readf: %s: %u\n", file, res);
1172
1173 } else if (strcmp(command, "read-s32") == 0) {
1174 int32_t res;
1175
1176 if (optind == argc)
1177 errx(EXIT_FAILURE, "<file> not defined");
1178 file = argv[optind++];
1179
1180 if (ul_path_read_s32(pc, &res, file) != 0)
1181 err(EXIT_FAILURE, "read u64 failed");
1182 printf("read: %s: %d\n", file, res);
1183
1184 if (ul_path_readf_s32(pc, &res, "%s", file) != 0)
1185 err(EXIT_FAILURE, "readf u64 failed");
1186 printf("readf: %s: %d\n", file, res);
1187
1188 } else if (strcmp(command, "read-u64") == 0) {
1189 uint64_t res;
1190
1191 if (optind == argc)
1192 errx(EXIT_FAILURE, "<file> not defined");
1193 file = argv[optind++];
1194
1195 if (ul_path_read_u64(pc, &res, file) != 0)
1196 err(EXIT_FAILURE, "read u64 failed");
1197 printf("read: %s: %" PRIu64 "\n", file, res);
1198
1199 if (ul_path_readf_u64(pc, &res, "%s", file) != 0)
1200 err(EXIT_FAILURE, "readf u64 failed");
1201 printf("readf: %s: %" PRIu64 "\n", file, res);
1202
1203 } else if (strcmp(command, "read-s64") == 0) {
1204 int64_t res;
1205
1206 if (optind == argc)
1207 errx(EXIT_FAILURE, "<file> not defined");
1208 file = argv[optind++];
1209
1210 if (ul_path_read_s64(pc, &res, file) != 0)
1211 err(EXIT_FAILURE, "read u64 failed");
1212 printf("read: %s: %" PRIu64 "\n", file, res);
1213
1214 if (ul_path_readf_s64(pc, &res, "%s", file) != 0)
1215 err(EXIT_FAILURE, "readf u64 failed");
1216 printf("readf: %s: %" PRIu64 "\n", file, res);
1217
1218 } else if (strcmp(command, "read-majmin") == 0) {
1219 dev_t res;
1220
1221 if (optind == argc)
1222 errx(EXIT_FAILURE, "<file> not defined");
1223 file = argv[optind++];
1224
1225 if (ul_path_read_majmin(pc, &res, file) != 0)
1226 err(EXIT_FAILURE, "read maj:min failed");
1227 printf("read: %s: %d\n", file, (int) res);
1228
1229 if (ul_path_readf_majmin(pc, &res, "%s", file) != 0)
1230 err(EXIT_FAILURE, "readf maj:min failed");
1231 printf("readf: %s: %d\n", file, (int) res);
1232
1233 } else if (strcmp(command, "read-string") == 0) {
1234 char *res;
1235
1236 if (optind == argc)
1237 errx(EXIT_FAILURE, "<file> not defined");
1238 file = argv[optind++];
1239
1240 if (ul_path_read_string(pc, &res, file) <= 0)
1241 err(EXIT_FAILURE, "read string failed");
1242 printf("read: %s: %s\n", file, res);
1243
1244 if (ul_path_readf_string(pc, &res, "%s", file) <= 0)
1245 err(EXIT_FAILURE, "readf string failed");
1246 printf("readf: %s: %s\n", file, res);
1247
1248 } else if (strcmp(command, "read-link") == 0) {
1249 char res[PATH_MAX];
1250
1251 if (optind == argc)
1252 errx(EXIT_FAILURE, "<file> not defined");
1253 file = argv[optind++];
1254
1255 if (ul_path_readlink(pc, res, sizeof(res), file) < 0)
1256 err(EXIT_FAILURE, "read symlink failed");
1257 printf("read: %s: %s\n", file, res);
1258
1259 if (ul_path_readlinkf(pc, res, sizeof(res), "%s", file) < 0)
1260 err(EXIT_FAILURE, "readf symlink failed");
1261 printf("readf: %s: %s\n", file, res);
1262
1263 } else if (strcmp(command, "write-string") == 0) {
1264 char *str;
1265
1266 if (optind + 1 == argc)
1267 errx(EXIT_FAILURE, "<file> <string> not defined");
1268 file = argv[optind++];
1269 str = argv[optind++];
1270
1271 if (ul_path_write_string(pc, str, file) != 0)
1272 err(EXIT_FAILURE, "write string failed");
1273 if (ul_path_writef_string(pc, str, "%s", file) != 0)
1274 err(EXIT_FAILURE, "writef string failed");
1275
1276 } else if (strcmp(command, "write-u64") == 0) {
1277 uint64_t num;
1278
1279 if (optind + 1 == argc)
1280 errx(EXIT_FAILURE, "<file> <num> not defined");
1281 file = argv[optind++];
1282 num = strtoumax(argv[optind++], NULL, 0);
1283
1284 if (ul_path_write_u64(pc, num, file) != 0)
1285 err(EXIT_FAILURE, "write u64 failed");
1286 if (ul_path_writef_u64(pc, num, "%s", file) != 0)
1287 err(EXIT_FAILURE, "writef u64 failed");
1288 }
1289
1290 ul_unref_path(pc);
1291 return EXIT_SUCCESS;
1292 }
1293 #endif /* TEST_PROGRAM_PATH */
1294