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
|
// 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:]
}
|