Module: Mountfd::MountInfoParser

Defined in:
lib/mountfd/mount_info.rb

Constant Summary collapse

ESCAPE =
/\\([0-3][0-7]{2})/
UINT32_MAX =
2**32 - 1
UINT64_MAX =
2**64 - 1

Class Method Summary collapse

Class Method Details

.decode(value) ⇒ Object



47
48
49
# File 'lib/mountfd/mount_info.rb', line 47

def self.decode(value)
  value.gsub(ESCAPE) { Regexp.last_match(1).to_i(8).chr(Encoding::BINARY) }
end

.parse(content) ⇒ Object



17
18
19
# File 'lib/mountfd/mount_info.rb', line 17

def self.parse(content)
  content.b.lines.filter_map { parse_line(_1) }
end

.parse_attrs(options, optional) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
# File 'lib/mountfd/mount_info.rb', line 66

def self.parse_attrs(options, optional)
  names = {
    "ro" => :rdonly, "nosuid" => :nosuid, "nodev" => :nodev,
    "noexec" => :noexec, "nodiratime" => :nodiratime,
    "nosymfollow" => :nosymfollow, "noatime" => :noatime,
    "strictatime" => :strictatime
  }
  values = options.filter_map { names[_1] }
  values << :idmap if optional.include?("idmapped")
  values.uniq.freeze
end

.parse_line(line) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/mountfd/mount_info.rb', line 21

def self.parse_line(line)
  fields = line.split
  separator = fields.index("-")
  return unless separator && separator >= 6 && fields.length >= separator + 4

  device = fields[2].split(":", -1)
  return unless device.length == 2

  mnt_id, parent_id = fields.first(2).map { Integer(_1, 10) }
  major, minor = device.map { Integer(_1, 10) }
  return unless mnt_id.between?(0, UINT64_MAX) && parent_id.between?(0, UINT64_MAX) &&
    major.between?(0, UINT32_MAX) && minor.between?(0, UINT32_MAX)

  mount_options = fields[5].split(",")
  super_options = fields[separator + 3].split(",")
  optional = fields[6...separator]
  MountInfo.new(
    mnt_id, parent_id, decode(fields[3]), decode(fields[4]),
    decode(fields[separator + 1]), decode(fields[separator + 2]),
    (mount_options + super_options).uniq.freeze, parse_propagation(optional),
    parse_attrs(mount_options + super_options, optional), major, minor
  )
rescue ArgumentError, TypeError
  nil
end

.parse_propagation(fields) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/mountfd/mount_info.rb', line 51

def self.parse_propagation(fields)
  propagation = fields.filter_map do |field|
    name, value = field.split(":", 2)
    case name
    when "shared", "master", "propagate_from"
      [name.to_sym, Integer(value, 10)] if value
    when "unbindable"
      [:unbindable, true]
    end
  end.to_h
  propagation[:slave] = true if propagation.key?(:master)
  propagation[:private] = true if propagation.empty?
  propagation.freeze
end