Skip to content

Commit

Permalink
Use authopen binary to get read/write access to USB disks
Browse files Browse the repository at this point in the history
This makes it work on Mac OSX Catalina

Initial code made by @MartinBriza
  • Loading branch information
grulja authored and MartinBriza committed Jul 3, 2020
1 parent c374d74 commit 3f7c34b
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 36 deletions.
4 changes: 4 additions & 0 deletions app/app.pro
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ macx {
QMAKE_INFO_PLIST = Info.plist
ICON = assets/icon/mediawriter.icns

MY_ENTITLEMENTS.name = CODE_SIGN_ENTITLEMENTS
MY_ENTITLEMENTS.value = "$$top_srcdir/app/Entitlements.plist"
QMAKE_MAC_XCODE_SETTINGS += MY_ENTITLEMENTS

QMAKE_POST_LINK += sed -i -e "s/@MEDIAWRITER_VERSION_SHORT@/$$MEDIAWRITER_VERSION_SHORT/g" \"./$${TARGET}.app/Contents/Info.plist\";
QMAKE_POST_LINK += sed -i -e "s/@MEDIAWRITER_VERSION@/$$MEDIAWRITER_VERSION/g" \"./$${TARGET}.app/Contents/Info.plist\";
}
Expand Down
24 changes: 8 additions & 16 deletions app/macdrivemanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,36 +91,28 @@ bool MacDrive::write(ReleaseVariant *data) {
connect(m_child, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &MacDrive::onFinished);
connect(m_child, &QProcess::readyRead, this, &MacDrive::onReadyRead);

m_child->setProgram("osascript");

QString command;
command.append("do shell script \"");
if (QFile::exists(qApp->applicationDirPath() + "/../../../../helper/mac/helper.app/Contents/MacOS/helper")) {
command.append(QString("'%1/../../../../helper/mac/helper.app/Contents/MacOS/helper'").arg(qApp->applicationDirPath()));
m_child->setProgram(QString("%1/../../../../helper/mac/helper.app/Contents/MacOS/helper").arg(qApp->applicationDirPath()));
}
else if (QFile::exists(qApp->applicationDirPath() + "/helper")) {
command.append(QString("'%1/helper'").arg(qApp->applicationDirPath()));
m_child->setProgram(QString("%1/helper").arg(qApp->applicationDirPath()));
}
else {
data->setErrorString(tr("Could not find the helper binary. Check your installation."));
setDelayedWrite(false);
return false;
}
command.append(" write ");
QStringList args;
args.append("write");
if (data->status() == ReleaseVariant::WRITING) {
command.append(QString("'%1'").arg(data->iso()));
args.append(QString("%1").arg(data->iso()));
}
else {
command.append(QString("'%1'").arg(data->temporaryPath()));
args.append(QString("%1").arg(data->temporaryPath()));
}
command.append(" ");
command.append(m_bsdDevice);
command.append("\" with administrator privileges without altering line endings");
args.append(m_bsdDevice);

QStringList args;
args << "-e";
args << command;
mCritical() << "The command is" << command;
mCritical() << "The command is" << m_child->program() << args;
m_child->setArguments(args);

m_child->start();
Expand Down
6 changes: 4 additions & 2 deletions dist/mac/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ set -e

PATH="/usr/local/opt/qt/bin:/usr/local/opt/git/bin:$PATH"

DEVELOPER_ID="Mac Developer: Martin Briza (N952V7G2F5)"
DEVELOPER_ID="Developer ID Application: Martin Briza (Z52EFCPL6D)"
QT_ROOT="/usr/local/opt/qt"
QMAKE="${QT_ROOT}/bin/qmake"
MACDEPLOYQT="${QT_ROOT}/bin/macdeployqt"
Expand Down Expand Up @@ -74,7 +74,9 @@ function sign() {
find app/Fedora\ Media\ Writer.app -name "*framework" | while read framework; do
codesign -s "$DEVELOPER_ID" --deep -v -f "$framework/Versions/Current/" -o runtime
done
codesign -s "$DEVELOPER_ID" --deep -v -f app/Fedora\ Media\ Writer.app/ -o runtime
codesign -s "$DEVELOPER_ID" --deep -v -f app/Fedora\ Media\ Writer.app/Contents/MacOS/Fedora\ Media\ Writer -o runtime --entitlements ../app/Entitlements.plist
codesign -s "$DEVELOPER_ID" --deep -v -f app/Fedora\ Media\ Writer.app/Contents/MacOS/helper -o runtime --entitlements ../app/Entitlements.plist
codesign -s "$DEVELOPER_ID" --deep -v -f app/Fedora\ Media\ Writer.app/ -o runtime --entitlements ../app/Entitlements.plist
popd >/dev/null
}

Expand Down
99 changes: 84 additions & 15 deletions helper/mac/writejob.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* Fedora Media Writer
* Copyright (C) 2016 Martin Bříza <[email protected]>
* Copyright (C) 2020 Jan Grulich <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
Expand Down Expand Up @@ -28,9 +29,29 @@
#include <QDebug>

#include <lzma.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

#include "isomd5/libcheckisomd5.h"

AuthOpenProcess::AuthOpenProcess(int parentSocket, int clientSocket, const QString &device, QObject *parent)
: QProcess(parent)
, m_parentSocket(parentSocket)
, m_clientSocket(clientSocket)
{
setProgram(QStringLiteral("/usr/libexec/authopen"));
setArguments({QStringLiteral("-stdoutpipe"), QStringLiteral("-o"), QString::number(O_RDWR), QStringLiteral("/dev/r") + device});
}

void AuthOpenProcess::setupChildProcess()
{
::close(m_parentSocket);
::dup2(m_clientSocket, STDOUT_FILENO);
}

WriteJob::WriteJob(const QString &what, const QString &where)
: QObject(nullptr), what(what), where(where)
{
Expand All @@ -55,20 +76,77 @@ int WriteJob::onMediaCheckAdvanced(long long offset, long long total) {
}

void WriteJob::work() {
int sockets[2];
int result = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
if (result == - 1) {
err << tr("Unable to allocate socket pair") << "\n";
err.flush();
return;
}

QProcess diskUtil;
diskUtil.setProgram("diskutil");
diskUtil.setArguments(QStringList() << "unmountDisk" << where);
diskUtil.start();
diskUtil.waitForFinished();

AuthOpenProcess p(sockets[0], sockets[1], where);
p.start(QIODevice::ReadOnly);

close(sockets[1]);

int fd = -1;
const size_t bufferSize = sizeof(struct cmsghdr) + sizeof(int);
char buffer[bufferSize];

struct iovec io_vec[1];
io_vec[0].iov_len = bufferSize;
io_vec[0].iov_base = buffer;

const socklen_t socketSize = static_cast<socklen_t>(CMSG_SPACE(sizeof(int)));
char cmsg_socket[socketSize];

struct msghdr message = { 0 };
message.msg_iov = io_vec;
message.msg_iovlen = 1;
message.msg_control = cmsg_socket;
message.msg_controllen = socketSize;

ssize_t size = recvmsg(sockets[0], &message, 0);

if (size > 0) {
struct cmsghdr *socketHeader = CMSG_FIRSTHDR(&message);
if (socketHeader && socketHeader->cmsg_level == SOL_SOCKET && socketHeader->cmsg_type == SCM_RIGHTS) {
fd = *reinterpret_cast<int*>(CMSG_DATA(socketHeader));
}
}

p.waitForFinished();

if (fd == -1) {
err << tr("Unable to open destination for writing") << "\n";
err.flush();
return;
}

out << "WRITE\n";
out.flush();

QFile target;
target.open(fd, QIODevice::ReadWrite, QFileDevice::AutoCloseHandle);

if (what.endsWith(".xz")) {
if (!writeCompressed()) {
if (!writeCompressed(target)) {
return;
}
}
else {
if (!writePlain()) {
if (!writePlain(target)) {
return;
}
}
check();

check(target);
}

void WriteJob::onFileChanged(const QString &path) {
Expand All @@ -80,24 +158,19 @@ void WriteJob::onFileChanged(const QString &path) {
work();
}

bool WriteJob::writePlain() {
bool WriteJob::writePlain(QFile &target) {
qint64 bytesTotal = 0;

QFile source(what);
QFile target("/dev/r"+where);
QByteArray buffer(BLOCK_SIZE, 0);

out << -1 << "\n";
out.flush();

QProcess diskUtil;
diskUtil.setProgram("diskutil");
diskUtil.setArguments(QStringList() << "unmountDisk" << where);
diskUtil.start();
diskUtil.waitForFinished();

source.open(QIODevice::ReadOnly);
target.open(QIODevice::WriteOnly);

while (source.isReadable() && !source.atEnd() && target.isWritable()) {
qint64 bytes = source.read(buffer.data(), BLOCK_SIZE);
Expand Down Expand Up @@ -129,7 +202,7 @@ bool WriteJob::writePlain() {
return true;
}

bool WriteJob::writeCompressed() {
bool WriteJob::writeCompressed(QFile &target) {
qint64 totalRead = 0;

lzma_stream strm = LZMA_STREAM_INIT;
Expand All @@ -140,8 +213,6 @@ bool WriteJob::writeCompressed() {

QFile source(what);
source.open(QIODevice::ReadOnly);
QFile target("/dev/r"+where);
target.open(QIODevice::WriteOnly);

ret = lzma_stream_decoder(&strm, MEDIAWRITER_LZMA_LIMIT, LZMA_CONCATENATED);
if (ret != LZMA_OK) {
Expand Down Expand Up @@ -210,9 +281,7 @@ bool WriteJob::writeCompressed() {
}
}

void WriteJob::check() {
QFile target("/dev/r"+where);
target.open(QIODevice::ReadOnly);
void WriteJob::check(QFile &target) {
out << "CHECK\n";
out.flush();
switch (mediaCheckFD(target.handle(), &WriteJob::staticOnMediaCheckAdvanced, this)) {
Expand Down
20 changes: 17 additions & 3 deletions helper/mac/writejob.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/*
* Fedora Media Writer
* Copyright (C) 2016 Martin Bříza <[email protected]>
* Copyright (C) 2020 Jan Grulich <[email protected]>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
Expand All @@ -21,6 +22,7 @@
#define WRITEJOB_H

#include <QObject>
#include <QFile>
#include <QTextStream>
#include <QProcess>
#include <QFileSystemWatcher>
Expand All @@ -30,6 +32,18 @@
# define MEDIAWRITER_LZMA_LIMIT (1024*1024*256)
#endif

class AuthOpenProcess : public QProcess {
Q_OBJECT
public:
AuthOpenProcess(int parentSocket, int clientSocket, const QString &device, QObject *parent = nullptr);

void setupChildProcess() override;

private:
int m_parentSocket;
int m_clientSocket;
};

class WriteJob : public QObject
{
Q_OBJECT
Expand All @@ -43,10 +57,10 @@ private slots:
void work();
void onFileChanged(const QString &path);

bool writePlain();
bool writeCompressed();
bool writePlain(QFile &target);
bool writeCompressed(QFile &target);

void check();
void check(QFile &target);
private:
QString what;
QString where;
Expand Down

0 comments on commit 3f7c34b

Please sign in to comment.