Compare commits

..
33 Commits
Author SHA1 Message Date
OpenCloud Devops abad26e657 🎉 Release 4.0.8 (#3003)
* 🎉 Release 4.0.8

* 🎉 Release 4.0.8

* 🎉 Release 4.0.8

* 🎉 Release 4.0.8

* 🎉 Release 4.0.8
2026-06-25 14:18:59 +02:00
Viktor Scharf 9ba009f069 Bump version 4.0.8 (#3021) 2026-06-25 14:04:03 +02:00
Viktor Scharf 9e10e420d7 reva-bump-2.40.5 (#3018) 2026-06-25 11:16:19 +02:00
Ralf Haferkamp 60c51aa1dd Merge pull request #3009 from rhafer/stable/4.0/backports
[stable-4.0] Revert "fix: disallow thumbnails for tiff and jpeg2000 images"
2026-06-24 13:20:10 +02:00
Ralf Haferkamp 87f86f8c35 Revert "fix: disallow thumbnails for tiff and jpeg2000 images"
The alpine base images now has a fixed libvips. So we can enable
previews for these formats again.

This reverts commit 66ba9504a2.
2026-06-24 10:56:48 +02:00
Ralf Haferkamp 4eb0591829 Merge pull request #2965 from rhafer/stable/4.0/alpine-bump
[stable-4.0] Bump alpine base image
2026-06-23 11:32:13 +02:00
Ralf Haferkamp c5fc3e075f Bump alpine base image
Also adjusts the build image to use the opencloudeu/golang image. So
we're in sync with how the binaries are built in CI. It already contains
all the required dependencies.

(cherry picked from commit dbc8a21085)
2026-06-18 11:32:24 +02:00
Michael Barz 38c783f890 fix: hotfix, do not push container during cron runs 2026-06-02 10:58:15 +02:00
OpenCloud Devops 392e8965d0 🎉 Release 4.0.7 (#2776)
* 🎉 Release 4.0.7

* 🎉 Release 4.0.7
2026-05-18 14:16:46 +02:00
Viktor Scharf 5caa34d51a [stable-4.0][decomposed] Bump version 4.0.7 (#2778) 2026-05-18 13:58:08 +02:00
Ralf Haferkamp 8f743fe9f3 Merge pull request #2761 from rhafer/backport/no-tiff
[stable/4.0] Backports
2026-05-18 10:30:51 +02:00
Ralf Haferkamp 08eb7321ee chore: bump docker base image to alpine 3.23 (#2754) 2026-05-13 15:19:37 +02:00
Ralf Haferkamp 66ba9504a2 fix: disallow thumbnails for tiff and jpeg2000 images 2026-05-13 15:19:06 +02:00
OpenCloud Devops 07859d9ac4 🎉 Release 4.0.6 (#2672)
* 🎉 Release 4.0.6

* 🎉 Release 4.0.6

* 🎉 Release 4.0.6

* 🎉 Release 4.0.6
2026-04-29 13:38:03 +02:00
Viktor Scharf 2aa6943eaf bump version 4.0.6 (#2694) 2026-04-29 12:30:26 +02:00
Benedikt Kulmann a7bb32bf64 chore(web): bump web to v4.2.2 (#2580) 2026-04-29 11:01:33 +02:00
Jörn Friedrich Dreyer df4bc7c1d8 update comment
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
(cherry picked from commit 9135eab27e)
2026-04-28 17:24:06 +02:00
Jörn Friedrich Dreyer 2ef5d1289d stop metrics tickers on context cancel
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
(cherry picked from commit 2d1cc3fb3a)
2026-04-28 17:24:06 +02:00
Ralf Haferkamp 8ec82331cc fix: avoid suture.Supervisor being shutdown early
Previously the Supervisor was shutdonw as soon as the main context was
canceled. Which cause the managed services to fail during shutdown (in
"trapShutdownCtx()") as the Supervisor was gone already.

We now pass a separate Context to Supervisor.ServeBackground() to avoid
this.

Fixes: #2282
(cherry picked from commit 7a149787d0)
2026-04-28 17:24:06 +02:00
Ralf Haferkamp 15fc59626b Revert "Hotfix for https://github.com/opencloud-eu/opencloud/issues/2282"
This reverts commit 5e889612d6.
2026-04-28 17:24:06 +02:00
Ralf Haferkamp 813f42f825 Revert "Fix timeout for ocwrapper"
This reverts commit 193431b875.
2026-04-28 17:24:06 +02:00
Ralf Haferkamp f0e9e10fa7 Revert "wrapper fix: kill all opencloud and nats processes"
This reverts commit 62674c71cf.
2026-04-28 17:24:06 +02:00
Ralf Haferkamp 32549ae0e1 Revert "use process group kill"
This reverts commit 7374ae61a9.
2026-04-28 17:24:06 +02:00
Ralf Haferkamp f70ce6d8a8 Revert "return waitAllServices"
This reverts commit 3434f55c78.
2026-04-28 17:24:06 +02:00
Ralf Haferkamp e9ce946fe0 Revert "introduce ci env variable"
This reverts commit 01686db6d9.
2026-04-28 17:24:06 +02:00
Ralf Haferkamp c0b0910000 Revert "fix typo"
This reverts commit 4af5add77d.
2026-04-28 17:24:06 +02:00
Christian Richter 4af5add77d fix typo
Signed-off-by: Christian Richter <c.richter@opencloud.eu>
2026-04-24 12:21:20 +02:00
Christian Richter 01686db6d9 introduce ci env variable
Signed-off-by: Christian Richter <c.richter@opencloud.eu>
2026-04-24 12:21:20 +02:00
v.scharf 3434f55c78 return waitAllServices 2026-04-24 12:21:20 +02:00
v.scharf 7374ae61a9 use process group kill 2026-04-24 12:21:20 +02:00
v.scharf 62674c71cf wrapper fix: kill all opencloud and nats processes 2026-04-24 12:21:20 +02:00
Christian Richter 193431b875 Fix timeout for ocwrapper
Signed-off-by: Christian Richter <c.richter@opencloud.eu>
2026-04-24 12:21:20 +02:00
Christian RichterandJörn Dreyer 5e889612d6 Hotfix for https://github.com/opencloud-eu/opencloud/issues/2282
Co-authored-by: Jörn Dreyer <jfd@butonic.de>
Signed-off-by: Christian Richter <c.richter@opencloud.eu>
2026-04-24 12:21:20 +02:00
26 changed files with 249 additions and 128 deletions

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
# The test runner source for UI tests
WEB_COMMITID=50e3fff6a518361d59cba864a927470f313b6f91
WEB_COMMITID=c247f854a95b7bcaf10f27577458d2f566b2f939
WEB_BRANCH=stable-4.2
-3
View File
@@ -1919,7 +1919,6 @@ def dockerRelease(ctx, repo, build_type):
],
},
"when": [
event["cron"],
event["tag"],
],
},
@@ -1948,8 +1947,6 @@ def dockerRelease(ctx, repo, build_type):
],
},
"when": [
event["cron"],
event["base"],
event["tag"],
],
},
+32
View File
@@ -1,5 +1,37 @@
# Changelog
## [4.0.8](https://github.com/opencloud-eu/opencloud/releases/tag/v4.0.8) - 2026-06-25
### ❤️ Thanks to all contributors! ❤️
@ScharfViktor, @rhafer
### 🐛 Bug Fixes
- bump reva 2.40.5 [[#3018](https://github.com/opencloud-eu/opencloud/pull/3018)]
- [stable-4.0] Revert "fix: disallow thumbnails for tiff and jpeg2000 images" [[#3009](https://github.com/opencloud-eu/opencloud/pull/3009)]
- [stable-4.0] Bump alpine base image [[#2965](https://github.com/opencloud-eu/opencloud/pull/2965)]
## [4.0.7](https://github.com/opencloud-eu/opencloud/releases/tag/v4.0.7) - 2026-05-18
### ❤️ Thanks to all contributors! ❤️
@ScharfViktor, @rhafer
### 🔒 Security
- [stable/4.0] Backports [[#2761](https://github.com/opencloud-eu/opencloud/pull/2761)]
## [4.0.6](https://github.com/opencloud-eu/opencloud/releases/tag/v4.0.6) - 2026-04-29
### ❤️ Thanks to all contributors! ❤️
@ScharfViktor, @dragonchaser, @kulmann, @rhafer
### 🐛 Bug Fixes
- [stable-4.0] More graceful shutdown fixes [[#2692](https://github.com/opencloud-eu/opencloud/pull/2692)]
## [4.0.5](https://github.com/opencloud-eu/opencloud/releases/tag/v4.0.5) - 2026-04-02
### ❤️ Thanks to all contributors! ❤️
+2 -3
View File
@@ -21,15 +21,14 @@ COPY ./ /opencloud/
WORKDIR /opencloud/opencloud
RUN make node-generate-prod
FROM golang:1.24-alpine AS build
RUN apk add bash make git curl gcc musl-dev libc-dev binutils-gold inotify-tools vips-dev
FROM quay.io/opencloudeu/golang-ci:1.25 AS build
COPY --from=generate /opencloud /opencloud
WORKDIR /opencloud/opencloud
RUN make go-generate build ENABLE_VIPS=true
FROM alpine:3.20
FROM alpine:3.24
RUN apk add --no-cache attr ca-certificates curl mailcap tree vips && \
echo 'hosts: files dns' >| /etc/nsswitch.conf
+1 -1
View File
@@ -64,7 +64,7 @@ require (
github.com/open-policy-agent/opa v1.10.1
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76
github.com/opencloud-eu/reva/v2 v2.40.4
github.com/opencloud-eu/reva/v2 v2.40.5
github.com/opensearch-project/opensearch-go/v4 v4.5.0
github.com/orcaman/concurrent-map v1.0.0
github.com/pkg/errors v0.9.1
+2 -2
View File
@@ -965,8 +965,8 @@ github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9 h1:dIft
github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9/go.mod h1:JWyDC6H+5oZRdUJUgKuaye+8Ph5hEs6HVzVoPKzWSGI=
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76 h1:vD/EdfDUrv4omSFjrinT8Mvf+8D7f9g4vgQ2oiDrVUI=
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q=
github.com/opencloud-eu/reva/v2 v2.40.4 h1:KTeFLHSwRp2bnK9J1dtaAGwPCreEiijYIXSyQ1A5tbo=
github.com/opencloud-eu/reva/v2 v2.40.4/go.mod h1:NN88hQAbjCVq011D2zhr0IOZ9Xclz3HS+Wf961YHwbg=
github.com/opencloud-eu/reva/v2 v2.40.5 h1:EfLoiwv0EouKMSYzSXd+1gAgZTgFdD4Q0d5r6yIVZVU=
github.com/opencloud-eu/reva/v2 v2.40.5/go.mod h1:NN88hQAbjCVq011D2zhr0IOZ9Xclz3HS+Wf961YHwbg=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
+2 -4
View File
@@ -1,12 +1,10 @@
FROM golang:alpine3.22 AS build
FROM quay.io/opencloudeu/golang-ci:1.25 AS build
ARG TARGETOS
ARG TARGETARCH
ARG VERSION
ARG STRING
ARG EDITION
RUN apk add bash make git curl gcc musl-dev libc-dev binutils-gold inotify-tools vips-dev
WORKDIR /build
RUN --mount=type=bind,target=/build,rw \
--mount=type=cache,target=/go/pkg/mod \
@@ -14,7 +12,7 @@ RUN --mount=type=bind,target=/build,rw \
GOOS="${TARGETOS:-linux}" GOARCH="${TARGETARCH:-amd64}" ; \
make -C opencloud/opencloud release-linux-docker-${TARGETARCH} ENABLE_VIPS=true DIST=/dist
FROM alpine:3.22
FROM alpine:3.24
ARG VERSION
ARG REVISION
ARG TARGETOS
+3 -6
View File
@@ -434,12 +434,9 @@ func Start(ctx context.Context, o ...Option) error {
// prepare the set of services to run
s.generateRunSet(s.cfg)
// There are reasons not to do this, but we have race conditions ourselves. Until we resolve them, mind the following disclaimer:
// Calling ServeBackground will CORRECTLY start the supervisor running in a new goroutine. It is risky to directly run
// go supervisor.Serve()
// because that will briefly create a race condition as it starts up, if you try to .Add() services immediately afterward.
// https://pkg.go.dev/github.com/thejerf/suture/v4@v4.0.0#Supervisor
go s.Supervisor.ServeBackground(ctx)
// We need to control the order in which services are started and shut down,
// so we need a backgroud context that will outlive the service execution.
go s.Supervisor.ServeBackground(context.Background())
for i, service := range s.Services {
scheduleServiceTokens(s, service)
+1 -1
View File
@@ -34,7 +34,7 @@ var (
// LatestTag is the latest released version plus the dev meta version.
// Will be overwritten by the release pipeline
// Needs a manual change for every tagged release
LatestTag = "4.0.5+dev"
LatestTag = "4.0.8+dev"
// Date indicates the build date.
// This has been removed, it looks like you can only replace static strings with recent go versions
+2
View File
@@ -91,7 +91,9 @@ func Server(cfg *config.Config) *cli.Command {
gr.Add(runner.New(cfg.Service.Name+".svc", func() error {
return natsServer.ListenAndServe()
}, func() {
logger.Info().Msg("Gracefully shutting down the NATS server...")
natsServer.Shutdown()
logger.Info().Msg("NATS server shutdown")
}))
grResults := gr.Run(ctx)
+18 -13
View File
@@ -88,7 +88,7 @@ func NewPostprocessingService(ctx context.Context, logger log.Logger, sto store.
m := metrics.New()
m.BuildInfo.WithLabelValues(version.GetString()).Set(1)
monitorMetrics(raw, "postprocessing-pull", m, logger)
monitorMetrics(ctx, raw, "postprocessing-pull", m, logger)
return &PostprocessingService{
ctx: ctx,
@@ -425,25 +425,30 @@ func (pps *PostprocessingService) findUploadsByStep(step events.Postprocessingst
return ids
}
func monitorMetrics(stream raw.Stream, name string, m *metrics.Metrics, logger log.Logger) {
ctx := context.Background()
func monitorMetrics(ctx context.Context, stream raw.Stream, name string, m *metrics.Metrics, logger log.Logger) {
consumer, err := stream.JetStream().Consumer(ctx, name)
if err != nil {
logger.Error().Err(err).Msg("failed to get consumer")
}
ticker := time.NewTicker(5 * time.Second)
go func() {
for range ticker.C {
info, err := consumer.Info(ctx)
if err != nil {
logger.Error().Err(err).Msg("failed to get consumer")
continue
}
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
info, err := consumer.Info(ctx)
if err != nil {
logger.Error().Err(err).Msg("failed to get consumer")
continue
}
m.EventsOutstandingAcks.Set(float64(info.NumAckPending))
m.EventsUnprocessed.Set(float64(info.NumPending))
m.EventsRedelivered.Set(float64(info.NumRedelivered))
logger.Trace().Msg("updated postprocessing event metrics")
m.EventsOutstandingAcks.Set(float64(info.NumAckPending))
m.EventsUnprocessed.Set(float64(info.NumPending))
m.EventsRedelivered.Set(float64(info.NumRedelivered))
logger.Trace().Msg("updated postprocessing event metrics")
}
}
}()
}
+18 -13
View File
@@ -88,7 +88,7 @@ func (s Service) Run() error {
}
if s.m != nil {
monitorMetrics(s.stream, "search-pull", s.m, s.log)
monitorMetrics(s.ctx, s.stream, "search-pull", s.m, s.log)
}
var wg sync.WaitGroup
@@ -202,25 +202,30 @@ func (s Service) processEvent(e raw.Event) error {
return nil
}
func monitorMetrics(stream raw.Stream, name string, m *metrics.Metrics, logger log.Logger) {
ctx := context.Background()
func monitorMetrics(ctx context.Context, stream raw.Stream, name string, m *metrics.Metrics, logger log.Logger) {
consumer, err := stream.JetStream().Consumer(ctx, name)
if err != nil {
logger.Error().Err(err).Msg("failed to get consumer")
}
ticker := time.NewTicker(5 * time.Second)
go func() {
for range ticker.C {
info, err := consumer.Info(ctx)
if err != nil {
logger.Error().Err(err).Msg("failed to get consumer")
continue
}
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
info, err := consumer.Info(ctx)
if err != nil {
logger.Error().Err(err).Msg("failed to get consumer")
continue
}
m.EventsOutstandingAcks.Set(float64(info.NumAckPending))
m.EventsUnprocessed.Set(float64(info.NumPending))
m.EventsRedelivered.Set(float64(info.NumRedelivered))
logger.Trace().Msg("updated search event metrics")
m.EventsOutstandingAcks.Set(float64(info.NumAckPending))
m.EventsUnprocessed.Set(float64(info.NumPending))
m.EventsRedelivered.Set(float64(info.NumRedelivered))
logger.Trace().Msg("updated search event metrics")
}
}
}()
}
+1 -1
View File
@@ -1,6 +1,6 @@
SHELL := bash
NAME := web
WEB_ASSETS_VERSION = v4.2.1
WEB_ASSETS_VERSION = v4.2.2
WEB_ASSETS_BRANCH = main
ifneq (, $(shell command -v go 2> /dev/null)) # suppress `command not found warnings` for non go targets in CI
@@ -0,0 +1,114 @@
package ignore
import (
"path/filepath"
"strings"
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/blobstore"
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/lookup"
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/options"
"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/tree/propagator"
"github.com/opencloud-eu/reva/v2/pkg/storage/utils/templates"
"github.com/rs/zerolog"
)
const (
// The needles include the separator at the beginning to avoid matching internal dir names in substrings
// The end segment boundary is checked in isInternalDir
trashNeedle = needle(string(filepath.Separator) + lookup.TrashDir)
metadataNeedle = needle(string(filepath.Separator) + lookup.MetadataDir)
tmpNeedle = needle(string(filepath.Separator) + blobstore.TMPDir)
)
type needle string
// Ignorer handles checking if paths should be ignored in posix operations
type Ignorer struct {
options *options.Options
log *zerolog.Logger
personalSpacesRoot string
projectSpacesRoot string
}
// NewIgnorer creates a new Ignorer given the posix options and logger
func NewIgnorer(options *options.Options, log *zerolog.Logger) *Ignorer {
return &Ignorer{
options: options,
log: log,
personalSpacesRoot: filepath.Clean(filepath.Join(options.Root, templates.Base(options.PersonalSpacePathTemplate))),
projectSpacesRoot: filepath.Clean(filepath.Join(options.Root, templates.Base(options.GeneralSpacePathTemplate))),
}
}
// IsIgnored checking if paths should be ignored in posix operations
func (i *Ignorer) IsIgnored(path string) bool {
return i.IsChanges(path) ||
i.IsIndex(path) ||
IsLockFile(path) ||
i.IsTrash(path) ||
i.IsMetadata(path) ||
i.IsTemporary(path) ||
i.IsUpload(path) ||
i.IsRootPath(path) ||
i.IsSpaceRoot(path)
}
func (i *Ignorer) IsChanges(path string) bool {
return strings.HasPrefix(path, filepath.Join(i.options.Root, propagator.ChangesDir))
}
func (i *Ignorer) IsIndex(path string) bool {
return strings.HasPrefix(path, filepath.Join(i.options.Root, lookup.IndexesDir))
}
func (i *Ignorer) IsUpload(path string) bool {
return strings.HasPrefix(path, i.options.UploadDirectory)
}
func (i *Ignorer) IsRootPath(path string) bool {
return path == i.options.Root ||
path == i.personalSpacesRoot ||
path == i.projectSpacesRoot
}
func (i *Ignorer) IsSpaceRoot(path string) bool {
parent := filepath.Dir(path)
return parent == i.personalSpacesRoot || parent == i.projectSpacesRoot
}
func IsLockFile(path string) bool {
return strings.HasSuffix(path, ".flock") || strings.HasSuffix(path, ".mlock")
}
func (i *Ignorer) IsMetadata(path string) bool {
return i.isInternalDir(path, metadataNeedle)
}
func (i *Ignorer) IsTemporary(path string) bool {
return i.isInternalDir(path, tmpNeedle)
}
func (i *Ignorer) IsTrash(path string) bool {
return i.isInternalDir(path, trashNeedle)
}
// isInternalDir checks if the path contains the match dir and that the match lives
// in the space root, e.g. "/storage/users/user1/.metadata/file" -> match is ".metadata",
// parent dir is "/storage/users/user1" which is a space root, so this would return true
func (i *Ignorer) isInternalDir(path string, match needle) bool {
idx := strings.Index(path, string(match))
if idx <= 0 {
return false
}
// must end at a segment boundary (end of path or separator)
if length := idx + len(match); length != len(path) && path[length] != filepath.Separator {
return false
}
// get the path of the parent dir, e.g. "/a/match" -> index of "match" is 3
// so parentPath is path[:2] -> "/a"
parentPath := path[:idx-1]
return len(parentPath) > 0 && i.IsSpaceRoot(parentPath)
}
@@ -34,6 +34,7 @@ import (
"github.com/opencloud-eu/reva/v2/pkg/appctx"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/options"
dfslookup "github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/lookup"
"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata"
"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/prefixes"
"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/node"
@@ -47,7 +48,11 @@ import (
var tracer trace.Tracer
const MetadataDir = ".oc-nodes"
const (
IndexesDir = dfslookup.IndexesDir
MetadataDir = ".oc-nodes"
TrashDir = ".Trash"
)
var _spaceTypePersonal = "personal"
var _spaceTypeProject = "project"
@@ -144,7 +144,7 @@ func (tb *Trashbin) Setup(fs storage.FS) error {
}
func trashRootForNode(n *node.Node) string {
return filepath.Join(n.SpaceRoot.InternalPath(), ".Trash")
return filepath.Join(n.SpaceRoot.InternalPath(), lookup.TrashDir)
}
func (tb *Trashbin) MoveToTrash(ctx context.Context, n *node.Node, path string) error {
@@ -191,7 +191,7 @@ func (tb *Trashbin) ListRecycle(ctx context.Context, spaceID string, key, relati
_, span := tracer.Start(ctx, "ListRecycle")
defer span.End()
trashRoot := filepath.Join(tb.lu.InternalPath(spaceID, spaceID), ".Trash")
trashRoot := filepath.Join(tb.lu.InternalPath(spaceID, spaceID), lookup.TrashDir)
base := filepath.Join(trashRoot, "files")
var originalPath string
@@ -303,7 +303,7 @@ func (tb *Trashbin) RestoreRecycleItem(ctx context.Context, spaceID string, key,
_, span := tracer.Start(ctx, "RestoreRecycleItem")
defer span.End()
trashRoot := filepath.Join(tb.lu.InternalPath(spaceID, spaceID), ".Trash")
trashRoot := filepath.Join(tb.lu.InternalPath(spaceID, spaceID), lookup.TrashDir)
trashPath := filepath.Clean(filepath.Join(trashRoot, "files", key+".trashitem", relativePath))
restorePath := ""
@@ -376,7 +376,7 @@ func (tb *Trashbin) PurgeRecycleItem(ctx context.Context, spaceID, key, relative
_, span := tracer.Start(ctx, "PurgeRecycleItem")
defer span.End()
trashRoot := filepath.Join(tb.lu.InternalPath(spaceID, spaceID), ".Trash")
trashRoot := filepath.Join(tb.lu.InternalPath(spaceID, spaceID), lookup.TrashDir)
trashPath := filepath.Clean(filepath.Join(trashRoot, "files", key+".trashitem", relativePath))
type item struct {
@@ -521,7 +521,7 @@ func (tb *Trashbin) EmptyRecycle(ctx context.Context, spaceID string) error {
_, span := tracer.Start(ctx, "EmptyRecycle")
defer span.End()
trashRoot := filepath.Join(tb.lu.InternalPath(spaceID, spaceID), ".Trash")
trashRoot := filepath.Join(tb.lu.InternalPath(spaceID, spaceID), lookup.TrashDir)
filesRoot := filepath.Join(trashRoot, "files")
entries, err := os.ReadDir(filesRoot)
@@ -581,7 +581,7 @@ func (tb *Trashbin) EmptyRecycle(ctx context.Context, spaceID string) error {
func (tb *Trashbin) IsEmpty(ctx context.Context, spaceID string) bool {
_, span := tracer.Start(ctx, "HasTrashedItems")
defer span.End()
trashRoot := filepath.Join(tb.lu.InternalPath(spaceID, spaceID), ".Trash", "info")
trashRoot := filepath.Join(tb.lu.InternalPath(spaceID, spaceID), lookup.TrashDir, "info")
trash, err := os.Open(filepath.Clean(trashRoot))
if err != nil {
// there is no trash for this space, so no trashed items
@@ -679,7 +679,7 @@ assimilate:
// The Space's name attribute might not match the directory name. Use the name as
// it was set before. Also the space root doesn't have a 'type' attribute
// currently so only set it for normal directories.
if t.isSpaceRoot(path) {
if t.Ignorer.IsSpaceRoot(path) {
if previousAttribs != nil && previousAttribs[prefixes.NameAttr] != nil {
attributes[prefixes.NameAttr] = previousAttribs[prefixes.NameAttr]
}
@@ -830,14 +830,13 @@ func (t *Tree) WarmupIDCache(root string, assimilate, onlyDirty bool) error {
}
// skip irrelevant files
if t.isInternal(path) ||
isLockFile(path) ||
isTrash(path) ||
t.isUpload(path) ||
t.isIndex(path) {
if !t.Ignorer.IsSpaceRoot(path) && t.Ignorer.IsIgnored(path) {
if info.IsDir() {
return filepath.SkipDir
}
return nil
}
if t.isRootPath(path) {
if t.Ignorer.IsRootPath(path) {
return nil // ignore the root paths
}
@@ -935,7 +934,7 @@ func (t *Tree) WarmupIDCache(root string, assimilate, onlyDirty bool) error {
})
for dir, size := range sizes {
if t.isRootPath(dir) {
if t.Ignorer.IsRootPath(dir) {
continue
}
spaceID, id, err := t.lookup.IDsForPath(context.Background(), dir)
@@ -83,7 +83,7 @@ func (w *CephFSWatcher) Watch(topic string) {
continue
}
if w.tree.isIgnored(ev.Path) {
if w.tree.Ignorer.IsIgnored(ev.Path) {
continue
}
@@ -82,7 +82,7 @@ start:
w.log.Error().Err(err).Str("line", line).Msg("error unmarshalling line")
continue
}
if w.tree.isIgnored(ev.Path) {
if w.tree.Ignorer.IsIgnored(ev.Path) {
continue
}
go func() {
@@ -65,7 +65,7 @@ func (w *GpfsWatchFolderWatcher) Watch(topic string) {
continue
}
if w.tree.isIgnored(lwev.Path) {
if w.tree.Ignorer.IsIgnored(lwev.Path) {
continue
}
@@ -88,7 +88,7 @@ func (iw *InotifyWatcher) Watch(path string) {
for {
select {
case event := <-events:
if iw.tree.isIgnored(event.Filename) {
if iw.tree.Ignorer.IsIgnored(event.Filename) {
continue
}
for _, e := range event.Events {
+16 -55
View File
@@ -43,7 +43,7 @@ import (
"github.com/opencloud-eu/reva/v2/pkg/appctx"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
"github.com/opencloud-eu/reva/v2/pkg/events"
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/blobstore"
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/ignore"
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/lookup"
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/options"
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/trashbin"
@@ -84,6 +84,7 @@ type Tree struct {
options *options.Options
personalSpacesRoot string
projectSpacesRoot string
Ignorer *ignore.Ignorer
userMapper usermapper.Mapper
idCache store.Store
@@ -119,6 +120,7 @@ func New(lu node.PathLookup, bs node.Blobstore, um usermapper.Mapper, trashbin *
log: log,
personalSpacesRoot: filepath.Clean(filepath.Join(o.Root, templates.Base(o.PersonalSpacePathTemplate))),
projectSpacesRoot: filepath.Clean(filepath.Join(o.Root, templates.Base(o.GeneralSpacePathTemplate))),
Ignorer: ignore.NewIgnorer(o, log),
}
if err := t.checkStorage(); err != nil {
return nil, errors.Wrap(err, "tree: unfit storage '"+o.Root+"'")
@@ -252,6 +254,10 @@ func (t *Tree) GetMD(ctx context.Context, n *node.Node) (os.FileInfo, error) {
// TouchFile creates a new empty file
func (t *Tree) TouchFile(ctx context.Context, n *node.Node, markprocessing bool, mtime string) error {
if t.Ignorer.IsIgnored(filepath.Join(n.ParentPath(), n.Name)) {
return errtypes.PermissionDenied(n.ID)
}
if n.Exists {
if markprocessing {
return n.SetXattr(ctx, prefixes.StatusPrefix, []byte(node.ProcessingStatus))
@@ -468,7 +474,7 @@ func (t *Tree) ListFolder(ctx context.Context, n *node.Node) ([]*node.Node, erro
g.Go(func() error {
defer close(work)
for _, name := range names {
if t.isInternal(name) || isLockFile(name) || isTrash(name) {
if t.Ignorer.IsIgnored(filepath.Join(dir, name)) {
continue
}
@@ -648,6 +654,10 @@ func (t *Tree) ResolveSpaceIDIndexEntry(spaceID string) (string, error) {
// InitNewNode initializes a new node
func (t *Tree) InitNewNode(ctx context.Context, n *node.Node, fsize uint64) (metadata.UnlockFunc, error) {
if t.Ignorer.IsIgnored(filepath.Join(n.ParentPath(), n.Name)) {
return nil, errtypes.PermissionDenied(n.ID)
}
_, span := tracer.Start(ctx, "InitNewNode")
defer span.End()
// create folder structure (if needed)
@@ -680,6 +690,10 @@ func (t *Tree) InitNewNode(ctx context.Context, n *node.Node, fsize uint64) (met
// TODO check if node exists?
func (t *Tree) createDirNode(ctx context.Context, n *node.Node) (err error) {
if t.Ignorer.IsIgnored(filepath.Join(n.ParentPath(), n.Name)) {
return errtypes.PermissionDenied(n.ID)
}
ctx, span := tracer.Start(ctx, "createDirNode")
defer span.End()
@@ -730,56 +744,3 @@ func (t *Tree) createDirNode(ctx context.Context, n *node.Node) (err error) {
}
return n.SetXattrsWithContext(ctx, attributes, false)
}
func (t *Tree) isIgnored(path string) bool {
return isLockFile(path) || isTrash(path) || t.isUpload(path) || t.isInternal(path) || t.isRootPath(path) || t.isSpaceRoot(path)
}
func (t *Tree) isUpload(path string) bool {
return strings.HasPrefix(path, t.options.UploadDirectory)
}
func (t *Tree) isIndex(path string) bool {
return strings.HasPrefix(path, filepath.Join(t.options.Root, "indexes"))
}
func (t *Tree) isTemporary(path string) bool {
if filepath.IsAbs(path) {
tmpDirPattern := filepath.Join(t.options.Root, "*", "*", blobstore.TMPDir)
isTempDir, err := filepath.Match(tmpDirPattern, path)
if err != nil {
t.log.Error().Err(err).Str("pattern", tmpDirPattern).Str("path", path).Msg("error matching temporary path")
return false
}
isTempParentDir, err := filepath.Match(tmpDirPattern, filepath.Dir(path))
if err != nil {
t.log.Error().Err(err).Str("pattern", tmpDirPattern).Str("path", filepath.Dir(path)).Msg("error matching temporary path")
return false
}
return isTempDir || isTempParentDir
}
return path == blobstore.TMPDir || filepath.Dir(path) == blobstore.TMPDir
}
func (t *Tree) isRootPath(path string) bool {
return path == t.options.Root ||
path == t.personalSpacesRoot ||
path == t.projectSpacesRoot
}
func (t *Tree) isSpaceRoot(path string) bool {
parent := filepath.Dir(path)
return parent == t.personalSpacesRoot || parent == t.projectSpacesRoot
}
func (t *Tree) isInternal(path string) bool {
return t.isIndex(path) || strings.Contains(path, lookup.MetadataDir) || t.isTemporary(path)
}
func isLockFile(path string) bool {
return strings.HasSuffix(path, ".flock") || strings.HasSuffix(path, ".mlock")
}
func isTrash(path string) bool {
return strings.HasSuffix(path, ".trashinfo") || strings.HasSuffix(path, ".trashitem") || strings.Contains(path, ".Trash")
}
@@ -203,17 +203,17 @@ func New(o *options.Options, aspects aspects.Aspects, log *zerolog.Logger) (stor
if o.LockCycleDurationFactor != 0 {
filelocks.SetLockCycleDurationFactor(o.LockCycleDurationFactor)
}
userSpaceIndex := spaceidindex.New(filepath.Join(o.Root, "indexes"), "by-user-id")
userSpaceIndex := spaceidindex.New(filepath.Join(o.Root, lookup.IndexesDir), "by-user-id")
err = userSpaceIndex.Init()
if err != nil {
return nil, err
}
groupSpaceIndex := spaceidindex.New(filepath.Join(o.Root, "indexes"), "by-group-id")
groupSpaceIndex := spaceidindex.New(filepath.Join(o.Root, lookup.IndexesDir), "by-group-id")
err = groupSpaceIndex.Init()
if err != nil {
return nil, err
}
spaceTypeIndex := spaceidindex.New(filepath.Join(o.Root, "indexes"), "by-type")
spaceTypeIndex := spaceidindex.New(filepath.Join(o.Root, lookup.IndexesDir), "by-type")
err = spaceTypeIndex.Init()
if err != nil {
return nil, err
@@ -45,6 +45,8 @@ var tracer trace.Tracer
const (
_spaceTypePersonal = "personal"
IndexesDir = "indexes"
)
func init() {
@@ -37,6 +37,10 @@ import (
"github.com/vmihailenco/msgpack/v5"
)
const (
ChangesDir = "changes"
)
var _propagationGracePeriod = 3 * time.Minute
type PropagationNode interface {
@@ -80,7 +84,7 @@ func NewAsyncPropagator(treeSizeAccounting, treeTimeAccounting bool, o options.A
return
}
changesDirPath := filepath.Join(p.lookup.InternalRoot(), "changes")
changesDirPath := filepath.Join(p.lookup.InternalRoot(), ChangesDir)
doSleep := false // switch to not sleep on the first iteration
for {
if doSleep {
@@ -426,5 +430,5 @@ func (p AsyncPropagator) propagate(ctx context.Context, pn PropagationNode, reca
}
func (p AsyncPropagator) changesPath(spaceID, nodeID, filename string) string {
return filepath.Join(p.lookup.InternalRoot(), "changes", spaceID[0:2], spaceID+":"+nodeID, filename)
return filepath.Join(p.lookup.InternalRoot(), ChangesDir, spaceID[0:2], spaceID+":"+nodeID, filename)
}
+2 -1
View File
@@ -1375,7 +1375,7 @@ github.com/opencloud-eu/icap-client
# github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76
## explicit; go 1.18
github.com/opencloud-eu/libre-graph-api-go
# github.com/opencloud-eu/reva/v2 v2.40.4
# github.com/opencloud-eu/reva/v2 v2.40.5
## explicit; go 1.24.1
github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace
github.com/opencloud-eu/reva/v2/cmd/revad/runtime
@@ -1684,6 +1684,7 @@ github.com/opencloud-eu/reva/v2/pkg/storage/fs/owncloudsql
github.com/opencloud-eu/reva/v2/pkg/storage/fs/owncloudsql/filecache
github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix
github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/blobstore
github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/ignore
github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/lookup
github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/options
github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/timemanager