eos

package
v3.7.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 17, 2026 License: Apache-2.0 Imports: 42 Imported by: 1

README

EOS Storage Driver

This package implements a Storage Provider for EOS.

What is EOS?

EOS is a distributed storage system built around a namespace service (MGM, or Management node) and storage servers (FSTs, FileStore Targets). The MGM handles all metadata and namespace operations; actual file data lives on FSTs and is accessed via the XRootD protocol (or HTTP). EOS provides versioning, a recycle bin, extended attributes, ACLs, quotas, and file locking natively.

Package structure

pkg/storage/fs/eos/
├── eos.go               # Entry point: registers the "eos" driver, creates EosFS
├── client/
│   ├── eosclient.go     # EOSClient interface, shared types (FileInfo, Authorization, …)
│   ├── utils.go         # Version folder path helpers, attribute key utilities
│   ├── binary/
│   │   └── eosbinary.go # Client implementation: shells out to the `eos` CLI + xrdcopy
│   └── grpc/
│       ├── eosgrpc.go   # Client implementation: EOS gRPC for metadata
│       ├── eoshttp.go   # HTTP for data I/O (reads/writes to FSTs)
│       ├── acl.go       # ACL operations over gRPC
│       ├── attributes.go
│       ├── auth.go      # Token generation
│       ├── file_info.go
│       ├── file_ops.go
│       ├── quota.go
│       ├── recycle.go
│       └── versions.go
├── config.go             # Config struct with all tunables
├── eosfs.go              # Eosfs struct: init, path wrap/unwrap, convert, permission set
├── auth.go               # Auth resolution: UID/GID, tokens, daemon/single-user modes
├── file_ops.go           # Upload, Download, CreateDir, Move, Delete, ListFolder, …
├── file_info.go          # GetMD, GetPathByID, getPath (inode → path)
├── grant.go              # CS3 grant ↔ EOS ACL translation
├── locks.go              # File locking (app.lock + payload xattr)
├── recycle.go            # Recycle bin list / restore / purge
├── revisions.go          # File version list / download / restore
├── quota.go              # Quota reporting
└── arbitrary_metadata.go # user.* xattrs exposed as arbitrary metadata

Architecture

Client abstraction (client/eosclient.go)

The EOSClient interface decouples the filesystem logic from the transport used to reach EOS. Two implementations exist, selected at init time via the use_grpc config key:

Binary client gRPC client
Metadata ops Shells out to /usr/bin/eos EOS gRPC API (cern-eos/go-eosgrpc)
Data I/O xrdcopy binary HTTP to FSTs via eoshttp.go
Auth Keytab / uid-gid env vars Keytab or pre-shared auth key
Streaming No Yes (optional temp-file buffering)

The binary client was the original driver and remains useful when a full EOS installation is available on the same host. It is slower and less secure, but easier to set up. This can be used for demo's.

The gRPC client is a more modern approach, and is recommended for production usage. For production usage, it is also recommended to set up TLS. For this, point http_client_certfile and http_client_keyfile to the correct files. If you want to force TLS, set allow_insecure to false (this is the default). The gRPC client uses two separate channels: gRPC to the MGM for all namespace operations (stat, list, mkdir, rename, ACL, xattr, quota, recycle, versions) and HTTP to the FSTs for actual file data.

Path handling

EOS paths are absolute on the cluster (/eos/user/j/jsmith/…). Storage providers operate on paths relative to the mount path, which is set up in the storage provider's config under mount_path. So, paths that arrive at the SP are relative to this mount path. On file operations, a namespace is wrapped / unwrapped:

  • wrap(ctx, path) → prepends conf.Namespace to get the EOS-absolute path
  • unwrap(ctx, internal) → strips conf.Namespace to get the SP-relative path

As an example, imagine that EOS has paths like /eos/user/j/jsmith/…, but you want to expose them as /users/j/jsmith. Then you would set the mount_path to /users. Requests come in at the SP with paths like /j/jsmith. The namespace should then be set to /eos/user, which means requests go out to EOS as /eos/user/j/jsmith. Responses from EOS are then "unwrapped" to become /j/jsmith again, before being prepended by the mount_path again (which is done by the Storage Provider).

