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