diff --git a/app/app.pro b/app/app.pro index ea16957a..442ff6c0 100644 --- a/app/app.pro +++ b/app/app.pro @@ -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\"; } diff --git a/app/macdrivemanager.cpp b/app/macdrivemanager.cpp index aa56eb88..e6a825b5 100644 --- a/app/macdrivemanager.cpp +++ b/app/macdrivemanager.cpp @@ -91,36 +91,28 @@ bool MacDrive::write(ReleaseVariant *data) { connect(m_child, static_cast(&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(); diff --git a/dist/mac/build.sh b/dist/mac/build.sh index f9e4cc48..64a95ec3 100644 --- a/dist/mac/build.sh +++ b/dist/mac/build.sh @@ -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" @@ -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 } diff --git a/helper/mac/writejob.cpp b/helper/mac/writejob.cpp index bcb93913..1823c379 100644 --- a/helper/mac/writejob.cpp +++ b/helper/mac/writejob.cpp @@ -1,6 +1,7 @@ /* * Fedora Media Writer * Copyright (C) 2016 Martin Bříza + * Copyright (C) 2020 Jan Grulich * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -28,9 +29,29 @@ #include #include +#include +#include +#include +#include +#include #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) { @@ -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(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(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) { @@ -80,11 +158,10 @@ 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"; @@ -92,12 +169,8 @@ bool WriteJob::writePlain() { 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); @@ -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; @@ -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) { @@ -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)) { diff --git a/helper/mac/writejob.h b/helper/mac/writejob.h index 00c20928..624ec046 100644 --- a/helper/mac/writejob.h +++ b/helper/mac/writejob.h @@ -1,6 +1,7 @@ /* * Fedora Media Writer * Copyright (C) 2016 Martin Bříza + * Copyright (C) 2020 Jan Grulich * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -21,6 +22,7 @@ #define WRITEJOB_H #include +#include #include #include #include @@ -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 @@ -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;