Coverage Report

Created: 2026-07-16 06:58

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