Class: OsCtld::Mount::SharedDir

Inherits:
Object
  • Object
show all
Includes:
OsCtl::Lib::Utils::Log, OsCtl::Lib::Utils::System, Utils::SwitchUser
Defined in:
lib/osctld/mount/shared_dir.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Utils::SwitchUser

#ct_attach, #ct_syscmd

Constructor Details

#initialize(ct) ⇒ SharedDir

Returns a new instance of SharedDir.



11
12
13
# File 'lib/osctld/mount/shared_dir.rb', line 11

def initialize(ct)
  @ct = ct
end

Instance Attribute Details

#ctObject (readonly, protected)

Returns the value of attribute ct.



121
122
123
# File 'lib/osctld/mount/shared_dir.rb', line 121

def ct
  @ct
end

Instance Method Details

#cleanup_pushed(dir) ⇒ Object

Cleanup after #map_and_push

Parameters:

  • dir (String)


83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/osctld/mount/shared_dir.rb', line 83

def cleanup_pushed(dir)
  host_path = host_path_for(dir)

  syscmd("umount \"#{host_path}\"", valid_rcs: [32]) # 32 = not mounted

  begin
    Dir.rmdir(host_path)
  rescue Errno::ENOENT
    # pass
  end

  nil
end

#createObject

Prepare the shared mount directory on the host



16
17
18
19
20
21
22
23
24
25
26
# File 'lib/osctld/mount/shared_dir.rb', line 16

def create
  dir = Pathname.new(path)

  unless dir.exist?
    dir.mkdir
    syscmd("mount --bind \"#{dir}\" \"#{dir}\"")
    syscmd("mount --make-rshared \"#{dir}\"")
  end

  create_readme unless File.exist?(readme_path)
end

#create_readmeObject (protected)



152
153
154
155
156
157
158
159
160
161
# File 'lib/osctld/mount/shared_dir.rb', line 152

def create_readme
  File.write(
    readme_path,
    <<~END
      Directory `#{File.join('/', mountpoint)}` is used by osctl from vpsAdminOS to
      propagate new mounts into this container. Do not remove nor unmount this
      directory, or you'll have to restart your container to create new mounts!
    END
  )
end

#dup(new_ct) ⇒ Object



113
114
115
116
117
# File 'lib/osctld/mount/shared_dir.rb', line 113

def dup(new_ct)
  ret = super()
  ret.instance_variable_set('@ct', new_ct)
  ret
end

#host_path_for(dir) ⇒ String

Returns:

  • (String)


98
99
100
# File 'lib/osctld/mount/shared_dir.rb', line 98

def host_path_for(dir)
  File.join(path, Digest::SHA2.hexdigest(dir))
end

#map_and_push(dir, ns_pid) ⇒ Object

Bind-mount path with ID-mapping and push it through the shared directory

Parameters:

  • dir (String)
  • ns_pid (Integer)
  • path (String)

    to the mountpoint, same in both init and ct mount namespaces



72
73
74
75
76
77
78
79
# File 'lib/osctld/mount/shared_dir.rb', line 72

def map_and_push(dir, ns_pid)
  host_path = host_path_for(dir)

  Dir.mkdir(host_path)
  syscmd("mount --bind -o X-mount.idmap=/proc/#{ns_pid}/ns/user #{dir} #{host_path}")

  host_path
end

#mountpointString

Mountpoint relative to the container's rootfs

Returns:

  • (String)


109
110
111
# File 'lib/osctld/mount/shared_dir.rb', line 109

def mountpoint
  'dev/.osctl-mount-helper'
end

#pathString

Returns:

  • (String)


103
104
105
# File 'lib/osctld/mount/shared_dir.rb', line 103

def path
  File.join(ct.pool.mount_dir, ct.id)
end

#propagate(mnt) ⇒ Object

Propagate a new mount inside the container via the shared directory

Parameters:



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/osctld/mount/shared_dir.rb', line 39

def propagate(mnt)
  # Bind-mount the new mount into the shared directory
  host_path = host_path_for(mnt.mountpoint)

  Dir.mkdir(host_path)

  opts =
    if ct.map_mode == 'native' && mnt.map_ids
      "-o X-mount.idmap=/proc/#{ct.init_pid}/ns/user"
    end

  syscmd("mount --bind #{opts} \"#{mnt.fs}\" \"#{host_path}\"")

  # Move the mount inside the container to the right place
  begin
    ContainerControl::Commands::Mount.run!(
      ct,
      shared_dir: File.join('/', mountpoint),
      src: File.basename(host_path),
      dst: File.join('/', mnt.mountpoint)
    )
  rescue ContainerControl::Error => e
    log(:warn, ct, "Failed to mount #{mnt.mountpoint} at runtime: #{e.message}")
  end

  syscmd("umount \"#{host_path}\"")
  Dir.rmdir(host_path)
end

#prune_empty_child_directories(dir) ⇒ Object (protected)



137
138
139
140
141
142
143
144
145
146
# File 'lib/osctld/mount/shared_dir.rb', line 137

def prune_empty_child_directories(dir)
  dir.children.each do |child|
    next unless File.lstat(child).directory?

    child.rmdir
    log(:warn, ct, "Removed stale empty shared mount directory #{child}")
  rescue Errno::ENOENT, Errno::ENOTEMPTY
    next
  end
end

#readme_pathObject (protected)



148
149
150
# File 'lib/osctld/mount/shared_dir.rb', line 148

def readme_path
  File.join(path, 'README.txt')
end

#removeObject

Remove the shared mount directory from the host



29
30
31
32
33
34
35
# File 'lib/osctld/mount/shared_dir.rb', line 29

def remove
  dir = Pathname.new(path)
  unmount(dir)
  FileUtils.rm_f(readme_path)
  prune_empty_child_directories(dir) if dir.exist?
  dir.rmdir if dir.exist?
end

#unmount(dir) ⇒ Object (protected)



123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/osctld/mount/shared_dir.rb', line 123

def unmount(dir)
  return unless dir.exist?

  cmd = "umount -f \"#{dir}\""
  ret = syscmd(cmd, valid_rcs: :all)

  return if ret.success?
  return if ret.output.include?('not mounted')
  return if ret.output.include?('not found')
  return if ret.output.include?('no mount point specified')

  raise SystemCommandFailed.new(cmd, ret.exitstatus, ret.output)
end