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