termio, flatpak: support spawning terminals in cwd (#6915)

Implements path access testing for Flatpak via test spawning. This is
required since Flatpak reserves certain paths from being accessible
regardless of permissions.

Opened mostly as an RFC, this approach seems to work without any
noticeable performance impact.

Ref:
https://docs.flatpak.org/en/latest/sandbox-permissions.html#reserved-paths
pull/7283/head
Mitchell Hashimoto 2025-05-06 13:59:48 -07:00 committed by GitHub
commit 5f8a0dc4a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 45 additions and 15 deletions

View File

@ -745,7 +745,7 @@ const Subprocess = struct {
}); });
arena: std.heap.ArenaAllocator, arena: std.heap.ArenaAllocator,
cwd: ?[]const u8, cwd: ?[:0]const u8,
env: ?EnvMap, env: ?EnvMap,
args: []const [:0]const u8, args: []const [:0]const u8,
grid_size: renderer.GridSize, grid_size: renderer.GridSize,
@ -985,8 +985,8 @@ const Subprocess = struct {
// We have to copy the cwd because there is no guarantee that // We have to copy the cwd because there is no guarantee that
// pointers in full_config remain valid. // pointers in full_config remain valid.
const cwd: ?[]u8 = if (cfg.working_directory) |cwd| const cwd: ?[:0]u8 = if (cfg.working_directory) |cwd|
try alloc.dupe(u8, cwd) try alloc.dupeZ(u8, cwd)
else else
null; null;
@ -1048,6 +1048,47 @@ const Subprocess = struct {
log.debug("starting command command={s}", .{self.args}); log.debug("starting command command={s}", .{self.args});
// If we can't access the cwd, then don't set any cwd and inherit.
// This is important because our cwd can be set by the shell (OSC 7)
// and we don't want to break new windows.
const cwd: ?[:0]const u8 = if (self.cwd) |proposed| cwd: {
if ((comptime build_config.flatpak) and internal_os.isFlatpak()) {
// Flatpak sandboxing prevents access to certain reserved paths
// regardless of configured permissions. Perform a test spawn
// to get around this problem
//
// https://docs.flatpak.org/en/latest/sandbox-permissions.html#reserved-paths
log.info("flatpak detected, will use host command to verify cwd access", .{});
const dev_null = try std.fs.cwd().openFile("/dev/null", .{ .mode = .read_write });
defer dev_null.close();
var cmd: internal_os.FlatpakHostCommand = .{
.argv = &[_][]const u8{
"/bin/sh",
"-c",
":",
},
.cwd = proposed,
.stdin = dev_null.handle,
.stdout = dev_null.handle,
.stderr = dev_null.handle,
};
_ = cmd.spawn(alloc) catch |err| {
log.warn("cannot spawn command at cwd, ignoring: {}", .{err});
break :cwd null;
};
_ = try cmd.wait();
break :cwd proposed;
}
if (std.fs.cwd().access(proposed, .{})) {
break :cwd proposed;
} else |err| {
log.warn("cannot access cwd, ignoring: {}", .{err});
break :cwd null;
}
} else null;
// In flatpak, we use the HostCommand to execute our shell. // In flatpak, we use the HostCommand to execute our shell.
if (internal_os.isFlatpak()) flatpak: { if (internal_os.isFlatpak()) flatpak: {
if (comptime !build_config.flatpak) { if (comptime !build_config.flatpak) {
@ -1058,6 +1099,7 @@ const Subprocess = struct {
// Flatpak command must have a stable pointer. // Flatpak command must have a stable pointer.
self.flatpak_command = .{ self.flatpak_command = .{
.argv = self.args, .argv = self.args,
.cwd = cwd,
.env = if (self.env) |*env| env else null, .env = if (self.env) |*env| env else null,
.stdin = pty.slave, .stdin = pty.slave,
.stdout = pty.slave, .stdout = pty.slave,
@ -1083,18 +1125,6 @@ const Subprocess = struct {
}; };
} }
// If we can't access the cwd, then don't set any cwd and inherit.
// This is important because our cwd can be set by the shell (OSC 7)
// and we don't want to break new windows.
const cwd: ?[]const u8 = if (self.cwd) |proposed| cwd: {
if (std.fs.cwd().access(proposed, .{})) {
break :cwd proposed;
} else |err| {
log.warn("cannot access cwd, ignoring: {}", .{err});
break :cwd null;
}
} else null;
// Build our subcommand // Build our subcommand
var cmd: Command = .{ var cmd: Command = .{
.path = self.args[0], .path = self.args[0],