Skip to content

Commit 3f7c34b

Browse files
gruljaMartinBriza
authored andcommitted
Use authopen binary to get read/write access to USB disks
This makes it work on Mac OSX Catalina Initial code made by @MartinBriza
1 parent c374d74 commit 3f7c34b

File tree

5 files changed

+117
-36
lines changed

5 files changed

+117
-36
lines changed

app/app.pro

+4
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ macx {
9191
QMAKE_INFO_PLIST = Info.plist
9292
ICON = assets/icon/mediawriter.icns
9393

94+
MY_ENTITLEMENTS.name = CODE_SIGN_ENTITLEMENTS
95+
MY_ENTITLEMENTS.value = "$$top_srcdir/app/Entitlements.plist"
96+
QMAKE_MAC_XCODE_SETTINGS += MY_ENTITLEMENTS
97+
9498
QMAKE_POST_LINK += sed -i -e "s/@MEDIAWRITER_VERSION_SHORT@/$$MEDIAWRITER_VERSION_SHORT/g" \"./$${TARGET}.app/Contents/Info.plist\";
9599
QMAKE_POST_LINK += sed -i -e "s/@MEDIAWRITER_VERSION@/$$MEDIAWRITER_VERSION/g" \"./$${TARGET}.app/Contents/Info.plist\";
96100
}

app/macdrivemanager.cpp