The UserLayout template (e.g. {{.Username}} or {{substr 0 1 .Username}}/{{.Username}}) is used when constructing home directories from the namespace root.

Authorization

Each call to EOS must carry authentication. The driver resolves the right auth on a per-request basis.

Rest of this: TODO after refactoring

UID/GID resolution cache

EOS stores file ownership as integer UIDs. Reva and CS3 work with opaque string user IDs. The driver maintains a TTL cache (userIDCache, up to user_id_cache_size entries) that maps between the two, using the Reva gateway (GetUser / GetUserByClaim) as the source of truth.

On startup a background goroutine (userIDcacheWarmup) walks the namespace to user_id_cache_warmup_depth levels and pre-populates the cache, which is important for latency on large deployments.

Versioning

EOS stores each version of a file in a hidden sibling folder named .sys.v#.<filename>. The driver:

  • Filters these folders from directory listings (matched by hiddenReg).
  • Exposes versions through ListRevisions / DownloadRevision / RestoreRevision.
  • With version_invariant: true (the default), performs an extra stat to ensure the inode returned for a file always refers to the current version, not the version folder. This keeps clients that cache inodes consistent after an overwrite.
Recycle bin

EOS recycle bins are per-user. The driver handles two cases:

  • User-owned spaces — EOS recycle bin of the file's owner. The driver impersonates the owner when listing/restoring/purging.
  • Project spaces (ownerless) — Identified by the sys.forced.recycleid EOS xattr. In this case a fixed recycleid is passed to the EOS recycle APIs rather than a user identity.

ListRecycle enforces a configurable date window (max_days_in_recycle_list) and entry count (max_recycle_entries) to prevent unbounded responses.

ACLs and grants

EOS uses its own ACL syntax stored in the sys.acl extended attribute. The driver translates between EOS ACL entries and CS3 Grant objects:

  • User grants → EOS u:<uid>:<perms> entries.
  • Group grants → EOS egroup:<name>:<perms> entries.
  • Lightweight account grants — EOS has no concept of lightweight users. These are stored as plain xattrs (sys.reva.lwshare.<opaqueId> = <perms>) and merged into the ACL evaluation at permission-check time.

Grant evaluation (permissionSet) also handles public share roles (viewer/uploader/editor) and OCM share roles, short-circuiting before inspecting ACLs.

File locking

Locking uses two xattrs:

  • sys.app.lock — The EOS-native lock attribute. EOS enforces this server-side, blocking writes from applications with a mismatched tag. Format: expires:<unix>,type:shared,owner:<user>:<app>.
  • sys.reva.lockpayload — Base64-encoded JSON of the full CS3 Lock proto. Reva reads this to serve GetLock.

The EOS lock type is always shared (EOS does not have an exclusive lock), but is sufficient for WOPI-style app locking and checkout/checkin workflows.

The app parameter passed to every write and xattr operation is derived from the lock holder's app name, encoded as http/reva_<appname>. This ensures EOS's lock enforcement operates correctly.

Data I/O (gRPC client)

Reads and writes go through eoshttp.go, which speaks directly to EOS FSTs over HTTPS using mutual TLS or an auth key. The base URL is the MGM URL (EOS redirects to the actual FST).

Two modes are supported for both directions:

  • Streaming (default) — data flows directly between the caller and EOS without touching disk on the Reva host.
  • Local temp (read_uses_local_temp / write_uses_local_temp) — data is buffered in cache_directory first. Required when the FST does not support HTTP chunked encoding on uploads.
Chunked uploads (binary client / legacy)

The binary client handles the OwnCloud-style chunked upload protocol via chunking.ChunkHandler. Chunks are assembled on the local filesystem before being written to EOS. The gRPC client does not use this mechanism; chunked uploads should be disabled at the gateway level for gRPC deployments.

Configuration reference

Key config options (see fs/config.go for the full list):

