void dbglock_init(void) { if (dbglock_fd != -1) return; if (!dbg_enabled_p()) return; const char envvar[] = "FB_ADB_DBGLOCK_NAME"; /* No, we can't just inherit the file descriptor. Without a * separate file open, taking the lock won't block. */ const char* fn = getenv(envvar); if (fn == NULL) { const char* pfx = DEFAULT_TEMP_DIR; char* tmpfname = xaprintf("%s/fb-adb-dbg-XXXXXX", pfx); struct cleanup* cl = cleanup_allocate(); int tmpfd = mkostemp(tmpfname, O_CLOEXEC); if (tmpfd != -1) { setenv(envvar, tmpfname, 1); cleanup_commit(cl, cleanup_dbginit, tmpfname); dbglock_fd = tmpfd; } return; } dbglock_fd = open(fn, O_CLOEXEC | O_RDWR); }
struct xenviron* xenviron_create(const char* const* copy_from) { struct cleanup* cl = cleanup_allocate(); struct xenviron* xe = calloc(1, sizeof (char*)); if (xe == NULL) die_oom(); cleanup_commit(cl, xenviron_cleanup, xe); if (copy_from == NULL) { xe->env = calloc(1, xenviron_allocsz(0)); if (xe->env == NULL) die_oom(); } else { size_t nr = 0; for (const char* const* pos = copy_from; *pos; ++pos) nr += 1; xe->env = calloc(1, xenviron_allocsz(nr)); if (xe->env == NULL) die_oom(); for (size_t i = 0; i < nr; ++i) { xe->env[i] = strdup(copy_from[i]); if (xe->env[i] == NULL) die_oom(); } } return xe; }
void xpipe(int* read_end, int* write_end) { struct cleanup* cl[2]; cl[0] = cleanup_allocate(); cl[1] = cleanup_allocate(); int fd[2]; if (pipe2(fd, O_CLOEXEC) < 0) die_errno("pipe2"); assert_cloexec(fd[0]); assert_cloexec(fd[1]); cleanup_commit_close_fd(cl[0], fd[0]); cleanup_commit_close_fd(cl[1], fd[1]); *read_end = fd[0]; *write_end = fd[1]; }
static struct property_vector* property_vector_new(void) { struct cleanup* cl = cleanup_allocate(); struct property_vector* pv = xalloc(sizeof (*pv)); pv->capacity = 16; pv->size = 0; pv->props = malloc(sizeof (pv->props[0]) * pv->capacity); cleanup_commit(cl, property_vector_cleanup, pv); return pv; }
int xopen(const char* pathname, int flags, mode_t mode) { struct cleanup* cl = cleanup_allocate(); int fd = open(pathname, flags | O_CLOEXEC, mode); if (fd == -1) die_errno("open(\"%s\")", pathname); assert_cloexec(fd); cleanup_commit_close_fd(cl, fd); return fd; }
int xdup(int fd) { struct cleanup* cl = cleanup_allocate(); int newfd = fcntl(fd, F_DUPFD_CLOEXEC, fd); if (newfd == -1) die_errno("F_DUPFD_CLOEXEC(%d)", fd); assert_cloexec(newfd); cleanup_commit_close_fd(cl, newfd); return newfd; }
int try_xopen(const char* pathname, int flags, mode_t mode) { struct cleanup* cl = cleanup_allocate(); int fd = open(pathname, flags | O_CLOEXEC, mode); if (fd == -1) { cleanup_forget(cl); return -1; } assert_cloexec(fd); cleanup_commit_close_fd(cl, fd); return fd; }
void dbglock(void) { int saved_errno = errno; if (!dbg_enabled_p()) return; if (dbglock_fd == -1) return; if (dbglock_level++ == 0) flock(dbglock_fd, LOCK_EX); cleanup_commit(cleanup_allocate(), cleanup_dbglock, 0); errno = saved_errno; }
void dbglock(void) { int saved_errno = errno; if (!dbg_enabled_p()) return; if (dbglock_fd == -1) return; if (dbglock_level++ == 0) { WITH_IO_SIGNALS_ALLOWED(); flock(dbglock_fd, LOCK_EX); } cleanup_commit(cleanup_allocate(), cleanup_dbglock, 0); errno = saved_errno; }
struct fdrecorder* fdrecorder_new(void) { struct reslist* fdr_rl = reslist_create(); WITH_CURRENT_RESLIST(fdr_rl); struct cleanup* fdr_cl = cleanup_allocate(); struct fdrecorder* fdr = xcalloc(sizeof (*fdr)); fdr->owner_rl = fdr_rl; fdr->pipe[0] = fdr->pipe[1] = -1; cleanup_commit(fdr_cl, fdrecorder_cleanup, fdr); #if FDRECORDER_USE_PIPE if (pipe2(fdr->pipe, O_CLOEXEC) == -1) die_errno("pipe2"); #else xsocketpairnc(AF_UNIX, SOCK_STREAM, 0, fdr->pipe); xshutdown_if_not_broken(fdr->pipe[0], SHUT_WR); xshutdown_if_not_broken(fdr->pipe[1], SHUT_RD); #endif fdr->sigio_cookie = sigio_register(fdrecorder_sigio_callback, fdr); xF_SETFL(fdr->pipe[0], xF_GETFL(fdr->pipe[0]) | O_ASYNC | O_NONBLOCK); if (fcntl(fdr->pipe[0], F_SETOWN, getpid()) == -1) die_errno("F_SETOWN(%d)", fdr->pipe[0]); return fdr; }
static void compile_dex_with_dexopt(const char* dex_file_name, const char* odex_file_name) { SCOPED_RESLIST(rl); int dex_file = xopen(dex_file_name, O_RDONLY, 0); const char* odex_temp_filename = xaprintf( "%s.tmp.%s", odex_file_name, gen_hex_random(ENOUGH_ENTROPY)); cleanup_commit(cleanup_allocate(), cleanup_tmpfile, odex_temp_filename); int odex_temp_file = xopen(odex_temp_filename, O_RDWR | O_CREAT | O_EXCL, 0644); allow_inherit(dex_file); allow_inherit(odex_temp_file); struct child_start_info csi = { .io[0] = CHILD_IO_DEV_NULL, .io[1] = CHILD_IO_PIPE, .io[2] = CHILD_IO_DUP_TO_STDOUT, .exename = "dexopt", .argv = ARGV( "dexopt", "--zip", xaprintf("%d", dex_file), xaprintf("%d", odex_temp_file), dex_file_name, "v=ao=fm=y"), }; struct child* dexopt = child_start(&csi); struct growable_buffer output = slurp_fd_buf(dexopt->fd[1]->fd); int status = child_status_to_exit_code(child_wait(dexopt)); if (status != 0) die(EINVAL, "dexopt failed: %s", massage_output_buf(output)); xrename(odex_temp_filename, odex_file_name); } static void compile_dex(const char* dex_file_name, const char* odex_file_name) { if (api_level() < 21) compile_dex_with_dexopt(dex_file_name, odex_file_name); } int rdex_main(const struct cmd_rdex_info* info) { const char* dex_file_name = info->dexfile; struct stat dex_stat = xstat(dex_file_name); const char* odex_file_name = make_odex_name(dex_file_name); bool need_recompile = true; struct stat odex_stat; if (stat(odex_file_name, &odex_stat) == 0 && dex_stat.st_mtime <= odex_stat.st_mtime) { need_recompile = false; } (void) need_recompile; (void) odex_file_name; (void) compile_dex; if (need_recompile) compile_dex(dex_file_name, odex_file_name); if (setenv("CLASSPATH", dex_file_name, 1) == -1) die_errno("setenv"); if (info->classname[0] == '-') die(EINVAL, "class name cannot begin with '-'"); execvp("app_process", (char* const*) ARGV_CONCAT( ARGV("app_process", xdirname(dex_file_name), info->classname), info->args ?: empty_argv)); die_errno("execvp(\"app_process\", ..."); }
static void xfd_op(struct xfd_op_ctx* ctx) { bool old_die_on_quit = hack_die_on_quit; hack_die_on_quit = true; struct errinfo ei = { .want_msg = true, }; if (catch_error(xfd_op_1, ctx, &ei)) { deferred_die(ei.err, "%s", ei.msg); errno = ei.err < 0 ? EIO : ei.err; ctx->result = -1; } hack_die_on_quit = old_die_on_quit; } // xfdopen_read and xfdopen_write are called from inside stdio // machinery and must always return locally --- never longjmp! If we // die inside one of these functions, we "defer" the die and actually // longjmp at the next safe opportunity. static custom_stream_ssize_t xfdopen_read(void* cookie, char* buf, custom_stream_size_t size) { struct xfd_op_ctx ctx = { .op = XFD_OP_READ, .fd = xfdopen_fd(cookie), .buf = buf, .size = size }; xfd_op(&ctx); return ctx.result; } static custom_stream_ssize_t xfdopen_write(void* cookie, const char* buf, custom_stream_size_t size) { struct xfd_op_ctx ctx = { .op = XFD_OP_WRITE, .fd = xfdopen_fd(cookie), .buf = (void*) buf, .size = size }; xfd_op(&ctx); return ctx.result; } FILE* xfdopen(int fd, const char* mode) { struct cleanup* cl = cleanup_allocate(); FILE* f = NULL; #if defined(HAVE_FOPENCOOKIE) cookie_io_functions_t funcs = { .read = xfdopen_read, .write = xfdopen_write, .seek = NULL, .close = NULL, }; f = fopencookie((void*) (intptr_t) fd, mode, funcs); #elif defined(HAVE_FUNOPEN) f = funopen((void*) (intptr_t) fd, xfdopen_read, xfdopen_write, NULL, NULL); #else # error This platform has no custom stdio stream support #endif if (f == NULL) die_errno("fdopen"); cleanup_commit(cl, xfopen_cleanup, f); return f; } // Like xdup, but return a structure that allows the fd to be // individually closed. struct fdh* fdh_dup(int fd) { struct reslist* rl = reslist_create(); WITH_CURRENT_RESLIST(rl); struct fdh* fdh = xalloc(sizeof (*fdh)); fdh->rl = rl; fdh->fd = xdup(fd); return fdh; } void fdh_destroy(struct fdh* fdh) { reslist_destroy(fdh->rl); } int xF_GETFL(int fd) { int flags = fcntl(fd, F_GETFL); if (flags == -1) die_errno("fcntl(%d, F_GETFL)", fd); return flags; }
static void do_xfer_recv(const struct xfer_opts xfer_opts, const char* filename, const char* desired_basename, int from_peer) { struct xfer_msg statm = recv_xfer_msg(from_peer); if (statm.type != XFER_MSG_STAT) die(ECOMM, "expected stat msg"); struct cleanup* error_cl = cleanup_allocate(); struct stat st; const char* parent_directory = NULL; const char* rename_to = NULL; const char* write_mode = NULL; int dest_fd; if (stat(filename, &st) == 0) { if (S_ISDIR(st.st_mode)) { if (desired_basename == NULL) die(EISDIR, "\"%s\" is a directory", filename); parent_directory = filename; filename = xaprintf("%s/%s", parent_directory, desired_basename); } else if (S_ISREG(st.st_mode)) { if (st.st_nlink > 1) write_mode = "inplace"; } else { write_mode = "inplace"; } } if (parent_directory == NULL) parent_directory = xdirname(filename); if (write_mode == NULL) write_mode = xfer_opts.write_mode; bool atomic; bool automatic_mode = false; if (write_mode == NULL) { automatic_mode = true; atomic = true; } else if (strcmp(write_mode, "atomic") == 0) { atomic = true; } else if (strcmp(write_mode, "inplace") == 0) { atomic = false; } else { die(EINVAL, "unknown write mode \"%s\"", write_mode); } bool regular_file = true; bool preallocated = false; bool chmod_explicit = false; mode_t chmod_explicit_modes = 0; if (xfer_opts.preserve) { chmod_explicit = true; chmod_explicit_modes = statm.u.stat.ugo_bits; } if (xfer_opts.mode) { char* endptr = NULL; errno = 0; unsigned long omode = strtoul(xfer_opts.mode, &endptr, 8); if (errno != 0 || *endptr != '\0' || (omode &~ 0777) != 0) die(EINVAL, "invalid mode bits: %s", xfer_opts.mode); chmod_explicit = true; chmod_explicit_modes = (mode_t) omode; } mode_t creat_mode = (chmod_explicit_modes ? 0200 : 0666); if (atomic) { rename_to = filename; filename = xaprintf("%s.fb-adb-%s", filename, gen_hex_random(ENOUGH_ENTROPY)); dest_fd = try_xopen( filename, O_CREAT | O_WRONLY | O_EXCL, creat_mode); if (dest_fd == -1) { if (errno == EACCES && automatic_mode) { atomic = false; filename = rename_to; rename_to = NULL; } else { die_errno("open(\"%s\")", filename); } } } if (!atomic) { dest_fd = xopen(filename, O_WRONLY | O_CREAT | O_TRUNC, creat_mode); if (!S_ISREG(xfstat(dest_fd).st_mode)) regular_file = false; } if (regular_file) cleanup_commit(error_cl, unlink_cleanup, filename); if (regular_file && statm.u.stat.size > 0) preallocated = fallocate_if_supported( dest_fd, statm.u.stat.size); uint64_t total_written = copy_loop_posix_recv(from_peer, dest_fd); if (preallocated && total_written < statm.u.stat.size) xftruncate(dest_fd, total_written); if (xfer_opts.preserve) { struct timeval times[2] = { { statm.u.stat.atime, statm.u.stat.atime_ns / 1000 }, { statm.u.stat.mtime, statm.u.stat.mtime_ns / 1000 }, }; #ifdef HAVE_FUTIMES if (futimes(dest_fd, times) == -1) die_errno("futimes"); #else if (utimes(filename, times) == -1) die_errno("times"); #endif } if (chmod_explicit) if (fchmod(dest_fd, chmod_explicit_modes) == -1) die_errno("fchmod"); if (xfer_opts.sync) xfsync(dest_fd); if (rename_to) xrename(filename, rename_to); if (xfer_opts.sync) xfsync(xopen(parent_directory, O_DIRECTORY|O_RDONLY, 0)); cleanup_forget(error_cl); }