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