Coverage Report

Created: 2025-06-13 06:36

/src/util-linux/lib/fileutils.c
Line
Count
Source (jump to first uncovered line)
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
#include <string.h>
15
16
#include "c.h"
17
#include "all-io.h"
18
#include "fileutils.h"
19
#include "pathnames.h"
20
21
int mkstemp_cloexec(char *template)
22
0
{
23
0
#ifdef HAVE_MKOSTEMP
24
0
  return mkostemp(template, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC);
25
#else
26
  int fd, old_flags, errno_save;
27
28
  fd = mkstemp(template);
29
  if (fd < 0)
30
    return fd;
31
32
  old_flags = fcntl(fd, F_GETFD, 0);
33
  if (old_flags < 0)
34
    goto unwind;
35
  if (fcntl(fd, F_SETFD, old_flags | O_CLOEXEC) < 0)
36
    goto unwind;
37
38
  return fd;
39
40
unwind:
41
  errno_save = errno;
42
  unlink(template);
43
  close(fd);
44
  errno = errno_save;
45
46
  return -1;
47
#endif
48
0
}
49
50
/* Create open temporary file in safe way.  Please notice that the
51
 * file permissions are -rw------- by default. */
52
int xmkstemp(char **tmpname, const char *dir, const char *prefix)
53
0
{
54
0
  char *localtmp;
55
0
  const char *tmpenv;
56
0
  mode_t old_mode;
57
0
  int fd, rc;
58
59
  /* Some use cases must be capable of being moved atomically
60
   * with rename(2), which is the reason why dir is here.  */
61
0
  tmpenv = dir ? dir : getenv("TMPDIR");
62
0
  if (!tmpenv)
63
0
    tmpenv = _PATH_TMP;
64
65
0
  rc = asprintf(&localtmp, "%s/%s.XXXXXX", tmpenv, prefix);
66
0
  if (rc < 0)
67
0
    return -1;
68
69
0
  old_mode = umask(077);
70
0
  fd = mkstemp_cloexec(localtmp);
71
0
  umask(old_mode);
72
0
  if (fd == -1) {
73
0
    free(localtmp);
74
0
    localtmp = NULL;
75
0
  }
76
0
  *tmpname = localtmp;
77
0
  return fd;
78
0
}
79
80
#ifdef F_DUPFD_CLOEXEC
81
int dup_fd_cloexec(int oldfd, int lowfd)
82
#else
83
int dup_fd_cloexec(int oldfd, int lowfd  __attribute__((__unused__)))
84
#endif
85
0
{
86
0
  int fd, flags, errno_save;
87
88
0
#ifdef F_DUPFD_CLOEXEC
89
0
  fd = fcntl(oldfd, F_DUPFD_CLOEXEC, lowfd);
90
0
  if (fd >= 0)
91
0
    return fd;
92
0
#endif
93
94
0
  fd = dup(oldfd);
95
0
  if (fd < 0)
96
0
    return fd;
97
98
0
  flags = fcntl(fd, F_GETFD);
99
0
  if (flags < 0)
100
0
    goto unwind;
101
0
  if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
102
0
    goto unwind;
103
104
0
  return fd;
105
106
0
unwind:
107
0
  errno_save = errno;
108
0
  close(fd);
109
0
  errno = errno_save;
110
111
0
  return -1;
112
0
}
113
114
/*
115
 * portable getdtablesize()
116
 */
117
unsigned int get_fd_tabsize(void)
118
0
{
119
0
  int m;
120
121
0
#if defined(HAVE_GETDTABLESIZE)
122
0
  m = getdtablesize();
123
#elif defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
124
  struct rlimit rl;
125
126
  getrlimit(RLIMIT_NOFILE, &rl);
127
  m = rl.rlim_cur;
128
#elif defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
129
  m = sysconf(_SC_OPEN_MAX);
130
#else
131
  m = OPEN_MAX;
132
#endif
133
0
  return m;
134
0
}
135
136
void ul_close_all_fds(unsigned int first, unsigned int last)
137
0
{
138
0
  struct dirent *d;
139
0
  DIR *dir;
140
141
0
  dir = opendir(_PATH_PROC_FDDIR);
142
0
  if (dir) {
143
0
    while ((d = xreaddir(dir))) {
144
0
      char *end;
145
0
      unsigned int fd;
146
0
      int dfd;
147
148
0
      errno = 0;
149
0
      fd = strtoul(d->d_name, &end, 10);
150
151
0
      if (errno || end == d->d_name || !end || *end)
152
0
        continue;
153
0
      dfd = dirfd(dir);
154
0
      if (dfd < 0)
155
0
        continue;
156
0
      if ((unsigned int)dfd == fd)
157
0
        continue;
158
0
      if (fd < first || last < fd)
159
0
        continue;
160
0
      close(fd);
161
0
    }
162
0
    closedir(dir);
163
0
  } else {
164
0
    unsigned fd, tbsz = get_fd_tabsize();
165
166
0
    for (fd = 0; fd < tbsz; fd++) {
167
0
      if (first <= fd && fd <= last)
168
0
        close(fd);
169
0
    }
170
0
  }
171
0
}
172
173
#ifdef TEST_PROGRAM_FILEUTILS
174
int main(int argc, char *argv[])
175
{
176
  if (argc < 2)
177
    errx(EXIT_FAILURE, "Usage %s --{mkstemp,close-fds,copy-file}", argv[0]);
178
179
  if (strcmp(argv[1], "--mkstemp") == 0) {
180
    FILE *f;
181
    char *tmpname = NULL;
182
183
    f = xfmkstemp(&tmpname, NULL, "test");
184
    unlink(tmpname);
185
    free(tmpname);
186
    fclose(f);
187
188
  } else if (strcmp(argv[1], "--close-fds") == 0) {
189
    ignore_result( dup(STDIN_FILENO) );
190
    ignore_result( dup(STDIN_FILENO) );
191
    ignore_result( dup(STDIN_FILENO) );
192
193
# ifdef HAVE_CLOSE_RANGE
194
    if (close_range(STDERR_FILENO + 1, ~0U, 0) < 0)
195
# endif
196
      ul_close_all_fds(STDERR_FILENO + 1, ~0U);
197
198
  } else if (strcmp(argv[1], "--copy-file") == 0) {
199
    int ret = ul_copy_file(STDIN_FILENO, STDOUT_FILENO);
200
    if (ret == UL_COPY_READ_ERROR)
201
      err(EXIT_FAILURE, "read");
202
    else if (ret == UL_COPY_WRITE_ERROR)
203
      err(EXIT_FAILURE, "write");
204
  }
205
  return EXIT_SUCCESS;
206
}
207
#endif
208
209
210
int ul_mkdir_p(const char *path, mode_t mode)
211
0
{
212
0
  char *p, *dir;
213
0
  int rc = 0;
214
215
0
  if (!path || !*path)
216
0
    return -EINVAL;
217
218
0
  dir = p = strdup(path);
219
0
  if (!dir)
220
0
    return -ENOMEM;
221
222
0
  if (*p == '/')
223
0
    p++;
224
225
0
  while (p && *p) {
226
0
    char *e = strchr(p, '/');
227
0
    if (e)
228
0
      *e = '\0';
229
0
    if (*p) {
230
0
      rc = mkdir(dir, mode);
231
0
      if (rc && errno != EEXIST)
232
0
        break;
233
0
      rc = 0;
234
0
    }
235
0
    if (!e)
236
0
      break;
237
0
    *e = '/';
238
0
    p = e + 1;
239
0
  }
240
241
0
  free(dir);
242
0
  return rc;
243
0
}
244
245
/* returns basename and keeps dirname in the @path, if @path is "/" (root)
246
 * then returns empty string */
