diff --git a/cmd/erasure-multipart-cleanup_test.go b/cmd/erasure-multipart-cleanup_test.go
new file mode 100644
index 000000000..fa0fe92ba
--- /dev/null
+++ b/cmd/erasure-multipart-cleanup_test.go
@@ -0,0 +1,84 @@
+// Copyright (c) 2026 clown15
+//
+// This file is part of Silo Object Storage stack
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package cmd
+
+import (
+ "encoding/base64"
+ "fmt"
+ "testing"
+ "time"
+)
+
+func TestExtractModTimeFromUploadIDDir(t *testing.T) {
+ now := time.Now().UTC().Truncate(time.Millisecond)
+ nano := now.UnixNano()
+ uuid := "d16ad42a-6fe5-4a3b-8092-69983142238f"
+
+ testCases := []struct {
+ name string
+ dirName string
+ expectedMod time.Time
+ wantZero bool
+ }{
+ {
+ name: "Modern Plaintext Format (x)",
+ dirName: fmt.Sprintf("%sx%d", uuid, nano),
+ expectedMod: time.Unix(0, nano),
+ wantZero: false,
+ },
+ {
+ name: "Legacy Format Without Timestamp ()",
+ dirName: uuid,
+ expectedMod: time.Time{},
+ wantZero: true,
+ },
+ {
+ name: "Base64 Encoded Format",
+ dirName: base64.RawURLEncoding.EncodeToString([]byte(fmt.Sprintf("%sx%d", uuid, nano))),
+ expectedMod: time.Unix(0, nano),
+ wantZero: false,
+ },
+ {
+ name: "Malformed Suffix (xinvalid)",
+ dirName: fmt.Sprintf("%sxnotanumber", uuid),
+ expectedMod: time.Time{},
+ wantZero: true,
+ },
+ {
+ name: "Empty Directory Name",
+ dirName: "",
+ expectedMod: time.Time{},
+ wantZero: true,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ got := extractModTimeFromUploadIDDir(tc.dirName)
+ if tc.wantZero {
+ if !got.IsZero() {
+ t.Fatalf("expected zero time for %q, got %v", tc.dirName, got)
+ }
+ return
+ }
+ if !got.Equal(tc.expectedMod) {
+ t.Fatalf("extractModTimeFromUploadIDDir(%q) = %v; want %v", tc.dirName, got, tc.expectedMod)
+ }
+ })
+ }
+}
diff --git a/cmd/erasure-multipart.go b/cmd/erasure-multipart.go
index 89355c762..b48b62d78 100644
--- a/cmd/erasure-multipart.go
+++ b/cmd/erasure-multipart.go
@@ -174,6 +174,23 @@ func (er erasureObjects) deleteAll(ctx context.Context, bucket, prefix string) {
wg.Wait()
}
+// extractModTimeFromUploadIDDir extracts creation time from the upload ID directory on disk.
+// On-disk directory names are of the form x.
+// For compatibility with legacy or encoded representations, if the directory is Base64 encoded,
+// it decodes the name before parsing.
+func extractModTimeFromUploadIDDir(uploadIDDir string) time.Time {
+ target := uploadIDDir
+ if b64, err := base64.RawURLEncoding.DecodeString(uploadIDDir); err == nil && strings.Contains(string(b64), "x") {
+ target = string(b64)
+ }
+ if split := strings.Split(target, "x"); len(split) == 2 {
+ if t, err := strconv.ParseInt(split[1], 10, 64); err == nil {
+ return time.Unix(0, t)
+ }
+ }
+ return time.Time{}
+}
+
// Remove the old multipart uploads on the given disk.
func (er erasureObjects) cleanupStaleUploadsOnDisk(ctx context.Context, disk StorageAPI) {
drivePath := disk.Endpoint().Path
@@ -181,16 +198,7 @@ func (er erasureObjects) cleanupStaleUploadsOnDisk(ctx context.Context, disk Sto
readDirFn(pathJoin(drivePath, minioMetaMultipartBucket), func(shaDir string, typ os.FileMode) error {
readDirFn(pathJoin(drivePath, minioMetaMultipartBucket, shaDir), func(uploadIDDir string, typ os.FileMode) error {
uploadIDPath := pathJoin(shaDir, uploadIDDir)
- var modTime time.Time
- // Upload IDs are of the form base64_url(x), we can extract the time from the UUID.
- if b64, err := base64.RawURLEncoding.DecodeString(uploadIDDir); err == nil {
- if split := strings.Split(string(b64), "x"); len(split) == 2 {
- t, err := strconv.ParseInt(split[1], 10, 64)
- if err == nil {
- modTime = time.Unix(0, t)
- }
- }
- }
+ modTime := extractModTimeFromUploadIDDir(uploadIDDir)
// Fallback for older uploads without time in the ID.
if modTime.IsZero() {
wait := deleteMultipartCleanupSleeper.Timer(ctx)