Coverage Report

Created: 2026-10-16 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/lib/fileutils.c
Line
Count
Source
1
/*
2
 * This code is in the public domain; do with it what you wish.
3
 *
4
 * Copyright (C) 2012 Sami Kerola <kerolasa@iki.fi>
5
 * Copyright (C) 2012-2024 Karel Zak <kzak@redhat.com>
6
 */
7
#include <stdio.h>
8
#include <stdlib.h>
9
#include <sys/types.h>
10
#include <sys/stat.h>
11
#include <unistd.h>
12
#include <sys/time.h>
13
#include <sys/resource.h>
14
#ifdef HAVE_SYS_SYSCALL_H
15
# include <sys/syscall.h>
16
#endif
17
#include <string.h>
18
#include <sys/wait.h>
19
#include <fcntl.h>
20
#include <errno.h>
21
22
#include "c.h"
23
#include "all-io.h"
24
#include "canonicalize.h"
25
#include "fileutils.h"
26
#include "pathnames.h"
27
#include "strutils.h"
28
29
int mkstemp_cloexec(char *template)
30
0
{
31
0
#ifdef HAVE_MKOSTEMP
32
0
  return mkostemp(template, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC);
33
#else
34
  int fd, old_flags, errno_save;
35
36
  fd = mkstemp(template);
37
  if (fd < 0)
38
    return fd;
39
40
  old_flags = fcntl(fd, F_GETFD, 0);
41
  if (old_flags < 0)
42
    goto unwind;
43
  if (fcntl(fd, F_SETFD, old_flags | O_CLOEXEC) < 0)
44
    goto unwind;
45
46
  return fd;
47
48
unwind:
49
  errno_save = errno;
50
  unlink(template);
51
  close(fd);
52
  errno = errno_save;
53
54
  return -1;
55
#endif
56
0
}
57
58
/* Create open temporary file in safe way.  Please notice that the
59
 * file permissions are -rw------- by default. */
60
int xmkstemp(char **tmpname, const char *dir, const char *prefix)
61
0
{
62
0
  char *localtmp;
63
0
  const char *tmpenv;
64
0
  mode_t old_mode;
65
0
  int fd, rc;
66
67
  /* Some use cases must be capable of being moved atomically
68
   * with rename(2), which is the reason why dir is here.  */
69
0
  tmpenv = dir ? dir : getenv("TMPDIR");
70
0
  if (!tmpenv)
71
0
    tmpenv = _PATH_TMP;
72
73
0
  rc = asprintf(&localtmp, "%s/%s.XXXXXX", tmpenv, prefix);
74
0
  if (rc < 0)
75
0
    return -1;
76
77
0
  old_mode = umask(077);
78
0
  fd = mkstemp_cloexec(localtmp);
79
0
  umask(old_mode);
80
0
  if (fd == -1) {
81
0
    free(localtmp);
82
0
    localtmp = NULL;
83
0
  }
84
0
  *tmpname = localtmp;
85
0
  return fd;
86
0
}
87
88
#ifdef F_DUPFD_CLOEXEC
89
int dup_fd_cloexec(int oldfd, int lowfd)
90
#else
91
int dup_fd_cloexec(int oldfd, int lowfd  __attribute__((__unused__)))
92
#endif
93
0
{
94
0
  int fd, flags, errno_save;
95
96
0
#ifdef F_DUPFD_CLOEXEC
97
0
  fd = fcntl(oldfd, F_DUPFD_CLOEXEC, lowfd);
98
0
  if (fd >= 0)
99
0
    return fd;
100
0
#endif
101
102
0
  fd = dup(oldfd);
103
0
  if (fd < 0)
104
0
    return fd;
105
106
0
  flags = fcntl(fd, F_GETFD);
107
0
  if (flags < 0)
108
0
    goto unwind;
109
0
  if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
110
0
    goto unwind;
111
112
0
  return fd;
113
114
0
unwind:
115
0
  errno_save = errno;
116
0
  close(fd);
117
0
  errno = errno_save;
118
119
0
  return -1;
120
0
}
121
122
/*
123
 * portable getdtablesize()
124
 */