247
char *stripoff_last_component(char *path)
248
0
{
249
0
  char *p = path ? strrchr(path, '/') : NULL;
250
251
0
  if (!p)
252
0
    return NULL;
253
0
  *p = '\0';
254
0
  return p + 1;
255
0
}
256
257
static int copy_file_simple(int from, int to)
258
0
{
259
0
  ssize_t nr;
260
0
  char buf[BUFSIZ];
261
262
0
  while ((nr = read_all(from, buf, sizeof(buf))) > 0)
263
0
    if (write_all(to, buf, nr) == -1)
264
0
      return UL_COPY_WRITE_ERROR;
265
0
  if (nr < 0)
266
0
    return UL_COPY_READ_ERROR;
267
0
#ifdef HAVE_EXPLICIT_BZERO
268
0
  explicit_bzero(buf, sizeof(buf));
269
0
#endif
270
0
  return 0;
271
0
}
272
273
/* Copies the contents of a file. Returns -1 on read error, -2 on write error. */
274
int ul_copy_file(int from, int to)
275
0
{
276
0
#ifdef HAVE_SENDFILE
277
0
  struct stat st;
278
0
  ssize_t nw;
279
280
0
  if (fstat(from, &st) == -1)
281
0
    return UL_COPY_READ_ERROR;
282
0
  if (!S_ISREG(st.st_mode))
283
0
    return copy_file_simple(from, to);
284
0
  if (sendfile_all(to, from, NULL, st.st_size) < 0)
285
0
    return copy_file_simple(from, to);
286
  /* ensure we either get an EOF or an error */
287
0
  while ((nw = sendfile_all(to, from, NULL, 16*1024*1024)) != 0)
288
0
    if (nw < 0)
289
0
      return copy_file_simple(from, to);
290
0
  return 0;
291
#else
292
  return copy_file_simple(from, to);
293
#endif
294
0
}
295
296
int ul_reopen(int fd, int flags)
297
0
{
298
0
  ssize_t ssz;
299
0
  char buf[PATH_MAX];
300
0
  char fdpath[ sizeof(_PATH_PROC_FDDIR) + sizeof(stringify_value(INT_MAX)) ];
301
302
0
  snprintf(fdpath, sizeof(fdpath), _PATH_PROC_FDDIR "/%d", fd);
303
304
0
  ssz = readlink(fdpath, buf, sizeof(buf) - 1);
305
0
  if (ssz < 0)
306
0
    return -errno;
307
308
0
  assert(ssz > 0);
309
310
0
  buf[ssz] = '\0';
311
312
0
  return open(buf, flags);
313
0
}
314
315
316
/* This is a libc-independent version of basename(), which is necessary to
317
 * maintain functionality across different libc implementations. It was
318
 * inspired by the behavior and implementation of glibc.
319
 */
320
char *ul_basename(char *path)
321
0
{
322
0
  char *p;
323
324
0
  if (!path || !*path)
325
0
    return (char *) "."; /* ugly, static string */
326
327
0
  p = strrchr(path, '/');
328
0
  if (!p)
329
0
    return path;   /* no '/', return original */
330
331
0
  if (*(p + 1) != '\0')
332
0
    return p + 1;   /* begin of the name */
333
334
0
  while (p > path && *(p - 1) == '/')
335
0
    --p;     /* remove trailing '/' */
336
337
0
  if (p > path) {
338
0
    *p-- = '\0';
339
0
    while (p > path && *(p - 1) != '/')
340
0
      --p;   /* move to the beginning of the name */
341
0
  } else while (*(p + 1) != '\0')
342
0
    ++p;
343
344
0
  return p;
345
0
}