Coverage Report

Created: 2026-04-11 06:29

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