1717#include < fmt/format.h>
1818#include < fmt/ranges.h>
1919
20+ #include < QDBusConnection>
21+ #include < QDBusInterface>
22+ #include < QDBusReply>
23+
2024#include < algorithm>
2125#include < fstream>
2226
@@ -32,6 +36,47 @@ const std::vector<std::string> buildContainerCaps = {
3236 " CAP_SETPCAP" , " CAP_SETUID" , " CAP_SYS_CHROOT" ,
3337};
3438
39+ auto getXDPDocumentsMountPoint () noexcept -> utils::error::Result<std::filesystem::path>
40+ {
41+ LINGLONG_TRACE (" get XDP Documents mount point" );
42+
43+ auto bus = QDBusConnection::sessionBus ();
44+ if (!bus.isConnected ()) {
45+ return LINGLONG_ERR (" session bus is not connected" );
46+ }
47+
48+ QDBusInterface documentsPortal (" org.freedesktop.portal.Documents" ,
49+ " /org/freedesktop/portal/documents" ,
50+ " org.freedesktop.portal.Documents" ,
51+ bus);
52+ if (!documentsPortal.isValid ()) {
53+ return LINGLONG_ERR (" org.freedesktop.portal.Documents is not available" );
54+ }
55+
56+ auto reply = documentsPortal.call (QDBus::Block, " GetMountPoint" );
57+ if (reply.type () == QDBusMessage::ErrorMessage) {
58+ return LINGLONG_ERR (reply.errorMessage ().toStdString ());
59+ }
60+
61+ if (reply.arguments ().isEmpty ()) {
62+ return LINGLONG_ERR (" Documents portal mount point reply is empty" );
63+ }
64+
65+ const auto &value = reply.arguments ().constFirst ();
66+ if (!value.canConvert <QByteArray>()) {
67+ return LINGLONG_ERR (
68+ fmt::format (" unexpected Documents portal mount point type: {}" , value.typeName ()));
69+ }
70+ auto mountPoint = value.toByteArray ().toStdString ();
71+
72+ if (mountPoint.empty ()) {
73+ return LINGLONG_ERR (" Documents portal mount point is empty" );
74+ }
75+
76+ // c_str is crucial here, without it the path may include trailing null bytes
77+ return std::filesystem::path{ mountPoint.c_str () };
78+ }
79+
3580} // namespace
3681
3782utils::error::Result<std::filesystem::path> makeBundleDir (const std::string &containerID,
@@ -68,6 +113,10 @@ std::string genContainerID(const api::types::v1::RunContextConfig &config) noexc
68113auto RunContainerOptions::applyRuntimeConfig (
69114 const api::types::v1::RuntimeConfigure &runtimeConfig) noexcept -> utils::error::Result<void>
70115{
116+ if (runtimeConfig.disableXdp .has_value ()) {
117+ this ->disableXdp = *runtimeConfig.disableXdp ;
118+ }
119+
71120 if (runtimeConfig.deviceMode ) {
72121 for (const auto &option : *runtimeConfig.deviceMode ) {
73122 if (option == api::types::v1::DeviceOption::Passthru) {
@@ -107,6 +156,9 @@ auto RunContainerOptions::applyCliRunOptions(const cli::RunOptions &options) noe
107156 this ->env .insert_or_assign (item.substr (0 , split), item.substr (split + 1 ));
108157 }
109158
159+ if (options.disableXdp .has_value ()) {
160+ this ->disableXdp = *options.disableXdp ;
161+ }
110162 this ->privileged = options.privileged ;
111163 this ->capabilities .insert (this ->capabilities .end (),
112164 options.capsAdd .begin (),
@@ -146,6 +198,11 @@ auto RunContainerOptions::isDevicePassthruEnabled() const noexcept -> bool
146198 return this ->devicePassthru ;
147199}
148200
201+ auto RunContainerOptions::isXdpDisabled () const noexcept -> bool
202+ {
203+ return this ->disableXdp ;
204+ }
205+
149206auto RunContainerOptions::isPrivileged () const noexcept -> bool
150207{
151208 return this ->privileged ;
@@ -473,6 +530,18 @@ auto ContainerBuilder::configureRunContainer(PreparedContainer &prepared,
473530 .bindIPC ()
474531 .forwardDefaultEnv ();
475532
533+ if (!options.isXdpDisabled ()) {
534+ auto docMountPoint = getXDPDocumentsMountPoint ();
535+ if (docMountPoint) {
536+ prepared.cfgBuilder .enableXDP (generator::XdpOption{
537+ .docMountPoint = std::move (*docMountPoint),
538+ });
539+ } else {
540+ LogW (" failed to get XDP Documents mount point: {}, skip XDP integration" ,
541+ docMountPoint.error ());
542+ }
543+ }
544+
476545 if (options.isDevicePassthruEnabled ()) {
477546 prepared.cfgBuilder .bindDev (true );
478547 } else {
0 commit comments