125
unsigned int get_fd_tabsize(void)
126
0
{
127
0
  int m;
128
129
0
#if defined(HAVE_GETDTABLESIZE)
130
0
  m = getdtablesize();
131
#elif defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
132
  struct rlimit rl;
133
134
  getrlimit(RLIMIT_NOFILE, &rl);
135
  m = rl.rlim_cur;
136
#elif defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
137
  m = sysconf(_SC_OPEN_MAX);
138
#else
139
  m = OPEN_MAX;
140
#endif
141
0
  return m;
142
0
}
143
144
void ul_close_all_fds(unsigned int first, unsigned int last)
145
0
{
146
0
  struct dirent *d;
147
0
  DIR *dir;
148
149
0
  dir = opendir(_PATH_PROC_FDDIR);
150
0
  if (dir) {
151
0
    while ((d = xreaddir(dir))) {
152
0
      char *end;
153
0
      unsigned int fd;
154
0
      int dfd;
155
156
0
      errno = 0;
157
0
      fd = strtoul(d->d_name, &end, 10);
158
159
0
      if (errno || end == d->d_name || !end || *end)
160
0
        continue;
161
0
      dfd = dirfd(dir);
162
0
      if (dfd < 0)
163
0
        continue;
164
0
      if ((unsigned int)dfd == fd)
165
0
        continue;
166
0
      if (fd < first || last < fd)
167
0
        continue;
168
0
      close(fd);
169
0
    }
170
0
    closedir(dir);
171
0
  } else {
172
0
    unsigned fd, tbsz = get_fd_tabsize();
173
174
0
    for (fd = 0; fd < tbsz; fd++) {
175
0
      if (first <= fd && fd <= last)
176
0
        close(fd);
177
0
    }
178
0
  }
179
0
}
180
181
/*
182
 * Fork, drop permissions, and call oper() and return result.
183
 */
184
char *ul_restricted_path_oper(const char *path,
185
      int (*oper)(const char *path, char **result, void *data),
186
      void *data)
187
0
{
188
0
  char *result = NULL;
189
0
  int errsv = 0;
190
0
  int pipes[2];
191
0
  ssize_t len;
192
0
  pid_t pid;
193
194
0
  if (!path || !*path)
195
0
    return NULL;
196
197
0
  if (pipe(pipes) != 0)
198
0
    return NULL;
199
  /*
200
   * To accurately assume identity of getuid() we must use setuid()
201
   * but if we do that, we lose ability to reassume euid of 0, so
202
   * we fork to do the check to keep euid intact.
203
   */
204
0
  pid = fork();
205
0
  switch (pid) {
206
0
  case -1:
207
0
    close(pipes[0]);
208
0
    close(pipes[1]);
209
0
    return NULL;     /* fork error */
210
0
  case 0:
211
0
    close(pipes[0]);    /* close unused end */
212
0
    pipes[0] = -1;
213
0
    errno = 0;
214
215
0
    if (drop_permissions() != 0)
216
0
      result = NULL; /* failed */
217
0
    else
218
0
      oper(path, &result, data);
219
220
0
    len = result ? (ssize_t) strlen(result) :
221
0
              errno ? -errno : -EINVAL;
222
223
    /* send length or errno */
224
0
    ul_write_all(pipes[1], (char *) &len, sizeof(len));
225
0
    if (result)
226
0
      ul_write_all(pipes[1], result, len);
227
0
    _exit(0);
228
0
  default:
229
0
    break;
230
0
  }
231
232
0
  close(pipes[1]);    /* close unused end */
233
0
  pipes[1] = -1;
234
235
  /* read size or -errno */
236
0
  if (ul_read_all(pipes[0], (char *) &len, sizeof(len)) != sizeof(len))
237
0
    goto done;
238
0
  if (len < 0) {
239
0
    errsv = -len;
240
0
    goto done;
241
0
  }
242
243
0
  result = malloc(len + 1);
244
0
  if (!result) {
245
0
    errsv = ENOMEM;
246
0
    goto done;
247
0
  }
248
  /* read path */
249
0
  if (ul_read_all(pipes[0], result, len) != len) {
250
0
    errsv = errno;
251
0
    goto done;
252
0
  }
253
0
  result[len] = '\0';
254
0
done:
255
0
  if (errsv) {
256
0
    free(result);
257
0
    result = NULL;
258
0
  }
259
0
  close(pipes[0]);
260
261
  /* We make a best effort to reap child */
262
0
  ignore_result( waitpid(pid, NULL, 0) );
263
264
0
  errno = errsv;
265
0
  return result;
266
267
0
}
268
269
int ul_mkdir_p(const char *path, mode_t mode)
270
0
{
271
0
  char *p, *dir;
272
0
  int rc = 0;
273
274
0
  if (!path || !*path)
275
0
    return -EINVAL;
276
277
0
  dir = p = strdup(path);
278
0
  if (!dir)
279
0
    return -ENOMEM;
280
281
0
  if (*p == '/')
282
0
    p++;
283
284
0
  while (p && *p) {
285
0
    char *e = strchr(p, '/');
286
0
    if (e)
287
0
      *e = '\0';
288
0
    if (*p) {
289
0
      rc = mkdir(dir, mode);
290
0
      if (rc && errno != EEXIST)
291
0
        break;
292
0
      rc = 0;
293
0
    }
294
0
    if (!e)
295
0
      break;
296
0
    *e = '/';
297
0
    p = e + 1;
298
0
  }
299
300
0
  free(dir);
301
0
  return rc;
302
0
}
303
304
/* returns basename and keeps dirname in the @path, if @path is "/" (root)
305
 * then returns empty string */
