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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +225 -0
- data/Rakefile +69 -0
- data/benchmark/mounts.rb +34 -0
- data/docs/kernel-notes.md +48 -0
- data/examples/atomic_swap.rb +12 -0
- data/examples/idmapped_volume.rb +17 -0
- data/examples/mini_container.rb +40 -0
- data/examples/readonly_sandbox.rb +39 -0
- data/ext/mountfd/compat.h +197 -0
- data/ext/mountfd/constants.c +53 -0
- data/ext/mountfd/extconf.rb +12 -0
- data/ext/mountfd/mount_info.c +166 -0
- data/ext/mountfd/mountfd.c +393 -0
- data/ext/mountfd/mountfd.h +17 -0
- data/ext/mountfd/namespace.c +74 -0
- data/ext/mountfd/user_namespace.c +220 -0
- data/lib/mountfd/attributes.rb +65 -0
- data/lib/mountfd/core.rb +346 -0
- data/lib/mountfd/mount_info.rb +78 -0
- data/lib/mountfd/namespace.rb +39 -0
- data/lib/mountfd/user_namespace.rb +84 -0
- data/lib/mountfd/version.rb +5 -0
- data/lib/mountfd.rb +32 -0
- data/sig/mountfd.rbs +119 -0
- data/tools/dump_constants.c +54 -0
- data/tools/generate_constants.rb +32 -0
- data/tools/research_idmap.rb +37 -0
- data/tools/vm/Makefile +8 -0
- data/tools/vm/config-fragment +7 -0
- data/tools/vm/run.sh +18 -0
- metadata +118 -0
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
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: []
|