Skip to content

Commit

Permalink
lang: Correctly handle stat(2) errors on directories/files
Browse files Browse the repository at this point in the history
Differentiate between ENOENT and other errors returned by stat(2) to
prevent confusing parse errors when the path provided is a directory
path. ENOENT is silently ignored as file or directory absence can be
expected depending on the specified input from the user.

Handle directory paths without trailing slashes from the user input on
the command line, correctly disambiguating directories from files with
the stat(2) result.

This fixes the rather confusing situation where mgmt would report a
parse error on the input string of the directory name as if it didn't
exist, but instead stat(2) just returned another error such as EACCES or
otherwise. Now correctly report errors that aren't ENOENT.

Before:

    $ mgmt run lang files
    2020-11-24 20:48:33.350321 I | cli: lang: input from code source: files
    2020-11-24 20:48:33.350389 I | cli: lang: lexing/parsing...
    2020-11-24 20:48:33.350528 I | run: error: cli parse error: could not generate AST: parser: `syntax error: unexpected $end` @1:1

After:

    $ mgmt run lang files
    2020-11-24 20:53:35.500436 I | run: error: cli parse error: could not activate an input parser: stat /home/frebib/mgmt/files/metadata.yaml: permission denied

Signed-off-by: Joe Groocock <[email protected]>
  • Loading branch information
frebib committed Nov 23, 2021
1 parent 4cb287a commit 5c1eaf5
Showing 1 changed file with 26 additions and 12 deletions.
38 changes: 26 additions & 12 deletions lang/inputs/inputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"

"github.com/pkg/errors"

"github.com/purpleidea/mgmt/engine"
"github.com/purpleidea/mgmt/gapi"
"github.com/purpleidea/mgmt/lang/interfaces"
Expand Down Expand Up @@ -205,6 +208,7 @@ func inputMetadata(s string, fs engine.Fs) (*ParsedInput, error) {
// real files/ directory
if metadata.Files != "" { // TODO: nil pointer instead?
filesDir := basePath + metadata.Files
// TODO: handle files that throw stat(2) errors
if _, err := fs.Stat(filesDir); err == nil {
files = append(files, filesDir)
}
Expand Down Expand Up @@ -274,25 +278,31 @@ func inputMcl(s string, fs engine.Fs) (*ParsedInput, error) {

// inputDirectory checks if we're given the path to a directory.
func inputDirectory(s string, fs engine.Fs) (*ParsedInput, error) {
if !strings.HasSuffix(s, "/") {
return nil, nil // not us, but no error
}
var err error
if s, err = absify(s); err != nil { // s is now absolute
return nil, err
}
// does dir exist?
fi, err := fs.Stat(s)
if err != nil {
return nil, errwrap.Wrapf(err, "dir: `%s` does not exist", s)
if errors.Is(err, os.ErrNotExist) {
return nil, nil // not us, but no error
}

return nil, errwrap.Wrapf(err, "could not stat dir `%s`", s)
}
if !fi.IsDir() {
return nil, errwrap.Wrapf(err, "dir: `%s` is not a dir", s)
}

if s, err = absify(s); err != nil { // s is now absolute
return nil, err
}

// try looking for a metadata file in the root
md := s + interfaces.MetadataFilename // absolute file
if _, err := fs.Stat(md); err == nil {
md := path.Join(s, interfaces.MetadataFilename) // absolute file
_, err = fs.Stat(md)
// only return errors if they're not ENOENT, e.g. EPERM, EACCES
// ignore ENOENT errors and assume that the file is deliberately absent
if err != nil && !errors.Is(err, os.ErrNotExist) {
return nil, err
} else if err == nil {
if x, err := inputMetadata(md, fs); err != nil { // recurse
return nil, err
} else if x != nil {
Expand All @@ -301,8 +311,12 @@ func inputDirectory(s string, fs engine.Fs) (*ParsedInput, error) {
}

// try looking for a main.mcl file in the root
mf := s + interfaces.MainFilename // absolute file
if _, err := fs.Stat(mf); err == nil {
mf := path.Join(s, interfaces.MainFilename) // absolute file
_, err = fs.Stat(mf)
// same error handling as above. ignore ENOENT and handle all others
if err != nil && !errors.Is(err, os.ErrNotExist) {
return nil, err
} else if err == nil {
if x, err := inputMcl(mf, fs); err != nil { // recurse
return nil, err
} else if x != nil {
Expand Down

0 comments on commit 5c1eaf5

Please sign in to comment.