blob: 91c5801f16393225bf981132be250f09f23f6cb2 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// +build !build_with_native_toolchain
package pkgfs
import (
"log"
"path/filepath"
"strings"
"time"
"go.fuchsia.dev/fuchsia/src/lib/thinfs/fs"
)
type packagesRoot struct {
unsupportedDirectory
fs *Filesystem
enforceNonStaticAllowlist bool
}
func (pr *packagesRoot) isAllowedNonStaticPackage(packageName string) bool {
if pr.fs.allowedNonStaticPackages == nil {
return false
}
return pr.fs.allowedNonStaticPackages.Contains(packageName)
}
func dynamicIndexHasPackageName(f *Filesystem, name string) bool {
pkgs := f.index.List()
found := false
for _, p := range pkgs {
if p.Name == name {
found = true
break
}
}
return found
}
func (pr *packagesRoot) Dup() (fs.Directory, error) {
return pr, nil
}
func (pr *packagesRoot) Close() error { return nil }
func (pr *packagesRoot) Open(name string, flags fs.OpenFlags) (fs.File, fs.Directory, *fs.Remote, error) {
name = clean(name)
if name == "" {
return nil, pr, nil, nil
}
parts := strings.Split(name, "/")
packageName := parts[0]
// Check for /pkgfs/packages visibility restrictions - we only want to show base packages, and packages on an allowlist
if pr.fs.static == nil || (pr.fs.static != nil && !pr.fs.static.HasName(packageName)) {
// This package isn't in the static index
if !pr.isAllowedNonStaticPackage(packageName) {
// This package isn't allowed by the allowlist to show up
// Check if the package is currently on the disk
// If it's not on the disk at all, we'll return ErrNotFound later, so do nothing special here
if dynamicIndexHasPackageName(pr.fs, packageName) {
// This package is on the system (indicated by presence in the _dynamic_ index),
// but we don't want it to be accessed. Log an error
log.Printf("pkgfs: attempted open of non-static package %s from /pkgfs/packages, which is deprecated. Please use the package resolver for accessing package directories, or /pkg to access your own package. See fxbug.dev/44527. Full path: %s", packageName, name)
if pr.enforceNonStaticAllowlist {
return nil, nil, nil, fs.ErrPermission
}
}
}
}
pld, err := newPackageListDir(packageName, pr.fs)
if err != nil {
// If the package isn't on the system at all, we'll return an error here
return nil, nil, nil, err
}
if len(parts) > 1 {
// We were asked for a specific variant, so return a package directory itself, not just the list of variants
file, directory, remote, err := pld.Open(filepath.Join(parts[1:]...), flags)
return file, directory, remote, err
}
// Return the list of variants
return nil, pld, nil, nil
}
func (pr *packagesRoot) Read() ([]fs.Dirent, error) {
var names = map[string]struct{}{}
if pr.fs.static != nil {
pkgs, err := pr.fs.static.List()
if err != nil {
return nil, err
}
for _, p := range pkgs {
names[p.Name] = struct{}{}
}
}
pkgs := pr.fs.index.List()
for _, p := range pkgs {
// Check visibility restrictions - only return non-base packages on an allowlist
if pr.enforceNonStaticAllowlist && !pr.isAllowedNonStaticPackage(p.Name) {
continue
}
names[p.Name] = struct{}{}
}
dirents := make([]fs.Dirent, 0, len(names))
for name := range names {
dirents = append(dirents, dirDirEnt(name))
}
return dirents, nil
}
func (pr *packagesRoot) Stat() (int64, time.Time, time.Time, error) {
// TODO(raggi): stat the index directory and pass on info
return 0, pr.fs.mountTime, pr.fs.mountTime, nil
}
// packageListDir is a directory in the pkgfs packages directory for an
// individual package that lists all versions of packages
type packageListDir struct {
unsupportedDirectory
fs *Filesystem
packageName string
}
func newPackageListDir(name string, f *Filesystem) (*packageListDir, error) {
if !f.static.HasName(name) {
if !dynamicIndexHasPackageName(f, name) {
return nil, fs.ErrNotFound
}
}
pld := packageListDir{
unsupportedDirectory: unsupportedDirectory(filepath.Join("/packages", name)),
fs: f,
packageName: name,
}
return &pld, nil
}
func (pld *packageListDir) Dup() (fs.Directory, error) {
return pld, nil
}
func (pld *packageListDir) Close() error {
return nil
}
func (pld *packageListDir) Open(name string, flags fs.OpenFlags) (fs.File, fs.Directory, *fs.Remote, error) {
name = clean(name)
if name == "" {
return nil, pld, nil, nil
}
parts := strings.Split(name, "/")
d, err := newPackageDir(pld.packageName, parts[0], pld.fs, pld.fs.shouldAllowExecutableOpenByPackageName(pld.packageName))
if err != nil {
return nil, nil, nil, err
}
if len(parts) > 1 {
return d.Open(filepath.Join(parts[1:]...), flags)
}
return nil, d, nil, nil
}
func (pld *packageListDir) Read() ([]fs.Dirent, error) {
if pld.fs.static != nil && pld.fs.static.HasName(pld.packageName) {
versions := pld.fs.static.ListVersions(pld.packageName)
dirents := make([]fs.Dirent, len(versions))
for i := range versions {
dirents[i] = dirDirEnt(versions[i])
}
return dirents, nil
}
var dirents []fs.Dirent
pkgs := pld.fs.index.List()
for _, p := range pkgs {
if p.Name == pld.packageName {
dirents = append(dirents, dirDirEnt(p.Version))
}
}
return dirents, nil
}
func (pld *packageListDir) Stat() (int64, time.Time, time.Time, error) {
// TODO(raggi): stat the index directory and pass on info
return 0, pld.fs.mountTime, pld.fs.mountTime, nil
}