/** * @brief Run authopen to acquire a file descriptor to the mmc device * * Like Linux, OSX does not allow processes to read and write devices as * normal users. OSX provides a utility called authopen that can ask the * user for permission to access a file. * * @param pathname the full path to the device * @return a descriptor or -1 on error */ static int authopen_fd(char * const pathname) { int sockets[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0) fwup_err(EXIT_FAILURE, "Can't create socketpair"); pid_t pid = fork(); if (pid == 0) { // child int devnull = open("/dev/null", O_RDWR); if (devnull < 0) fwup_err(EXIT_FAILURE, "/dev/null"); close(STDIN_FILENO); close(STDOUT_FILENO); if (dup2(devnull, STDIN_FILENO) < 0) fwup_err(EXIT_FAILURE, "dup2 devnull"); if (dup2(sockets[1], STDOUT_FILENO) < 0) fwup_err(EXIT_FAILURE, "dup2 pipe"); close(devnull); char permissions[16]; sprintf(permissions, "%d", O_RDWR); char * const exec_argv[] = { "/usr/libexec/authopen", "-stdoutpipe", "-o", permissions, pathname, 0 }; execvp(exec_argv[0], exec_argv); // Not supposed to reach here. fwup_err(EXIT_FAILURE, "execvp failed"); } else { // parent close(sockets[1]); // No writes to the pipe // Receive the authorized file descriptor from authopen char buffer[sizeof(struct cmsghdr) + sizeof(int)]; struct iovec io_vec[1]; io_vec[0].iov_base = buffer; io_vec[0].iov_len = sizeof(buffer); struct msghdr message; memset(&message, 0, sizeof(message)); message.msg_iov = io_vec; message.msg_iovlen = 1; char cmsg_socket[CMSG_SPACE(sizeof(int))]; message.msg_control = cmsg_socket; message.msg_controllen = sizeof(cmsg_socket); int fd = -1; for (;;) { ssize_t size = recvmsg(sockets[0], &message, 0); if (size > 0) { struct cmsghdr* cmsg_socket_header = CMSG_FIRSTHDR(&message); if (cmsg_socket_header && cmsg_socket_header->cmsg_level == SOL_SOCKET && cmsg_socket_header->cmsg_type == SCM_RIGHTS) { // Got file descriptor memcpy(&fd, CMSG_DATA(cmsg_socket_header), sizeof(fd)); break; } } else if (errno != EINTR) { // Any other cause break; } } // No more reads from the pipe. close(sockets[0]); return fd; } }
int mmc_umount_all(const char *mmc_device) { FILE *fp = fopen("/proc/mounts", "r"); if (!fp) fwup_err(EXIT_FAILURE, "/proc/mounts"); char *todo[64] = {0}; int todo_ix = 0; int ultimate_rc = 0; char line[FWUP_BLOCK_SIZE] = {0}; while (!feof(fp) && fgets(line, sizeof(line), fp)) { char devname[64]; char mountpoint[256]; if (sscanf(line, "%63s %255s", devname, mountpoint) != 2) continue; if (strstr(devname, mmc_device) == devname) { // mmc_device is a prefix of this device, i.e. mmc_device is /dev/sdc // and /dev/sdc1 is mounted. if (todo_ix == NUM_ELEMENTS(todo)) fwup_errx(EXIT_FAILURE, "Device mounted too many times"); // strings from /proc/mounts are escaped, so unescape them todo[todo_ix++] = unescape_string(mountpoint); } } fclose(fp); int mtab_exists = (access("/etc/mtab", F_OK) != -1); for (int i = 0; i < todo_ix; i++) { if (mtab_exists) { // If /etc/mtab, then call umount(8) so that // gets updated correctly. int rc = fork_exec("/bin/umount", todo[i]); if (rc != 0) { fwup_warnx("Error calling umount on '%s'", todo[i]); ultimate_rc = -1; } } else { // No /etc/mtab, so call the kernel directly. #if HAS_UMOUNT if (umount(todo[i]) < 0) { fwup_warnx("umount %s", todo[i]); ultimate_rc = -1; } #else // If no umount on this platform, warn, but don't // return failure. fwup_warnx("umount %s: not supported", todo[i]); #endif } } for (int i = 0; i < todo_ix; i++) free(todo[i]); return ultimate_rc; }