1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
|
// Package mountinfo provides util-linux bindings for parsing
// proc_pid_mountinfo(5).
//
// This package must never be used outside integration tests, a much better
// implementation can be found in package vfs.
//
// Attempting to import this package outside testing causes the resulting
// program to panic.
package mountinfo
/*
#cgo linux pkg-config: --static mount
#include <stdlib.h>
#include <stdio.h>
#include <libmount.h>
const char *HAKUREI_MOUNTINFO_PATH = "/proc/self/mountinfo";
*/
import "C"
import (
"errors"
"fmt"
"runtime"
"unsafe"
)
var (
// ErrParse is returned by [Open] when encountering a bad record.
ErrParse = errors.New("invalid mountinfo record")
// ErrIter is returned by [Open] if an iterator cannot be allocated.
ErrIter = errors.New("cannot allocate iterator")
// ErrIterAdvance is stored when the iterator is unable to advance.
ErrIterAdvance = errors.New("unable to advance iterator")
)
type (
// Iter refers to libmnt iterator state.
Iter struct {
// Last stored error.
err error
// Whether iteration has concluded.
ok bool
// Whether Close had already been called.
closed bool
tb *C.struct_libmnt_table
itr *C.struct_libmnt_iter
fs *C.struct_libmnt_fs
}
// Entry represents deterministic mountinfo parts of a libmnt_fs entry.
Entry struct {
// mount ID: a unique ID for the mount (may be reused after umount(2)).
ID int `json:"id"`
// parent ID: the ID of the parent mount (or of self for the root of
// this mount namespace's mount tree).
Parent int `json:"parent"`
// root: the pathname of the directory in the filesystem which forms the
// root of this mount.
Root string `json:"root"`
// mount point: the pathname of the mount point relative to the
// process's root directory.
Target string `json:"target"`
// mount options: per-mount options (see mount(2)).
VfsOptstr string `json:"vfs_optstr"`
// filesystem type: the filesystem type in the form "type[.subtype]".
FsType string `json:"fstype"`
// mount source: filesystem-specific information or "none".
Source string `json:"source"`
// super options: per-superblock options (see mount(2)).
FsOptstr string `json:"fs_optstr"`
}
)
// Copy populates v with the current record.
func (m *Iter) Copy(v *Entry) {
if m.fs == nil {
panic("invalid entry")
}
v.ID = int(C.mnt_fs_get_id(m.fs))
v.Parent = int(C.mnt_fs_get_parent_id(m.fs))
v.Root = C.GoString(C.mnt_fs_get_root(m.fs))
v.Target = C.GoString(C.mnt_fs_get_target(m.fs))
v.VfsOptstr = C.GoString(C.mnt_fs_get_vfs_options(m.fs))
v.FsType = C.GoString(C.mnt_fs_get_fstype(m.fs))
v.Source = C.GoString(C.mnt_fs_get_source(m.fs))
v.FsOptstr = C.GoString(C.mnt_fs_get_fs_options(m.fs))
}
// Err returns the saved iterator error.
func (m *Iter) Err() error { return m.err }
// Open opens a mountinfo document. If name is an empty string, the mountinfo
// document of the current process is opened instead.
func Open(name string) (*Iter, error) {
var m Iter
if name == "" {
m.tb = C.mnt_new_table_from_file(C.HAKUREI_MOUNTINFO_PATH)
} else {
_name := C.CString(name)
m.tb = C.mnt_new_table_from_file(_name)
C.free(unsafe.Pointer(_name))
}
if m.tb == nil {
return nil, ErrParse
}
m.itr = C.mnt_new_iter(C.MNT_ITER_FORWARD)
if m.itr == nil {
C.mnt_unref_table(m.tb)
return nil, ErrIter
}
m.ok = true
runtime.SetFinalizer(&m, (*Iter).Close)
return &m, nil
}
// Close frees the iterator.
func (m *Iter) Close() {
if m.closed {
return
}
if m.tb == nil {
panic("unref called before open")
}
C.mnt_unref_table(m.tb)
C.mnt_free_iter(m.itr)
m.closed = true
runtime.SetFinalizer(m, nil)
}
// Reset resets the iterator to the first record for reuse.
func (m *Iter) Reset() {
if m.err != nil {
panic("attempting to reset a faulted iterator")
}
m.ok = true
C.mnt_reset_iter(m.itr, -1)
}
// Next advances the iterator to the next record. The record may be copied if
// Next returns true.
func (m *Iter) Next() bool {
if !m.ok || m.err != nil {
return false
}
r := C.mnt_table_next_fs(m.tb, m.itr, &m.fs)
if r < 0 {
m.err = ErrIterAdvance
}
m.ok = r == 0
return m.ok
}
// EqualWithIgnore compares e with want, ignoring fields with the specified
// ignore value.
func (e *Entry) EqualWithIgnore(want *Entry, ignore string) bool {
return (e.ID == want.ID || want.ID == -1) &&
(e.Parent == want.Parent || want.Parent == -1) &&
(e.Root == want.Root || want.Root == ignore) &&
(e.Target == want.Target || want.Target == ignore) &&
(e.VfsOptstr == want.VfsOptstr || want.VfsOptstr == ignore) &&
(e.FsType == want.FsType || want.FsType == ignore) &&
(e.Source == want.Source || want.Source == ignore) &&
(e.FsOptstr == want.FsOptstr || want.FsOptstr == ignore)
}
// String returns a text representation of e loosely following the kernel format.
func (e *Entry) String() string {
return fmt.Sprintf("%d %d %s %s %s %s %s %s",
e.ID, e.Parent, e.Root, e.Target, e.VfsOptstr, e.FsType, e.Source, e.FsOptstr)
}
|