306
char *stripoff_last_component(char *path)
307
0
{
308
0
  char *p = path ? strrchr(path, '/') : NULL;
309
310
0
  if (!p)
311
0
    return NULL;
312
0
  *p = '\0';
313
0
  return p + 1;
314
0
}
315
316
static int copy_file_simple(int from, int to)
317
0
{
318
0
  ssize_t nr;
319
0
  char buf[BUFSIZ];
320
321
0
  while ((nr = ul_read_all(from, buf, sizeof(buf))) > 0)
322
0
    if (ul_write_all(to, buf, nr) == -1)
323
0
      return UL_COPY_WRITE_ERROR;
324
0
  if (nr < 0)
325
0
    return UL_COPY_READ_ERROR;
326
0
#ifdef HAVE_EXPLICIT_BZERO
327
0
  explicit_bzero(buf, sizeof(buf));
328
0
#endif
329
0
  return 0;
330
0
}
331
332
/* Copies the contents of a file. Returns -1 on read error, -2 on write error. */
333
int ul_copy_file(int from, int to)
334
0
{
335
0
#ifdef HAVE_SENDFILE
336
0
  struct stat st;
337
0
  ssize_t nw;
338
339
0
  if (fstat(from, &st) == -1)
340
0
    return UL_COPY_READ_ERROR;
341
0
  if (!S_ISREG(st.st_mode))
342
0
    return copy_file_simple(from, to);
343
0
  if (ul_sendfile_all(to, from, NULL, st.st_size) < 0)
344
0
    return copy_file_simple(from, to);
345
  /* ensure we either get an EOF or an error */
346
0
  while ((nw = ul_sendfile_all(to, from, NULL, 16*1024*1024)) != 0)
347
0
    if (nw < 0)
348
0
      return copy_file_simple(from, to);
349
0
  return 0;
350
#else
351
  return copy_file_simple(from, to);
352
#endif
353
0
}
354
355
/* Composes the /proc/self/fd/<fd> pathname for @fd. The @bufsz has to be at
356
 * least UL_FDPATH_BUFSIZ bytes.
357
 *
358
 * This is the only place where the /proc/self/fd/ pathnames are generated.
359
 *
360
 * Returns @buf, or NULL on error.
361
 */
362
char *ul_fd_mkpath(char *buf, size_t bufsz, int fd)
363
0
{
364
0
  int len;
365
366
0
  if (fd < 0) {
367
0
    errno = EBADF;
368
0
    return NULL;
369
0
  }
370
371
0
  len = snprintf(buf, bufsz, _PATH_PROC_FDDIR "/%d", fd);
372
0
  if (len < 0 || (size_t) len >= bufsz) {
373
0
    errno = ENAMETOOLONG;
374
0
    return NULL;
375
0
  }
376
377
0
  return buf;
378
0
}
379
380
/* Returns the pathname the @fd refers to as used by the kernel, or NULL on
381
 * error. The result has to be deallocated by free().
382
 */
383
char *ul_fd_get_path(int fd)
384
0
{
385
0
  ssize_t ssz;
386
0
  char buf[PATH_MAX];
387
0
  char fdpath[UL_FDPATH_BUFSIZ];
388
389
0
  if (!ul_fd_mkpath(fdpath, sizeof(fdpath), fd))
390
0
    return NULL;
391
392
0
  ssz = readlink(fdpath, buf, sizeof(buf));
393
0
  if (ssz < 0)
394
0
    return NULL;
395
396
  /* readlink() does not terminate the result and it does not report
397
   * truncation; a name we cannot read completely is unusable */
398
0
  if ((size_t) ssz >= sizeof(buf)) {
399
0
    errno = ENAMETOOLONG;
400
0
    return NULL;
401
0
  }
402
403
0
  buf[ssz] = '\0';
404
405
  /* readlink() also succeeds for things without a pathname (pipes,
406
   * sockets, ...) and it returns "<path> (deleted)" for unlinked files;
407
   * refuse all of it rather than return a bogus path.
408
   *
409
   * Note that this also refuses a real file named "foo (deleted)". To
410
   * tell it apart we would have to stat() the name, and such a path
411
   * lookup may trigger an automount or block on an unreachable NFS
412
   * server. */
413
0
  if (*buf != '/' || ul_endswith(buf, PATH_DELETED_SUFFIX)) {
414
0
    errno = ENOENT;
415
0
    return NULL;
416
0
  }
417
418
0
  return strdup(buf);
419
0
}
420
421
int ul_reopen(int fd, int flags)
422
0
{
423
0
  char *path = ul_fd_get_path(fd);
424
0
  int ret;
425
426
0
  if (!path)
427
0
    return -errno;
428
429
0
  ret = open(path, flags);
430
0
  free(path);
431
432
0
  return ret;
433
0
}
434
435
436
/* This is a libc-independent version of basename(), which is necessary to
437
 * maintain functionality across different libc implementations. It was
438
 * inspired by the behavior and implementation of glibc.
439
 */
