]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/fileutils.c
lib/env: fix function name remote_entry -> remove_entry
[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 #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 {
86 int fd, flags, errno_save;
87
88 #ifdef F_DUPFD_CLOEXEC
89 fd = fcntl(oldfd, F_DUPFD_CLOEXEC, lowfd);
90 if (fd >= 0)
91 return fd;
92 #endif
93
94 fd = dup(oldfd);
95 if (fd < 0)
96 return fd;
97
98 flags = fcntl(fd, F_GETFD);
99 if (flags < 0)
100 goto unwind;
101 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0)
102 goto unwind;
103
104 return fd;
105
106 unwind:
107 errno_save = errno;
108 close(fd);
109 errno = errno_save;
110
111 return -1;
112 }
113
114 /*
115 * portable getdtablesize()
116 */
117 unsigned int get_fd_tabsize(void)
118 {
119 int m;
120
121 #if defined(HAVE_GETDTABLESIZE)
122 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 return m;
134 }
135
136 void ul_close_all_fds(unsigned int first, unsigned int last)
137 {
138 struct dirent *d;
139 DIR *dir;
140
141 dir = opendir(_PATH_PROC_FDDIR);
142 if (dir) {
143 while ((d = xreaddir(dir))) {
144 char *end;
145 unsigned int fd;
146 int dfd;
147
148 errno = 0;
149 fd = strtoul(d->d_name, &end, 10);
150
151 if (errno || end == d->d_name || !end || *end)
152 continue;
153 dfd = dirfd(dir);
154 if (dfd < 0)
155 continue;
156 if ((unsigned int)dfd == fd)
157 continue;
158 if (fd < first || last < fd)
159 continue;
160 close(fd);
161 }
162 closedir(dir);
163 } else {
164 unsigned fd, tbsz = get_fd_tabsize();
165
166 for (fd = 0; fd < tbsz; fd++) {
167 if (first <= fd && fd <= last)
168 close(fd);
169 }
170 }
171 }
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 {
212 char *p, *dir;
213 int rc = 0;
214
215 if (!path || !*path)
216 return -EINVAL;
217
218 dir = p = strdup(path);
219 if (!dir)
220 return -ENOMEM;
221
222 if (*p == '/')
223 p++;
224
225 while (p && *p) {
226 char *e = strchr(p, '/');
227 if (e)
228 *e = '\0';
229 if (*p) {
230 rc = mkdir(dir, mode);
231 if (rc && errno != EEXIST)
232 break;
233 rc = 0;
234 }
235 if (!e)
236 break;
237 *e = '/';
238 p = e + 1;
239 }
240
241 free(dir);
242 return rc;
243 }
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 {
249 char *p = path ? strrchr(path, '/') : NULL;
250
251 if (!p)
252 return NULL;
253 *p = '\0';
254 return p + 1;
255 }
256
257 static int copy_file_simple(int from, int to)
258 {
259 ssize_t nr;
260 char buf[BUFSIZ];
261
262 while ((nr = read_all(from, buf, sizeof(buf))) > 0)
263 if (write_all(to, buf, nr) == -1)
264 return UL_COPY_WRITE_ERROR;
265 if (nr < 0)
266 return UL_COPY_READ_ERROR;
267 #ifdef HAVE_EXPLICIT_BZERO
268 explicit_bzero(buf, sizeof(buf));
269 #endif
270 return 0;
271 }
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 {
276 #ifdef HAVE_SENDFILE
277 struct stat st;
278 ssize_t nw;
279
280 if (fstat(from, &st) == -1)
281 return UL_COPY_READ_ERROR;
282 if (!S_ISREG(st.st_mode))
283 return copy_file_simple(from, to);
284 if (sendfile_all(to, from, NULL, st.st_size) < 0)
285 return copy_file_simple(from, to);
286 /* ensure we either get an EOF or an error */
287 while ((nw = sendfile_all(to, from, NULL, 16*1024*1024)) != 0)
288 if (nw < 0)
289 return copy_file_simple(from, to);
290 return 0;
291 #else
292 return copy_file_simple(from, to);
293 #endif
294 }
295
296 int ul_reopen(int fd, int flags)
297 {
298 ssize_t ssz;
299 char buf[PATH_MAX];
300 char fdpath[ sizeof(_PATH_PROC_FDDIR) + sizeof(stringify_value(INT_MAX)) ];
301
302 snprintf(fdpath, sizeof(fdpath), _PATH_PROC_FDDIR "/%d", fd);
303
304 ssz = readlink(fdpath, buf, sizeof(buf) - 1);
305 if (ssz < 0)
306 return -errno;
307
308 assert(ssz > 0);
309
310 buf[ssz] = '\0';
311
312 return open(buf, flags);
313 }