X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fshared%2Futil.c;h=97ff320bc8320c82b3d2f44b72f08dfe31eea4f7;hp=8c1cf52c0a695c653b74b8a9cfd869a84e8d9178;hb=5f8cc96a0301c1177b11dd2e89370ef0b2ef577b;hpb=32802361561403cb6441198c82d9c499e0513863 diff --git a/src/shared/util.c b/src/shared/util.c index 8c1cf52c0..97ff320bc 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -521,7 +521,7 @@ int safe_atod(const char *s, double *ret_d) { static size_t strcspn_escaped(const char *s, const char *reject) { bool escaped = false; - size_t n; + int n; for (n=0; s[n]; n++) { if (escaped) @@ -531,6 +531,7 @@ static size_t strcspn_escaped(const char *s, const char *reject) { else if (strchr(reject, s[n])) break; } + /* if s ends in \, return index of previous char */ return n - escaped; } @@ -558,7 +559,7 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo *l = strcspn_escaped(current + 1, quotechars); if (current[*l + 1] == '\0' || (current[*l + 2] && !strchr(separator, current[*l + 2]))) { - /* right quote missing or garbage at the end*/ + /* right quote missing or garbage at the end */ *state = current; return NULL; } @@ -566,6 +567,11 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo *state = current++ + *l + 2; } else if (quoted) { *l = strcspn_escaped(current, separator); + if (current[*l] && !strchr(separator, current[*l])) { + /* unfinished escape */ + *state = current; + return NULL; + } *state = current + *l; } else { *l = strcspn(current, separator); @@ -1436,7 +1442,7 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre } case 0: - /* premature end of string.*/ + /* premature end of string. */ *(t++) = '\\'; goto finish; @@ -1504,7 +1510,7 @@ char *ascii_strlower(char *t) { return t; } -_pure_ static bool ignore_file_allow_backup(const char *filename) { +_pure_ static bool hidden_file_allow_backup(const char *filename) { assert(filename); return @@ -1521,13 +1527,13 @@ _pure_ static bool ignore_file_allow_backup(const char *filename) { endswith(filename, ".swp"); } -bool ignore_file(const char *filename) { +bool hidden_file(const char *filename) { assert(filename); if (endswith(filename, "~")) return true; - return ignore_file_allow_backup(filename); + return hidden_file_allow_backup(filename); } int fd_nonblock(int fd, bool nonblock) { @@ -1621,7 +1627,7 @@ int close_all_fds(const int except[], unsigned n_except) { while ((de = readdir(d))) { int fd = -1; - if (ignore_file(de->d_name)) + if (hidden_file(de->d_name)) continue; if (safe_atoi(de->d_name, &fd) < 0) @@ -2100,9 +2106,9 @@ int acquire_terminal( assert(notify >= 0); for (;;) { - uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX]; - ssize_t l; + uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event); struct inotify_event *e; + ssize_t l; if (timeout != USEC_INFINITY) { usec_t n; @@ -2123,9 +2129,8 @@ int acquire_terminal( } } - l = read(notify, inotify_buffer, sizeof(inotify_buffer)); + l = read(notify, buffer, sizeof(buffer)); if (l < 0) { - if (errno == EINTR || errno == EAGAIN) continue; @@ -2133,21 +2138,11 @@ int acquire_terminal( goto fail; } - e = (struct inotify_event*) inotify_buffer; - - while (l > 0) { - size_t step; - + FOREACH_INOTIFY_EVENT(e, buffer, l) { if (e->wd != wd || !(e->mask & IN_CLOSE)) { r = -EIO; goto fail; } - - step = sizeof(struct inotify_event) + e->len; - assert(step <= (size_t) l); - - e = (struct inotify_event*) ((uint8_t*) e + step); - l -= step; } break; @@ -2279,21 +2274,25 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { ssize_t k; k = read(fd, p, nbytes); - if (k < 0 && errno == EINTR) - continue; + if (k < 0) { + if (errno == EINTR) + continue; - if (k < 0 && errno == EAGAIN && do_poll) { + if (errno == EAGAIN && do_poll) { - /* We knowingly ignore any return value here, - * and expect that any error/EOF is reported - * via read() */ + /* We knowingly ignore any return value here, + * and expect that any error/EOF is reported + * via read() */ - fd_wait_for_event(fd, POLLIN, USEC_INFINITY); - continue; + fd_wait_for_event(fd, POLLIN, USEC_INFINITY); + continue; + } + + return n > 0 ? n : -errno; } - if (k <= 0) - return n > 0 ? n : (k < 0 ? -errno : 0); + if (k == 0) + return n; p += k; nbytes -= k; @@ -2303,39 +2302,42 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { return n; } -ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { +int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { const uint8_t *p = buf; - ssize_t n = 0; assert(fd >= 0); assert(buf); + errno = 0; + while (nbytes > 0) { ssize_t k; k = write(fd, p, nbytes); - if (k < 0 && errno == EINTR) - continue; + if (k < 0) { + if (errno == EINTR) + continue; - if (k < 0 && errno == EAGAIN && do_poll) { + if (errno == EAGAIN && do_poll) { + /* We knowingly ignore any return value here, + * and expect that any error/EOF is reported + * via write() */ - /* We knowingly ignore any return value here, - * and expect that any error/EOF is reported - * via write() */ + fd_wait_for_event(fd, POLLOUT, USEC_INFINITY); + continue; + } - fd_wait_for_event(fd, POLLOUT, USEC_INFINITY); - continue; + return -errno; } - if (k <= 0) - return n > 0 ? n : (k < 0 ? -errno : 0); + if (k == 0) /* Can't really happen */ + return -EIO; p += k; nbytes -= k; - n += k; } - return n; + return 0; } int parse_size(const char *t, off_t base, off_t *size) { @@ -2472,9 +2474,9 @@ int make_stdio(int fd) { assert(fd >= 0); - r = dup3(fd, STDIN_FILENO, 0); - s = dup3(fd, STDOUT_FILENO, 0); - t = dup3(fd, STDERR_FILENO, 0); + r = dup2(fd, STDIN_FILENO); + s = dup2(fd, STDOUT_FILENO); + t = dup2(fd, STDERR_FILENO); if (fd >= 3) safe_close(fd); @@ -2482,7 +2484,11 @@ int make_stdio(int fd) { if (r < 0 || s < 0 || t < 0) return -errno; - /* We rely here that the new fd has O_CLOEXEC not set */ + /* Explicitly unset O_CLOEXEC, since if fd was < 3, then + * dup2() was a NOP and the bit hence possibly set. */ + fd_cloexec(STDIN_FILENO, false); + fd_cloexec(STDOUT_FILENO, false); + fd_cloexec(STDERR_FILENO, false); return 0; } @@ -2525,7 +2531,7 @@ int dir_is_empty(const char *path) { if (!de) return 1; - if (!ignore_file(de->d_name)) + if (!hidden_file(de->d_name)) return 0; } } @@ -2779,23 +2785,36 @@ char *getusername_malloc(void) { return lookup_uid(getuid()); } -int getttyname_malloc(int fd, char **r) { - char path[PATH_MAX], *c; - int k; +int getttyname_malloc(int fd, char **ret) { + size_t l = 100; + int r; - assert(r); + assert(fd >= 0); + assert(ret); - k = ttyname_r(fd, path, sizeof(path)); - if (k > 0) - return -k; + for (;;) { + char path[l]; - char_array_0(path); + r = ttyname_r(fd, path, sizeof(path)); + if (r == 0) { + const char *p; + char *c; - c = strdup(startswith(path, "/dev/") ? path + 5 : path); - if (!c) - return -ENOMEM; + p = startswith(path, "/dev/"); + c = strdup(p ?: path); + if (!c) + return -ENOMEM; + + *ret = c; + return 0; + } + + if (r != ERANGE) + return -r; + + l *= 2; + } - *r = c; return 0; } @@ -3063,7 +3082,7 @@ static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bo fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); if (fd < 0) { - if (errno != ENOTDIR) + if (errno != ENOTDIR && errno != ELOOP) return -errno; if (!dangerous) { @@ -3670,7 +3689,7 @@ char *unquote(const char *s, const char* quotes) { /* This is rather stupid, simply removes the heading and * trailing quotes if there is one. Doesn't care about * escaping or anything. We should make this smarter one - * day...*/ + * day... */ l = strlen(s); if (l < 2) @@ -3990,7 +4009,7 @@ const char *default_term_for_tty(const char *tty) { bool dirent_is_file(const struct dirent *de) { assert(de); - if (ignore_file(de->d_name)) + if (hidden_file(de->d_name)) return false; if (de->d_type != DT_REG && @@ -4009,7 +4028,7 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { de->d_type != DT_UNKNOWN) return false; - if (ignore_file_allow_backup(de->d_name)) + if (hidden_file_allow_backup(de->d_name)) return false; return endswith(de->d_name, suffix); @@ -4251,6 +4270,23 @@ bool machine_name_is_valid(const char *s) { return true; } +bool image_name_is_valid(const char *s) { + if (!filename_is_valid(s)) + return false; + + if (string_has_cc(s, NULL)) + return false; + + if (!utf8_is_valid(s)) + return false; + + /* Temporary files for atomically creating new files */ + if (startswith(s, ".#")) + return false; + + return true; +} + int pipe_eof(int fd) { struct pollfd pollfd = { .fd = fd, @@ -4292,15 +4328,15 @@ int fd_wait_for_event(int fd, int event, usec_t t) { int fopen_temporary(const char *path, FILE **_f, char **_temp_path) { FILE *f; char *t; - int fd; + int r, fd; assert(path); assert(_f); assert(_temp_path); - t = tempfn_xxxxxx(path); - if (!t) - return -ENOMEM; + r = tempfn_xxxxxx(path, &t); + if (r < 0) + return r; fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC); if (fd < 0) { @@ -4411,13 +4447,14 @@ int vt_disallocate(const char *name) { int symlink_atomic(const char *from, const char *to) { _cleanup_free_ char *t = NULL; + int r; assert(from); assert(to); - t = tempfn_random(to); - if (!t) - return -ENOMEM; + r = tempfn_random(to, &t); + if (r < 0) + return r; if (symlink(from, t) < 0) return -errno; @@ -4432,12 +4469,13 @@ int symlink_atomic(const char *from, const char *to) { int mknod_atomic(const char *path, mode_t mode, dev_t dev) { _cleanup_free_ char *t = NULL; + int r; assert(path); - t = tempfn_random(path); - if (!t) - return -ENOMEM; + r = tempfn_random(path, &t); + if (r < 0) + return r; if (mknod(t, mode, dev) < 0) return -errno; @@ -4452,12 +4490,13 @@ int mknod_atomic(const char *path, mode_t mode, dev_t dev) { int mkfifo_atomic(const char *path, mode_t mode) { _cleanup_free_ char *t = NULL; + int r; assert(path); - t = tempfn_random(path); - if (!t) - return -ENOMEM; + r = tempfn_random(path, &t); + if (r < 0) + return r; if (mkfifo(t, mode) < 0) return -errno; @@ -5569,7 +5608,7 @@ int get_shell(char **_s) { return 0; } -bool filename_is_safe(const char *p) { +bool filename_is_valid(const char *p) { if (isempty(p)) return false; @@ -5900,7 +5939,7 @@ int on_ac_power(void) { if (!de) break; - if (ignore_file(de->d_name)) + if (hidden_file(de->d_name)) continue; device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY); @@ -6971,42 +7010,61 @@ int fflush_and_check(FILE *f) { return 0; } -char *tempfn_xxxxxx(const char *p) { +int tempfn_xxxxxx(const char *p, char **ret) { const char *fn; char *t; - size_t k; assert(p); + assert(ret); - t = new(char, strlen(p) + 1 + 6 + 1); - if (!t) - return NULL; + /* + * Turns this: + * /foo/bar/waldo + * + * Into this: + * /foo/bar/.#waldoXXXXXX + */ fn = basename(p); - k = fn - p; + if (!filename_is_valid(fn)) + return -EINVAL; + + t = new(char, strlen(p) + 2 + 6 + 1); + if (!t) + return -ENOMEM; - strcpy(stpcpy(stpcpy(mempcpy(t, p, k), "."), fn), "XXXXXX"); + strcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), "XXXXXX"); - return t; + *ret = path_kill_slashes(t); + return 0; } -char *tempfn_random(const char *p) { +int tempfn_random(const char *p, char **ret) { const char *fn; char *t, *x; uint64_t u; - size_t k; unsigned i; assert(p); + assert(ret); - t = new(char, strlen(p) + 1 + 16 + 1); - if (!t) - return NULL; + /* + * Turns this: + * /foo/bar/waldo + * + * Into this: + * /foo/bar/.#waldobaa2a261115984a9 + */ fn = basename(p); - k = fn - p; + if (!filename_is_valid(fn)) + return -EINVAL; - x = stpcpy(stpcpy(mempcpy(t, p, k), "."), fn); + t = new(char, strlen(p) + 2 + 16 + 1); + if (!t) + return -ENOMEM; + + x = stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn); u = random_u64(); for (i = 0; i < 16; i++) { @@ -7016,7 +7074,40 @@ char *tempfn_random(const char *p) { *x = 0; - return t; + *ret = path_kill_slashes(t); + return 0; +} + +int tempfn_random_child(const char *p, char **ret) { + char *t, *x; + uint64_t u; + unsigned i; + + assert(p); + assert(ret); + + /* Turns this: + * /foo/bar/waldo + * Into this: + * /foo/bar/waldo/.#3c2b6219aa75d7d0 + */ + + t = new(char, strlen(p) + 3 + 16 + 1); + if (!t) + return -ENOMEM; + + x = stpcpy(stpcpy(t, p), "/.#"); + + u = random_u64(); + for (i = 0; i < 16; i++) { + *(x++) = hexchar(u & 0xF); + u >>= 4; + } + + *x = 0; + + *ret = path_kill_slashes(t); + return 0; } /* make sure the hostname is not "localhost" */ @@ -7355,3 +7446,116 @@ int sethostname_idempotent(const char *s) { return 1; } + +int ptsname_malloc(int fd, char **ret) { + size_t l = 100; + + assert(fd >= 0); + assert(ret); + + for (;;) { + char *c; + + c = new(char, l); + if (!c) + return -ENOMEM; + + if (ptsname_r(fd, c, l) == 0) { + *ret = c; + return 0; + } + if (errno != ERANGE) { + free(c); + return -errno; + } + + free(c); + l *= 2; + } +} + +int openpt_in_namespace(pid_t pid, int flags) { + _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1; + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int))]; + } control = {}; + struct msghdr mh = { + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct cmsghdr *cmsg; + siginfo_t si; + pid_t child; + int r; + + assert(pid > 0); + + r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &rootfd); + if (r < 0) + return r; + + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return -errno; + + if (child == 0) { + int master; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd); + if (r < 0) + _exit(EXIT_FAILURE); + + master = posix_openpt(flags); + if (master < 0) + _exit(EXIT_FAILURE); + + cmsg = CMSG_FIRSTHDR(&mh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &master, sizeof(int)); + + mh.msg_controllen = cmsg->cmsg_len; + + if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + pair[1] = safe_close(pair[1]); + + r = wait_for_terminate(child, &si); + if (r < 0) + return r; + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return -EIO; + + if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0) + return -errno; + + for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { + int *fds; + unsigned n_fds; + + fds = (int*) CMSG_DATA(cmsg); + n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + + if (n_fds != 1) { + close_many(fds, n_fds); + return -EIO; + } + + return fds[0]; + } + + return -EIO; +}