File indexing completed on 2024-05-19 05:32:29

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2022 MBition GmbH
0006     SPDX-FileContributor: Kai Uwe Broulik <kai_uwe.broulik@mbition.io>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "ramfile.h"
0012 #include "common.h" // for logging
0013 
0014 #include <QScopeGuard>
0015 
0016 #include <cerrno>
0017 #include <fcntl.h>
0018 #include <sys/mman.h>
0019 #include <unistd.h>
0020 #include <utility>
0021 
0022 namespace KWin
0023 {
0024 
0025 RamFile::RamFile(const char *name, const void *inData, int size, RamFile::Flags flags)
0026     : m_size(size)
0027     , m_flags(flags)
0028 {
0029     auto guard = qScopeGuard([this] {
0030         cleanup();
0031     });
0032 
0033 #if HAVE_MEMFD
0034     m_fd = FileDescriptor(memfd_create(name, MFD_CLOEXEC | MFD_ALLOW_SEALING));
0035     if (!m_fd.isValid()) {
0036         qCWarning(KWIN_CORE).nospace() << name << ": Can't create memfd: " << strerror(errno);
0037         return;
0038     }
0039 
0040     if (ftruncate(m_fd.get(), size) < 0) {
0041         qCWarning(KWIN_CORE).nospace() << name << ": Failed to ftruncate memfd: " << strerror(errno);
0042         return;
0043     }
0044 
0045     void *data = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd.get(), 0);
0046     if (data == MAP_FAILED) {
0047         qCWarning(KWIN_CORE).nospace() << name << ": mmap failed: " << strerror(errno);
0048         return;
0049     }
0050 #else
0051     m_tmp = std::make_unique<QTemporaryFile>();
0052     if (!m_tmp->open()) {
0053         qCWarning(KWIN_CORE).nospace() << name << ": Can't open temporary file";
0054         return;
0055     }
0056 
0057     if (unlink(m_tmp->fileName().toUtf8().constData()) != 0) {
0058         qCWarning(KWIN_CORE).nospace() << name << ": Failed to remove temporary file from filesystem: " << strerror(errno);
0059     }
0060 
0061     if (!m_tmp->resize(size)) {
0062         qCWarning(KWIN_CORE).nospace() << name << ": Failed to resize temporary file";
0063         return;
0064     }
0065 
0066     uchar *data = m_tmp->map(0, size);
0067     if (!data) {
0068         qCWarning(KWIN_CORE).nospace() << name << ": map failed";
0069         return;
0070     }
0071 #endif
0072 
0073     memcpy(data, inData, size);
0074 
0075 #if HAVE_MEMFD
0076     munmap(data, size);
0077 #else
0078     m_tmp->unmap(data);
0079 #endif
0080 
0081     int seals = F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL;
0082     if (flags.testFlag(RamFile::Flag::SealWrite)) {
0083         seals |= F_SEAL_WRITE;
0084     }
0085     // This can fail for QTemporaryFile based on the underlying file system.
0086     if (fcntl(fd(), F_ADD_SEALS, seals) != 0) {
0087         qCDebug(KWIN_CORE).nospace() << name << ": Failed to seal RamFile: " << strerror(errno);
0088     }
0089 
0090     guard.dismiss();
0091 }
0092 
0093 RamFile::RamFile(RamFile &&other) Q_DECL_NOEXCEPT
0094     : m_size(std::exchange(other.m_size, 0))
0095     , m_flags(std::exchange(other.m_flags, RamFile::Flags{}))
0096 #if HAVE_MEMFD
0097     , m_fd(std::exchange(other.m_fd, KWin::FileDescriptor{}))
0098 #else
0099     , m_tmp(std::exchange(other.m_tmp, {}))
0100 #endif
0101 {
0102 }
0103 
0104 RamFile &RamFile::operator=(RamFile &&other) Q_DECL_NOEXCEPT
0105 {
0106     cleanup();
0107     m_size = std::exchange(other.m_size, 0);
0108     m_flags = std::exchange(other.m_flags, RamFile::Flags{});
0109 #if HAVE_MEMFD
0110     m_fd = std::exchange(other.m_fd, KWin::FileDescriptor{});
0111 #else
0112     m_tmp = std::exchange(other.m_tmp, {});
0113 #endif
0114     return *this;
0115 }
0116 
0117 RamFile::~RamFile()
0118 {
0119     cleanup();
0120 }
0121 
0122 void RamFile::cleanup()
0123 {
0124 #if HAVE_MEMFD
0125     m_fd = KWin::FileDescriptor();
0126 #else
0127     m_tmp.reset();
0128 #endif
0129 }
0130 
0131 bool RamFile::isValid() const
0132 {
0133     return fd() != -1;
0134 }
0135 
0136 RamFile::Flags RamFile::effectiveFlags() const
0137 {
0138     Flags flags = {};
0139 
0140     const int seals = fcntl(fd(), F_GET_SEALS);
0141     if (seals > 0) {
0142         if (seals & F_SEAL_WRITE) {
0143             flags.setFlag(Flag::SealWrite);
0144         }
0145     }
0146 
0147     return flags;
0148 }
0149 
0150 int RamFile::fd() const
0151 {
0152 #if HAVE_MEMFD
0153     return m_fd.get();
0154 #else
0155     return m_tmp->handle();
0156 #endif
0157 }
0158 
0159 int RamFile::size() const
0160 {
0161     return m_size;
0162 }
0163 
0164 } // namespace KWin