440
char *ul_basename(char *path)
441
0
{
442
0
  char *p;
443
444
0
  if (!path || !*path)
445
0
    return (char *) "."; /* ugly, static string */
446
447
0
  p = strrchr(path, '/');
448
0
  if (!p)
449
0
    return path;   /* no '/', return original */
450
451
0
  if (*(p + 1) != '\0')
452
0
    return p + 1;   /* begin of the name */
453
454
0
  while (p > path && *(p - 1) == '/')
455
0
    --p;     /* remove trailing '/' */
456
457
0
  if (p > path) {
458
0
    *p-- = '\0';
459
0
    while (p > path && *(p - 1) != '/')
460
0
      --p;   /* move to the beginning of the name */
461
0
  } else while (*(p + 1) != '\0')
462
0
    ++p;
463
464
0
  return p;
465
0
}
466
467
#ifdef HAVE_OPENAT
468
/*
469
 * fopen_at_no_link() - Open a file stream that is not a symbolic/hard link.
470
 *
471
 * This function wraps around openat(2), fstat(2), ftruncate(2) and fdopen(3)
472
 * to create a file stream that is not a symbolic or hard link in a race-free
473
 * manner.
474
 *
475
 * @dir:  dirfd as passed to openat(2), e.g. AT_FDCWD for the calling process
476
 *    current working directory
477
 * @filename: name of the target file
478
 * @flags:  open(2) file creation/status flags, O_NOFOLLOW is implicitly set
479
 * @perm: open(2) file mode, can be bitwise ORed, these are only relevant
480
 *    when O_CREAT is set in @flags, otherwise pass as 0.
481
 * @mode: fopen(3) mode
482
 *
483
 * Return: On success, a valid pointer to a file stream is returned.
484
 *         On failure, NULL is returned and errno is set to indicate the issue.
485
 */
486
FILE *fopen_at_no_link(int dir, const char *filename,
487
                             int flags, mode_t perm, const char *mode)
488
0
{
489
0
  FILE *fp;
490
0
  int fd;
491
0
  struct stat st;
492
493
  /* We temporarily clear the O_TRUNC bit because we do not want
494
   * to accidentally truncate the target file if it is a hard link
495
   * instead of a symbolic one, where the latter is what we are
496
   * guarding against here. The test for the hard link is done below
497
   * with fstat()...
498
   */
499
0
  fd = openat(dir, filename, ((flags & ~O_TRUNC) | O_NOFOLLOW | O_CLOEXEC), perm);
500
0
  if (fd < 0)
501
0
    return NULL;
502
503
0
  if (fstat(fd, &st)) {
504
0
    close(fd);
505
0
    return NULL;
506
0
  }
507
508
0
  if (st.st_nlink > 1) {
509
0
    close(fd);
510
0
    errno = EMLINK;
511
0
    return NULL;
512
0
  }
513
514
0
  if ((flags & O_TRUNC) && ftruncate(fd, 0)) {
515
0
    close(fd);
516
0
    return NULL;
517
0
  }
518
519
0
  fp = fdopen(fd, mode);
520
0
  if (!fp)
521
0
    close(fd);
522
0
  return fp;
523
0
}
524
#endif /* HAVE_OPENAT */
525
526
#if defined(SYS_openat2)
527
int ul_openat_resolve(int dirfd, const char *path, int flags,
528
          mode_t mode, unsigned long long resolve)
529
{
530
  struct open_how how = {
531
    .flags = (__u64) flags,
532
    .mode = (__u64) mode,
533
    .resolve = resolve,
534
  };
535
536
  return syscall(SYS_openat2, dirfd, path, &how, sizeof(how));
537
}
538
#else
539
int ul_openat_resolve(
540
    int dirfd __attribute__((__unused__)),
541
    const char *path __attribute__((__unused__)),
542
    int flags __attribute__((__unused__)),
543
    mode_t mode __attribute__((__unused__)),
544
    unsigned long long resolve __attribute__((__unused__)))
