]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/path.c
Merge branch 'lsfd--resolve-mqueue-nodev' of https://github.com/masatake/util-linux
[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 1022 size_t setsize, len = maxcpus * 7;
c866bbbe 1023 char *buf;
91bf5817 1024 int rc;
8148217b 1025
9023b201
KZ
1026 *set = NULL;
1027
c866bbbe
TW
1028 buf = malloc(len);
1029 if (!buf)
1030 return -ENOMEM;
1031
1ed21c80 1032 f = ul_path_vfopenf(pc, "r" UL_CLOEXECSTR, path, ap);
c866bbbe
TW
1033 if (!f) {
1034 rc = -errno;
1035 goto out;
1036 }
8148217b 1037
d2081ec7 1038 rc = fgets(buf, len, f) == NULL ? -EIO : 0;
1ed21c80 1039 fclose(f);
8148217b 1040
91bf5817 1041 if (rc)
c866bbbe 1042 goto out;
91bf5817 1043
8148217b
HC
1044 len = strlen(buf);
1045 if (buf[len - 1] == '\n')
1046 buf[len - 1] = '\0';
1047
1ed21c80 1048 *set = cpuset_alloc(maxcpus, &setsize, NULL);
c866bbbe
TW
1049 if (!*set) {
1050 rc = -EINVAL;
1051 goto out;
1052 }
8148217b
HC
1053
1054 if (islist) {
9023b201 1055 if (cpulist_parse(buf, *set, setsize, 0)) {
c866bbbe
TW
1056 rc = -EINVAL;
1057 goto out;
9023b201 1058 }
8148217b 1059 } else {
9023b201 1060 if (cpumask_parse(buf, *set, setsize)) {
c866bbbe
TW
1061 rc = -EINVAL;
1062 goto out;
9023b201 1063 }
8148217b 1064 }
c866bbbe
TW
1065 rc = 0;
1066
1067out:
1068 if (rc)
1069 cpuset_free(*set);
1070 free(buf);
1071 return rc;
8148217b
HC
1072}
1073
1ed21c80 1074int ul_path_readf_cpuset(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...)
8148217b
HC
1075{
1076 va_list ap;
1ed21c80 1077 int rc = 0;
8148217b
HC
1078
1079 va_start(ap, path);
1ed21c80 1080 rc = ul_path_cpuparse(pc, set, maxcpus, 0, path, ap);
8148217b
HC
1081 va_end(ap);
1082
1ed21c80 1083 return rc;
8148217b
HC
1084}
1085
1ed21c80 1086int ul_path_readf_cpulist(struct path_cxt *pc, cpu_set_t **set, int maxcpus, const char *path, ...)
8148217b
HC
1087{
1088 va_list ap;
1ed21c80 1089 int rc = 0;
8148217b
HC
1090
1091 va_start(ap, path);
1ed21c80 1092 rc = ul_path_cpuparse(pc, set, maxcpus, 1, path, ap);
8148217b
HC
1093 va_end(ap);
1094
1ed21c80 1095 return rc;
8148217b 1096}
1ed21c80 1097
4a04d7f3 1098#endif /* HAVE_CPU_SET_T */
1ed21c80
KZ
1099
1100
1101#ifdef TEST_PROGRAM_PATH
1102#include <getopt.h>
1103
1104static void __attribute__((__noreturn__)) usage(void)
1105{
1106 fprintf(stdout, " %s [options] <dir> <command>\n\n", program_invocation_short_name);
1107 fputs(" -p, --prefix <dir> redirect hardcoded paths to <dir>\n", stdout);
1108
1109 fputs(" Commands:\n", stdout);
1110 fputs(" read-u64 <file> read uint64_t from file\n", stdout);
1111 fputs(" read-s64 <file> read int64_t from file\n", stdout);
1112 fputs(" read-u32 <file> read uint32_t from file\n", stdout);
1113 fputs(" read-s32 <file> read int32_t from file\n", stdout);
1114 fputs(" read-string <file> read string from file\n", stdout);
1115 fputs(" read-majmin <file> read devno from file\n", stdout);
1116 fputs(" read-link <file> read symlink\n", stdout);
1117 fputs(" write-string <file> <str> write string from file\n", stdout);
1118 fputs(" write-u64 <file> <str> write uint64_t from file\n", stdout);
1119
1120 exit(EXIT_SUCCESS);
1121}
1122
1123int main(int argc, char *argv[])
1124{
1125 int c;
1126 const char *prefix = NULL, *dir, *file, *command;
1127 struct path_cxt *pc = NULL;
1128
1129 static const struct option longopts[] = {
1130 { "prefix", 1, NULL, 'p' },
1131 { "help", 0, NULL, 'h' },
1132 { NULL, 0, NULL, 0 },
1133 };
1134
1135 while((c = getopt_long(argc, argv, "p:h", longopts, NULL)) != -1) {
1136 switch(c) {
1137 case 'p':
24c329bb 1138 prefix = optarg;
1ed21c80
KZ
1139 break;
1140 case 'h':
1141 usage();
1142 break;
1143 default:
1144 err(EXIT_FAILURE, "try --help");
1145 }
1146 }
1147
1148 if (optind == argc)
1149 errx(EXIT_FAILURE, "<dir> not defined");
1150 dir = argv[optind++];
1151
bcf445fd
KZ
1152 ul_path_init_debug();
1153
5a826102 1154 pc = ul_new_path("%s", dir);
1ed21c80
KZ
1155 if (!pc)
1156 err(EXIT_FAILURE, "failed to initialize path context");
1157 if (prefix)
1158 ul_path_set_prefix(pc, prefix);
1159
1160 if (optind == argc)
1161 errx(EXIT_FAILURE, "<command> not defined");
1162 command = argv[optind++];
1163
1164 if (strcmp(command, "read-u32") == 0) {
1165 uint32_t res;
1166
1167 if (optind == argc)
1168 errx(EXIT_FAILURE, "<file> not defined");
1169 file = argv[optind++];
1170
1171 if (ul_path_read_u32(pc, &res, file) != 0)
1172 err(EXIT_FAILURE, "read u64 failed");
1173 printf("read: %s: %u\n", file, res);
1174
1175 if (ul_path_readf_u32(pc, &res, "%s", file) != 0)
1176 err(EXIT_FAILURE, "readf u64 failed");
1177 printf("readf: %s: %u\n", file, res);
1178
1179 } else if (strcmp(command, "read-s32") == 0) {
1180 int32_t res;
1181
1182 if (optind == argc)
1183 errx(EXIT_FAILURE, "<file> not defined");
1184 file = argv[optind++];
1185
1186 if (ul_path_read_s32(pc, &res, file) != 0)
1187 err(EXIT_FAILURE, "read u64 failed");
1188 printf("read: %s: %d\n", file, res);
1189
1190 if (ul_path_readf_s32(pc, &res, "%s", file) != 0)
1191 err(EXIT_FAILURE, "readf u64 failed");
1192 printf("readf: %s: %d\n", file, res);
1193
1194 } else if (strcmp(command, "read-u64") == 0) {
1195 uint64_t res;
1196
1197 if (optind == argc)
1198 errx(EXIT_FAILURE, "<file> not defined");
1199 file = argv[optind++];
1200
1201 if (ul_path_read_u64(pc, &res, file) != 0)
1202 err(EXIT_FAILURE, "read u64 failed");
1203 printf("read: %s: %" PRIu64 "\n", file, res);
1204
1205 if (ul_path_readf_u64(pc, &res, "%s", file) != 0)
1206 err(EXIT_FAILURE, "readf u64 failed");
1207 printf("readf: %s: %" PRIu64 "\n", file, res);
1208
1209 } else if (strcmp(command, "read-s64") == 0) {
1210 int64_t res;
1211
1212 if (optind == argc)
1213 errx(EXIT_FAILURE, "<file> not defined");
1214 file = argv[optind++];
1215
1216 if (ul_path_read_s64(pc, &res, file) != 0)
1217 err(EXIT_FAILURE, "read u64 failed");
1218 printf("read: %s: %" PRIu64 "\n", file, res);
1219
1220 if (ul_path_readf_s64(pc, &res, "%s", file) != 0)
1221 err(EXIT_FAILURE, "readf u64 failed");
1222 printf("readf: %s: %" PRIu64 "\n", file, res);
1223
1224 } else if (strcmp(command, "read-majmin") == 0) {
1225 dev_t res;
1226
1227 if (optind == argc)
1228 errx(EXIT_FAILURE, "<file> not defined");
1229 file = argv[optind++];
1230
1231 if (ul_path_read_majmin(pc, &res, file) != 0)
1232 err(EXIT_FAILURE, "read maj:min failed");
1233 printf("read: %s: %d\n", file, (int) res);
1234
1235 if (ul_path_readf_majmin(pc, &res, "%s", file) != 0)
1236 err(EXIT_FAILURE, "readf maj:min failed");
1237 printf("readf: %s: %d\n", file, (int) res);
1238
1239 } else if (strcmp(command, "read-string") == 0) {
1240 char *res;
1241
1242 if (optind == argc)
1243 errx(EXIT_FAILURE, "<file> not defined");
1244 file = argv[optind++];
1245
1e881378 1246 if (ul_path_read_string(pc, &res, file) <= 0)
905bc781 1247 err(EXIT_FAILURE, "read string failed");
1ed21c80
KZ
1248 printf("read: %s: %s\n", file, res);
1249
1e881378 1250 if (ul_path_readf_string(pc, &res, "%s", file) <= 0)
905bc781 1251 err(EXIT_FAILURE, "readf string failed");
1ed21c80
KZ
1252 printf("readf: %s: %s\n", file, res);
1253
1254 } else if (strcmp(command, "read-link") == 0) {
1255 char res[PATH_MAX];
1256
1257 if (optind == argc)
1258 errx(EXIT_FAILURE, "<file> not defined");
1259 file = argv[optind++];
1260
1261 if (ul_path_readlink(pc, res, sizeof(res), file) < 0)
1262 err(EXIT_FAILURE, "read symlink failed");
1263 printf("read: %s: %s\n", file, res);
1264
1265 if (ul_path_readlinkf(pc, res, sizeof(res), "%s", file) < 0)
1266 err(EXIT_FAILURE, "readf symlink failed");
1267 printf("readf: %s: %s\n", file, res);
1268
1269 } else if (strcmp(command, "write-string") == 0) {
1270 char *str;
1271
1272 if (optind + 1 == argc)
1273 errx(EXIT_FAILURE, "<file> <string> not defined");
1274 file = argv[optind++];
1275 str = argv[optind++];
1276
1277 if (ul_path_write_string(pc, str, file) != 0)
1278 err(EXIT_FAILURE, "write string failed");
1279 if (ul_path_writef_string(pc, str, "%s", file) != 0)
1280 err(EXIT_FAILURE, "writef string failed");
1281
1282 } else if (strcmp(command, "write-u64") == 0) {
1283 uint64_t num;
1284
1285 if (optind + 1 == argc)
1286 errx(EXIT_FAILURE, "<file> <num> not defined");
1287 file = argv[optind++];
1288 num = strtoumax(argv[optind++], NULL, 0);
1289
1290 if (ul_path_write_u64(pc, num, file) != 0)
1291 err(EXIT_FAILURE, "write u64 failed");
1292 if (ul_path_writef_u64(pc, num, "%s", file) != 0)
1293 err(EXIT_FAILURE, "writef u64 failed");
1294 }
1295
1296 ul_unref_path(pc);
1297 return EXIT_SUCCESS;
1298}
1299#endif /* TEST_PROGRAM_PATH */
1300