github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/pkg/mount/libmount/mount_table.go (about) 1 /* 2 Copyright 2020 The OpenEBS Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package libmount provides utilties to parse and operate on 18 // the various mount files (fstab, mtab, mounts, mountinfo, etc.). 19 // This package is a pure go implementation of the util-linux/libmount 20 // C library - https://github.com/karelzak/util-linux/tree/master/libmount 21 package libmount 22 23 import ( 24 "bufio" 25 "errors" 26 "os" 27 ) 28 29 type MountTabFormat int 30 type MountTabOpt func(*MountTab) error 31 32 // MountTab represents a mount table that may contain multiple 33 // filesystem entries 34 type MountTab struct { 35 format MountTabFormat 36 fileName string 37 entries []*Filesystem 38 allowFilters []FsFilter 39 denyFilters []FsFilter 40 } 41 42 const ( 43 MntFmtGuess MountTabFormat = iota 44 MntFmtFstab 45 MntFmtMountInfo 46 MntFmtUtab 47 MntFmtSwaps 48 MntFmtMtab = MntFmtFstab 49 ) 50 51 var ( 52 ErrInvalidArgument error = errors.New("invalid argument provided") 53 ErrFilesystemBusy error = errors.New("filesystem busy") 54 ErrDeniedByFilters error = errors.New("fs denied by filters") 55 ) 56 57 // NewMountTab initializes and returns a new mount tab configured 58 // with the given options 59 func NewMountTab(opts ...MountTabOpt) (*MountTab, error) { 60 mt := MountTab{} 61 for _, opt := range opts { 62 if err := opt(&mt); err != nil { 63 return nil, err 64 } 65 } 66 if mt.fileName != "" { 67 err := mt.parseFile() 68 if err != nil { 69 return nil, err 70 } 71 } 72 return &mt, nil 73 } 74 75 // FromFile option tells NewMountTab to fill the mount tab from the 76 // specified file. 77 func FromFile(fileName string, format MountTabFormat) MountTabOpt { 78 return func(mt *MountTab) error { 79 _, err := os.Stat(fileName) 80 if err != nil { 81 return err 82 } 83 mt.format = format 84 mt.fileName = fileName 85 return nil 86 } 87 } 88 89 // WithAllowFilter option tells NewMountTab to add the given filter 90 // to mount tab as an allow filter. 91 func WithAllowFilter(filter FsFilter) MountTabOpt { 92 return func(mt *MountTab) error { 93 mt.allowFilters = append(mt.allowFilters, filter) 94 return nil 95 } 96 } 97 98 // WithDenyFilter option tells NewMountTab to add the given filter 99 // to mount tab as a deny filter. 100 func WithDenyFilter(filter FsFilter) MountTabOpt { 101 return func(mt *MountTab) error { 102 mt.denyFilters = append(mt.denyFilters, filter) 103 return nil 104 } 105 } 106 107 func (mt *MountTab) applyFilters(fs *Filesystem) bool { 108 isAllowed := false 109 isDenied := false 110 111 if len(mt.allowFilters) == 0 { 112 isAllowed = true 113 } 114 115 for _, filter := range mt.denyFilters { 116 isDenied = isDenied || filter(fs) 117 } 118 119 for _, filter := range mt.allowFilters { 120 isAllowed = isAllowed || filter(fs) 121 } 122 123 return !isDenied && isAllowed 124 } 125 126 // AddFilesystem adds a filesystem to the mount tab. 127 func (mt *MountTab) AddFilesystem(fs *Filesystem) error { 128 if fs == nil { 129 return ErrInvalidArgument 130 } 131 132 if fs.GetMountTable() != nil { 133 return ErrFilesystemBusy 134 } 135 136 if !mt.applyFilters(fs) { 137 return ErrDeniedByFilters 138 } 139 140 mt.entries = append(mt.entries, fs) 141 fs.SetMountTable(mt) 142 return nil 143 } 144 145 // Size returns the number of filesystems present in the mount tab 146 func (mt *MountTab) Size() int { 147 return len(mt.entries) 148 } 149 150 // Find returns the first filesystem entry in the mount tab that 151 // passes all the given filters. 152 func (mt *MountTab) Find(filters ...FsFilter) *Filesystem { 153 if len(filters) == 0 { 154 return nil 155 } 156 for _, entry := range mt.entries { 157 res := true 158 for _, filter := range filters { 159 res = res && filter(entry) 160 } 161 if res { 162 return entry 163 } 164 } 165 return nil 166 } 167 168 // Entries returns all the filesystem entries in the mount tab. 169 func (mt *MountTab) Entries() []*Filesystem { 170 return mt.entries 171 } 172 173 func (mt *MountTab) parseFile() error { 174 file, err := os.Open(mt.fileName) 175 if err != nil { 176 return err 177 } 178 defer file.Close() 179 180 stream := bufio.NewScanner(file) 181 parser := NewParser(mt.format) 182 183 for stream.Scan() { 184 line := stream.Text() 185 fs, err := parser.Parse(line) 186 if err != nil { 187 return err 188 } 189 err = mt.AddFilesystem(fs) 190 if err != nil { 191 // this is a recoverable error. continue parsing further 192 if err == ErrDeniedByFilters { 193 continue 194 } 195 return err 196 } 197 } 198 return nil 199 }