Coverage Report

Created: 2025-06-13 06:36

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