]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/fileutils.c
lscpu: fix variable shadowing
[thirdparty/util-linux.git] / lib / fileutils.c
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-2020 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 {
23 #ifdef HAVE_MKOSTEMP
24 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 }
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 {
54 char *localtmp;
55 const char *tmpenv;
56 mode_t old_mode;
57 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 tmpenv = dir ? dir : getenv("TMPDIR");
62 if (!tmpenv)
63 tmpenv = _PATH_TMP;
64
65 rc = asprintf(&localtmp, "%s/%s.XXXXXX", tmpenv, prefix);
66 if (rc < 0)
67 return -1;
68
69 old_mode = umask(077);
70 fd = mkstemp_cloexec(localtmp);
71 umask(old_mode);
72 if (fd == -1) {
73 free(localtmp);
74 localtmp = NULL;
75 }
76 *tmpname = localtmp;
77 return fd;
78 }
79
80 int dup_fd_cloexec(int oldfd, int lowfd)
81 {
82 int fd, flags, errno_save;
83
84 #ifdef F_DUPFD_CLOEXEC
85 fd = fcntl(oldfd, F_DUPFD_CLOEXEC, lowfd);
86 if (fd >= 0)
87 return fd;
88 #endif
89
90 fd = dup(oldfd);
91 if (fd < 0)
92 return fd;
93
94 flags = fcntl(fd, F_GETFD);
95 if (flags < 0)
96 goto unwind;
97 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
98 goto unwind;
99
100 return fd;
101
102 unwind:
103 errno_save = errno;
104 close(fd);
105 errno = errno_save;
106
107 return -1;
108 }
109
110 /*
111 * portable getdtablesize()
112 */
113 unsigned int get_fd_tabsize(void)
114 {
115 int m;
116
117 #if defined(HAVE_GETDTABLESIZE)
118 m = getdtablesize();
119 #elif defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
120 struct rlimit rl;
121
122 getrlimit(RLIMIT_NOFILE, &rl);
123 m = rl.rlim_cur;
124 #elif defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
125 m = sysconf(_SC_OPEN_MAX);
126 #else
127 m = OPEN_MAX;
128 #endif
129 return m;
130 }
131
132 #ifndef HAVE_CLOSE_RANGE
133 void close_all_fds(unsigned int first, unsigned int last)
134 {
135 struct dirent *d;
136 DIR *dir;
137
138 dir = opendir(_PATH_PROC_FDDIR);
139 if (dir) {
140 while ((d = xreaddir(dir))) {
141 char *end;
142 unsigned int fd;
143 int dfd;
144
145 errno = 0;
146 fd = strtoul(d->d_name, &end, 10);
147
148 if (errno || end == d->d_name || !end || *end)
149 continue;
150 dfd = dirfd(dir);
151 if (dfd < 0)
152 continue;
153 if ((unsigned int)dfd == fd)
154 continue;
155 if (fd < first || last < fd)
156 continue;
157 close(fd);
158 }
159 closedir(dir);
160 } else {
161 unsigned fd, tbsz = get_fd_tabsize();
162
163 for (fd = 0; fd < tbsz; fd++) {
164 if (first <= fd && fd <= last)
165 close(fd);
166 }
167 }
168 }
169 #endif
170
171 #ifdef TEST_PROGRAM_FILEUTILS
172 int main(int argc, char *argv[])
173 {
174 if (argc < 2)
175 errx(EXIT_FAILURE, "Usage %s --{mkstemp,close-fds,copy-file}", argv[0]);
176
177 if (strcmp(argv[1], "--mkstemp") == 0) {
178 FILE *f;
179 char *tmpname = NULL;
180
181 f = xfmkstemp(&tmpname, NULL, "test");
182 unlink(tmpname);
183 free(tmpname);
184 fclose(f);
185
186 } else if (strcmp(argv[1], "--close-fds") == 0) {
187 ignore_result( dup(STDIN_FILENO) );
188 ignore_result( dup(STDIN_FILENO) );
189 ignore_result( dup(STDIN_FILENO) );
190
191 # ifdef HAVE_CLOSE_RANGE
192 close_range(STDERR_FILENO + 1, ~0U);
193 # else
194 close_all_fds(STDERR_FILENO + 1, ~0U);
195 # endif
196 } else if (strcmp(argv[1], "--copy-file") == 0) {
197 int ret = ul_copy_file(STDIN_FILENO, STDOUT_FILENO);
198 if (ret == UL_COPY_READ_ERROR)
199 err(EXIT_FAILURE, "read");
200 else if (ret == UL_COPY_WRITE_ERROR)
201 err(EXIT_FAILURE, "write");
202 }
203 return EXIT_SUCCESS;
204 }
205 #endif
206
207
208 int mkdir_p(const char *path, mode_t mode)
209 {
210 char *p, *dir;
211 int rc = 0;
212
213 if (!path || !*path)
214 return -EINVAL;
215
216 dir = p = strdup(path);
217 if (!dir)
218 return -ENOMEM;
219
220 if (*p == '/')
221 p++;
222
223 while (p && *p) {
224 char *e = strchr(p, '/');
225 if (e)
226 *e = '\0';
227 if (*p) {
228 rc = mkdir(dir, mode);
229 if (rc && errno != EEXIST)
230 break;
231 rc = 0;
232 }
233 if (!e)
234 break;
235 *e = '/';
236 p = e + 1;
237 }
238
239 free(dir);
240 return rc;
241 }
242
243 /* returns basename and keeps dirname in the @path, if @path is "/" (root)
244 * then returns empty string */
245 char *stripoff_last_component(char *path)
246 {
247 char *p = path ? strrchr(path, '/') : NULL;
248
249 if (!p)
250 return NULL;
251 *p = '\0';
252 return p + 1;
253 }
254
255 static int copy_file_simple(int from, int to)
256 {
257 ssize_t nr;
258 char buf[BUFSIZ];
259
260 while ((nr = read_all(from, buf, sizeof(buf))) > 0)
261 if (write_all(to, buf, nr) == -1)
262 return UL_COPY_WRITE_ERROR;
263 if (nr < 0)
264 return UL_COPY_READ_ERROR;
265 #ifdef HAVE_EXPLICIT_BZERO
266 explicit_bzero(buf, sizeof(buf));
267 #endif
268 return 0;
269 }
270
271 /* Copies the contents of a file. Returns -1 on read error, -2 on write error. */
272 int ul_copy_file(int from, int to)
273 {
274 #ifdef HAVE_SENDFILE
275 struct stat st;
276 ssize_t nw;
277
278 if (fstat(from, &st) == -1)
279 return UL_COPY_READ_ERROR;
280 if (!S_ISREG(st.st_mode))
281 return copy_file_simple(from, to);
282 if (sendfile_all(to, from, NULL, st.st_size) < 0)
283 return copy_file_simple(from, to);
284 /* ensure we either get an EOF or an error */
285 while ((nw = sendfile_all(to, from, NULL, 16*1024*1024)) != 0)
286 if (nw < 0)
287 return copy_file_simple(from, to);
288 return 0;
289 #else
290 return copy_file_simple(from, to);
291 #endif
292 }