545
0
{
546
0
  errno = ENOSYS;
547
0
  return -1;
548
0
}
549
#endif
550
551
#ifdef __linux__
552
/*
553
 * Fallback for kernels without openat2() (Linux < 5.6).
554
 *
555
 * Open the path and then ask the kernel for the name of the result. If any
556
 * component of the path is a symbolic link then the name reported by the
557
 * kernel differs from the requested path and we refuse the file descriptor.
558
 * A concurrent rename is detected the same way. The name always belongs to
559
 * the file descriptor we return, so there is no time-of-check-to-time-of-use
560
 * window between the check and the use.
561
 *
562
 * Note that the symlink is detected after it has been followed rather than
563
 * refused during the path resolution. The path is opened with O_PATH to keep
564
 * this free of side effects (no device open, no blocking on a FIFO, ...) and
565
 * the caller's flags are applied by re-opening the verified file descriptor.
566
 *
567
 * Returns a file descriptor, or -1 and sets errno to ELOOP when a symlink has
568
 * been detected, or to ENOSYS when the check is not possible.
569
 */
570
static int open_no_symlinks_fallback(const char *path, int flags, mode_t mode)
571
0
{
572
0
  char *abspath = NULL, *kpath = NULL;
573
0
  struct stat st;
574
0
  int fd = -1, errsv;
575
576
  /* we cannot verify a file we have to create first */
577
0
  if (!path || (flags & O_CREAT)) {
578
0
    errno = ENOSYS;
579
0
    return -1;
580
0
  }
581
582
0
  if (ul_is_relative_path(path)) {
583
    /* the kernel reports an absolute pathname; note that
584
     * ul_absolute_path() only prepends the CWD as returned by
585
     * getcwd(), it resolves nothing */
586
0
    abspath = ul_absolute_path(path);
587
0
    if (!abspath)
588
0
      return -1;
589
0
  }
590
591
0
  if (flags & O_PATH)
592
0
    fd = open(path, flags);
593
0
  else
594
0
    fd = open(path, O_PATH | O_CLOEXEC |
595
0
        (flags & (O_NOFOLLOW | O_DIRECTORY)));
596
0
  if (fd < 0)
597
0
    goto fail;
598
599
  /* O_PATH|O_NOFOLLOW returns a FD to the symlink itself */
600
0
  if (fstat(fd, &st) != 0)
601
0
    goto fail;
602
0
  if (S_ISLNK(st.st_mode)) {
603
0
    errno = ELOOP;
604
0
    goto fail;
605
0
  }
606
607
0
  kpath = ul_fd_get_path(fd);
608
0
  if (!kpath) {
609
0
    errno = ENOSYS;   /* no /proc, no verification */
610
0
    goto fail;
611
0
  }
612
613
  /* streq_paths() ignores duplicate and trailing slashes, but "." and
614
   * ".." in the requested path are refused as a symlink */
615
0
  if (streq_paths(abspath ? abspath : path, kpath) != 1) {
616
0
    errno = ELOOP;
617
0
    goto fail;
618
0
  }
619
620
0
  if (!(flags & O_PATH)) {
621
0
    char fdpath[UL_FDPATH_BUFSIZ];
622
0
    int nfd = -1;
623
624
    /* apply the caller's flags; the /proc link refers to the
625
     * verified file, the path is not resolved for the second time */
626
0
    if (ul_fd_mkpath(fdpath, sizeof(fdpath), fd))
627
0
      nfd = open(fdpath, flags, mode);
628
0
    if (nfd < 0)
629
0
      goto fail;
630
0
    close(fd);
631
0
    fd = nfd;
632
0
  }
633
634
0
  free(abspath);
635
0
  free(kpath);
636
0
  return fd;
637
0
fail:
638
0
  errsv = errno;
639
0
  free(abspath);
640
0
  free(kpath);
641
0
  if (fd >= 0)
642
0
    close(fd);
643
0
  errno = errsv;
644
0
  return -1;
645
0
}
646
#else /* !__linux__ */
647
/* O_PATH and the /proc/self/fd/ names are Linux specific */
648
static int open_no_symlinks_fallback(
649
      const char *path __attribute__((__unused__)),
650
      int flags __attribute__((__unused__)),
651
      mode_t mode __attribute__((__unused__)))
652
{
653
  errno = ENOSYS;
654
  return -1;
655
}
656
#endif /* __linux__ */
657
658
/* Opens @path with the guarantee that no component of the path is a symbolic
659
 * link, otherwise it fails with ELOOP.
660
 */
