mountfd 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/sig/mountfd.rbs ADDED
@@ -0,0 +1,119 @@
1
+ module Mountfd
2
+ VERSION: String
3
+ AT_FDCWD: Integer
4
+
5
+ class Error < StandardError
6
+ end
7
+
8
+ class UnsupportedError < Error
9
+ end
10
+
11
+ class ConfigError < Error
12
+ attr_reader diagnostics: Array[Diagnostic]
13
+ def initialize: (String message, ?Array[Diagnostic] diagnostics) -> void
14
+ end
15
+
16
+ class MountError < ConfigError
17
+ end
18
+
19
+ class IdmapError < Error
20
+ end
21
+
22
+ class Diagnostic
23
+ attr_reader level: :error | :warning | :info
24
+ attr_reader text: String
25
+ def self.parse: (String raw) -> Array[Diagnostic]
26
+ def to_s: () -> String
27
+ end
28
+
29
+ module Attributes
30
+ def self.build: (Hash[Symbol | String, untyped] attributes) -> [Integer, Integer]
31
+ def self.flags: (Integer | Array[Symbol | String] names) -> Integer
32
+ def self.propagation: (Symbol | String | nil value) -> Integer
33
+ end
34
+
35
+ class FsContext
36
+ attr_reader diagnostics: Array[Diagnostic]
37
+ def self.open: [T] (String filesystem, ?flags: Integer) { (FsContext) -> T } -> T
38
+ | (String filesystem, ?flags: Integer) -> FsContext
39
+ def self.pick: (String path, ?flags: Integer) -> FsContext
40
+ def initialize: (String filesystem, ?flags: Integer, ?handle: untyped) -> void
41
+ def fileno: () -> Integer
42
+ def closed?: () -> bool
43
+ def warnings: () -> Array[Diagnostic]
44
+ def set: (String | Symbol key, ?untyped value) -> FsContext
45
+ def set_flag: (String | Symbol key) -> FsContext
46
+ def set_path: (String | Symbol key, String path, ?dfd: Integer) -> FsContext
47
+ def set_fd: (String | Symbol key, _Fileno io) -> FsContext
48
+ def set_binary: (String | Symbol key, String bytes) -> FsContext
49
+ def create!: (?exclusive: bool) -> FsContext
50
+ def reconfigure!: () -> FsContext
51
+ def mount: (?attrs: Hash[Symbol, untyped]) -> DetachedMount
52
+ def close: () -> nil
53
+ end
54
+
55
+ interface _Fileno
56
+ def fileno: () -> Integer
57
+ end
58
+
59
+ class DetachedMount
60
+ def fileno: () -> Integer
61
+ def closed?: () -> bool
62
+ def discard: () -> nil
63
+ def close: () -> nil
64
+ def set_attributes: (?set: Integer | Array[Symbol], ?clr: Integer | Array[Symbol], ?propagation: Symbol?, ?idmap: _Fileno?, ?recursive: bool) -> DetachedMount
65
+ def idmap!: (_Fileno userns) -> DetachedMount
66
+ def attach: (String path, ?beneath: bool) -> AttachedMount
67
+ end
68
+
69
+ class AttachedMount
70
+ attr_reader path: String
71
+ end
72
+
73
+ class UserNamespace
74
+ def self.create: (?uid: untyped, ?gid: untyped, ?helper: :auto | bool) -> UserNamespace
75
+ def self.from_pid: (Integer pid) -> UserNamespace
76
+ def self.from_path: (String path) -> UserNamespace
77
+ def self.normalize: (untyped mapping) -> Array[[Integer, Integer, Integer]]
78
+ def fileno: () -> Integer
79
+ def closed?: () -> bool
80
+ def close: () -> nil
81
+ end
82
+
83
+ class MountInfo
84
+ attr_reader mnt_id: Integer
85
+ attr_reader parent_id: Integer
86
+ attr_reader mnt_root: String
87
+ attr_reader mount_point: String
88
+ attr_reader fs_type: String
89
+ attr_reader source: String?
90
+ attr_reader options: Array[String]
91
+ attr_reader propagation: Hash[Symbol, Integer | bool]
92
+ attr_reader attrs: Array[Symbol]
93
+ attr_reader dev_major: Integer
94
+ attr_reader dev_minor: Integer
95
+ def idmapped?: () -> bool
96
+ def readonly?: () -> bool
97
+ end
98
+
99
+ module Namespace
100
+ def self.reexec_user!: () -> nil
101
+ def self.unshare_mount!: (?propagation: :private | nil) -> nil
102
+ def self.unshare_user!: (?map_root: bool) -> nil
103
+ end
104
+
105
+ def self.supported?: () -> bool
106
+ def self.features: () -> Array[Symbol]
107
+ def self.pending_mounts: () -> Array[DetachedMount]
108
+ def self.open_tree: [T] (String path, ?recursive: bool) { (DetachedMount) -> T } -> T
109
+ | (String path, ?recursive: bool) -> DetachedMount
110
+ def self.mount: (String source, String target, ?type: String, ?options: Hash[Symbol | String, untyped], ?attrs: Hash[Symbol, untyped]) -> AttachedMount
111
+ def self.bind: (String source, String target, ?recursive: bool, ?attrs: Hash[Symbol, untyped], ?idmap: UserNamespace | Hash[Symbol, untyped]?) -> AttachedMount
112
+ def self.umount: (String path, ?detach: bool, ?force: bool) -> nil
113
+ def self.pivot_root: (String new_root, String put_old) -> nil
114
+ def self.set_attributes: (String path, ?attrs: Hash[Symbol, untyped], ?propagation: Symbol?, ?recursive: bool) -> nil
115
+ def self.replace: (String path) { () -> DetachedMount } -> AttachedMount
116
+ def self.mounts: (?ns: (Integer | _Fileno)?, ?backend: :statmount | :mountinfo) -> Array[MountInfo]
117
+ def self.mounts_backend: () -> (:statmount | :mountinfo)
118
+ def self.mount_at: (String path, ?ns: (Integer | _Fileno)?, ?backend: :statmount | :mountinfo) -> MountInfo?
119
+ end
@@ -0,0 +1,54 @@
1
+ #include "compat.h"
2
+ #include <stdio.h>
3
+
4
+ #define DUMP(name) printf(#name "=%llu\n", (unsigned long long)(name))
5
+
6
+ int main(void)
7
+ {
8
+ DUMP(FSOPEN_CLOEXEC);
9
+ DUMP(FSPICK_CLOEXEC);
10
+ DUMP(FSPICK_SYMLINK_NOFOLLOW);
11
+ DUMP(FSPICK_NO_AUTOMOUNT);
12
+ DUMP(FSPICK_EMPTY_PATH);
13
+ DUMP(FSCONFIG_SET_FLAG);
14
+ DUMP(FSCONFIG_SET_STRING);
15
+ DUMP(FSCONFIG_SET_BINARY);
16
+ DUMP(FSCONFIG_SET_PATH);
17
+ DUMP(FSCONFIG_SET_PATH_EMPTY);
18
+ DUMP(FSCONFIG_SET_FD);
19
+ DUMP(FSCONFIG_CMD_CREATE);
20
+ DUMP(FSCONFIG_CMD_RECONFIGURE);
21
+ DUMP(FSCONFIG_CMD_CREATE_EXCL);
22
+ DUMP(FSMOUNT_CLOEXEC);
23
+ DUMP(OPEN_TREE_CLONE);
24
+ DUMP(OPEN_TREE_CLOEXEC);
25
+ DUMP(MOVE_MOUNT_F_SYMLINKS);
26
+ DUMP(MOVE_MOUNT_F_AUTOMOUNTS);
27
+ DUMP(MOVE_MOUNT_F_EMPTY_PATH);
28
+ DUMP(MOVE_MOUNT_T_SYMLINKS);
29
+ DUMP(MOVE_MOUNT_T_AUTOMOUNTS);
30
+ DUMP(MOVE_MOUNT_T_EMPTY_PATH);
31
+ DUMP(MOVE_MOUNT_SET_GROUP);
32
+ DUMP(MOVE_MOUNT_BENEATH);
33
+ DUMP(MOUNT_ATTR_RDONLY);
34
+ DUMP(MOUNT_ATTR_NOSUID);
35
+ DUMP(MOUNT_ATTR_NODEV);
36
+ DUMP(MOUNT_ATTR_NOEXEC);
37
+ DUMP(MOUNT_ATTR__ATIME);
38
+ DUMP(MOUNT_ATTR_RELATIME);
39
+ DUMP(MOUNT_ATTR_NOATIME);
40
+ DUMP(MOUNT_ATTR_STRICTATIME);
41
+ DUMP(MOUNT_ATTR_NODIRATIME);
42
+ DUMP(MOUNT_ATTR_IDMAP);
43
+ DUMP(MOUNT_ATTR_NOSYMFOLLOW);
44
+ DUMP(AT_RECURSIVE);
45
+ DUMP(AT_EMPTY_PATH);
46
+ DUMP(MS_UNBINDABLE);
47
+ DUMP(MS_REC);
48
+ DUMP(MS_PRIVATE);
49
+ DUMP(MS_SLAVE);
50
+ DUMP(MS_SHARED);
51
+ DUMP(CLONE_NEWNS);
52
+ DUMP(CLONE_NEWUSER);
53
+ return 0;
54
+ }
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tmpdir"
4
+
5
+ abort "Linux headers are required" unless RUBY_PLATFORM.include?("linux")
6
+
7
+ root = File.expand_path("..", __dir__)
8
+ values = Dir.mktmpdir do |directory|
9
+ executable = File.join(directory, "dump_constants")
10
+ command = [ENV.fetch("CC", "cc"), "-I#{File.join(root, "ext/mountfd")}",
11
+ File.join(__dir__, "dump_constants.c"), "-o", executable]
12
+ abort "failed to compile constant dumper" unless system(*command)
13
+
14
+ IO.popen([executable], &:read)
15
+ end
16
+
17
+ definitions = values.lines(chomp: true).map do |line|
18
+ name, value = line.split("=", 2)
19
+ %( DEF("#{name}", #{value}ULL);)
20
+ end.join("\n")
21
+
22
+ File.write(File.join(root, "ext/mountfd/constants.c"), <<~C)
23
+ /* Generated by tools/generate_constants.rb from Linux UAPI headers. */
24
+ #include "ruby.h"
25
+
26
+ void mountfd_define_constants(VALUE native)
27
+ {
28
+ #define DEF(name, value) rb_define_const(native, name, ULL2NUM(value))
29
+ #{definitions}
30
+ #undef DEF
31
+ }
32
+ C
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mountfd"
4
+
5
+ abort "Linux is required" unless Mountfd::Native.linux?
6
+
7
+ Mountfd::Namespace.reexec_user!
8
+ Mountfd::Namespace.unshare_mount!
9
+ namespace = nil
10
+ begin
11
+ namespace = Mountfd::UserNamespace.create(helper: false)
12
+ # ponytail: arbitrary nodev probes can block in get_tree; extend this list
13
+ # only with filesystems proven to be source-free in an isolated VM.
14
+ filesystems = %w[tmpfs ramfs hugetlbfs] & File.read("/proc/filesystems").split
15
+
16
+ puts "| filesystem | idmap |"
17
+ puts "|---|---|"
18
+ filesystems.each do |filesystem|
19
+ supported = false
20
+ mount = nil
21
+ begin
22
+ Mountfd::FsContext.open(filesystem) do |context|
23
+ context.create!
24
+ mount = context.mount
25
+ mount.idmap!(namespace)
26
+ supported = true
27
+ end
28
+ rescue Mountfd::Error, SystemCallError
29
+ # A source-free probe cannot configure every filesystem.
30
+ ensure
31
+ mount&.discard unless mount&.closed?
32
+ end
33
+ puts "| #{filesystem} | #{supported ? 'yes' : 'no/unavailable'} |"
34
+ end
35
+ ensure
36
+ namespace&.close
37
+ end
data/tools/vm/Makefile ADDED
@@ -0,0 +1,8 @@
1
+ KVER ?= 6.12.20
2
+
3
+ .PHONY: all test
4
+ all: test
5
+
6
+ test:
7
+ command -v vng >/dev/null
8
+ KVER=$(KVER) ./run.sh
@@ -0,0 +1,7 @@
1
+ CONFIG_NAMESPACES=y
2
+ CONFIG_USER_NS=y
3
+ CONFIG_FANOTIFY=y
4
+ CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y
5
+ CONFIG_USERFAULTFD=y
6
+ CONFIG_BLK_DEV_UBLK=y
7
+ CONFIG_PSI=y
data/tools/vm/run.sh ADDED
@@ -0,0 +1,18 @@
1
+ #!/bin/sh
2
+ set -eu
3
+
4
+ cd -- "$(dirname -- "$0")/../.."
5
+
6
+ kernel_version=${KVER:-6.12.20}
7
+ bundle exec rake compile
8
+
9
+ set -- env MOUNTFD_SYSTEM=1 MOUNTFD_MATRIX=1 RSPEC_STATUS_PATH=/tmp/mountfd-rspec-status
10
+ if [ -n "${GEM_HOME:-}" ]; then
11
+ set -- "$@" "GEM_HOME=$GEM_HOME" "GEM_PATH=${GEM_PATH:-$GEM_HOME}"
12
+ fi
13
+ set -- "$@" bundle exec rspec spec/system/mountfd_system_spec.rb
14
+
15
+ case "$kernel_version" in
16
+ 5.10*) exec vng -v -r "v${kernel_version}" -a psi=0 -- "$@" ;;
17
+ *) exec vng -v -r "v${kernel_version}" -- "$@" ;;
18
+ esac
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mountfd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yudai Takada
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rake-compiler
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.3'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.3'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rbs
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '4.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '4.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: yard
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.9'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.9'
54
+ description: Build, configure, inspect, and attach Linux mounts as file descriptors.
55
+ email:
56
+ - t.yudai92@gmail.com
57
+ executables: []
58
+ extensions:
59
+ - ext/mountfd/extconf.rb
60
+ extra_rdoc_files: []
61
+ files:
62
+ - LICENSE.txt
63
+ - README.md
64
+ - Rakefile
65
+ - benchmark/mounts.rb
66
+ - docs/kernel-notes.md
67
+ - examples/atomic_swap.rb
68
+ - examples/idmapped_volume.rb
69
+ - examples/mini_container.rb
70
+ - examples/readonly_sandbox.rb
71
+ - ext/mountfd/compat.h
72
+ - ext/mountfd/constants.c
73
+ - ext/mountfd/extconf.rb
74
+ - ext/mountfd/mount_info.c
75
+ - ext/mountfd/mountfd.c
76
+ - ext/mountfd/mountfd.h
77
+ - ext/mountfd/namespace.c
78
+ - ext/mountfd/user_namespace.c
79
+ - lib/mountfd.rb
80
+ - lib/mountfd/attributes.rb
81
+ - lib/mountfd/core.rb
82
+ - lib/mountfd/mount_info.rb
83
+ - lib/mountfd/namespace.rb
84
+ - lib/mountfd/user_namespace.rb
85
+ - lib/mountfd/version.rb
86
+ - sig/mountfd.rbs
87
+ - tools/dump_constants.c
88
+ - tools/generate_constants.rb
89
+ - tools/research_idmap.rb
90
+ - tools/vm/Makefile
91
+ - tools/vm/config-fragment
92
+ - tools/vm/run.sh
93
+ homepage: https://github.com/ydah/mountfd#readme
94
+ licenses:
95
+ - MIT
96
+ metadata:
97
+ allowed_push_host: https://rubygems.org
98
+ homepage_uri: https://github.com/ydah/mountfd#readme
99
+ source_code_uri: https://github.com/ydah/mountfd
100
+ rubygems_mfa_required: 'true'
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: 3.2.0
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubygems_version: 4.0.19
116
+ specification_version: 4
117
+ summary: Ruby bindings for the Linux file-descriptor mount API
118
+ test_files: []