Use authopen to acquire permissions to write to drives on macOS.
authorCarl Hetherington <cth@carlh.net>
Sat, 4 Apr 2020 22:24:32 +0000 (00:24 +0200)
committerCarl Hetherington <cth@carlh.net>
Sat, 4 Apr 2020 23:38:29 +0000 (01:38 +0200)
blockdev/linux/file_dev.c

index ef440283e2e6dd0f202e171399a6eec5c8053cbf..279724805eeddcf131eb9aceb775dbccfac820f6 100644 (file)
@@ -39,6 +39,8 @@
 #include <fcntl.h>
 #include <sys/disk.h>
 #include <unistd.h>
+#include <stdlib.h>
+#include <sys/socket.h>
 #endif
 
 /**@brief   Default filename.*/
@@ -68,14 +70,58 @@ EXT4_BLOCKDEV_STATIC_INSTANCE(file_dev, EXT4_FILEDEV_BSIZE, 0, file_dev_open,
 static int file_dev_open(struct ext4_blockdev *bdev)
 {
 #ifdef __APPLE__
-       /* The fseek/ftell approach to finding the device's size does not seem
-        * to work on macOS so do it this way instead.
+       /* We need to use authopen to open the device.  It asks the user for permission
+        * then give us an open fd over a socket.
         */
-       int dev = open(fname, O_RDONLY);
-       if (dev == -1) {
+
+       int pipe[2];
+       if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe) < 0) {
+               return EFAULT;
+       }
+
+       pid_t pid = fork();
+       if (pid < 0) {
+               return EFAULT;
+       }
+
+       if (pid == 0) {
+               close(pipe[0]);
+               dup2(pipe[1], STDOUT_FILENO);
+               execl("/usr/libexec/authopen", "/usr/libexec/authopen", "-stdoutpipe", "-w", "-a", fname, (char *) 0);
+               exit(-1);
+       }
+
+       close(pipe[1]);
+
+       int dev = -1;
+
+       size_t const data_buffer_size = sizeof(struct cmsghdr) + sizeof(int);
+       char data_buffer[data_buffer_size];
+
+       struct iovec io_vec[1];
+       io_vec[0].iov_base = data_buffer;
+       io_vec[0].iov_len = data_buffer_size;
+
+       socklen_t const cmsg_socket_size = CMSG_SPACE(sizeof(int));
+       char cmsg_socket[cmsg_socket_size];
+       struct msghdr message = { 0 };
+       message.msg_iov = io_vec;
+       message.msg_iovlen = 1;
+       message.msg_control = cmsg_socket;
+       message.msg_controllen = cmsg_socket_size;
+
+       if (recvmsg(pipe[0], &message, 0) <= 0) {
                return EIO;
        }
 
+       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) {
+               dev = *((int *) CMSG_DATA(cmsg_socket_header));
+       }
+
+       /* The fseek/ftell approach to finding the device's size does not seem
+        * to work on macOS so do it this way instead.
+        */
        uint64_t sectors = 0;
        if (ioctl(dev, DKIOCGETBLOCKCOUNT, &sectors) < 0) {
                close(dev);
@@ -88,10 +134,11 @@ static int file_dev_open(struct ext4_blockdev *bdev)
        }
 
        off_t size = sectors * sector_size;
-       close(dev);
-#endif
 
+       dev_file = fdopen(dev, "r+b");
+#else
        dev_file = fopen(fname, "r+b");
+#endif
 
        if (!dev_file)
                return EIO;