1 /****************************************************************************
2  * bfs                                                                      *
3  * Copyright (C) 2016-2022 Tavian Barnes <tavianator@tavianator.com>        *
4  *                                                                          *
5  * Permission to use, copy, modify, and/or distribute this software for any *
6  * purpose with or without fee is hereby granted.                           *
7  *                                                                          *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF         *
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR  *
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   *
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN    *
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  *
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.           *
15  ****************************************************************************/
16 
17 #include "util.h"
18 #include "dstring.h"
19 #include <assert.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <langinfo.h>
23 #include <nl_types.h>
24 #include <regex.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32 
33 #if BFS_HAS_SYS_PARAM
34 #	include <sys/param.h>
35 #endif
36 
37 #if BFS_HAS_SYS_SYSMACROS
38 #	include <sys/sysmacros.h>
39 #elif BFS_HAS_SYS_MKDEV
40 #	include <sys/mkdev.h>
41 #endif
42 
43 #if BFS_HAS_UTIL
44 #	include <util.h>
45 #endif
46 
xreadlinkat(int fd,const char * path,size_t size)47 char *xreadlinkat(int fd, const char *path, size_t size) {
48 	ssize_t len;
49 	char *name = NULL;
50 
51 	if (size == 0) {
52 		size = 64;
53 	} else {
54 		++size; // NUL terminator
55 	}
56 
57 	while (true) {
58 		char *new_name = realloc(name, size);
59 		if (!new_name) {
60 			goto error;
61 		}
62 		name = new_name;
63 
64 		len = readlinkat(fd, path, name, size);
65 		if (len < 0) {
66 			goto error;
67 		} else if ((size_t)len >= size) {
68 			size *= 2;
69 		} else {
70 			break;
71 		}
72 	}
73 
74 	name[len] = '\0';
75 	return name;
76 
77 error:
78 	free(name);
79 	return NULL;
80 }
81 
dup_cloexec(int fd)82 int dup_cloexec(int fd) {
83 #ifdef F_DUPFD_CLOEXEC
84 	return fcntl(fd, F_DUPFD_CLOEXEC, 0);
85 #else
86 	int ret = dup(fd);
87 	if (ret < 0) {
88 		return -1;
89 	}
90 
91 	if (fcntl(ret, F_SETFD, FD_CLOEXEC) == -1) {
92 		close_quietly(ret);
93 		return -1;
94 	}
95 
96 	return ret;
97 #endif
98 }
99 
pipe_cloexec(int pipefd[2])100 int pipe_cloexec(int pipefd[2]) {
101 #if __linux__ || (BSD && !__APPLE__)
102 	return pipe2(pipefd, O_CLOEXEC);
103 #else
104 	if (pipe(pipefd) != 0) {
105 		return -1;
106 	}
107 
108 	if (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) == -1 || fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) == -1) {
109 		close_quietly(pipefd[1]);
110 		close_quietly(pipefd[0]);
111 		return -1;
112 	}
113 
114 	return 0;
115 #endif
116 }
117 
xregerror(int err,const regex_t * regex)118 char *xregerror(int err, const regex_t *regex) {
119 	size_t len = regerror(err, regex, NULL, 0);
120 	char *str = malloc(len);
121 	if (str) {
122 		regerror(err, regex, str, len);
123 	}
124 	return str;
125 }
126 
127 /** Get the single character describing the given file type. */
type_char(mode_t mode)128 static char type_char(mode_t mode) {
129 	switch (mode & S_IFMT) {
130 	case S_IFREG:
131 		return '-';
132 	case S_IFBLK:
133 		return 'b';
134 	case S_IFCHR:
135 		return 'c';
136 	case S_IFDIR:
137 		return 'd';
138 	case S_IFLNK:
139 		return 'l';
140 	case S_IFIFO:
141 		return 'p';
142 	case S_IFSOCK:
143 		return 's';
144 #ifdef S_IFDOOR
145 	case S_IFDOOR:
146 		return 'D';
147 #endif
148 #ifdef S_IFPORT
149 	case S_IFPORT:
150 		return 'P';
151 #endif
152 #ifdef S_IFWHT
153 	case S_IFWHT:
154 		return 'w';
155 #endif
156 	}
157 
158 	return '?';
159 }
160 
xstrmode(mode_t mode,char str[11])161 void xstrmode(mode_t mode, char str[11]) {
162 	strcpy(str, "----------");
163 
164 	str[0] = type_char(mode);
165 
166 	if (mode & 00400) {
167 		str[1] = 'r';
168 	}
169 	if (mode & 00200) {
170 		str[2] = 'w';
171 	}
172 	if ((mode & 04100) == 04000) {
173 		str[3] = 'S';
174 	} else if (mode & 04000) {
175 		str[3] = 's';
176 	} else if (mode & 00100) {
177 		str[3] = 'x';
178 	}
179 
180 	if (mode & 00040) {
181 		str[4] = 'r';
182 	}
183 	if (mode & 00020) {
184 		str[5] = 'w';
185 	}
186 	if ((mode & 02010) == 02000) {
187 		str[6] = 'S';
188 	} else if (mode & 02000) {
189 		str[6] = 's';
190 	} else if (mode & 00010) {
191 		str[6] = 'x';
192 	}
193 
194 	if (mode & 00004) {
195 		str[7] = 'r';
196 	}
197 	if (mode & 00002) {
198 		str[8] = 'w';
199 	}
200 	if ((mode & 01001) == 01000) {
201 		str[9] = 'T';
202 	} else if (mode & 01000) {
203 		str[9] = 't';
204 	} else if (mode & 00001) {
205 		str[9] = 'x';
206 	}
207 }
208 
xbasename(const char * path)209 const char *xbasename(const char *path) {
210 	const char *i;
211 
212 	// Skip trailing slashes
213 	for (i = path + strlen(path); i > path && i[-1] == '/'; --i);
214 
215 	// Find the beginning of the name
216 	for (; i > path && i[-1] != '/'; --i);
217 
218 	// Skip leading slashes
219 	for (; i[0] == '/' && i[1]; ++i);
220 
221 	return i;
222 }
223 
xfaccessat(int fd,const char * path,int amode)224 int xfaccessat(int fd, const char *path, int amode) {
225 	int ret = faccessat(fd, path, amode, 0);
226 
227 #ifdef AT_EACCESS
228 	// Some platforms, like Hurd, only support AT_EACCESS.  Other platforms,
229 	// like Android, don't support AT_EACCESS at all.
230 	if (ret != 0 && (errno == EINVAL || errno == ENOTSUP)) {
231 		ret = faccessat(fd, path, amode, AT_EACCESS);
232 	}
233 #endif
234 
235 	return ret;
236 }
237 
xstrtofflags(const char ** str,unsigned long long * set,unsigned long long * clear)238 int xstrtofflags(const char **str, unsigned long long *set, unsigned long long *clear) {
239 #if BSD && !__GNU__
240 	char *str_arg = (char *)*str;
241 	unsigned long set_arg = 0;
242 	unsigned long clear_arg = 0;
243 
244 #if __NetBSD__
245 	int ret = string_to_flags(&str_arg, &set_arg, &clear_arg);
246 #else
247 	int ret = strtofflags(&str_arg, &set_arg, &clear_arg);
248 #endif
249 
250 	*str = str_arg;
251 	*set = set_arg;
252 	*clear = clear_arg;
253 
254 	if (ret != 0) {
255 		errno = EINVAL;
256 	}
257 	return ret;
258 #else // !BSD
259 	errno = ENOTSUP;
260 	return -1;
261 #endif
262 }
263 
is_nonexistence_error(int error)264 bool is_nonexistence_error(int error) {
265 	return error == ENOENT || errno == ENOTDIR;
266 }
267 
268 /** Compile and execute a regular expression for xrpmatch(). */
xrpregex(nl_item item,const char * response)269 static int xrpregex(nl_item item, const char *response) {
270 	const char *pattern = nl_langinfo(item);
271 	if (!pattern) {
272 		return REG_BADPAT;
273 	}
274 
275 	regex_t regex;
276 	int ret = regcomp(&regex, pattern, REG_EXTENDED);
277 	if (ret != 0) {
278 		return ret;
279 	}
280 
281 	ret = regexec(&regex, response, 0, NULL, 0);
282 	regfree(&regex);
283 	return ret;
284 }
285 
286 /** Check if a response is affirmative or negative. */
xrpmatch(const char * response)287 static int xrpmatch(const char *response) {
288 	int ret = xrpregex(NOEXPR, response);
289 	if (ret == 0) {
290 		return 0;
291 	} else if (ret != REG_NOMATCH) {
292 		return -1;
293 	}
294 
295 	ret = xrpregex(YESEXPR, response);
296 	if (ret == 0) {
297 		return 1;
298 	} else if (ret != REG_NOMATCH) {
299 		return -1;
300 	}
301 
302 	// Failsafe: always handle y/n
303 	char c = response[0];
304 	if (c == 'n' || c == 'N') {
305 		return 0;
306 	} else if (c == 'y' || c == 'Y') {
307 		return 1;
308 	} else {
309 		return -1;
310 	}
311 }
312 
ynprompt(void)313 int ynprompt(void) {
314 	fflush(stderr);
315 
316 	char *line = xgetdelim(stdin, '\n');
317 	int ret = line ? xrpmatch(line) : -1;
318 	free(line);
319 	return ret;
320 }
321 
bfs_makedev(int ma,int mi)322 dev_t bfs_makedev(int ma, int mi) {
323 #ifdef makedev
324 	return makedev(ma, mi);
325 #else
326 	return (ma << 8) | mi;
327 #endif
328 }
329 
bfs_major(dev_t dev)330 int bfs_major(dev_t dev) {
331 #ifdef major
332 	return major(dev);
333 #else
334 	return dev >> 8;
335 #endif
336 }
337 
bfs_minor(dev_t dev)338 int bfs_minor(dev_t dev) {
339 #ifdef minor
340 	return minor(dev);
341 #else
342 	return dev & 0xFF;
343 #endif
344 }
345 
xread(int fd,void * buf,size_t nbytes)346 size_t xread(int fd, void *buf, size_t nbytes) {
347 	size_t count = 0;
348 
349 	while (count < nbytes) {
350 		ssize_t ret = read(fd, (char *)buf + count, nbytes - count);
351 		if (ret < 0) {
352 			if (errno == EINTR) {
353 				continue;
354 			} else {
355 				break;
356 			}
357 		} else if (ret == 0) {
358 			// EOF
359 			errno = 0;
360 			break;
361 		} else {
362 			count += ret;
363 		}
364 	}
365 
366 	return count;
367 }
368 
xwrite(int fd,const void * buf,size_t nbytes)369 size_t xwrite(int fd, const void *buf, size_t nbytes) {
370 	size_t count = 0;
371 
372 	while (count < nbytes) {
373 		ssize_t ret = write(fd, (const char *)buf + count, nbytes - count);
374 		if (ret < 0) {
375 			if (errno == EINTR) {
376 				continue;
377 			} else {
378 				break;
379 			}
380 		} else if (ret == 0) {
381 			// EOF?
382 			errno = 0;
383 			break;
384 		} else {
385 			count += ret;
386 		}
387 	}
388 
389 	return count;
390 }
391 
xconfstr(int name)392 char *xconfstr(int name) {
393 	size_t len = confstr(name, NULL, 0);
394 	if (len == 0) {
395 		return NULL;
396 	}
397 
398 	char *str = malloc(len);
399 	if (!str) {
400 		return NULL;
401 	}
402 
403 	if (confstr(name, str, len) != len) {
404 		free(str);
405 		return NULL;
406 	}
407 
408 	return str;
409 }
410 
xgetdelim(FILE * file,char delim)411 char *xgetdelim(FILE *file, char delim) {
412 	char *chunk = NULL;
413 	size_t n = 0;
414 	ssize_t len = getdelim(&chunk, &n, delim, file);
415 	if (len >= 0) {
416 		if (chunk[len] == delim) {
417 			chunk[len] = '\0';
418 		}
419 		return chunk;
420 	} else {
421 		free(chunk);
422 		if (!ferror(file)) {
423 			errno = 0;
424 		}
425 		return NULL;
426 	}
427 }
428 
xfopen(const char * path,int flags)429 FILE *xfopen(const char *path, int flags) {
430 	char mode[4];
431 
432 	switch (flags & O_ACCMODE) {
433 	case O_RDONLY:
434 		strcpy(mode, "rb");
435 		break;
436 	case O_WRONLY:
437 		strcpy(mode, "wb");
438 		break;
439 	case O_RDWR:
440 		strcpy(mode, "r+b");
441 		break;
442 	default:
443 		assert(!"Invalid access mode");
444 		errno = EINVAL;
445 		return NULL;
446 	}
447 
448 	if (flags & O_APPEND) {
449 		mode[0] = 'a';
450 	}
451 
452 	int fd;
453 	if (flags & O_CREAT) {
454 		fd = open(path, flags, 0666);
455 	} else {
456 		fd = open(path, flags);
457 	}
458 
459 	if (fd < 0) {
460 		return NULL;
461 	}
462 
463 	FILE *ret = fdopen(fd, mode);
464 	if (!ret) {
465 		close_quietly(fd);
466 		return NULL;
467 	}
468 
469 	return ret;
470 }
471 
xclose(int fd)472 int xclose(int fd) {
473 	int ret = close(fd);
474 	if (ret != 0) {
475 		assert(errno != EBADF);
476 	}
477 	return ret;
478 }
479 
close_quietly(int fd)480 void close_quietly(int fd) {
481 	int error = errno;
482 	xclose(fd);
483 	errno = error;
484 }
485