661
int ul_open_no_symlinks(const char *path, int flags, mode_t mode)
662
0
{
663
0
  int fd = ul_openat_resolve(AT_FDCWD, path, flags, mode,
664
0
           RESOLVE_NO_SYMLINKS);
665
666
  /* openat2() is Linux 5.6+ */
667
0
  if (fd < 0 && errno == ENOSYS)
668
0
    fd = open_no_symlinks_fallback(path, flags, mode);
669
670
0
  return fd;
671
0
}
672
673
#ifdef HAVE_UL_SAFE_STATX
674
/* statx() that never asks the filesystem and never triggers an automount, so
675
 * it cannot block on a hung NFS server or FUSE daemon. AT_EMPTY_PATH is added
676
 * to @flags when @path is NULL or empty.
677
 *
678
 * Note that statx(2) is not obliged to return all the attributes the caller
679
 * asked for; the caller has to check stx_mask.
680
 *
681
 * The @stx is always zeroized, so it never contains stale data on error.
682
 *
683
 * Returns 0 on success, otherwise negative errno (and errno is set too).
684
 */
685
int ul_safe_statx(int dirfd, const char *path, int flags,
686
      unsigned int mask, struct statx *stx)
687
0
{
688
0
  assert(stx);
689
690
0
  memset(stx, 0, sizeof(*stx));
691
692
0
  flags |= AT_STATX_DONT_SYNC | AT_NO_AUTOMOUNT;
693
694
0
#ifdef AT_EMPTY_PATH
695
0
  if (!path || !*path)
696
0
    flags |= AT_EMPTY_PATH;
697
0
#endif
698
0
  if (statx(dirfd, path ? path : "", flags, mask, stx) != 0)
699
0
    return -errno;
700
701
0
  return 0;
702
0
}
703
704
/* Converts @stx to @st. statx(2) is not obliged to return everything the
705
 * caller asked for -- especially with AT_STATX_DONT_SYNC -- so copy only the
706
 * attributes advertised by stx_mask and leave the rest zeroed. Callers that
707
 * care have to check stx_mask themselves.
708
 */
709
void ul_statx_to_stat(const struct statx *stx, struct stat *st)
710
0
{
711
0
  memset(st, 0, sizeof(*st));
712
713
  /* no bit in stx_mask, always returned by the kernel */
714
0
  st->st_dev     = makedev(stx->stx_dev_major, stx->stx_dev_minor);
715
0
  st->st_rdev    = makedev(stx->stx_rdev_major, stx->stx_rdev_minor);
716
0
  st->st_blksize = stx->stx_blksize;
717
718
  /* st_mode mixes two independently reported things */
719
0
  if (stx->stx_mask & STATX_TYPE)
720
0
    st->st_mode |= stx->stx_mode & S_IFMT;
721
0
  if (stx->stx_mask & STATX_MODE)
722
0
    st->st_mode |= stx->stx_mode & ~S_IFMT;
723
724
0
  if (stx->stx_mask & STATX_INO)
725
0
    st->st_ino = stx->stx_ino;
726
0
  if (stx->stx_mask & STATX_NLINK)
727
0
    st->st_nlink = stx->stx_nlink;
728
0
  if (stx->stx_mask & STATX_UID)
729
0
    st->st_uid = stx->stx_uid;
730
0
  if (stx->stx_mask & STATX_GID)
731
0
    st->st_gid = stx->stx_gid;
732
0
  if (stx->stx_mask & STATX_SIZE)
733
0
    st->st_size = stx->stx_size;
734
0
  if (stx->stx_mask & STATX_BLOCKS)
735
0
    st->st_blocks = stx->stx_blocks;
736
737
0
  if (stx->stx_mask & STATX_ATIME) {
738
0
    st->st_atim.tv_sec  = stx->stx_atime.tv_sec;
739
0
    st->st_atim.tv_nsec = stx->stx_atime.tv_nsec;
740
0
  }
741
0
  if (stx->stx_mask & STATX_MTIME) {
742
0
    st->st_mtim.tv_sec  = stx->stx_mtime.tv_sec;
743
0
    st->st_mtim.tv_nsec = stx->stx_mtime.tv_nsec;
744
0
  }
745
0
  if (stx->stx_mask & STATX_CTIME) {
746
0
    st->st_ctim.tv_sec  = stx->stx_ctime.tv_sec;
747
0
    st->st_ctim.tv_nsec = stx->stx_ctime.tv_nsec;
748
0
  }
749
0
}
750
#endif
751
752
/* This very simplified stat() alternative uses cached VFS data and does not
753
 * directly ask the filesystem for details. It requires a kernel that supports
754
 * statx() with AT_STATX_DONT_SYNC.
755
 *
756
 * @mask is a statx(2) attribute mask, see UL_STATX_*. UL_STATX_ESSENTIAL is
757
 * always added to it, so zero is a valid request for the minimum. Attributes
758
 * the kernel did not return are zero in @st.
759
 *
760
 * The optional @retmask returns the mask of the attributes the kernel really
761
 * provided. Note that it may be less than requested (that's the whole point
762
 * of AT_STATX_DONT_SYNC), but also more, because the kernel returns whatever
763
 * it has cheaply at hand.
764
 *
765
 * Returns 0 on success, otherwise negative errno (and errno is set too).
766
 * -EOPNOTSUPP means the kernel did not provide the essential attributes and
767
 * the caller should fall back to stat().
768
 */
