github.com/anchore/syft@v1.38.2/syft/file/location.go (about)

     1  package file
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/go-multierror"
     7  
     8  	"github.com/anchore/stereoscope/pkg/file"
     9  	"github.com/anchore/stereoscope/pkg/image"
    10  )
    11  
    12  const (
    13  	// VisibleAnnotationKey is the key used to indicate if the location is visible or not at runtime
    14  	VisibleAnnotationKey = "visible"
    15  
    16  	// HiddenAnnotation is the value used to indicate that the location is not visible at runtime because it was deleted
    17  	HiddenAnnotation = "false"
    18  
    19  	// VisibleAnnotation is the value used to indicate that the location is visible at runtime
    20  	VisibleAnnotation = "true"
    21  )
    22  
    23  // Location represents a path relative to a particular filesystem resolved to a specific file.Reference. This struct is used as a key
    24  // in content fetching to uniquely identify a file relative to a request (the AccessPath).
    25  type Location struct {
    26  	LocationData     `cyclonedx:""`
    27  	LocationMetadata `cyclonedx:""`
    28  }
    29  
    30  // LocationData contains the core identifying information for a file location.
    31  type LocationData struct {
    32  	Coordinates `cyclonedx:""` // Empty string here means there is no intermediate property name, e.g. syft:locations:0:path without "coordinates"
    33  	// note: it is IMPORTANT to ignore anything but the coordinates for a Location when considering the ID (hash value)
    34  	// since the coordinates are the minimally correct ID for a location (symlinks should not come into play)
    35  
    36  	// AccessPath is the path used to retrieve file contents (which may or may not have hardlinks / symlinks in the path)
    37  	AccessPath string `hash:"ignore" json:"accessPath"`
    38  
    39  	// ref is the stereoscope file reference relative to the stereoscope.FileCatalog that has more information about this location.
    40  	ref file.Reference `hash:"ignore"`
    41  }
    42  
    43  func (l LocationData) Reference() file.Reference {
    44  	return l.ref
    45  }
    46  
    47  // LocationMetadata provides additional contextual information about a file location.
    48  type LocationMetadata struct {
    49  	Annotations map[string]string `json:"annotations,omitempty"` // Arbitrary key-value pairs that can be used to annotate a location
    50  }
    51  
    52  func (m *LocationMetadata) merge(other LocationMetadata) error {
    53  	var errs error
    54  	for k, v := range other.Annotations {
    55  		if otherV, ok := m.Annotations[k]; ok {
    56  			if v != otherV {
    57  				err := fmt.Errorf("unable to merge location metadata: conflicting values for key=%q: %q != %q", k, v, otherV)
    58  				errs = multierror.Append(errs, err)
    59  				continue
    60  			}
    61  		}
    62  		m.Annotations[k] = v
    63  	}
    64  	return errs
    65  }
    66  
    67  func (l Location) WithAnnotation(key, value string) Location {
    68  	if key == "" || value == "" {
    69  		return l
    70  	}
    71  	if l.Annotations == nil {
    72  		l.Annotations = map[string]string{}
    73  	}
    74  	l.Annotations[key] = value
    75  	return l
    76  }
    77  
    78  func (l Location) WithoutAnnotations() Location {
    79  	l.Annotations = map[string]string{}
    80  
    81  	return l
    82  }
    83  
    84  // NewLocation creates a new Location representing a path without denoting a filesystem or FileCatalog reference.
    85  func NewLocation(realPath string) Location {
    86  	return Location{
    87  		LocationData: LocationData{
    88  			Coordinates: Coordinates{
    89  				RealPath: realPath,
    90  			},
    91  			AccessPath: realPath,
    92  		},
    93  		LocationMetadata: LocationMetadata{
    94  			Annotations: map[string]string{},
    95  		},
    96  	}
    97  }
    98  
    99  // NewVirtualLocation creates a new location for a path accessed by a virtual path (a path with a symlink or hardlink somewhere in the path)
   100  func NewVirtualLocation(realPath, accessPath string) Location {
   101  	return Location{
   102  		LocationData: LocationData{
   103  			Coordinates: Coordinates{
   104  				RealPath: realPath,
   105  			},
   106  			AccessPath: accessPath,
   107  		},
   108  		LocationMetadata: LocationMetadata{
   109  			Annotations: map[string]string{},
   110  		}}
   111  }
   112  
   113  // NewLocationFromCoordinates creates a new location for the given Coordinates.
   114  func NewLocationFromCoordinates(coordinates Coordinates) Location {
   115  	return Location{
   116  		LocationData: LocationData{
   117  			Coordinates: coordinates,
   118  			AccessPath:  coordinates.RealPath,
   119  		},
   120  		LocationMetadata: LocationMetadata{
   121  			Annotations: map[string]string{},
   122  		}}
   123  }
   124  
   125  // NewVirtualLocationFromCoordinates creates a new location for the given Coordinates via a virtual path.
   126  func NewVirtualLocationFromCoordinates(coordinates Coordinates, accessPath string) Location {
   127  	return Location{
   128  		LocationData: LocationData{
   129  			Coordinates: coordinates,
   130  			AccessPath:  accessPath,
   131  		},
   132  		LocationMetadata: LocationMetadata{
   133  			Annotations: map[string]string{},
   134  		}}
   135  }
   136  
   137  // NewLocationFromImage creates a new Location representing the given path (extracted from the Reference) relative to the given image.
   138  func NewLocationFromImage(accessPath string, ref file.Reference, img *image.Image) Location {
   139  	layer := img.FileCatalog.Layer(ref)
   140  	return Location{
   141  		LocationData: LocationData{
   142  			Coordinates: Coordinates{
   143  				RealPath:     string(ref.RealPath),
   144  				FileSystemID: layer.Metadata.Digest,
   145  			},
   146  			AccessPath: accessPath,
   147  			ref:        ref,
   148  		},
   149  		LocationMetadata: LocationMetadata{
   150  			Annotations: map[string]string{},
   151  		},
   152  	}
   153  }
   154  
   155  // NewLocationFromDirectory creates a new Location representing the given path (extracted from the Reference) relative to the given directory.
   156  func NewLocationFromDirectory(responsePath string, ref file.Reference) Location {
   157  	return Location{
   158  		LocationData: LocationData{
   159  			Coordinates: Coordinates{
   160  				RealPath: responsePath,
   161  			},
   162  			AccessPath: responsePath,
   163  			ref:        ref,
   164  		},
   165  		LocationMetadata: LocationMetadata{
   166  			Annotations: map[string]string{},
   167  		},
   168  	}
   169  }
   170  
   171  // NewVirtualLocationFromDirectory creates a new Location representing the given path (extracted from the Reference) relative to the given directory with a separate virtual access path.
   172  func NewVirtualLocationFromDirectory(responsePath, responseAccessPath string, ref file.Reference) Location {
   173  	return Location{
   174  		LocationData: LocationData{
   175  			Coordinates: Coordinates{
   176  				RealPath: responsePath,
   177  			},
   178  			AccessPath: responseAccessPath,
   179  			ref:        ref,
   180  		},
   181  		LocationMetadata: LocationMetadata{
   182  			Annotations: map[string]string{},
   183  		},
   184  	}
   185  }
   186  
   187  func (l Location) Path() string {
   188  	if l.AccessPath != "" {
   189  		return l.AccessPath
   190  	}
   191  	return l.RealPath
   192  }
   193  
   194  func (l Location) String() string {
   195  	str := ""
   196  	if l.ref.ID() != 0 {
   197  		str += fmt.Sprintf("id=%d ", l.ref.ID())
   198  	}
   199  
   200  	str += fmt.Sprintf("RealPath=%q", l.RealPath)
   201  
   202  	if l.AccessPath != "" && l.AccessPath != l.RealPath {
   203  		str += fmt.Sprintf(" AccessPath=%q", l.AccessPath)
   204  	}
   205  
   206  	if l.FileSystemID != "" {
   207  		str += fmt.Sprintf(" Layer=%q", l.FileSystemID)
   208  	}
   209  	return fmt.Sprintf("Location<%s>", str)
   210  }
   211  
   212  func (l Location) Equals(other Location) bool {
   213  	return l.RealPath == other.RealPath &&
   214  		l.AccessPath == other.AccessPath &&
   215  		l.FileSystemID == other.FileSystemID
   216  }