]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/path.c
8dce1da408907dc9be249780244489e6753b2a59
[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 buf[--rc] = '\0';
614 else
615 buf[rc - 1] = '\0';
616
617 return rc;
618 }
619
620 int ul_path_readf_buffer(struct path_cxt *pc, char *buf, size_t bufsz, const char *path, ...)
621 {
622 const char *p;
623 va_list ap;
624
625 va_start(ap, path);
626 p = ul_path_mkpath(pc, path, ap);
627 va_end(ap);
628
629 return !p ? -errno : ul_path_read_buffer(pc, buf, bufsz, p);
630 }
631
632 int ul_path_scanf(struct path_cxt *pc, const char *path, const char *fmt, ...)
633 {
634 FILE *f;
635 va_list fmt_ap;
636 int rc;
637
638 f = ul_path_fopen(pc, "r" UL_CLOEXECSTR, path);
639 if (!f)
640 return -EINVAL;
641
642 DBG(CXT, ul_debug(" fscanf [%s] '%s'", fmt, path));
643
644 va_start(fmt_ap, fmt);
645 rc = vfscanf(f, fmt, fmt_ap);
646 va_end(fmt_ap);
647
648 fclose(f);
649 return rc;
650 }
651
652 int ul_path_scanff(struct path_cxt *pc, const char *path, va_list ap, const char *fmt, ...)
653 {
654 FILE *f;
655 va_list fmt_ap;
656 int rc;
657
658 f = ul_path_vfopenf(pc, "r" UL_CLOEXECSTR, path, ap);
659 if (!f)
660 return -EINVAL;
661
662 va_start(fmt_ap, fmt);
663 rc = vfscanf(f, fmt, fmt_ap);
664 va_end(fmt_ap);
665
666 fclose(f);
667 return rc;
668 }
669
670
671 int ul_path_read_s64(struct path_cxt *pc, int64_t *res, const char *path)
672 {
673 int64_t x = 0;
674 int rc;
675
676 rc = ul_path_scanf(pc, path, "%"SCNd64, &x);
677 if (rc != 1)
678 return -1;
679 if (res)
680 *res = x;
681 return 0;
682 }
683
684 int ul_path_readf_s64(struct path_cxt *pc, int64_t *res, const char *path, ...)
685 {
686 const char *p;
687 va_list ap;
688
689 va_start(ap, path);
690 p = ul_path_mkpath(pc, path, ap);
691 va_end(ap);
692
693 return !p ? -errno : ul_path_read_s64(pc, res, p);
694 }
695
696 int ul_path_read_u64(struct path_cxt *pc, uint64_t *res, const char *path)
697 {
698 uint64_t x = 0;
699 int rc;
700
701 rc = ul_path_scanf(pc, path, "%"SCNu64, &x);
702 if (rc != 1)
703 return -1;
704 if (res)
705 *res = x;
706 return 0;
707 }
708
709 int ul_path_readf_u64(struct path_cxt *pc, uint64_t *res, const char *path, ...)
710 {
711 const char *p;
712 va_list ap;
713
714 va_start(ap, path);
715 p = ul_path_mkpath(pc, path, ap);
716 va_end(ap);
717
718 return !p ? -errno : ul_path_read_u64(pc, res, p);
719 }
720
721 int ul_path_read_s32(struct path_cxt *pc, int *res, const char *path)
722 {
723 int rc, x = 0;
724
725 rc = ul_path_scanf(pc, path, "%d", &x);
726 if (rc != 1)
727 return -1;
728 if (res)
729 *res = x;
730 return 0;
731 }
732
733 int ul_path_readf_s32(struct path_cxt *pc, int *res, const char *path, ...)
734 {
735 const char *p;
736 va_list ap;
737
738 va_start(ap, path);
739 p = ul_path_mkpath(pc, path, ap);
740 va_end(ap);
741
742 return !p ? -errno : ul_path_read_s32(pc, res, p);
743 }
744
745 int ul_path_read_u32(struct path_cxt *pc, unsigned int *res, const char *path)
746 {
747 int rc;
748 unsigned int x;
749
750 rc = ul_path_scanf(pc, path, "%u", &x);
751 if (rc != 1)
752 return -1;
753 if (res)
754 *res = x;
755 return 0;
756 }
757
758 int ul_path_readf_u32(struct path_cxt *pc, unsigned int *res, const char *path, ...)
759 {
760 const char *p;
761 va_list ap;
762
763 va_start(ap, path);
764 p = ul_path_mkpath(pc, path, ap);
765 va_end(ap);
766
767 return !p ? -errno : ul_path_read_u32(pc, res, p);
768 }
769
770 int ul_path_read_majmin(struct path_cxt *pc, dev_t *res, const char *path)
771 {
772 int rc, maj, min;
773
774 rc = ul_path_scanf(pc, path, "%d:%d", &maj, &min);
775 if (rc != 2)
776 return -1;
777 if (res)
778 *res = makedev(maj, min);
779 return 0;
780 }
781
782 int ul_path_readf_majmin(struct path_cxt *pc, dev_t *res, const char *path, ...)
783 {
784 const char *p;
785 va_list ap;
786
787 va_start(ap, path);
788 p = ul_path_mkpath(pc, path, ap);
789 va_end(ap);
790
791 return !p ? -errno : ul_path_read_majmin(pc, res, p);
792 }
793
794 int ul_path_write_string(struct path_cxt *pc, const char *str, const char *path)
795 {
796 int rc, errsv;
797 int fd;
798
799 fd = ul_path_open(pc, O_WRONLY|O_CLOEXEC, path);
800 if (fd < 0)
801 return -errno;
802
803 rc = write_all(fd, str, strlen(str));
804
805 errsv = errno;
806 close(fd);
807 errno = errsv;
808 return rc;
809 }
810
811 int ul_path_writef_string(struct path_cxt *pc, const char *str, const char *path, ...)
812 {
813 const char *p;
814 va_list ap;
815
816 va_start(ap, path);
817 p = ul_path_mkpath(pc, path, ap);
818 va_end(ap);
819
820 return !p ? -errno : ul_path_write_string(pc, str, p);
821 }
822
823 int ul_path_write_s64(struct path_cxt *pc, int64_t num, const char *path)
824 {
825 char buf[sizeof(stringify_value(LLONG_MAX))];
826 int rc, errsv;
827 int fd, len;
828
829 fd = ul_path_open(pc, O_WRONLY|O_CLOEXEC, path);
830 if (fd < 0)
831 return -errno;
832
833 len = snprintf(buf, sizeof(buf), "%" PRId64, num);
834 if (len < 0 || (size_t) len >= sizeof(buf))
835 rc = len < 0 ? -errno : -E2BIG;
836 else
837 rc = write_all(fd, buf, len);
838
839 errsv = errno;
840 close(fd);
841 errno = errsv;
842 return rc;
843 }
844
845 int ul_path_write_u64(struct path_cxt *pc, uint64_t num, const char *path)
846 {
847 char buf[sizeof(stringify_value(ULLONG_MAX))];
848 int rc, errsv;
849 int fd, len;
850
851 fd = ul_path_open(pc, O_WRONLY|O_CLOEXEC, path);
852 if (fd < 0)
853 return -errno;
854
855 len = snprintf(buf, sizeof(buf), "%" PRIu64, num);
856 if (len < 0 || (size_t) len >= sizeof(buf))
857 rc = len < 0 ? -errno : -E2BIG;
858 else
859 rc = write_all(fd, buf, len);
860
861 errsv = errno;
862 close(fd);
863 errno = errsv;
864 return rc;
865 }
866
867 int ul_path_writef_u64(struct path_cxt *pc, uint64_t num, const char *path, ...)
868 {
869 const char *p;
870 va_list ap;
871
872 va_start(ap, path);
873 p = ul_path_mkpath(pc, path, ap);
874 va_end(ap);
875
876 return !p ? -errno : ul_path_write_u64(pc, num, p);
877
878 }
879
880 int ul_path_count_dirents(struct path_cxt *pc, const char *path)
881 {
882 DIR *dir;
883 int r = 0;
884
885 dir = ul_path_opendir(pc, path);
886 if (!dir)
887 return 0;
888
889 while (xreaddir(dir)) r++;
890
891 closedir(dir);
892 return r;
893 }
894
895 int ul_path_countf_dirents(struct path_cxt *pc, const char *path, ...)
896 {
897 const char *p;
898 va_list ap;
899
900 va_start(ap, path);
901 p = ul_path_mkpath(pc, path, ap);
902 va_end(ap);
903
904 return !p ? -errno : ul_path_count_dirents(pc, p);
905 }
906
907 /*
908 * Like fopen() but, @path is always prefixed by @prefix. This function is
909 * useful in case when ul_path_* API is overkill.
910 */
911 FILE *ul_prefix_fopen(const char *prefix, const char *path, const char *mode)
912 {
913 char buf[PATH_MAX];
914
915 if (!path)
916 return NULL;
917 if (!prefix)
918 return fopen(path, mode);
919 if (*path == '/')
920 path++;
921
922 snprintf(buf, sizeof(buf), "%s/%s", prefix, path);
923 return fopen(buf, mode);
924 }
925
926 #ifdef HAVE_CPU_SET_T
927 static int ul_path_cpuparse(struct path_cxt *pc, cpu_set_t **set, int maxcpus, int islist, const char *path, va_list ap)
928 {
929 FILE *f;
930 size_t setsize, len = maxcpus * 7;
931 char buf[len];
932 int rc;
933
934 *set = NULL;
935
936 f = ul_path_vfopenf(pc, "r" UL_CLOEXECSTR, path, ap);
937 if (!f)
938 return -errno;
939
940 rc = fgets(buf, len, f) == NULL ? -errno : 0;
941 fclose(f);
942
943 if (rc)
944 return rc;
945
946 len = strlen(buf);
947 if (buf[len - 1] == '\n')
948 buf[len - 1] = '\0';
949
950 *set = cpuset_alloc(maxcpus, &setsize, NULL);
951 if (!*set)
952 return -ENOMEM;
953
954 if (islist) {
955 if (cpulist_parse(buf, *set, setsize, 0)) {
956 cpuset_free(*set);
957 return -EINVAL;
958 }
959 } else {
960 if (cpumask_parse(buf, *set, setsize)) {
961 cpuset_free(*set);
962 return -EINVAL;
963 }
964 }
965 return 0;
966 }
967
968 int ul_path_readf_cpuset(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...)
969 {
970 va_list ap;
971 int rc = 0;
972
973 va_start(ap, path);
974 rc = ul_path_cpuparse(pc, set, maxcpus, 0, path, ap);
975 va_end(ap);
976
977 return rc;
978 }
979
980 int ul_path_readf_cpulist(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...)
981 {
982 va_list ap;
983 int rc = 0;
984
985 va_start(ap, path);
986 rc = ul_path_cpuparse(pc, set, maxcpus, 1, path, ap);
987 va_end(ap);
988
989 return rc;
990 }
991
992 #endif /* HAVE_CPU_SET_T */
993
994
995 #ifdef TEST_PROGRAM_PATH
996 #include <getopt.h>
997
998 static void __attribute__((__noreturn__)) usage(void)
999 {
1000 fprintf(stdout, " %s [options] <dir> <command>\n\n", program_invocation_short_name);
1001 fputs(" -p, --prefix <dir> redirect hardcoded paths to <dir>\n", stdout);
1002
1003 fputs(" Commands:\n", stdout);
1004 fputs(" read-u64 <file> read uint64_t from file\n", stdout);
1005 fputs(" read-s64 <file> read int64_t from file\n", stdout);
1006 fputs(" read-u32 <file> read uint32_t from file\n", stdout);
1007 fputs(" read-s32 <file> read int32_t from file\n", stdout);
1008 fputs(" read-string <file> read string from file\n", stdout);
1009 fputs(" read-majmin <file> read devno from file\n", stdout);
1010 fputs(" read-link <file> read symlink\n", stdout);
1011 fputs(" write-string <file> <str> write string from file\n", stdout);
1012 fputs(" write-u64 <file> <str> write uint64_t from file\n", stdout);
1013
1014 exit(EXIT_SUCCESS);
1015 }
1016
1017 int main(int argc, char *argv[])
1018 {
1019 int c;
1020 const char *prefix = NULL, *dir, *file, *command;
1021 struct path_cxt *pc = NULL;
1022
1023 static const struct option longopts[] = {
1024 { "prefix", 1, NULL, 'p' },
1025 { "help", 0, NULL, 'h' },
1026 { NULL, 0, NULL, 0 },
1027 };
1028
1029 while((c = getopt_long(argc, argv, "p:h", longopts, NULL)) != -1) {
1030 switch(c) {
1031 case 'p':
1032 prefix = optarg;
1033 break;
1034 case 'h':
1035 usage();
1036 break;
1037 default:
1038 err(EXIT_FAILURE, "try --help");
1039 }
1040 }
1041
1042 if (optind == argc)
1043 errx(EXIT_FAILURE, "<dir> not defined");
1044 dir = argv[optind++];
1045
1046 ul_path_init_debug();
1047
1048 pc = ul_new_path(dir);
1049 if (!pc)
1050 err(EXIT_FAILURE, "failed to initialize path context");
1051 if (prefix)
1052 ul_path_set_prefix(pc, prefix);
1053
1054 if (optind == argc)
1055 errx(EXIT_FAILURE, "<command> not defined");
1056 command = argv[optind++];
1057
1058 if (strcmp(command, "read-u32") == 0) {
1059 uint32_t res;
1060
1061 if (optind == argc)
1062 errx(EXIT_FAILURE, "<file> not defined");
1063 file = argv[optind++];
1064
1065 if (ul_path_read_u32(pc, &res, file) != 0)
1066 err(EXIT_FAILURE, "read u64 failed");
1067 printf("read: %s: %u\n", file, res);
1068
1069 if (ul_path_readf_u32(pc, &res, "%s", file) != 0)
1070 err(EXIT_FAILURE, "readf u64 failed");
1071 printf("readf: %s: %u\n", file, res);
1072
1073 } else if (strcmp(command, "read-s32") == 0) {
1074 int32_t res;
1075
1076 if (optind == argc)
1077 errx(EXIT_FAILURE, "<file> not defined");
1078 file = argv[optind++];
1079
1080 if (ul_path_read_s32(pc, &res, file) != 0)
1081 err(EXIT_FAILURE, "read u64 failed");
1082 printf("read: %s: %d\n", file, res);
1083
1084 if (ul_path_readf_s32(pc, &res, "%s", file) != 0)
1085 err(EXIT_FAILURE, "readf u64 failed");
1086 printf("readf: %s: %d\n", file, res);
1087
1088 } else if (strcmp(command, "read-u64") == 0) {
1089 uint64_t res;
1090
1091 if (optind == argc)
1092 errx(EXIT_FAILURE, "<file> not defined");
1093 file = argv[optind++];
1094
1095 if (ul_path_read_u64(pc, &res, file) != 0)
1096 err(EXIT_FAILURE, "read u64 failed");
1097 printf("read: %s: %" PRIu64 "\n", file, res);
1098
1099 if (ul_path_readf_u64(pc, &res, "%s", file) != 0)
1100 err(EXIT_FAILURE, "readf u64 failed");
1101 printf("readf: %s: %" PRIu64 "\n", file, res);
1102
1103 } else if (strcmp(command, "read-s64") == 0) {
1104 int64_t res;
1105
1106 if (optind == argc)
1107 errx(EXIT_FAILURE, "<file> not defined");
1108 file = argv[optind++];
1109
1110 if (ul_path_read_s64(pc, &res, file) != 0)
1111 err(EXIT_FAILURE, "read u64 failed");
1112 printf("read: %s: %" PRIu64 "\n", file, res);
1113
1114 if (ul_path_readf_s64(pc, &res, "%s", file) != 0)
1115 err(EXIT_FAILURE, "readf u64 failed");
1116 printf("readf: %s: %" PRIu64 "\n", file, res);
1117
1118 } else if (strcmp(command, "read-majmin") == 0) {
1119 dev_t res;
1120
1121 if (optind == argc)
1122 errx(EXIT_FAILURE, "<file> not defined");
1123 file = argv[optind++];
1124
1125 if (ul_path_read_majmin(pc, &res, file) != 0)
1126 err(EXIT_FAILURE, "read maj:min failed");
1127 printf("read: %s: %d\n", file, (int) res);
1128
1129 if (ul_path_readf_majmin(pc, &res, "%s", file) != 0)
1130 err(EXIT_FAILURE, "readf maj:min failed");
1131 printf("readf: %s: %d\n", file, (int) res);
1132
1133 } else if (strcmp(command, "read-string") == 0) {
1134 char *res;
1135
1136 if (optind == argc)
1137 errx(EXIT_FAILURE, "<file> not defined");
1138 file = argv[optind++];
1139
1140 if (ul_path_read_string(pc, &res, file) < 0)
1141 err(EXIT_FAILURE, "read string failed");
1142 printf("read: %s: %s\n", file, res);
1143
1144 if (ul_path_readf_string(pc, &res, "%s", file) < 0)
1145 err(EXIT_FAILURE, "readf string failed");
1146 printf("readf: %s: %s\n", file, res);
1147
1148 } else if (strcmp(command, "read-link") == 0) {
1149 char res[PATH_MAX];
1150
1151 if (optind == argc)
1152 errx(EXIT_FAILURE, "<file> not defined");
1153 file = argv[optind++];
1154
1155 if (ul_path_readlink(pc, res, sizeof(res), file) < 0)
1156 err(EXIT_FAILURE, "read symlink failed");
1157 printf("read: %s: %s\n", file, res);
1158
1159 if (ul_path_readlinkf(pc, res, sizeof(res), "%s", file) < 0)
1160 err(EXIT_FAILURE, "readf symlink failed");
1161 printf("readf: %s: %s\n", file, res);
1162
1163 } else if (strcmp(command, "write-string") == 0) {
1164 char *str;
1165
1166 if (optind + 1 == argc)
1167 errx(EXIT_FAILURE, "<file> <string> not defined");
1168 file = argv[optind++];
1169 str = argv[optind++];
1170
1171 if (ul_path_write_string(pc, str, file) != 0)
1172 err(EXIT_FAILURE, "write string failed");
1173 if (ul_path_writef_string(pc, str, "%s", file) != 0)
1174 err(EXIT_FAILURE, "writef string failed");
1175
1176 } else if (strcmp(command, "write-u64") == 0) {
1177 uint64_t num;
1178
1179 if (optind + 1 == argc)
1180 errx(EXIT_FAILURE, "<file> <num> not defined");
1181 file = argv[optind++];
1182 num = strtoumax(argv[optind++], NULL, 0);
1183
1184 if (ul_path_write_u64(pc, num, file) != 0)
1185 err(EXIT_FAILURE, "write u64 failed");
1186 if (ul_path_writef_u64(pc, num, "%s", file) != 0)
1187 err(EXIT_FAILURE, "writef u64 failed");
1188 }
1189
1190 ul_unref_path(pc);
1191 return EXIT_SUCCESS;
1192 }
1193 #endif /* TEST_PROGRAM_PATH */
1194