Coverage Report

Created: 2023-03-26 06:54

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