retro-imager/mac/macfile.cpp
Floris Bos 7e4f5c9045 Mac: open preferences for user if Imager is not allowed access
Normally the system will prompt the user to allow Imager access
to removable volumes.
Open preferences and ask user to allow Imager manually in a
corner case in which this does not occur.
2020-07-21 15:04:38 +02:00

126 lines
3.5 KiB
C++

/*
* SPDX-License-Identifier: Apache-2.0
* Copyright (C) 2020 Raspberry Pi (Trading) Limited
*/
#include "macfile.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <security/Authorization.h>
#include <QDebug>
MacFile::MacFile(QObject *parent)
: QFile(parent)
{
}
/* Prevent that Qt thinks /dev/rdisk does not permit seeks because it does not report size */
bool MacFile::isSequential() const
{
return false;
}
MacFile::authOpenResult MacFile::authOpen(const QByteArray &filename)
{
int fd = -1;
QByteArray right = "sys.openfile.readwrite."+filename;
AuthorizationItem item = {right, 0, nullptr, 0};
AuthorizationRights rights = {1, &item};
AuthorizationFlags flags = kAuthorizationFlagInteractionAllowed |
kAuthorizationFlagExtendRights |
kAuthorizationFlagPreAuthorize;
AuthorizationRef authRef;
if (AuthorizationCreate(&rights, nullptr, flags, &authRef) != 0)
return authOpenCancelled;
AuthorizationExternalForm externalForm;
if (AuthorizationMakeExternalForm(authRef, &externalForm) != 0)
{
AuthorizationFree(authRef, 0);
return authOpenError;
}
const char *cmd = "/usr/libexec/authopen";
QByteArray mode = QByteArray::number(O_RDWR);
int pipe[2];
int stdinpipe[2];
::socketpair(AF_UNIX, SOCK_STREAM, 0, pipe);
::pipe(stdinpipe);
pid_t pid = ::fork();
if (pid == 0)
{
// child
::close(pipe[0]);
::close(stdinpipe[1]);
::dup2(pipe[1], STDOUT_FILENO);
::dup2(stdinpipe[0], STDIN_FILENO);
::execl(cmd, cmd, "-stdoutpipe", "-extauth", "-o", mode.data(), filename.data(), NULL);
::exit(-1);
}
else
{
::close(pipe[1]);
::close(stdinpipe[0]);
::write(stdinpipe[1], externalForm.bytes, sizeof(externalForm.bytes));
::close(stdinpipe[1]);
const size_t bufSize = CMSG_SPACE(sizeof(int));
char buf[bufSize];
struct iovec io_vec[1];
io_vec[0].iov_base = buf;
io_vec[0].iov_len = bufSize;
const size_t cmsgSize = CMSG_SPACE(sizeof(int));
char cmsg[cmsgSize];
struct msghdr msg = {0};
msg.msg_iov = io_vec;
msg.msg_iovlen = 1;
msg.msg_control = cmsg;
msg.msg_controllen = cmsgSize;
ssize_t size;
do {
size = recvmsg(pipe[0], &msg, 0);
} while (size == -1 && errno == EINTR);
qDebug() << "RECEIVED SIZE:" << size;
if (size > 0) {
struct cmsghdr *chdr = CMSG_FIRSTHDR(&msg);
if (chdr && chdr->cmsg_type == SCM_RIGHTS) {
qDebug() << "SCMRIGHTS";
fd = *( (int*) (CMSG_DATA(chdr)) );
}
else
{
qDebug() << "NOT SCMRIGHTS";
}
}
pid_t wpid;
int status;
do {
wpid = ::waitpid(pid, &status, 0);
} while (wpid == -1 && errno == EINTR);
if (wpid == -1)
{
qDebug() << "waitpid() failed executing authopen";
return authOpenError;
}
if (WEXITSTATUS(status))
{
qDebug() << "authopen returned failure code" << WEXITSTATUS(status);
return authOpenError;
}
qDebug() << "fd received:" << fd;
}
AuthorizationFree(authRef, 0);
return open(fd, QIODevice::ReadWrite | QIODevice::ExistingOnly | QIODevice::Unbuffered, QFileDevice::AutoCloseHandle) ? authOpenSuccess : authOpenError;
}