]>
Commit | Line | Data |
---|---|---|
6b79eb38 | 1 | /* |
3836cd2d KZ |
2 | * This code is in the public domain; do with it what you wish. |
3 | * | |
6b79eb38 | 4 | * Copyright (C) 2012 Sami Kerola <kerolasa@iki.fi> |
3836cd2d | 5 | * Copyright (C) 2012-2020 Karel Zak <kzak@redhat.com> |
6b79eb38 | 6 | */ |
6b79eb38 SK |
7 | #include <stdio.h> |
8 | #include <stdlib.h> | |
a8b4e7ca | 9 | #include <sys/types.h> |
6b79eb38 SK |
10 | #include <sys/stat.h> |
11 | #include <unistd.h> | |
be92327e KZ |
12 | #include <sys/time.h> |
13 | #include <sys/resource.h> | |
b9dcd384 | 14 | #include <string.h> |
6b79eb38 SK |
15 | |
16 | #include "c.h" | |
f19a1655 | 17 | #include "all-io.h" |
7961acce | 18 | #include "fileutils.h" |
6b79eb38 SK |
19 | #include "pathnames.h" |
20 | ||
4d751c00 RM |
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 | ||
6b79eb38 SK |
50 | /* Create open temporary file in safe way. Please notice that the |
51 | * file permissions are -rw------- by default. */ | |
bde91c85 | 52 | int xmkstemp(char **tmpname, const char *dir, const char *prefix) |
6b79eb38 SK |
53 | { |
54 | char *localtmp; | |
bde91c85 | 55 | const char *tmpenv; |
6b79eb38 | 56 | mode_t old_mode; |
f272b32c | 57 | int fd, rc; |
6b79eb38 | 58 | |
3c4fed09 DR |
59 | /* Some use cases must be capable of being moved atomically |
60 | * with rename(2), which is the reason why dir is here. */ | |
bde91c85 KZ |
61 | tmpenv = dir ? dir : getenv("TMPDIR"); |
62 | if (!tmpenv) | |
63 | tmpenv = _PATH_TMP; | |
f272b32c | 64 | |
bde91c85 | 65 | rc = asprintf(&localtmp, "%s/%s.XXXXXX", tmpenv, prefix); |
f272b32c KZ |
66 | if (rc < 0) |
67 | return -1; | |
68 | ||
6b79eb38 | 69 | old_mode = umask(077); |
4d751c00 | 70 | fd = mkstemp_cloexec(localtmp); |
6b79eb38 | 71 | umask(old_mode); |
7961acce SK |
72 | if (fd == -1) { |
73 | free(localtmp); | |
74 | localtmp = NULL; | |
75 | } | |
6b79eb38 | 76 | *tmpname = localtmp; |
7961acce | 77 | return fd; |
6b79eb38 SK |
78 | } |
79 | ||
05c13b2b | 80 | #ifdef F_DUPFD_CLOEXEC |
8e86d93d | 81 | int dup_fd_cloexec(int oldfd, int lowfd) |
05c13b2b KZ |
82 | #else |
83 | int dup_fd_cloexec(int oldfd, int lowfd __attribute__((__unused__))) | |
84 | #endif | |
8e86d93d GJ |
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 | ||
be92327e KZ |
114 | /* |
115 | * portable getdtablesize() | |
116 | */ | |
a75700d8 | 117 | unsigned int get_fd_tabsize(void) |
be92327e KZ |
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 | ||
867df261 | 136 | void ul_close_all_fds(unsigned int first, unsigned int last) |
488f65fc KZ |
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; | |
a75700d8 SK |
145 | unsigned int fd; |
146 | int dfd; | |
488f65fc KZ |
147 | |
148 | errno = 0; | |
a75700d8 | 149 | fd = strtoul(d->d_name, &end, 10); |
488f65fc KZ |
150 | |
151 | if (errno || end == d->d_name || !end || *end) | |
152 | continue; | |
a75700d8 SK |
153 | dfd = dirfd(dir); |
154 | if (dfd < 0) | |
155 | continue; | |
156 | if ((unsigned int)dfd == fd) | |
488f65fc | 157 | continue; |
a75700d8 | 158 | if (fd < first || last < fd) |
488f65fc KZ |
159 | continue; |
160 | close(fd); | |
161 | } | |
162 | closedir(dir); | |
163 | } else { | |
a75700d8 | 164 | unsigned fd, tbsz = get_fd_tabsize(); |
488f65fc KZ |
165 | |
166 | for (fd = 0; fd < tbsz; fd++) { | |
a75700d8 | 167 | if (first <= fd && fd <= last) |
488f65fc KZ |
168 | close(fd); |
169 | } | |
170 | } | |
171 | } | |
172 | ||
e8f7acb0 | 173 | #ifdef TEST_PROGRAM_FILEUTILS |
488f65fc | 174 | int main(int argc, char *argv[]) |
6b79eb38 | 175 | { |
488f65fc | 176 | if (argc < 2) |
7951164c | 177 | errx(EXIT_FAILURE, "Usage %s --{mkstemp,close-fds,copy-file}", argv[0]); |
488f65fc KZ |
178 | |
179 | if (strcmp(argv[1], "--mkstemp") == 0) { | |
180 | FILE *f; | |
7ed57952 KZ |
181 | char *tmpname = NULL; |
182 | ||
488f65fc KZ |
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) { | |
488f65fc KZ |
189 | ignore_result( dup(STDIN_FILENO) ); |
190 | ignore_result( dup(STDIN_FILENO) ); | |
191 | ignore_result( dup(STDIN_FILENO) ); | |
192 | ||
a75700d8 | 193 | # ifdef HAVE_CLOSE_RANGE |
cae071ed | 194 | if (close_range(STDERR_FILENO + 1, ~0U, 0) < 0) |
a75700d8 | 195 | # endif |
cae071ed KZ |
196 | ul_close_all_fds(STDERR_FILENO + 1, ~0U); |
197 | ||
7951164c EC |
198 | } else if (strcmp(argv[1], "--copy-file") == 0) { |
199 | int ret = ul_copy_file(STDIN_FILENO, STDOUT_FILENO); | |
cabbf61f | 200 | if (ret == UL_COPY_READ_ERROR) |
7951164c | 201 | err(EXIT_FAILURE, "read"); |
cabbf61f | 202 | else if (ret == UL_COPY_WRITE_ERROR) |
7951164c | 203 | err(EXIT_FAILURE, "write"); |
488f65fc KZ |
204 | } |
205 | return EXIT_SUCCESS; | |
6b79eb38 SK |
206 | } |
207 | #endif | |
934530c7 KZ |
208 | |
209 | ||
867df261 | 210 | int ul_mkdir_p(const char *path, mode_t mode) |
934530c7 KZ |
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 | } | |
d4eaabc8 KZ |
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 | } | |
b9dcd384 | 256 | |
a8b4e7ca | 257 | static int copy_file_simple(int from, int to) |
b9dcd384 | 258 | { |
f19a1655 | 259 | ssize_t nr; |
e4530996 | 260 | char buf[BUFSIZ]; |
b9dcd384 | 261 | |
f19a1655 EC |
262 | while ((nr = read_all(from, buf, sizeof(buf))) > 0) |
263 | if (write_all(to, buf, nr) == -1) | |
cabbf61f | 264 | return UL_COPY_WRITE_ERROR; |
b9dcd384 | 265 | if (nr < 0) |
cabbf61f | 266 | return UL_COPY_READ_ERROR; |
b9dcd384 EC |
267 | #ifdef HAVE_EXPLICIT_BZERO |
268 | explicit_bzero(buf, sizeof(buf)); | |
269 | #endif | |
270 | return 0; | |
271 | } | |
a8b4e7ca EC |
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 | { | |
360cdaa6 | 276 | #ifdef HAVE_SENDFILE |
a8b4e7ca EC |
277 | struct stat st; |
278 | ssize_t nw; | |
a8b4e7ca EC |
279 | |
280 | if (fstat(from, &st) == -1) | |
cabbf61f | 281 | return UL_COPY_READ_ERROR; |
a8b4e7ca EC |
282 | if (!S_ISREG(st.st_mode)) |
283 | return copy_file_simple(from, to); | |
212bde6c EC |
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) | |
a8b4e7ca EC |
288 | if (nw < 0) |
289 | return copy_file_simple(from, to); | |
290 | return 0; | |
360cdaa6 EC |
291 | #else |
292 | return copy_file_simple(from, to); | |
293 | #endif | |
a8b4e7ca | 294 | } |
2d24f963 KZ |
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 | } |