mirror of
https://github.com/bluenviron/mediamtx.git
synced 2026-01-10 12:22:07 -08:00
Some checks are pending
code_lint / golangci_lint (push) Waiting to run
code_lint / mod_tidy (push) Waiting to run
code_lint / api_docs (push) Waiting to run
code_test / test_64 (push) Waiting to run
code_test / test_32 (push) Waiting to run
code_test / test_highlevel (push) Waiting to run
The API crashed when - '%path%' is not present in 'recordPath' - 'all_others' is in 'paths' - there's at least one recording segment A recording segment without path is parsed as a segment with an empty path. This path is then passed to FindPathConf(), that returns an error in case of empty or invalid paths. This error is not checked for performance reasons, leading to the crash. This PR prevents empty or invalid paths from reaching FindPathConf().
199 lines
4.4 KiB
Go
199 lines
4.4 KiB
Go
package recordstore
|
|
|
|
import (
|
|
"errors"
|
|
"io/fs"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/bluenviron/mediamtx/internal/conf"
|
|
)
|
|
|
|
// ErrNoSegmentsFound is returned when no recording segments have been found.
|
|
var ErrNoSegmentsFound = errors.New("no recording segments found")
|
|
|
|
var errFound = errors.New("found")
|
|
|
|
// Segment is a recording segment.
|
|
type Segment struct {
|
|
Fpath string
|
|
Start time.Time
|
|
}
|
|
|
|
func fixedPathHasSegments(pathConf *conf.Path) bool {
|
|
recordPath := PathAddExtension(
|
|
strings.ReplaceAll(pathConf.RecordPath, "%path", pathConf.Name),
|
|
pathConf.RecordFormat,
|
|
)
|
|
|
|
// we have to convert to absolute paths
|
|
// otherwise, recordPath and fpath inside Walk() won't have common elements
|
|
recordPath, _ = filepath.Abs(recordPath)
|
|
|
|
commonPath := CommonPath(recordPath)
|
|
|
|
err := filepath.WalkDir(commonPath, func(fpath string, info fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
var pa Path
|
|
ok := pa.Decode(recordPath, fpath)
|
|
if ok {
|
|
return errFound
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil && !errors.Is(err, errFound) {
|
|
return false
|
|
}
|
|
|
|
return errors.Is(err, errFound)
|
|
}
|
|
|
|
func regexpPathFindPathsWithSegments(pathConf *conf.Path) map[string]struct{} {
|
|
recordPath := PathAddExtension(
|
|
pathConf.RecordPath,
|
|
pathConf.RecordFormat,
|
|
)
|
|
|
|
// we have to convert to absolute paths
|
|
// otherwise, recordPath and fpath inside Walk() won't have common elements
|
|
recordPath, _ = filepath.Abs(recordPath)
|
|
|
|
commonPath := CommonPath(recordPath)
|
|
|
|
ret := make(map[string]struct{})
|
|
|
|
filepath.WalkDir(commonPath, func(fpath string, info fs.DirEntry, err error) error { //nolint:errcheck
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
var pa Path
|
|
if ok := pa.Decode(recordPath, fpath); ok {
|
|
if err := conf.IsValidPathName(pa.Path); err == nil {
|
|
if pathConf.Regexp.FindStringSubmatch(pa.Path) != nil {
|
|
ret[pa.Path] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
return ret
|
|
}
|
|
|
|
// FindAllPathsWithSegments returns all paths that have at least one segment.
|
|
func FindAllPathsWithSegments(pathConfs map[string]*conf.Path) []string {
|
|
pathNames := make(map[string]struct{})
|
|
|
|
for _, pathConf := range pathConfs {
|
|
if pathConf.Regexp == nil {
|
|
if fixedPathHasSegments(pathConf) {
|
|
pathNames[pathConf.Name] = struct{}{}
|
|
}
|
|
} else {
|
|
for name := range regexpPathFindPathsWithSegments(pathConf) {
|
|
pathNames[name] = struct{}{}
|
|
}
|
|
}
|
|
}
|
|
|
|
out := make([]string, len(pathNames))
|
|
n := 0
|
|
for k := range pathNames {
|
|
out[n] = k
|
|
n++
|
|
}
|
|
sort.Strings(out)
|
|
|
|
return out
|
|
}
|
|
|
|
// FindSegments returns all segments of a path.
|
|
// Segments can be filtered by start date and end date.
|
|
func FindSegments(
|
|
pathConf *conf.Path,
|
|
pathName string,
|
|
start *time.Time,
|
|
end *time.Time,
|
|
) ([]*Segment, error) {
|
|
recordPath := PathAddExtension(
|
|
strings.ReplaceAll(pathConf.RecordPath, "%path", pathName),
|
|
pathConf.RecordFormat,
|
|
)
|
|
|
|
// we have to convert to absolute paths
|
|
// otherwise, recordPath and fpath inside Walk() won't have common elements
|
|
recordPath, _ = filepath.Abs(recordPath)
|
|
|
|
commonPath := CommonPath(recordPath)
|
|
var segments []*Segment
|
|
|
|
err := filepath.WalkDir(commonPath, func(fpath string, info fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
var pa Path
|
|
ok := pa.Decode(recordPath, fpath)
|
|
|
|
// gather all segments that starts before the end of the playback
|
|
if ok && (end == nil || !end.Before(pa.Start)) {
|
|
segments = append(segments, &Segment{
|
|
Fpath: fpath,
|
|
Start: pa.Start,
|
|
})
|
|
}
|
|
}
|
|
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if segments == nil {
|
|
return nil, ErrNoSegmentsFound
|
|
}
|
|
|
|
sort.Slice(segments, func(i, j int) bool {
|
|
return segments[i].Start.Before(segments[j].Start)
|
|
})
|
|
|
|
if start != nil {
|
|
if start.Before(segments[0].Start) {
|
|
return segments, nil
|
|
}
|
|
|
|
// find the segment that may contain the start of the playback and remove all previous ones
|
|
found := false
|
|
for i := 0; i < len(segments)-1; i++ {
|
|
if !start.Before(segments[i].Start) && start.Before(segments[i+1].Start) {
|
|
segments = segments[i:]
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
|
|
// otherwise, keep the last segment only and check if it may contain the start of the playback
|
|
if !found {
|
|
segments = segments[len(segments)-1:]
|
|
if segments[len(segments)-1].Start.After(*start) {
|
|
return nil, ErrNoSegmentsFound
|
|
}
|
|
}
|
|
}
|
|
|
|
return segments, nil
|
|
}
|