Key Description
namespace EOS path prefix (e.g. /eos/user/)
quota_node EOS path used for quota queries; defaults to namespace
use_grpc true → gRPC+HTTP client; false (default) → binary client
master_grpc_uri gRPC endpoint of the EOS MGM (e.g. eos-mgm.example.org:50051)
master_url XRootD/HTTP base URL (e.g. root://eos-mgm.example.org)
grpc_auth_key Shared secret for gRPC calls
https_auth_key Shared secret for HTTP data calls
keytab / use_keytab Kerberos keytab authentication
user_layout Go template for user home path (e.g. {{substr 0 1 .Username}}/{{.Username}})
version_invariant Stable inodes across file versions (default true)
enable_home_creation Allow CreateHome calls; requires create_home_hook
create_home_hook Path to a script invoked to provision new home directories
force_single_user_mode / single_username Impersonate one account for all calls
read_uses_local_temp / write_uses_local_temp Buffer I/O via cache_directory
user_id_cache_size Max entries in the UID ↔ CS3 user ID cache (default 1 000 000)
user_id_cache_warmup_depth Namespace walk depth on startup for cache warmup (default 2)
max_recycle_entries Max items returned by ListRecycle (default 2000)
max_days_in_recycle_list Max date span for ListRecycle (default 14 days)

Unsupported operations

The following storage.FS methods return errtypes.NotSupported:

  • GetHome — home path is handled by the spaces registry, not this driver.
  • CreateStorageSpace / ListStorageSpaces / UpdateStorageSpace — spaces are managed by an external spaces registry.
  • EmptyRecycle — only targeted purge of individual items is supported.

EOS documentation

Documentation

Index

Constants

View Source
const (
	// SystemAttr is the system extended attribute.
	SystemAttr eosclient.AttrType = iota
	// UserAttr is the user extended attribute.
	UserAttr
)

Variables

This section is empty.

Functions

func New

func New(ctx context.Context, m map[string]any) (storage.FS, error)

New returns a new implementation of the storage.FS interface that connects to EOS.

func NewEOSFS added in v3.6.0

func NewEOSFS(ctx context.Context, c *Config) (storage.FS, error)

NewEOSFS returns a storage.FS interface implementation that connects to an EOS instance.

Types

type Config added in v3.6.0

type Config struct {
	// Namespace for metadata operations
	Namespace string `mapstructure:"namespace"`

	// QuotaNode for storing quota information
	QuotaNode string `mapstructure:"quota_node"`

	// Location of the eos binary.
	// Default is /usr/bin/eos.
	EosBinary string `mapstructure:"eos_binary"`

	// Location of the xrdcopy binary.
	// Default is /opt/eos/xrootd/bin/xrdcopy.
	XrdcopyBinary string `mapstructure:"xrdcopy_binary"`

	// URL of the Master EOS MGM.
	// Default is root://eos-example.org
	MasterURL string `mapstructure:"master_url"`

	// URL of the Slave EOS MGM.
	// Default is root://eos-example.org
	SlaveURL string `mapstructure:"slave_url"`

	// Location on the local fs where to store reads.
	// Defaults to os.TempDir()
	CacheDirectory string `mapstructure:"cache_directory"`

	// SecProtocol specifies the xrootd security protocol to use between the server and EOS.
	SecProtocol string `mapstructure:"sec_protocol"`

	// Keytab specifies the location of the keytab to use to authenticate to EOS.
	Keytab string `mapstructure:"keytab"`

	// SingleUsername is the username to use when SingleUserMode is enabled
	SingleUsername string `mapstructure:"single_username"`

	// UserLayout wraps the internal path with user information.
	// Example: if conf.Namespace is /eos/user and received path is /docs
	// and the UserLayout is {{.Username}} the internal path will be:
	// /eos/user/<username>/docs
	UserLayout string `mapstructure:"user_layout"`

	// Enables logging of the commands executed
	// Defaults to false
	EnableLogging bool `mapstructure:"enable_logging"`

	// ShowHiddenSysFiles shows internal EOS files like
	// .sys.v# and .sys.a# files.
	ShowHiddenSysFiles bool `mapstructure:"show_hidden_sys_files"`

	// UseKeyTabAuth changes will authenticate requests by using an EOS keytab.
	UseKeytab bool `mapstructure:"use_keytab"`

	// EnableHome enables the creation of home directories.
	EnableHomeCreation bool `mapstructure:"enable_home_creation"`

	// Whether to maintain the same inode across various versions of a file.
	// Requires extra metadata operations if set to true
	VersionInvariant bool `mapstructure:"version_invariant"`

	// UseGRPC controls whether we spawn eosclient processes or use GRPC to connect to EOS.
	UseGRPC bool `mapstructure:"use_grpc"`

	// GatewaySvc stores the endpoint at which the GRPC gateway is exposed.
	GatewaySvc string `mapstructure:"gatewaysvc"`

	// GRPCAuthkey is the key that authorizes this client to connect to the EOS GRPC service
	GRPCAuthkey string `mapstructure:"grpc_auth_key"`

	// HTTPSAuthkey is the key that authorizes this client to connect to the EOS HTTPS service
	HTTPSAuthkey string `mapstructure:"https_auth_key"`

	// URI of the EOS MGM grpc server
	// Default is empty
	GrpcURI string `mapstructure:"master_grpc_uri"`

	// Size of the cache used to store user ID and UID resolution.
	// Default value is 1000000.
	UserIDCacheSize int `mapstructure:"user_id_cache_size"`

	// The depth, starting from root, that we'll parse directories to lookup the
	// owner and warm up the cache. For example, for a layout of {{substr 0 1 .Username}}/{{.Username}}
	// and a depth of 2, we'll lookup each user's home directory.
	// Default value is 2.
	UserIDCacheWarmupDepth int `mapstructure:"user_id_cache_warmup_depth"`

	// Normally the eosgrpc plugin streams data on the fly.
	// Setting this to true will make reva use the temp cachedirectory
	// as intermediate step for read operations
	ReadUsesLocalTemp bool `mapstructure:"read_uses_local_temp"`

	// Normally the eosgrpc plugin streams data on the fly.
	// Setting this to true will make reva use the temp cachedirectory
	// as intermediate step for write operations
	// Beware: in pure streaming mode the FST must support
	// the HTTP chunked encoding
	WriteUsesLocalTemp bool `mapstructure:"write_uses_local_temp"`

	// Whether to allow recycle operations on base paths.
	// If set to true, we'll look up the owner of the passed path and perform
	// operations on that user's recycle bin.
	AllowPathRecycleOperations bool `mapstructure:"allow_path_recycle_operations"`

	// HTTP connections to EOS: max number of idle conns
	MaxIdleConns int `mapstructure:"max_idle_conns"`

	// HTTP connections to EOS: max number of conns per host
	MaxConnsPerHost int `mapstructure:"max_conns_per_host"`

	// HTTP connections to EOS: max number of idle conns per host
	MaxIdleConnsPerHost int `mapstructure:"max_idle_conns_per_host"`

	// HTTP connections to EOS: idle conections TTL
	IdleConnTimeout int `mapstructure:"idle_conn_timeout"`

	// HTTP connections to EOS: client certificate (usually a X509 host certificate)
	ClientCertFile string `mapstructure:"http_client_certfile"`
	// HTTP connections to EOS: client certificate key (usually a X509 host certificate)
	ClientKeyFile string `mapstructure:"http_client_keyfile"`
	// HTTP connections to EOS: CA directories
	ClientCADirs string `mapstructure:"http_client_cadirs"`
	// HTTP connections to EOS: CA files
	ClientCAFiles string `mapstructure:"http_client_cafiles"`

	// TokenExpiry stores in seconds the time after which generated tokens will expire
	// Default is 3600
	TokenExpiry int

	// Path of the script to run in order to create a user home folder
	// TODO(lopresti): to be replaced by a call to the Resource Lifecycle API being developed
	CreateHomeHook string `mapstructure:"create_home_hook"`

	// Maximum entries count a ListRecycle call may return: if exceeded, ListRecycle
	// will return a BadRequest error
	MaxRecycleEntries int `mapstructure:"max_recycle_entries"`

	// Maximum time span in days a ListRecycle call may return: if exceeded, ListRecycle
	// will override the "to" date with "from" + this value
	MaxDaysInRecycleList int `mapstructure:"max_days_in_recycle_list"`

	// AllowInsecure determines whether EOS can fall back to no TLS
	// Default is false
	AllowInsecure bool `mapstructure:"allow_insecure"`

	// EnableQuotaCache enables an in-memory cache for GetQuota results.
	// When enabled, cached values are returned immediately and refreshed in the background
	// after QuotaCacheTTL seconds. Entries are never evicted, only refreshed.
	// Default is false.
	EnableQuotaCache bool `mapstructure:"enable_quota_cache"`

	// QuotaCacheTTL is the time-to-live in seconds for quota cache entries.
	// After this period, the cached value is still returned but a background refresh is triggered.
	// Default is 600.
	QuotaCacheTTL int `mapstructure:"quota_cache_ttl"`
	// The ExternalAccountsUser is a user on whose behalf accesses to EOS are done when requests are made by an external user (such as lightweight or federated users).
	// Since these users are unknown to EOS, access is done on behalf of this user. Note that this user will be added to the ACLs when such a request is made.
	ExternalAccountsUserName string `` /* 157-byte string literal not displayed */
	ExternalAccountsUserUID  string `` /* 151-byte string literal not displayed */
	ExternalAccountsUserGID  string `` /* 151-byte string literal not displayed */
}

Config holds the configuration details for the EOS fs.

func (*Config) ApplyDefaults added in v3.6.0

func (c *Config) ApplyDefaults()

type Eosfs added in v3.6.0

type Eosfs struct {
	// contains filtered or unexported fields
}

func (*Eosfs) AddGrant added in v3.6.0

func (fs *Eosfs) AddGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error

func (*Eosfs) CreateDir added in v3.6.0

func (fs *Eosfs) CreateDir(ctx context.Context, ref *provider.Reference) error

func (*Eosfs) CreateHome added in v3.6.0

func (fs *Eosfs) CreateHome(ctx context.Context) error

func (*Eosfs) CreateReference added in v3.6.0

func (fs *Eosfs) CreateReference(ctx context.Context, fn string, targetURI *url.URL) error

func (*Eosfs) CreateStorageSpace added in v3.6.0

CreateStorageSpace creates a storage space.

func (*Eosfs) Delete added in v3.6.0

func (fs *Eosfs) Delete(ctx context.Context, ref *provider.Reference) error

func (*Eosfs) DenyGrant added in v3.6.0

func (fs *Eosfs) DenyGrant(ctx context.Context, ref *provider.Reference, g *provider.Grantee) error

func (*Eosfs) Download added in v3.6.0

func (fs *Eosfs) Download(ctx context.Context, ref *provider.Reference, ranges []storage.Range) (io.ReadCloser, error)

func (*Eosfs) DownloadRevision added in v3.6.0

func (fs *Eosfs) DownloadRevision(ctx context.Context, ref *provider.Reference, revisionKey string) (io.ReadCloser, error)

func (*Eosfs) EmptyRecycle added in v3.6.0

func (fs *Eosfs) EmptyRecycle(ctx context.Context) error

func (*Eosfs) EncodeAppName added in v3.6.0

func (fs *Eosfs) EncodeAppName(a string) string

EncodeAppName returns the string to be used as EOS "app" tag, both in uploads and when handling locks. Note that the GET (and PUT) operations in eosbinary.go and eoshttp.go use a `read` (resp. `write`) app name when no locks are involved.

func (*Eosfs) GetHome added in v3.6.0

func (fs *Eosfs) GetHome(ctx context.Context) (string, error)

func (*Eosfs) GetLock added in v3.6.0

func (fs *Eosfs) GetLock(ctx context.Context, ref *provider.Reference) (*provider.Lock, error)

GetLock returns an existing lock on the given reference.

func (*Eosfs) GetMD added in v3.6.0

func (fs *Eosfs) GetMD(ctx context.Context, ref *provider.Reference, mdKeys []string) (*provider.ResourceInfo, error)

func (*Eosfs) GetPathByID added in v3.6.0

func (fs *Eosfs) GetPathByID(ctx context.Context, id *provider.ResourceId) (string, error)

func (*Eosfs) GetQuota added in v3.6.0

func (fs *Eosfs) GetQuota(ctx context.Context, ref *provider.Reference) (totalbytes, usedbytes uint64, err error)

func (*Eosfs) InitiateUpload added in v3.6.0

func (fs *Eosfs) InitiateUpload(ctx context.Context, ref *provider.Reference, uploadLength int64, metadata map[string]string) (map[string]string, error)

func (*Eosfs) ListFolder added in v3.6.0

func (fs *Eosfs) ListFolder(ctx context.Context, ref *provider.Reference, mdKeys []string) ([]*provider.ResourceInfo, error)

func (*Eosfs) ListGrants added in v3.6.0

func (fs *Eosfs) ListGrants(ctx context.Context, ref *provider.Reference) ([]*provider.Grant, error)

func (*Eosfs) ListRecycle added in v3.6.0

func (fs *Eosfs) ListRecycle(ctx context.Context, basePath, key, relativePath string, from, to *types.Timestamp) ([]*provider.RecycleItem, error)

func (*Eosfs) ListRevisions added in v3.6.0

func (fs *Eosfs) ListRevisions(ctx context.Context, ref *provider.Reference) ([]*provider.FileVersion, error)

func (*Eosfs) ListStorageSpaces added in v3.6.0

func (fs *Eosfs) ListStorageSpaces(ctx context.Context, filter []*provider.ListStorageSpacesRequest_Filter) ([]*provider.StorageSpace, error)

func (*Eosfs) ListWithRegex added in v3.6.0

func (fs *Eosfs) ListWithRegex(ctx context.Context, path, regex string, depth uint, user *userpb.User) ([]*provider.ResourceInfo, error)

func (*Eosfs) Move added in v3.6.0

func (fs *Eosfs) Move(ctx context.Context, oldRef, newRef *provider.Reference) error

func (*Eosfs) PurgeRecycleItem added in v3.6.0

func (fs *Eosfs) PurgeRecycleItem(ctx context.Context, basePath, key, relativePath string) error

func (*Eosfs) RefreshLock added in v3.6.0

func (fs *Eosfs) RefreshLock(ctx context.Context, ref *provider.Reference, newLock *provider.Lock, existingLockID string) error

RefreshLock refreshes an existing lock on the given reference.

func (*Eosfs) RemoveGrant added in v3.6.0

func (fs *Eosfs) RemoveGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error

func (*Eosfs) RestoreRecycleItem added in v3.6.0

func (fs *Eosfs) RestoreRecycleItem(ctx context.Context, basePath, key, relativePath string, restoreRef *provider.Reference) error

func (*Eosfs) RestoreRevision added in v3.6.0

func (fs *Eosfs) RestoreRevision(ctx context.Context, ref *provider.Reference, revisionKey string) error

func (*Eosfs) SetArbitraryMetadata added in v3.6.0

func (fs *Eosfs) SetArbitraryMetadata(ctx context.Context, ref *provider.Reference, md *provider.ArbitraryMetadata) error

func (*Eosfs) SetLock added in v3.6.0

func (fs *Eosfs) SetLock(ctx context.Context, ref *provider.Reference, l *provider.Lock) error

SetLock puts a lock on the given reference.

func (*Eosfs) Shutdown added in v3.6.0

func (fs *Eosfs) Shutdown(ctx context.Context) error

func (*Eosfs) TouchFile added in v3.6.0

func (fs *Eosfs) TouchFile(ctx context.Context, ref *provider.Reference) error

Create a new, empty file

func (*Eosfs) Unlock added in v3.6.0

func (fs *Eosfs) Unlock(ctx context.Context, ref *provider.Reference, lock *provider.Lock) error

Unlock removes an existing lock from the given reference.

func (*Eosfs) UnsetArbitraryMetadata added in v3.6.0

func (fs *Eosfs) UnsetArbitraryMetadata(ctx context.Context, ref *provider.Reference, keys []string) error

func (*Eosfs) UpdateGrant added in v3.6.0

func (fs *Eosfs) UpdateGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error

func (*Eosfs) UpdateStorageSpace added in v3.6.0

UpdateStorageSpace updates a storage space.

func (*Eosfs) Upload added in v3.6.0

func (fs *Eosfs) Upload(ctx context.Context, ref *provider.Reference, r io.ReadCloser, metadata map[string]string) error

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL