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  }