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