+8-16
Original file line numberDiff line numberDiff line change
@@ -91,36 +91,28 @@ bool MacDrive::write(ReleaseVariant *data) {
9191
connect(m_child, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this, &MacDrive::onFinished);
9292
connect(m_child, &QProcess::readyRead, this, &MacDrive::onReadyRead);
9393

94-
m_child->setProgram("osascript");
95-
96-
QString command;
97-
command.append("do shell script \"");
9894
if (QFile::exists(qApp->applicationDirPath() + "/../../../../helper/mac/helper.app/Contents/MacOS/helper")) {
99-
command.append(QString("'%1/../../../../helper/mac/helper.app/Contents/MacOS/helper'").arg(qApp->applicationDirPath()));
95+
m_child->setProgram(QString("%1/../../../../helper/mac/helper.app/Contents/MacOS/helper").arg(qApp->applicationDirPath()));
10096
}
10197
else if (QFile::exists(qApp->applicationDirPath() + "/helper")) {
102-
command.append(QString("'%1/helper'").arg(qApp->applicationDirPath()));
98+
m_child->setProgram(QString("%1/helper").arg(qApp->applicationDirPath()));
10399
}
104100
else {
105101
data->setErrorString(tr("Could not find the helper binary. Check your installation."));
106102
setDelayedWrite(false);
107103
return false;
108104
}
109-
command.append(" write ");
105+
QStringList args;
106+
args.append("write");
110107
if (data->status() == ReleaseVariant::WRITING) {
111-
command.append(QString("'%1'").arg(data->iso()));
108+
args.append(QString("%1").arg(data->iso()));
112109
}
113110
else {
114-
command.append(QString("'%1'").arg(data->temporaryPath()));
111+
args.append(QString("%1").arg(data->temporaryPath()));
115112
}
116-
command.append(" ");
117-
command.append(m_bsdDevice);
118-
command.append("\" with administrator privileges without altering line endings");
113+
args.append(m_bsdDevice);
119114

120-
QStringList args;
121-
args << "-e";
122-
args << command;
123-
mCritical() << "The command is" << command;
115+
mCritical() << "The command is" << m_child->program() << args;
124116
m_child->setArguments(args);
125117

126118
m_child->start();

dist/mac/build.sh

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ set -e
55

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

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

helper/mac/writejob.cpp

+84-15
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Fedora Media Writer
33
* Copyright (C) 2016 Martin Bříza <[email protected]>
4+
* Copyright (C) 2020 Jan Grulich <[email protected]>
45
*
56
* This program is free software; you can redistribute it and/or
67
* modify it under the terms of the GNU General Public License
@@ -28,9 +29,29 @@
2829
#include <QDebug>
2930

3031
#include <lzma.h>
32+
#include <unistd.h>
33+
#include <sys/socket.h>
34+
#include <sys/stat.h>
35+
#include <sys/types.h>
36+
#include <fcntl.h>
3137

3238
#include "isomd5/libcheckisomd5.h"
3339

40+
AuthOpenProcess::AuthOpenProcess(int parentSocket, int clientSocket, const QString &device, QObject *parent)
41+
: QProcess(parent)
42+
, m_parentSocket(parentSocket)
43+
, m_clientSocket(clientSocket)
44+
{
45+
setProgram(QStringLiteral("/usr/libexec/authopen"));
46+
setArguments({QStringLiteral("-stdoutpipe"), QStringLiteral("-o"), QString::number(O_RDWR), QStringLiteral("/dev/r") + device});
47+
}
48+
49+
void AuthOpenProcess::setupChildProcess()
50+
{
51+
::close(m_parentSocket);
52+
::dup2(m_clientSocket, STDOUT_FILENO);
53+
}
54+
3455
WriteJob::WriteJob(const QString &what, const QString &where)
3556
: QObject(nullptr), what(what), where(where)
3657
{
@@ -55,20 +76,77 @@ int WriteJob::onMediaCheckAdvanced(long long offset, long long total) {
5576
}
5677

5778
void WriteJob::work() {
79+
int sockets[2];
80+
int result = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
81+
if (result == - 1) {
82+
err << tr("Unable to allocate socket pair") << "\n";
83+
err.flush();
84+
return;
85+
}
86+
87+
QProcess diskUtil;
88+
diskUtil.setProgram("diskutil");
89+
diskUtil.setArguments(QStringList() << "unmountDisk" << where);
90+
diskUtil.start();
91+
diskUtil.waitForFinished();
92+
93+
AuthOpenProcess p(sockets[0], sockets[1], where);
94+
p.start(QIODevice::ReadOnly);
95+
96+
close(sockets[1]);
97+
98+
int fd = -1;
99+
const size_t bufferSize = sizeof(struct cmsghdr) + sizeof(int);
100+
char buffer[bufferSize];
101+
102+
struct iovec io_vec[1];
103+
io_vec[0].iov_len = bufferSize;
104+
io_vec[0].iov_base = buffer;
105+
106+
const socklen_t socketSize = static_cast<socklen_t>(CMSG_SPACE(sizeof(int)));
107+
char cmsg_socket[socketSize];
108+
109+
struct msghdr message = { 0 };
110+
message.msg_iov = io_vec;
111+
message.msg_iovlen = 1;
112+
message.msg_control = cmsg_socket;
113+
message.msg_controllen = socketSize;
114+
115+
ssize_t size = recvmsg(sockets[0], &message, 0);
116+
117+
if (size > 0) {
118+
struct cmsghdr *socketHeader = CMSG_FIRSTHDR(&message);
119+
if (socketHeader && socketHeader->cmsg_level == SOL_SOCKET && socketHeader->cmsg_type == SCM_RIGHTS) {
120+
fd = *reinterpret_cast<int*>(CMSG_DATA(socketHeader));
121+
}
122+
}
123+
124+
p.waitForFinished();
125+
126+
if (fd == -1) {
127+
err << tr("Unable to open destination for writing") << "\n";
128+
err.flush();
129+
return;
130+
}
131+
58132
out << "WRITE\n";
59133
out.flush();
60134

135+
QFile target;
136+
target.open(fd, QIODevice::ReadWrite, QFileDevice::AutoCloseHandle);
137+
61138
if (what.endsWith(".xz")) {
62-
if (!writeCompressed()) {
139+
if (!writeCompressed(target)) {
63140
return;
64141
}
65142
}
66143
else {
67-
if (!writePlain()) {
144+
if (!writePlain(target)) {
68145
return;
69146
}
70147
}
71-
check();
148+
149+
check(target);
72150
}
73151

74152
void WriteJob::onFileChanged(const QString &path) {
@@ -80,24 +158,19 @@ void WriteJob::onFileChanged(const QString &path) {
80158
work();
81159
}
82160

83-
bool WriteJob::writePlain() {
161+
bool WriteJob::writePlain(QFile &target) {
84162
qint64 bytesTotal = 0;
85163

86164
QFile source(what);
87-
QFile target("/dev/r"+where);
88165
QByteArray buffer(BLOCK_SIZE, 0);
89166

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

93170
QProcess diskUtil;
94171
diskUtil.setProgram("diskutil");
95-
diskUtil.setArguments(QStringList() << "unmountDisk" << where);
96-
diskUtil.start();
97-
diskUtil.waitForFinished();
98172

99173
source.open(QIODevice::ReadOnly);
100-
target.open(QIODevice::WriteOnly);
101174

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

132-
bool WriteJob::writeCompressed() {
205+
bool WriteJob::writeCompressed(QFile &target) {
133206
qint64 totalRead = 0;
134207

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

141214
QFile source(what);
142215
source.open(QIODevice::ReadOnly);
143-
QFile target("/dev/r"+where);
144-
target.open(QIODevice::WriteOnly);
145216

146217
ret = lzma_stream_decoder(&strm, MEDIAWRITER_LZMA_LIMIT, LZMA_CONCATENATED);
147218
if (ret != LZMA_OK) {
@@ -210,9 +281,7 @@ bool WriteJob::writeCompressed() {
210281
}
211282
}
212283

213-
void WriteJob::check() {
214-
QFile target("/dev/r"+where);
215-
target.open(QIODevice::ReadOnly);
284+
void WriteJob::check(QFile &target) {
216285
out << "CHECK\n";
217286
out.flush();
218287
switch (mediaCheckFD(target.handle(), &WriteJob::staticOnMediaCheckAdvanced, this)) {

helper/mac/writejob.h

+17-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* Fedora Media Writer
33
* Copyright (C) 2016 Martin Bříza <[email protected]>
4+
* Copyright (C) 2020 Jan Grulich <[email protected]>
45
*
56
* This program is free software; you can redistribute it and/or
67
* modify it under the terms of the GNU General Public License
@@ -21,6 +22,7 @@
2122
#define WRITEJOB_H
2223

2324
#include <QObject>
25+
#include <QFile>
2426
#include <QTextStream>
2527
#include <QProcess>
2628
#include <QFileSystemWatcher>
@@ -30,6 +32,18 @@
3032
# define MEDIAWRITER_LZMA_LIMIT (1024*1024*256)
3133
#endif
3234

35+
class AuthOpenProcess : public QProcess {
36+
Q_OBJECT
37+
public:
38+
AuthOpenProcess(int parentSocket, int clientSocket, const QString &device, QObject *parent = nullptr);
39+
40+
void setupChildProcess() override;
41+
42+
private:
43+
int m_parentSocket;
44+
int m_clientSocket;
45+
};
46+
3347
class WriteJob : public QObject
3448
{
3549
Q_OBJECT
@@ -43,10 +57,10 @@ private slots:
4357
void work();
4458
void onFileChanged(const QString &path);
4559

46-
bool writePlain();
47-
bool writeCompressed();
60+
bool writePlain(QFile &target);
61+
bool writeCompressed(QFile &target);
4862

49-
void check();
63+
void check(QFile &target);
5064
private:
5165
QString what;
5266
QString where;

0 commit comments

Comments
 (0)