769
int ul_safe_stat(const char *target, struct stat *st,
770
     int nofollow __attribute__((__unused__)),
771
     unsigned int mask __attribute__((__unused__)),
772
     unsigned int *retmask)
773
0
{
774
0
  assert(target);
775
0
  assert(st);
776
777
0
  memset(st, 0, sizeof(struct stat));
778
0
  if (retmask)
779
0
    *retmask = 0;
780
781
0
#ifdef HAVE_UL_SAFE_STAT
782
0
  {
783
0
    struct statx stx = { 0 };
784
0
    int rc;
785
786
0
    mask |= UL_STATX_ESSENTIAL;
787
788
0
    rc = ul_safe_statx(AT_FDCWD, target,
789
0
        nofollow ? AT_SYMLINK_NOFOLLOW : 0, mask, &stx);
790
0
    if (rc)
791
0
      return rc;
792
793
0
    ul_statx_to_stat(&stx, st);
794
795
0
    if (retmask)
796
0
      *retmask = stx.stx_mask;
797
798
0
    if ((stx.stx_mask & UL_STATX_ESSENTIAL) != UL_STATX_ESSENTIAL) {
799
0
      errno = EOPNOTSUPP;
800
0
      return -EOPNOTSUPP;
801
0
    }
802
803
0
    return 0;
804
0
  }
805
#else
806
  errno = ENOSYS;
807
  return -ENOSYS;
808
#endif
809
0
}
810
811
812
#ifdef TEST_PROGRAM_FILEUTILS
813
int main(int argc, char *argv[])
814
{
815
  if (argc < 2)
816
    errx(EXIT_FAILURE, "Usage %s --{mkstemp,close-fds,copy-file,open-no-symlinks,safe-stat}",
817
        argv[0]);
818
819
  if (strcmp(argv[1], "--mkstemp") == 0) {
820
    FILE *f;
821
    char *tmpname = NULL;
822
823
    f = xfmkstemp(&tmpname, NULL, "test");
824
    unlink(tmpname);
825
    free(tmpname);
826
    fclose(f);
827
828
  } else if (strcmp(argv[1], "--close-fds") == 0) {
829
    ignore_result( dup(STDIN_FILENO) );
830
    ignore_result( dup(STDIN_FILENO) );
831
    ignore_result( dup(STDIN_FILENO) );
832
833
# ifdef HAVE_CLOSE_RANGE
834
    if (close_range(STDERR_FILENO + 1, ~0U, 0) < 0)
835
# endif
836
      ul_close_all_fds(STDERR_FILENO + 1, ~0U);
837
838
  } else if (strcmp(argv[1], "--copy-file") == 0) {
839
    int ret = ul_copy_file(STDIN_FILENO, STDOUT_FILENO);
840
    if (ret == UL_COPY_READ_ERROR)
841
      err(EXIT_FAILURE, "read");
842
    else if (ret == UL_COPY_WRITE_ERROR)
843
      err(EXIT_FAILURE, "write");
844
845
  } else if (strcmp(argv[1], "--open-no-symlinks") == 0) {
846
#ifdef __linux__
847
    int flags = O_PATH | O_CLOEXEC;
848
#else
849
    int flags = O_RDONLY | O_CLOEXEC;
850
#endif
851
    int fallback = 0;
852
    char *name;
853
    int fd, i;
854
855
    if (argc < 3)
856
      errx(EXIT_FAILURE, "no path specified");
857
858
    for (i = 3; i < argc; i++) {
859
      /* the fallback is used on kernels without openat2()
860
       * only, "--fallback" makes it testable anywhere */
861
      if (strcmp(argv[i], "--fallback") == 0)
862
        fallback = 1;
863
      else if (strcmp(argv[i], "--rdonly") == 0)
864
        flags = O_RDONLY | O_CLOEXEC;
865
    }
866
867
    if (fallback)
868
      fd = open_no_symlinks_fallback(argv[2], flags, 0);
869
    else
870
      fd = ul_open_no_symlinks(argv[2], flags, 0);
871
    if (fd < 0)
872
      err(EXIT_FAILURE, "%s", argv[2]);
873
874
    name = ul_fd_get_path(fd);
875
    printf("%s\n", name);
876
    free(name);
877
    close(fd);
878
879
  } else if (strcmp(argv[1], "--safe-stat") == 0) {
880
    unsigned int mask = 0, retmask = 0;
881
    int nofollow = 0;
882
    struct stat st, ref;
883
    int rc, i;
884
885
    if (argc < 3)
886
      errx(EXIT_FAILURE, "no path specified");
887
888
    for (i = 3; i < argc; i++) {
889
      if (strcmp(argv[i], "--nofollow") == 0)
890
        nofollow = 1;
891
      else if (strcmp(argv[i], "--basic") == 0)
892
        mask |= UL_STATX_BASIC;
893
    }
894
895
    printf("%s:\n", argv[2]);
896
897
    rc = ul_safe_stat(argv[2], &st, nofollow, mask, &retmask);
898
899
    /* the function returns -errno and it sets errno too */
900
    printf("      rc: %d\n", rc);
901
    printf("   errno: %d\n", rc ? errno : 0);
902
    if (rc)
903
      return EXIT_FAILURE;
904
905
    printf("    type: %s\n", S_ISDIR(st.st_mode)  ? "dir" :
906
           S_ISREG(st.st_mode)  ? "reg" :
907
           S_ISLNK(st.st_mode)  ? "lnk" :
908
           S_ISBLK(st.st_mode)  ? "blk" :
909
           S_ISCHR(st.st_mode)  ? "chr" :
910
           S_ISFIFO(st.st_mode) ? "fifo" :
911
           S_ISSOCK(st.st_mode) ? "sock" : "unknown");
912
913
    /* The attribute values depend on the system, so compare them
914
     * with the classic stat() rather than print them. Only the
915
     * attributes advertised by @retmask are comparable, the rest
916
     * is zero in @st.
917
     */
918
    if (nofollow ? lstat(argv[2], &ref) : stat(argv[2], &ref))
919
      err(EXIT_FAILURE, "%s", argv[2]);
920
921
    printf("  st_dev: %s\n", st.st_dev == ref.st_dev ? "OK" : "FAILED");
922
    printf(" st_rdev: %s\n", st.st_rdev == ref.st_rdev ? "OK" : "FAILED");
923
#ifdef HAVE_UL_SAFE_STAT
924
    {
925
      /* Report all the attributes we asked for, so that
926
       * the output does not depend on how generous the
927
       * kernel is; it may return more than requested.
928
       *
929
       * An attribute missing in @retmask is zero in @st by
930
       * design, there is nothing to compare and it's not a
931
       * failure.
932
       */
933
      unsigned int emask = mask | UL_STATX_ESSENTIAL;
934
935
# define RESULT(bit, x) (!(retmask & (bit)) || (x) ? "OK" : "FAILED")
936
937
      if (emask & STATX_TYPE)
938
        printf("  S_IFMT: %s\n", RESULT(STATX_TYPE,
939
          (st.st_mode & S_IFMT) == (ref.st_mode & S_IFMT)));
940
      if (emask & STATX_MODE)
941
        printf(" st_mode: %s\n", RESULT(STATX_MODE,
942
          (st.st_mode & ~S_IFMT) == (ref.st_mode & ~S_IFMT)));
943
      if (emask & STATX_INO)
944
        printf("  st_ino: %s\n",
945
          RESULT(STATX_INO, st.st_ino == ref.st_ino));
946
      if (emask & STATX_NLINK)
947
        printf("st_nlink: %s\n",
948
          RESULT(STATX_NLINK, st.st_nlink == ref.st_nlink));
949
      if (emask & STATX_UID)
950
        printf("  st_uid: %s\n",
951
          RESULT(STATX_UID, st.st_uid == ref.st_uid));
952
      if (emask & STATX_GID)
953
        printf("  st_gid: %s\n",
954
          RESULT(STATX_GID, st.st_gid == ref.st_gid));
955
      if (emask & STATX_SIZE)
956
        printf(" st_size: %s\n",
957
          RESULT(STATX_SIZE, st.st_size == ref.st_size));
958
# undef RESULT
959
    }
960
#endif
961
  }
962
  return EXIT_SUCCESS;
963
}
964
#endif