apparmor.d/pkg/paths/readdir.go
2024-04-28 00:30:59 +01:00

258 lines
6.9 KiB
Go

/*
* This file is part of PathsHelper library.
*
* Copyright 2018-2022 Arduino AG (http://www.arduino.cc/)
*
* PathsHelper library is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* As a special exception, you may use this file as part of a free software
* library without restriction. Specifically, if other files instantiate
* templates or use macros or inline functions from this file, or you compile
* this file and link it with other files to produce an executable, this
* file does not by itself cause the resulting executable to be covered by
* the GNU General Public License. This exception does not however
* invalidate any other reasons why the executable file might be covered by
* the GNU General Public License.
*/
package paths
import (
"errors"
"os"
"strings"
)
// ReadDirFilter is a filter for Path.ReadDir and Path.ReadDirRecursive methods.
// The filter should return true to accept a file or false to reject it.
type ReadDirFilter func(file *Path) bool
// ReadDir returns a PathList containing the content of the directory
// pointed by the current Path. The resulting list is filtered by the given filters chained.
func (p *Path) ReadDir(filters ...ReadDirFilter) (PathList, error) {
infos, err := os.ReadDir(p.path)
if err != nil {
return nil, err
}
accept := func(p *Path) bool {
for _, filter := range filters {
if !filter(p) {
return false
}
}
return true
}
paths := PathList{}
for _, info := range infos {
path := p.Join(info.Name())
if !accept(path) {
continue
}
paths.Add(path)
}
return paths, nil
}
// ReadDirRecursive returns a PathList containing the content of the directory
// and its subdirectories pointed by the current Path
func (p *Path) ReadDirRecursive() (PathList, error) {
return p.ReadDirRecursiveFiltered(nil)
}
// ReadDirRecursiveFiltered returns a PathList containing the content of the directory
// and its subdirectories pointed by the current Path, filtered by the given skipFilter
// and filters:
// - `recursionFilter` is a filter that is checked to determine if the subdirectory must
// by visited recursively (if the filter rejects the entry, the entry is not visited
// but can still be added to the result)
// - `filters` are the filters that are checked to determine if the entry should be
// added to the resulting PathList
func (p *Path) ReadDirRecursiveFiltered(recursionFilter ReadDirFilter, filters ...ReadDirFilter) (PathList, error) {
var search func(*Path) (PathList, error)
explored := map[string]bool{}
search = func(currPath *Path) (PathList, error) {
canonical := currPath.Canonical().path
if explored[canonical] {
return nil, errors.New("directories symlink loop detected")
}
explored[canonical] = true
defer delete(explored, canonical)
infos, err := os.ReadDir(currPath.path)
if err != nil {
return nil, err
}
accept := func(p *Path) bool {
for _, filter := range filters {
if !filter(p) {
return false
}
}
return true
}
paths := PathList{}
for _, info := range infos {
path := currPath.Join(info.Name())
if accept(path) {
paths.Add(path)
}
if recursionFilter == nil || recursionFilter(path) {
if path.IsDir() {
subPaths, err := search(path)
if err != nil {
return nil, err
}
paths.AddAll(subPaths)
}
}
}
return paths, nil
}
return search(p)
}
// FilterDirectories is a ReadDirFilter that accepts only directories
func FilterDirectories() ReadDirFilter {
return func(path *Path) bool {
return path.IsDir()
}
}
// FilterOutDirectories is a ReadDirFilter that rejects all directories
func FilterOutDirectories() ReadDirFilter {
return func(path *Path) bool {
return !path.IsDir()
}
}
// FilterNames is a ReadDirFilter that accepts only the given filenames
func FilterNames(allowedNames ...string) ReadDirFilter {
return func(file *Path) bool {
for _, name := range allowedNames {
if file.Base() == name {
return true
}
}
return false
}
}
// FilterOutNames is a ReadDirFilter that rejects the given filenames
func FilterOutNames(rejectedNames ...string) ReadDirFilter {
return func(file *Path) bool {
for _, name := range rejectedNames {
if file.Base() == name {
return false
}
}
return true
}
}
// FilterSuffixes creates a ReadDirFilter that accepts only the given
// filename suffixes
func FilterSuffixes(allowedSuffixes ...string) ReadDirFilter {
return func(file *Path) bool {
for _, suffix := range allowedSuffixes {
if strings.HasSuffix(file.String(), suffix) {
return true
}
}
return false
}
}
// FilterOutSuffixes creates a ReadDirFilter that rejects all the given
// filename suffixes
func FilterOutSuffixes(rejectedSuffixes ...string) ReadDirFilter {
return func(file *Path) bool {
for _, suffix := range rejectedSuffixes {
if strings.HasSuffix(file.String(), suffix) {
return false
}
}
return true
}
}
// FilterPrefixes creates a ReadDirFilter that accepts only the given
// filename prefixes
func FilterPrefixes(allowedPrefixes ...string) ReadDirFilter {
return func(file *Path) bool {
name := file.Base()
for _, prefix := range allowedPrefixes {
if strings.HasPrefix(name, prefix) {
return true
}
}
return false
}
}
// FilterOutPrefixes creates a ReadDirFilter that rejects all the given
// filename prefixes
func FilterOutPrefixes(rejectedPrefixes ...string) ReadDirFilter {
return func(file *Path) bool {
name := file.Base()
for _, prefix := range rejectedPrefixes {
if strings.HasPrefix(name, prefix) {
return false
}
}
return true
}
}
// OrFilter creates a ReadDirFilter that accepts all items that are accepted
// by any (at least one) of the given filters
func OrFilter(filters ...ReadDirFilter) ReadDirFilter {
return func(path *Path) bool {
for _, f := range filters {
if f(path) {
return true
}
}
return false
}
}
// AndFilter creates a ReadDirFilter that accepts all items that are accepted
// by all the given filters
func AndFilter(filters ...ReadDirFilter) ReadDirFilter {
return func(path *Path) bool {
for _, f := range filters {
if !f(path) {
return false
}
}
return true
}
}
// NotFilter creates a ReadDifFilter that accepts all items rejected by x and viceversa
func NotFilter(x ReadDirFilter) ReadDirFilter {
return func(path *Path) bool {
return !x(path)
}
}