KWin
Loading...
Searching...
No Matches
ramfile.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2022 MBition GmbH
6 SPDX-FileContributor: Kai Uwe Broulik <kai_uwe.broulik@mbition.io>
7
8 SPDX-License-Identifier: GPL-2.0-or-later
9*/
10
11#include "ramfile.h"
12#include "common.h" // for logging
13
14#include <QScopeGuard>
15
16#include <cerrno>
17#include <fcntl.h>
18#include <sys/mman.h>
19#include <unistd.h>
20#include <utility>
21
22namespace KWin
23{
24
25RamFile::RamFile(const char *name, const void *inData, int size, RamFile::Flags flags)
26 : m_size(size)
27 , m_flags(flags)
28{
29 auto guard = qScopeGuard([this] {
30 cleanup();
31 });
32
33#if HAVE_MEMFD
34 m_fd = FileDescriptor(memfd_create(name, MFD_CLOEXEC | MFD_ALLOW_SEALING));
35 if (!m_fd.isValid()) {
36 qCWarning(KWIN_CORE).nospace() << name << ": Can't create memfd: " << strerror(errno);
37 return;
38 }
39
40 if (ftruncate(m_fd.get(), size) < 0) {
41 qCWarning(KWIN_CORE).nospace() << name << ": Failed to ftruncate memfd: " << strerror(errno);
42 return;
43 }
44
45 void *data = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd.get(), 0);
46 if (data == MAP_FAILED) {
47 qCWarning(KWIN_CORE).nospace() << name << ": mmap failed: " << strerror(errno);
48 return;
49 }
50#else
51 m_tmp = std::make_unique<QTemporaryFile>();
52 if (!m_tmp->open()) {
53 qCWarning(KWIN_CORE).nospace() << name << ": Can't open temporary file";
54 return;
55 }
56
57 if (unlink(m_tmp->fileName().toUtf8().constData()) != 0) {
58 qCWarning(KWIN_CORE).nospace() << name << ": Failed to remove temporary file from filesystem: " << strerror(errno);
59 }
60
61 if (!m_tmp->resize(size)) {
62 qCWarning(KWIN_CORE).nospace() << name << ": Failed to resize temporary file";
63 return;
64 }
65
66 uchar *data = m_tmp->map(0, size);
67 if (!data) {
68 qCWarning(KWIN_CORE).nospace() << name << ": map failed";
69 return;
70 }
71#endif
72
73 memcpy(data, inData, size);
74
75#if HAVE_MEMFD
76 munmap(data, size);
77#else
78 m_tmp->unmap(data);
79#endif
80
81 int seals = F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL;
82 if (flags.testFlag(RamFile::Flag::SealWrite)) {
83 seals |= F_SEAL_WRITE;
84 }
85 // This can fail for QTemporaryFile based on the underlying file system.
86 if (fcntl(fd(), F_ADD_SEALS, seals) != 0) {
87 qCDebug(KWIN_CORE).nospace() << name << ": Failed to seal RamFile: " << strerror(errno);
88 }
89
90 guard.dismiss();
91}
92
93RamFile::RamFile(RamFile &&other) Q_DECL_NOEXCEPT
94 : m_size(std::exchange(other.m_size, 0))
95 , m_flags(std::exchange(other.m_flags, RamFile::Flags{}))
96#if HAVE_MEMFD
97 , m_fd(std::exchange(other.m_fd, KWin::FileDescriptor{}))
98#else
99 , m_tmp(std::exchange(other.m_tmp, {}))
100#endif
101{
102}
103
104RamFile &RamFile::operator=(RamFile &&other) Q_DECL_NOEXCEPT
105{
106 cleanup();
107 m_size = std::exchange(other.m_size, 0);
108 m_flags = std::exchange(other.m_flags, RamFile::Flags{});
109#if HAVE_MEMFD
110 m_fd = std::exchange(other.m_fd, KWin::FileDescriptor{});
111#else
112 m_tmp = std::exchange(other.m_tmp, {});
113#endif
114 return *this;
115}
116
117RamFile::~RamFile()
118{
119 cleanup();
120}
121
122void RamFile::cleanup()
123{
124#if HAVE_MEMFD
125 m_fd = KWin::FileDescriptor();
126#else
127 m_tmp.reset();
128#endif
129}
130
131bool RamFile::isValid() const
132{
133 return fd() != -1;
134}
135
136RamFile::Flags RamFile::effectiveFlags() const
137{
138 Flags flags = {};
139
140 const int seals = fcntl(fd(), F_GET_SEALS);
141 if (seals > 0) {
142 if (seals & F_SEAL_WRITE) {
143 flags.setFlag(Flag::SealWrite);
144 }
145 }
146
147 return flags;
148}
149
150int RamFile::fd() const
151{
152#if HAVE_MEMFD
153 return m_fd.get();
154#else
155 return m_tmp->handle();
156#endif
157}
158
159int RamFile::size() const
160{
161 return m_size;
162}
163
164} // namespace KWin
Creates a file in memory.
Definition ramfile.h:47
RamFile()=default