mirror of https://github.com/kubernetes/kops.git
104 lines
3.2 KiB
Go
104 lines
3.2 KiB
Go
// Copyright 2023 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package configfsi
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"path"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
// TsmPrefix is the path to the configfs tsm system.
|
|
TsmPrefix = "/sys/kernel/config/tsm"
|
|
|
|
// How many random characters to use when replacing * in a temporary path pattern.
|
|
randomPathSize = 10
|
|
)
|
|
|
|
// TsmPath represents a configfs file path decomposed into the components
|
|
// that are expected for TSM.
|
|
type TsmPath struct {
|
|
// Subsystem is the TSM subsystem the path is targeting, e.g., "report"
|
|
Subsystem string
|
|
// Entry is the directory under the subsystem that represents a single
|
|
// user's interface with the subsystem.
|
|
Entry string
|
|
// Attribute is a file under Entry that may be readable or writable depending
|
|
// on its name.
|
|
Attribute string
|
|
}
|
|
|
|
// String returns the configfs path that the TsmPath stands for.
|
|
func (p *TsmPath) String() string {
|
|
return path.Join(TsmPrefix, p.Subsystem, p.Entry, p.Attribute)
|
|
}
|
|
|
|
// ParseTsmPath decomposes a configfs path to TSM into its expected format, or returns
|
|
// an error.
|
|
func ParseTsmPath(filepath string) (*TsmPath, error) {
|
|
p := path.Clean(filepath)
|
|
if !strings.HasPrefix(p, TsmPrefix) {
|
|
return nil, fmt.Errorf("%q does not begin with %q", p, TsmPrefix)
|
|
}
|
|
// If just the tsm folder is given, there won't be a "/", but if there is a subpath,
|
|
// then it will have the leading "/".
|
|
rest := strings.TrimPrefix(strings.TrimPrefix(p, TsmPrefix), "/")
|
|
if rest == "" {
|
|
return nil, fmt.Errorf("%q does not contain a subsystem", p)
|
|
}
|
|
|
|
dir := path.Dir(rest)
|
|
file := path.Base(rest)
|
|
if dir == "." {
|
|
return &TsmPath{Subsystem: file}, nil
|
|
}
|
|
gdir := path.Dir(dir) // grand-dir
|
|
mfile := path.Base(dir)
|
|
if gdir == "." {
|
|
return &TsmPath{Subsystem: mfile, Entry: file}, nil
|
|
}
|
|
ggdir := path.Dir(gdir) // grand-grand-dir
|
|
subsystem := path.Base(gdir)
|
|
if ggdir != "." {
|
|
return nil, fmt.Errorf("%q suffix expected to be of form subsystem[/entry[/attribute]] (debug %q)", rest, ggdir)
|
|
}
|
|
return &TsmPath{Subsystem: subsystem, Entry: mfile, Attribute: file}, nil
|
|
}
|
|
|
|
func readableString(data []byte) string {
|
|
var sb strings.Builder
|
|
for _, b := range data {
|
|
sb.WriteRune(rune('0' + (b % 10)))
|
|
}
|
|
return sb.String()
|
|
}
|
|
|
|
// TempName returns a random filename following the pattern semantics
|
|
// of os.MkdirTemp. Does not have a root directory.
|
|
func TempName(rand io.Reader, pattern string) string {
|
|
data := make([]byte, randomPathSize)
|
|
if n, err := rand.Read(data); err != nil || n != len(data) {
|
|
return "rdfail"
|
|
}
|
|
randString := readableString(data)
|
|
lastAsterisk := strings.LastIndex(pattern, "*")
|
|
if lastAsterisk == -1 {
|
|
return pattern + randString
|
|
}
|
|
return pattern[0:lastAsterisk] + randString + pattern[lastAsterisk+1:]
|
|
}
|