Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
bbedeb0
<bugfix> fix inode refcnt
VVoidV Apr 1, 2025
959087d
Properly handle dirtyQueueId during inode ID swaps in directory rename
temka-io Jul 25, 2025
0643f83
Release 0.43.1
temka-io Jul 25, 2025
a5cc92e
Fix panic in partRange when uploading exactly 10000 parts
KurlesHS Aug 5, 2025
92bb88a
Fixup processing of the boundaries of the requested range when downlo…
temka-io Aug 12, 2025
23f37b7
Release 0.43.2
temka-io Aug 12, 2025
6a5eefc
Merge pull request #135 from VVoidV/patch-1
temka-io Dec 8, 2025
458ebfa
Merge pull request #149 from KurlesHS/fix/panic-on-max-multipart-parts
temka-io Dec 8, 2025
e1904fa
Set up test workflows to run on pull requests
temka-io Dec 9, 2025
80e0f3b
docker build, update golang with depends (#163)
borislitv Dec 9, 2025
97e9bcc
Prevent panic when setting user metadata on implicit directories
temka-io Dec 9, 2025
611646b
Merge pull request #164 from temka-io/fixup_xattr
temka-io Dec 9, 2025
be37a98
feat: convert hardlink -> symlink
borislitv Dec 10, 2025
3dbf4fa
Merge pull request #165 from borislitv/master
temka-io Dec 10, 2025
82461c4
ignore tests tmp files
borislitv Dec 10, 2025
4a1f4eb
Merge pull request #166 from borislitv/master
temka-io Dec 10, 2025
9810911
remove second conversion of GСInterval to bytes
nikitos Dec 25, 2025
f4968ef
Merge pull request #155 from nikitos/fix
temka-io Jan 12, 2026
feddd0a
Add flag to ignore root directory attribute errors
temka-io Jan 23, 2026
4362e26
Merge pull request #169 from temka-io/root_option
temka-io Jan 23, 2026
e322174
Release 0.43.3
temka-io Jan 23, 2026
ae2c4ed
Fix infinite retry loop on read errors
temka-io Jan 26, 2026
a67cd58
Merge pull request #170 from temka-io/fix_unlimited_retries
temka-io Jan 26, 2026
5fd9575
fix(s3): invalid CopySource format with virtual-hosted-style endpoint…
derwesx Feb 20, 2026
f495e05
Release 0.43.4
temka-io Feb 26, 2026
400338d
fix(s3): URI escaping in VHS mode (#174)
derwesx Mar 4, 2026
ec41e16
Release 0.43.5
temka-io Mar 4, 2026
7d857d0
Update deps (#176)
temka-io Apr 20, 2026
c87c59d
Release 0.43.6
temka-io Apr 20, 2026
8fdec0d
Fix multipart part boundary handling without allowing out-of-range pa…
temka-io May 5, 2026
8da9c52
Release 0.43.7
temka-io May 5, 2026
255009b
chore: merge master (upstream 0.43.7) into dev, bump to 0.43.7-dc.3
alexsavio May 19, 2026
3b196be
test(docker): add multipart-copy boundary fsync reproducer
alexsavio May 20, 2026
e12cc5a
test(docker): tighten multipart-copy reproducer with RAM pressure
alexsavio May 20, 2026
b5a7e29
test(docker): wrap fio/dd with timeout to fail-fast on bug-hang
alexsavio May 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.22
go-version: 1.25

- name: Build linux amd64
run: env CGO_ENABLED=0 go build -o geesefs-linux-amd64 -v && ln -s geesefs-linux-amd64 geesefs
Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@ on:
push:
branches:
- master
pull_request:
branches:
- master

jobs:

build:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v2

- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.22
go-version: 1.25

- name: Build linux amd64
run: env CGO_ENABLED=0 go build -o geesefs-linux-amd64 -v && ln -s geesefs-linux-amd64 geesefs
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
*~
geesefs
goofys
goofys.test
xout
s3proxy.jar
test/s3proxy_test.properties
core/*.log
26 changes: 26 additions & 0 deletions Dockerfile.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Dockerfile for building geesefs
FROM golang:1.25.9-alpine AS builder

# Install required packages
RUN apk add --no-cache git make bash

# Set working directory
WORKDIR /build

# Copy source code (needed for replace directives in go.mod)
COPY . .

# Download dependencies
RUN go mod download

# Set environment variables for static build
ENV CGO_ENABLED=0
ENV GOOS=linux

# Build binary
RUN go build -ldflags "-X main.Version=docker-build -s -w" -o geesefs .

# Final stage - minimal image with binary only (optional)
FROM scratch AS final
COPY --from=builder /build/geesefs /geesefs
ENTRYPOINT ["/geesefs"]
17 changes: 16 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,21 @@ install:
go install -ldflags "-X main.Version=`git rev-parse HEAD`"


.PHONY: protoc
# Docker build targets
docker-build:
docker build -f Dockerfile.build -t geesefs-builder:latest .

docker-binary: docker-build
@echo "Extracting binary from Docker container..."
@# Create temporary container and copy binary
@CONTAINER_ID=$$(docker create geesefs-builder:latest) && \
docker cp $$CONTAINER_ID:/geesefs ./geesefs && \
docker rm $$CONTAINER_ID && \
echo "Binary geesefs copied to project root"

docker-clean:
docker rmi geesefs-builder:latest 2>/dev/null || true

.PHONY: protoc docker-build docker-binary docker-clean
protoc:
protoc --go_out=. --experimental_allow_proto3_optional --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative core/pb/*.proto
2 changes: 1 addition & 1 deletion core/backend_adlv1.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
"syscall"
"time"

uuid "github.com/satori/go.uuid"
uuid "github.com/gofrs/uuid"
"github.com/sirupsen/logrus"

adl "github.com/Azure/azure-sdk-for-go/services/datalake/store/2016-11-01/filesystem"
Expand Down
22 changes: 11 additions & 11 deletions core/backend_azblob.go
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ func (b *AZBlob) HeadBlob(param *HeadBlobInput) (*HeadBlobOutput, error) {
}

blob := c.NewBlobURL(param.Key)
resp, err := blob.GetProperties(context.TODO(), azblob.BlobAccessConditions{})
resp, err := blob.GetProperties(context.TODO(), azblob.BlobAccessConditions{}, azblob.ClientProvidedKeyOptions{})
if err != nil {
return nil, mapAZBError(err)
}
Expand Down Expand Up @@ -481,7 +481,7 @@ func (b *AZBlob) ListBlobs(param *ListBlobsInput) (*ListBlobsOutput, error) {
prefixes := make([]BlobPrefixOutput, 0)
items := make([]BlobItemOutput, 0)

var blobItems []azblob.BlobItem
var blobItems []azblob.BlobItemInternal
var nextMarker *string

options := azblob.ListBlobsSegmentOptions{
Expand Down Expand Up @@ -703,8 +703,8 @@ func (b *AZBlob) CopyBlob(param *CopyBlobInput) (*CopyBlobOutput, error) {

src := c.NewBlobURL(param.Source)
dest := c.NewBlobURL(param.Destination)
resp, err := dest.StartCopyFromURL(context.TODO(), src.URL(), nilMetadata(param.Metadata),
azblob.ModifiedAccessConditions{}, azblob.BlobAccessConditions{})
resp, err := dest.StartCopyFromURL(context.TODO(), src.URL(), azblob.Metadata(nilMetadata(param.Metadata)),
azblob.ModifiedAccessConditions{}, azblob.BlobAccessConditions{}, azblob.AccessTierNone, azblob.BlobTagsMap{})
if err != nil {
return nil, mapAZBError(err)
}
Expand All @@ -713,7 +713,7 @@ func (b *AZBlob) CopyBlob(param *CopyBlobInput) (*CopyBlobOutput, error) {
time.Sleep(50 * time.Millisecond)

var copy *azblob.BlobGetPropertiesResponse
for copy, err = dest.GetProperties(context.TODO(), azblob.BlobAccessConditions{}); err == nil; copy, err = dest.GetProperties(context.TODO(), azblob.BlobAccessConditions{}) {
for copy, err = dest.GetProperties(context.TODO(), azblob.BlobAccessConditions{}, azblob.ClientProvidedKeyOptions{}); err == nil; copy, err = dest.GetProperties(context.TODO(), azblob.BlobAccessConditions{}, azblob.ClientProvidedKeyOptions{}) {
// if there's a new copy, we can only assume the last one was done
if copy.CopyStatus() != azblob.CopyStatusPending || copy.CopyID() != resp.CopyID() {
break
Expand Down Expand Up @@ -745,7 +745,7 @@ func (b *AZBlob) GetBlob(param *GetBlobInput) (*GetBlobOutput, error) {
ModifiedAccessConditions: azblob.ModifiedAccessConditions{
IfMatch: ifMatch,
},
}, false)
}, false, azblob.ClientProvidedKeyOptions{})
if err != nil {
return nil, mapAZBError(err)
}
Expand Down Expand Up @@ -798,7 +798,7 @@ func (b *AZBlob) PutBlob(param *PutBlobInput) (*PutBlobOutput, error) {
azblob.BlobHTTPHeaders{
ContentType: NilStr(param.ContentType),
},
nilMetadata(param.Metadata), azblob.BlobAccessConditions{})
azblob.Metadata(nilMetadata(param.Metadata)), azblob.BlobAccessConditions{}, azblob.AccessTierNone, azblob.BlobTagsMap{}, azblob.ClientProvidedKeyOptions{}, azblob.ImmutabilityPolicyOptions{})
if err != nil {
return nil, mapAZBError(err)
}
Expand Down Expand Up @@ -837,7 +837,7 @@ func (b *AZBlob) MultipartBlobAdd(param *MultipartBlobAddInput) (*MultipartBlobA
base64BlockId := base64.StdEncoding.EncodeToString([]byte(blockId))

_, err = blob.StageBlock(context.TODO(), base64BlockId, param.Body,
azblob.LeaseAccessConditions{}, nil)
azblob.LeaseAccessConditions{}, nil, azblob.ClientProvidedKeyOptions{})
if err != nil {
return nil, mapAZBError(err)
}
Expand Down Expand Up @@ -880,7 +880,7 @@ func (b *AZBlob) MultipartBlobCopy(param *MultipartBlobCopyInput) (*MultipartBlo

_, err = blob.StageBlockFromURL(context.TODO(), base64BlockId,
srcBlobURL, int64(param.Offset), int64(param.Size),
azblob.LeaseAccessConditions{}, azblob.ModifiedAccessConditions{})
azblob.LeaseAccessConditions{}, azblob.ModifiedAccessConditions{}, azblob.ClientProvidedKeyOptions{}, nil)
if err != nil {
return nil, mapAZBError(err)
}
Expand Down Expand Up @@ -909,8 +909,8 @@ func (b *AZBlob) MultipartBlobCommit(param *MultipartBlobCommitInput) (*Multipar
}

resp, err := blob.CommitBlockList(context.TODO(), parts,
azblob.BlobHTTPHeaders{}, nilMetadata(param.Metadata),
azblob.BlobAccessConditions{})
azblob.BlobHTTPHeaders{}, azblob.Metadata(nilMetadata(param.Metadata)),
azblob.BlobAccessConditions{}, azblob.AccessTierNone, azblob.BlobTagsMap{}, azblob.ClientProvidedKeyOptions{}, azblob.ImmutabilityPolicyOptions{})
if err != nil {
return nil, mapAZBError(err)
}
Expand Down
14 changes: 12 additions & 2 deletions core/backend_s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"github.com/aws/aws-sdk-go/aws/corehandlers"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/private/protocol/rest"
"github.com/aws/aws-sdk-go/service/s3"
)

Expand All @@ -64,6 +65,10 @@ func NewS3(bucket string, flags *cfg.FlagStorage, config *cfg.S3Config) (*S3Back
if config.MultipartCopyThreshold == 0 {
config.MultipartCopyThreshold = 128 * 1024 * 1024
}
config.CopySourceBucket = bucket
if config.Subdomain {
bucket = "/"
}

if config.ProjectId != "" {
log.Infof("Using Ceph multitenancy format bucket naming: %s", bucket)
Expand Down Expand Up @@ -268,6 +273,11 @@ func (s *S3Backend) newS3() {
Fn: request.MakeAddToUserAgentHandler("GeeseFS", cfg.GEESEFS_VERSION,
runtime.Version(), runtime.GOOS, runtime.GOARCH),
})
s.S3.Handlers.Build.PushBack(func(req *request.Request) {
if s.config.Subdomain {
req.HTTPRequest.URL.RawPath = rest.EscapePath(req.HTTPRequest.URL.Path, false)
}
})
}

func (s *S3Backend) detectBucketLocationByHEAD() (err error, isAws bool) {
Expand Down Expand Up @@ -816,7 +826,7 @@ func (s *S3Backend) CopyBlob(param *CopyBlobInput) (*CopyBlobOutput, error) {
metadataDirective = s3.MetadataDirectiveReplace
}

from := s.bucket + "/" + param.Source
from := s.config.CopySourceBucket + "/" + param.Source

// Copy into the same object is used to just update metadata
// and should be very quick regardless of parameters
Expand Down Expand Up @@ -1130,7 +1140,7 @@ func (s *S3Backend) MultipartBlobCopy(param *MultipartBlobCopyInput) (*Multipart
Bucket: &s.bucket,
Key: param.Commit.Key,
PartNumber: aws.Int64(int64(param.PartNumber)),
CopySource: aws.String(pathEscape(s.bucket + "/" + param.CopySource)),
CopySource: aws.String(pathEscape(s.config.CopySourceBucket + "/" + param.CopySource)),
UploadId: param.Commit.UploadId,
}
if param.Size != 0 {
Expand Down
4 changes: 2 additions & 2 deletions core/cfg/conf_azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ func azureFindAccount(client azblob.AccountsClient, account string) (*azblob.End
return nil, "", err
}

for _, acc := range *accountsRes.Value {
for _, acc := range accountsRes.Values() {
if *acc.Name == account {
// /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/...
parts := strings.SplitN(*acc.ID, "/", 6)
Expand Down Expand Up @@ -370,7 +370,7 @@ func AzureBlobConfig(endpoint string, location string, storageType string) (conf

if key == "" {
var keysRes azblob.AccountListKeysResult
keysRes, err = client.ListKeys(context.TODO(), resourceGroup, account)
keysRes, err = client.ListKeys(context.TODO(), resourceGroup, account, "")
if err != nil || len(*keysRes.Keys) == 0 {
err = fmt.Errorf("Missing key: configure via AZURE_STORAGE_KEY "+
"or %v/config", configDir)
Expand Down
14 changes: 12 additions & 2 deletions core/cfg/conf_s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"encoding/base64"
"fmt"
"net/http"
"net/url"
"time"

"github.com/aws/aws-sdk-go/aws"
Expand Down Expand Up @@ -70,7 +71,8 @@ type S3Config struct {
ListV2 bool
ListV1Ext bool

Subdomain bool
CopySourceBucket string
Subdomain bool

UseIAM bool
IAMFlavor string
Expand Down Expand Up @@ -132,10 +134,18 @@ func (c *S3Config) ToAwsConfig(flags *FlagStorage) (*aws.Config, error) {
c.Credentials = credentials.NewStaticCredentials(c.AccessKey, c.SecretKey, "")
}
}

if flags.Endpoint != "" {
if c.Subdomain {
u, err := url.Parse(flags.Endpoint)
if err != nil {
return nil, err
}
u.Host = c.CopySourceBucket + "." + u.Host
flags.Endpoint = u.String()
}
awsConfig.Endpoint = &flags.Endpoint
}

awsConfig.S3ForcePathStyle = aws.Bool(!c.Subdomain)

awsConfig.Retryer = client.DefaultRetryer{
Expand Down
3 changes: 3 additions & 0 deletions core/cfg/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ type FlagStorage struct {
Setuid int
Setgid int

IgnoreSettingAttrsForRootDirErrors bool

// Common Backend Config
UseContentType bool
Endpoint string
Expand Down Expand Up @@ -88,6 +90,7 @@ type FlagStorage struct {
EnablePerms bool
EnableSpecials bool
EnableMtime bool
EmulateHardlinks bool
DisableXattr bool
UidAttr string
GidAttr string
Expand Down
Loading