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