diff --git a/weed/glog/glog_test.go b/weed/glog/glog_test.go index 48d1c9ec5..a311e320c 100644 --- a/weed/glog/glog_test.go +++ b/weed/glog/glog_test.go @@ -333,10 +333,13 @@ func TestRollover(t *testing.T) { logExitFunc = func(e error) { err = e } + + Info("x") // Be sure we have a file (also triggers createLogDirs via sync.Once). + + // Set MaxSize after the first Info call so that createLogDirs (which + // overwrites MaxSize from the flag default) has already executed. defer func(previous uint64) { MaxSize = previous }(MaxSize) MaxSize = 512 - - Info("x") // Be sure we have a file. info, ok := logging.file[infoLog].(*syncBuffer) if !ok { t.Fatal("info wasn't created") diff --git a/weed/s3api/s3api_object_handlers_put.go b/weed/s3api/s3api_object_handlers_put.go index 4b4772e2e..d944f496c 100644 --- a/weed/s3api/s3api_object_handlers_put.go +++ b/weed/s3api/s3api_object_handlers_put.go @@ -139,6 +139,22 @@ func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request) fullDirPath = fullDirPath + "/" + dirName } + // Read any content through dataReader (handles chunked encoding properly) + var dirContent []byte + if r.ContentLength != 0 { + var readErr error + dirContent, readErr = io.ReadAll(dataReader) + if readErr != nil { + glog.Errorf("PutObjectHandler: failed to read directory marker content %s/%s: %v", bucket, object, readErr) + s3err.WriteErrorResponse(w, r, s3err.ErrInternalError) + return + } + } + + // Compute MD5 for ETag (md5.Sum of nil/empty = MD5 of empty content) + dirMd5 := md5.Sum(dirContent) + dirEtag := fmt.Sprintf("%x", dirMd5) + glog.Infof("PutObjectHandler: explicit directory marker %s/%s (contentType=%q, len=%d)", bucket, object, objectContentType, r.ContentLength) if err := s3a.mkdir( @@ -147,10 +163,17 @@ func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request) if objectContentType == "" { objectContentType = s3_constants.FolderMimeType } - if r.ContentLength > 0 { - entry.Content, _ = io.ReadAll(r.Body) + if len(dirContent) > 0 { + entry.Content = dirContent } entry.Attributes.Mime = objectContentType + entry.Attributes.Md5 = dirMd5[:] + + // Store ETag in extended attributes for consistency with regular objects + if entry.Extended == nil { + entry.Extended = make(map[string][]byte) + } + entry.Extended[s3_constants.ExtETagKey] = []byte(dirEtag) // Set object owner for directory objects (same as regular objects) s3a.setObjectOwnerFromRequest(r, bucket, entry) @@ -158,6 +181,7 @@ func (s3a *S3ApiServer) PutObjectHandler(w http.ResponseWriter, r *http.Request) s3err.WriteErrorResponse(w, r, s3err.ErrInternalError) return } + setEtag(w, dirEtag) } else { // Get detailed versioning state for the bucket versioningState, err := s3a.getVersioningState(bucket)