diff --git a/src/fd.cpp b/src/fd.cpp index f856c68..2a190d1 100644 --- a/src/fd.cpp +++ b/src/fd.cpp @@ -5,14 +5,12 @@ #include "main.hpp" -#include #include -#include -#include #include #include #include #include +#include /// Matches libzfs @@ -69,74 +67,64 @@ int write_exact(const char * path, const void * data, size_t len, mode_t mode) { /// TRY_MAIN rules, plus -1 for ENOENT static int get_key_material_helper(const char * helper, const char * whom, bool again, bool newkey, uint8_t *& buf, size_t & len_out) { -#if __linux__ || __FreeBSD__ - auto outfd = TRY_HELPER("create helper output", memfd_create(whom, MFD_CLOEXEC)); -#else - int outfd; - char fname[8 + 10 + 1 + 20 + 1]; // 4294967296, 18446744073709551616 - auto pid = getpid(); - for(uint64_t i = 0; i < UINT64_MAX; ++i) { - snprintf(fname, sizeof(fname), "/tzpfms:%" PRIu32 ":%" PRIu64 "", static_cast(pid), i); - if((outfd = shm_open(fname, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0000)) != -1 || errno != EEXIST) - break; + int pipes[2]; + TRY("create IPC pipe", pipe2(pipes, O_CLOEXEC)); + quickscope_wrapper pipes_r_deleter{[=] { close(pipes[0]); }}; + + if(auto pid = TRY_HELPER("create child", fork()); pid == 0) { // child + dup2(pipes[1], 1); + + char * msg; + if(asprintf(&msg, "%sassphrase for %s%s", newkey ? "New p" : "P", whom, again ? " (again)" : "") == -1) + msg = const_cast(whom); + execl("/bin/sh", "sh", "-c", helper, helper, msg, whom, newkey ? "new" : "", again ? "again" : "", nullptr); + + int exec_err = errno; + fprintf(stderr, "exec(/bin/sh): %s\n", strerror(errno)); + _exit(exec_err == ENOENT ? 127 : 126); } - TRY_HELPER("create helper output", outfd); - shm_unlink(fname); -#endif - quickscope_wrapper outfd_deleter{[=] { close(outfd); }}; + close(pipes[1]); - switch(auto pid = TRY_HELPER("create child", fork())) { - case 0: // child - dup2(outfd, 1); - char * msg; - if(asprintf(&msg, "%sassphrase for %s%s", newkey ? "New p" : "P", whom, again ? " (again)" : "") == -1) - msg = const_cast(whom); - execl("/bin/sh", "sh", "-c", helper, helper, msg, whom, newkey ? "new" : "", again ? "again" : "", nullptr); - fprintf(stderr, "exec(/bin/sh): %s\n", strerror(errno)); - _exit(127); + buf = nullptr; + len_out = 0; + for(size_t len_cap = 0;;) { // 99% of cases this is 1 allocation and 1 read + if(len_out == len_cap) { + len_cap += 120; + buf = TRY_PTR("allocate passphrase", static_cast(realloc(buf, len_cap))); + } + + ssize_t rd; + while((rd = read(pipes[0], buf + len_out, len_cap - len_out)) == -1 && errno == EINTR) + ; + TRY("read passphrase from helper", rd); + if(rd == 0) break; - default: // parent - int err, ret; - while((ret = waitpid(pid, &err, 0)) == -1 && errno == EINTR) - ; - TRY("wait for helper", ret); + len_out += rd; + } + if(len_out && buf[len_out - 1] == '\n') + --len_out; - if(WIFEXITED(err)) { - switch(WEXITSTATUS(err)) { - case 0: - struct stat sb; - fstat(outfd, &sb); - if(!sb.st_size) // unmmappable - return buf = nullptr, len_out = 0, 0; - else if(auto out = static_cast(mmap(nullptr, sb.st_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE, outfd, 0)); out != MAP_FAILED) { - quickscope_wrapper out_deleter{[=] { munmap(out, sb.st_size); }}; - len_out = sb.st_size; - if(out[len_out - 1] == '\n') // Trim ending newline, if any - --len_out; - if(!len_out) - buf = nullptr; - else { - if(!(buf = static_cast(malloc(len_out)))) - len_out = 0, (void)TRY("allocate passphrase", -1); - memcpy(buf, out, len_out); - } - return 0; - } else - TRY("read back passphrase", -1); - case 127: // ENOENT, error already written by shell or child - return -1; + int err, ret; + while((ret = wait(&err)) == -1 && errno == EINTR) + ; + TRY("wait for helper", ret); - default: - fprintf(stderr, "Helper '%s' failed with %d.\n", helper, WEXITSTATUS(err)); - return __LINE__; - } - } else { - fprintf(stderr, "Helper '%s' died to signal %d: %s.\n", helper, WTERMSIG(err), strsignal(WTERMSIG(err))); + if(WIFEXITED(err)) + switch(WEXITSTATUS(err)) { + case 0: + return 0; + case 127: // ENOENT, error already written by shell or child + return -1; + default: + fprintf(stderr, "Helper '%s' failed with %d.\n", helper, WEXITSTATUS(err)); return __LINE__; - } + } + else { + fprintf(stderr, "Helper '%s' died to signal %d: %s.\n", helper, WTERMSIG(err), strsignal(WTERMSIG(err))); + return __LINE__; } }