github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/storage-rest-server.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "bufio" 22 "context" 23 "encoding/binary" 24 "encoding/gob" 25 "encoding/hex" 26 "errors" 27 "fmt" 28 "io" 29 "net/http" 30 "os/user" 31 "path" 32 "runtime" 33 "runtime/debug" 34 "strconv" 35 "strings" 36 "sync" 37 "time" 38 39 "github.com/minio/minio/internal/grid" 40 "github.com/tinylib/msgp/msgp" 41 42 jwtreq "github.com/golang-jwt/jwt/v4/request" 43 "github.com/minio/madmin-go/v3" 44 "github.com/minio/minio/internal/config" 45 xhttp "github.com/minio/minio/internal/http" 46 xioutil "github.com/minio/minio/internal/ioutil" 47 xjwt "github.com/minio/minio/internal/jwt" 48 "github.com/minio/minio/internal/logger" 49 "github.com/minio/mux" 50 xnet "github.com/minio/pkg/v2/net" 51 ) 52 53 var errDiskStale = errors.New("drive stale") 54 55 // To abstract a disk over network. 56 type storageRESTServer struct { 57 endpoint Endpoint 58 } 59 60 var ( 61 storageCheckPartsRPC = grid.NewSingleHandler[*CheckPartsHandlerParams, grid.NoPayload](grid.HandlerCheckParts, func() *CheckPartsHandlerParams { return &CheckPartsHandlerParams{} }, grid.NewNoPayload) 62 storageDeleteFileRPC = grid.NewSingleHandler[*DeleteFileHandlerParams, grid.NoPayload](grid.HandlerDeleteFile, func() *DeleteFileHandlerParams { return &DeleteFileHandlerParams{} }, grid.NewNoPayload).AllowCallRequestPool(true) 63 storageDeleteVersionRPC = grid.NewSingleHandler[*DeleteVersionHandlerParams, grid.NoPayload](grid.HandlerDeleteVersion, func() *DeleteVersionHandlerParams { return &DeleteVersionHandlerParams{} }, grid.NewNoPayload) 64 storageDiskInfoRPC = grid.NewSingleHandler[*DiskInfoOptions, *DiskInfo](grid.HandlerDiskInfo, func() *DiskInfoOptions { return &DiskInfoOptions{} }, func() *DiskInfo { return &DiskInfo{} }).WithSharedResponse().AllowCallRequestPool(true) 65 storageNSScannerRPC = grid.NewStream[*nsScannerOptions, grid.NoPayload, *nsScannerResp](grid.HandlerNSScanner, func() *nsScannerOptions { return &nsScannerOptions{} }, nil, func() *nsScannerResp { return &nsScannerResp{} }) 66 storageReadAllRPC = grid.NewSingleHandler[*ReadAllHandlerParams, *grid.Bytes](grid.HandlerReadAll, func() *ReadAllHandlerParams { return &ReadAllHandlerParams{} }, grid.NewBytes).AllowCallRequestPool(true) 67 storageWriteAllRPC = grid.NewSingleHandler[*WriteAllHandlerParams, grid.NoPayload](grid.HandlerWriteAll, func() *WriteAllHandlerParams { return &WriteAllHandlerParams{} }, grid.NewNoPayload) 68 storageReadVersionRPC = grid.NewSingleHandler[*grid.MSS, *FileInfo](grid.HandlerReadVersion, grid.NewMSS, func() *FileInfo { return &FileInfo{} }) 69 storageReadXLRPC = grid.NewSingleHandler[*grid.MSS, *RawFileInfo](grid.HandlerReadXL, grid.NewMSS, func() *RawFileInfo { return &RawFileInfo{} }) 70 storageRenameDataRPC = grid.NewSingleHandler[*RenameDataHandlerParams, *RenameDataResp](grid.HandlerRenameData, func() *RenameDataHandlerParams { return &RenameDataHandlerParams{} }, func() *RenameDataResp { return &RenameDataResp{} }) 71 storageRenameFileRPC = grid.NewSingleHandler[*RenameFileHandlerParams, grid.NoPayload](grid.HandlerRenameFile, func() *RenameFileHandlerParams { return &RenameFileHandlerParams{} }, grid.NewNoPayload).AllowCallRequestPool(true) 72 storageStatVolRPC = grid.NewSingleHandler[*grid.MSS, *VolInfo](grid.HandlerStatVol, grid.NewMSS, func() *VolInfo { return &VolInfo{} }) 73 storageUpdateMetadataRPC = grid.NewSingleHandler[*MetadataHandlerParams, grid.NoPayload](grid.HandlerUpdateMetadata, func() *MetadataHandlerParams { return &MetadataHandlerParams{} }, grid.NewNoPayload) 74 storageWriteMetadataRPC = grid.NewSingleHandler[*MetadataHandlerParams, grid.NoPayload](grid.HandlerWriteMetadata, func() *MetadataHandlerParams { return &MetadataHandlerParams{} }, grid.NewNoPayload) 75 storageListDirRPC = grid.NewStream[*grid.MSS, grid.NoPayload, *ListDirResult](grid.HandlerListDir, grid.NewMSS, nil, func() *ListDirResult { return &ListDirResult{} }).WithOutCapacity(1) 76 ) 77 78 func getStorageViaEndpoint(endpoint Endpoint) StorageAPI { 79 globalLocalDrivesMu.RLock() 80 defer globalLocalDrivesMu.RUnlock() 81 if len(globalLocalSetDrives) == 0 { 82 for _, drive := range globalLocalDrives { 83 if drive != nil && drive.Endpoint().Equal(endpoint) { 84 return drive 85 } 86 } 87 } 88 return globalLocalSetDrives[endpoint.PoolIdx][endpoint.SetIdx][endpoint.DiskIdx] 89 } 90 91 func (s *storageRESTServer) getStorage() StorageAPI { 92 return getStorageViaEndpoint(s.endpoint) 93 } 94 95 func (s *storageRESTServer) writeErrorResponse(w http.ResponseWriter, err error) { 96 err = unwrapAll(err) 97 switch err { 98 case errDiskStale: 99 w.WriteHeader(http.StatusPreconditionFailed) 100 case errFileNotFound, errFileVersionNotFound: 101 w.WriteHeader(http.StatusNotFound) 102 case errInvalidAccessKeyID, errAccessKeyDisabled, errNoAuthToken, errMalformedAuth, errAuthentication, errSkewedAuthTime: 103 w.WriteHeader(http.StatusUnauthorized) 104 case context.Canceled, context.DeadlineExceeded: 105 w.WriteHeader(499) 106 default: 107 w.WriteHeader(http.StatusForbidden) 108 } 109 w.Write([]byte(err.Error())) 110 } 111 112 // DefaultSkewTime - skew time is 15 minutes between minio peers. 113 const DefaultSkewTime = 15 * time.Minute 114 115 // Authenticates storage client's requests and validates for skewed time. 116 func storageServerRequestValidate(r *http.Request) error { 117 token, err := jwtreq.AuthorizationHeaderExtractor.ExtractToken(r) 118 if err != nil { 119 if err == jwtreq.ErrNoTokenInRequest { 120 return errNoAuthToken 121 } 122 return errMalformedAuth 123 } 124 125 claims := xjwt.NewStandardClaims() 126 if err = xjwt.ParseWithStandardClaims(token, claims, []byte(globalActiveCred.SecretKey)); err != nil { 127 return errAuthentication 128 } 129 130 owner := claims.AccessKey == globalActiveCred.AccessKey || claims.Subject == globalActiveCred.AccessKey 131 if !owner { 132 return errAuthentication 133 } 134 135 if claims.Audience != r.URL.RawQuery { 136 return errAuthentication 137 } 138 139 requestTimeStr := r.Header.Get("X-Minio-Time") 140 requestTime, err := time.Parse(time.RFC3339, requestTimeStr) 141 if err != nil { 142 return errMalformedAuth 143 } 144 utcNow := UTCNow() 145 delta := requestTime.Sub(utcNow) 146 if delta < 0 { 147 delta *= -1 148 } 149 if delta > DefaultSkewTime { 150 return errSkewedAuthTime 151 } 152 153 return nil 154 } 155 156 // IsAuthValid - To authenticate and verify the time difference. 157 func (s *storageRESTServer) IsAuthValid(w http.ResponseWriter, r *http.Request) bool { 158 if s.getStorage() == nil { 159 s.writeErrorResponse(w, errDiskNotFound) 160 return false 161 } 162 163 if err := storageServerRequestValidate(r); err != nil { 164 s.writeErrorResponse(w, err) 165 return false 166 } 167 168 return true 169 } 170 171 // IsValid - To authenticate and check if the disk-id in the request corresponds to the underlying disk. 172 func (s *storageRESTServer) IsValid(w http.ResponseWriter, r *http.Request) bool { 173 if !s.IsAuthValid(w, r) { 174 return false 175 } 176 177 if err := r.ParseForm(); err != nil { 178 s.writeErrorResponse(w, err) 179 return false 180 } 181 182 diskID := r.Form.Get(storageRESTDiskID) 183 if diskID == "" { 184 // Request sent empty disk-id, we allow the request 185 // as the peer might be coming up and trying to read format.json 186 // or create format.json 187 return true 188 } 189 190 storedDiskID, err := s.getStorage().GetDiskID() 191 if err != nil { 192 s.writeErrorResponse(w, err) 193 return false 194 } 195 196 if diskID != storedDiskID { 197 s.writeErrorResponse(w, errDiskStale) 198 return false 199 } 200 201 // If format.json is available and request sent the right disk-id, we allow the request 202 return true 203 } 204 205 // checkID - check if the disk-id in the request corresponds to the underlying disk. 206 func (s *storageRESTServer) checkID(wantID string) bool { 207 if s.getStorage() == nil { 208 return false 209 } 210 if wantID == "" { 211 // Request sent empty disk-id, we allow the request 212 // as the peer might be coming up and trying to read format.json 213 // or create format.json 214 return true 215 } 216 217 storedDiskID, err := s.getStorage().GetDiskID() 218 if err != nil { 219 return false 220 } 221 222 return wantID == storedDiskID 223 } 224 225 // HealthHandler handler checks if disk is stale 226 func (s *storageRESTServer) HealthHandler(w http.ResponseWriter, r *http.Request) { 227 s.IsValid(w, r) 228 } 229 230 // DiskInfoHandler - returns disk info. 231 func (s *storageRESTServer) DiskInfoHandler(opts *DiskInfoOptions) (*DiskInfo, *grid.RemoteErr) { 232 if !s.checkID(opts.DiskID) { 233 return nil, grid.NewRemoteErr(errDiskNotFound) 234 } 235 info, err := s.getStorage().DiskInfo(context.Background(), *opts) 236 if err != nil { 237 info.Error = err.Error() 238 } 239 return &info, nil 240 } 241 242 func (s *storageRESTServer) NSScannerHandler(ctx context.Context, params *nsScannerOptions, out chan<- *nsScannerResp) *grid.RemoteErr { 243 if !s.checkID(params.DiskID) { 244 return grid.NewRemoteErr(errDiskNotFound) 245 } 246 if params.Cache == nil { 247 return grid.NewRemoteErrString("NSScannerHandler: provided cache is nil") 248 } 249 250 // Collect updates, stream them before the full cache is sent. 251 updates := make(chan dataUsageEntry, 1) 252 var wg sync.WaitGroup 253 wg.Add(1) 254 go func() { 255 defer wg.Done() 256 for update := range updates { 257 resp := storageNSScannerRPC.NewResponse() 258 resp.Update = &update 259 out <- resp 260 } 261 }() 262 ui, err := s.getStorage().NSScanner(ctx, *params.Cache, updates, madmin.HealScanMode(params.ScanMode), nil) 263 wg.Wait() 264 if err != nil { 265 return grid.NewRemoteErr(err) 266 } 267 // Send final response. 268 resp := storageNSScannerRPC.NewResponse() 269 resp.Final = &ui 270 out <- resp 271 return nil 272 } 273 274 // MakeVolHandler - make a volume. 275 func (s *storageRESTServer) MakeVolHandler(w http.ResponseWriter, r *http.Request) { 276 if !s.IsValid(w, r) { 277 return 278 } 279 volume := r.Form.Get(storageRESTVolume) 280 err := s.getStorage().MakeVol(r.Context(), volume) 281 if err != nil { 282 s.writeErrorResponse(w, err) 283 } 284 } 285 286 // MakeVolBulkHandler - create multiple volumes as a bulk operation. 287 func (s *storageRESTServer) MakeVolBulkHandler(w http.ResponseWriter, r *http.Request) { 288 if !s.IsValid(w, r) { 289 return 290 } 291 volumes := strings.Split(r.Form.Get(storageRESTVolumes), ",") 292 err := s.getStorage().MakeVolBulk(r.Context(), volumes...) 293 if err != nil { 294 s.writeErrorResponse(w, err) 295 } 296 } 297 298 // StatVolHandler - stat a volume. 299 func (s *storageRESTServer) StatVolHandler(params *grid.MSS) (*VolInfo, *grid.RemoteErr) { 300 if !s.checkID(params.Get(storageRESTDiskID)) { 301 return nil, grid.NewRemoteErr(errDiskNotFound) 302 } 303 info, err := s.getStorage().StatVol(context.Background(), params.Get(storageRESTVolume)) 304 if err != nil { 305 return nil, grid.NewRemoteErr(err) 306 } 307 return &info, nil 308 } 309 310 // AppendFileHandler - append data from the request to the file specified. 311 func (s *storageRESTServer) AppendFileHandler(w http.ResponseWriter, r *http.Request) { 312 if !s.IsValid(w, r) { 313 return 314 } 315 volume := r.Form.Get(storageRESTVolume) 316 filePath := r.Form.Get(storageRESTFilePath) 317 318 buf := make([]byte, r.ContentLength) 319 _, err := io.ReadFull(r.Body, buf) 320 if err != nil { 321 s.writeErrorResponse(w, err) 322 return 323 } 324 err = s.getStorage().AppendFile(r.Context(), volume, filePath, buf) 325 if err != nil { 326 s.writeErrorResponse(w, err) 327 } 328 } 329 330 // CreateFileHandler - copy the contents from the request. 331 func (s *storageRESTServer) CreateFileHandler(w http.ResponseWriter, r *http.Request) { 332 if !s.IsValid(w, r) { 333 return 334 } 335 336 volume := r.Form.Get(storageRESTVolume) 337 filePath := r.Form.Get(storageRESTFilePath) 338 origvolume := r.Form.Get(storageRESTOrigVolume) 339 340 fileSizeStr := r.Form.Get(storageRESTLength) 341 fileSize, err := strconv.Atoi(fileSizeStr) 342 if err != nil { 343 s.writeErrorResponse(w, err) 344 return 345 } 346 347 done, body := keepHTTPReqResponseAlive(w, r) 348 done(s.getStorage().CreateFile(r.Context(), origvolume, volume, filePath, int64(fileSize), body)) 349 } 350 351 // DeleteVersionHandler delete updated metadata. 352 func (s *storageRESTServer) DeleteVersionHandler(p *DeleteVersionHandlerParams) (np grid.NoPayload, gerr *grid.RemoteErr) { 353 if !s.checkID(p.DiskID) { 354 return np, grid.NewRemoteErr(errDiskNotFound) 355 } 356 volume := p.Volume 357 filePath := p.FilePath 358 forceDelMarker := p.ForceDelMarker 359 360 opts := DeleteOptions{} 361 err := s.getStorage().DeleteVersion(context.Background(), volume, filePath, p.FI, forceDelMarker, opts) 362 return np, grid.NewRemoteErr(err) 363 } 364 365 // ReadVersionHandlerWS read metadata of versionID 366 func (s *storageRESTServer) ReadVersionHandlerWS(params *grid.MSS) (*FileInfo, *grid.RemoteErr) { 367 if !s.checkID(params.Get(storageRESTDiskID)) { 368 return nil, grid.NewRemoteErr(errDiskNotFound) 369 } 370 origvolume := params.Get(storageRESTOrigVolume) 371 volume := params.Get(storageRESTVolume) 372 filePath := params.Get(storageRESTFilePath) 373 versionID := params.Get(storageRESTVersionID) 374 readData, err := strconv.ParseBool(params.Get(storageRESTReadData)) 375 if err != nil { 376 return nil, grid.NewRemoteErr(err) 377 } 378 379 healing, err := strconv.ParseBool(params.Get(storageRESTHealing)) 380 if err != nil { 381 return nil, grid.NewRemoteErr(err) 382 } 383 384 fi, err := s.getStorage().ReadVersion(context.Background(), origvolume, volume, filePath, versionID, ReadOptions{ReadData: readData, Healing: healing}) 385 if err != nil { 386 return nil, grid.NewRemoteErr(err) 387 } 388 return &fi, nil 389 } 390 391 // ReadVersionHandler read metadata of versionID 392 func (s *storageRESTServer) ReadVersionHandler(w http.ResponseWriter, r *http.Request) { 393 if !s.IsValid(w, r) { 394 return 395 } 396 origvolume := r.Form.Get(storageRESTOrigVolume) 397 volume := r.Form.Get(storageRESTVolume) 398 filePath := r.Form.Get(storageRESTFilePath) 399 versionID := r.Form.Get(storageRESTVersionID) 400 readData, err := strconv.ParseBool(r.Form.Get(storageRESTReadData)) 401 if err != nil { 402 s.writeErrorResponse(w, err) 403 return 404 } 405 healing, err := strconv.ParseBool(r.Form.Get(storageRESTHealing)) 406 if err != nil { 407 s.writeErrorResponse(w, err) 408 return 409 } 410 fi, err := s.getStorage().ReadVersion(r.Context(), origvolume, volume, filePath, versionID, ReadOptions{ReadData: readData, Healing: healing}) 411 if err != nil { 412 s.writeErrorResponse(w, err) 413 return 414 } 415 416 logger.LogIf(r.Context(), msgp.Encode(w, &fi)) 417 } 418 419 // WriteMetadataHandler rpc handler to write new updated metadata. 420 func (s *storageRESTServer) WriteMetadataHandler(p *MetadataHandlerParams) (np grid.NoPayload, gerr *grid.RemoteErr) { 421 if !s.checkID(p.DiskID) { 422 return grid.NewNPErr(errDiskNotFound) 423 } 424 425 volume := p.Volume 426 filePath := p.FilePath 427 origvolume := p.OrigVolume 428 429 err := s.getStorage().WriteMetadata(context.Background(), origvolume, volume, filePath, p.FI) 430 return np, grid.NewRemoteErr(err) 431 } 432 433 // UpdateMetadataHandler update new updated metadata. 434 func (s *storageRESTServer) UpdateMetadataHandler(p *MetadataHandlerParams) (grid.NoPayload, *grid.RemoteErr) { 435 if !s.checkID(p.DiskID) { 436 return grid.NewNPErr(errDiskNotFound) 437 } 438 volume := p.Volume 439 filePath := p.FilePath 440 441 return grid.NewNPErr(s.getStorage().UpdateMetadata(context.Background(), volume, filePath, p.FI, p.UpdateOpts)) 442 } 443 444 // CheckPartsHandler - check if a file metadata exists. 445 func (s *storageRESTServer) CheckPartsHandler(p *CheckPartsHandlerParams) (grid.NoPayload, *grid.RemoteErr) { 446 if !s.checkID(p.DiskID) { 447 return grid.NewNPErr(errDiskNotFound) 448 } 449 volume := p.Volume 450 filePath := p.FilePath 451 return grid.NewNPErr(s.getStorage().CheckParts(context.Background(), volume, filePath, p.FI)) 452 } 453 454 func (s *storageRESTServer) WriteAllHandler(p *WriteAllHandlerParams) (grid.NoPayload, *grid.RemoteErr) { 455 if !s.checkID(p.DiskID) { 456 return grid.NewNPErr(errDiskNotFound) 457 } 458 459 volume := p.Volume 460 filePath := p.FilePath 461 462 return grid.NewNPErr(s.getStorage().WriteAll(context.Background(), volume, filePath, p.Buf)) 463 } 464 465 // ReadAllHandler - read all the contents of a file. 466 func (s *storageRESTServer) ReadAllHandler(p *ReadAllHandlerParams) (*grid.Bytes, *grid.RemoteErr) { 467 if !s.checkID(p.DiskID) { 468 return nil, grid.NewRemoteErr(errDiskNotFound) 469 } 470 471 volume := p.Volume 472 filePath := p.FilePath 473 474 buf, err := s.getStorage().ReadAll(context.Background(), volume, filePath) 475 return grid.NewBytesWith(buf), grid.NewRemoteErr(err) 476 } 477 478 // ReadXLHandler - read xl.meta for an object at path. 479 func (s *storageRESTServer) ReadXLHandler(w http.ResponseWriter, r *http.Request) { 480 if !s.IsValid(w, r) { 481 return 482 } 483 volume := r.Form.Get(storageRESTVolume) 484 filePath := r.Form.Get(storageRESTFilePath) 485 readData, err := strconv.ParseBool(r.Form.Get(storageRESTReadData)) 486 if err != nil { 487 s.writeErrorResponse(w, err) 488 return 489 } 490 491 rf, err := s.getStorage().ReadXL(r.Context(), volume, filePath, readData) 492 if err != nil { 493 s.writeErrorResponse(w, err) 494 return 495 } 496 497 logger.LogIf(r.Context(), msgp.Encode(w, &rf)) 498 } 499 500 // ReadXLHandlerWS - read xl.meta for an object at path. 501 func (s *storageRESTServer) ReadXLHandlerWS(params *grid.MSS) (*RawFileInfo, *grid.RemoteErr) { 502 if !s.checkID(params.Get(storageRESTDiskID)) { 503 return nil, grid.NewRemoteErr(errDiskNotFound) 504 } 505 volume := params.Get(storageRESTVolume) 506 filePath := params.Get(storageRESTFilePath) 507 readData, err := strconv.ParseBool(params.Get(storageRESTReadData)) 508 if err != nil { 509 return nil, grid.NewRemoteErr(err) 510 } 511 512 rf, err := s.getStorage().ReadXL(context.Background(), volume, filePath, readData) 513 if err != nil { 514 return nil, grid.NewRemoteErr(err) 515 } 516 517 return &rf, nil 518 } 519 520 // ReadFileHandler - read section of a file. 521 func (s *storageRESTServer) ReadFileHandler(w http.ResponseWriter, r *http.Request) { 522 if !s.IsValid(w, r) { 523 return 524 } 525 volume := r.Form.Get(storageRESTVolume) 526 filePath := r.Form.Get(storageRESTFilePath) 527 offset, err := strconv.Atoi(r.Form.Get(storageRESTOffset)) 528 if err != nil { 529 s.writeErrorResponse(w, err) 530 return 531 } 532 length, err := strconv.Atoi(r.Form.Get(storageRESTLength)) 533 if err != nil { 534 s.writeErrorResponse(w, err) 535 return 536 } 537 if offset < 0 || length < 0 { 538 s.writeErrorResponse(w, errInvalidArgument) 539 return 540 } 541 var verifier *BitrotVerifier 542 if r.Form.Get(storageRESTBitrotAlgo) != "" { 543 hashStr := r.Form.Get(storageRESTBitrotHash) 544 var hash []byte 545 hash, err = hex.DecodeString(hashStr) 546 if err != nil { 547 s.writeErrorResponse(w, err) 548 return 549 } 550 verifier = NewBitrotVerifier(BitrotAlgorithmFromString(r.Form.Get(storageRESTBitrotAlgo)), hash) 551 } 552 buf := make([]byte, length) 553 defer metaDataPoolPut(buf) // Reuse if we can. 554 _, err = s.getStorage().ReadFile(r.Context(), volume, filePath, int64(offset), buf, verifier) 555 if err != nil { 556 s.writeErrorResponse(w, err) 557 return 558 } 559 w.Header().Set(xhttp.ContentLength, strconv.Itoa(len(buf))) 560 w.Write(buf) 561 } 562 563 // ReadFileStreamHandler - read section of a file. 564 func (s *storageRESTServer) ReadFileStreamHandler(w http.ResponseWriter, r *http.Request) { 565 if !s.IsValid(w, r) { 566 return 567 } 568 volume := r.Form.Get(storageRESTVolume) 569 filePath := r.Form.Get(storageRESTFilePath) 570 offset, err := strconv.Atoi(r.Form.Get(storageRESTOffset)) 571 if err != nil { 572 s.writeErrorResponse(w, err) 573 return 574 } 575 length, err := strconv.Atoi(r.Form.Get(storageRESTLength)) 576 if err != nil { 577 s.writeErrorResponse(w, err) 578 return 579 } 580 581 w.Header().Set(xhttp.ContentLength, strconv.Itoa(length)) 582 583 rc, err := s.getStorage().ReadFileStream(r.Context(), volume, filePath, int64(offset), int64(length)) 584 if err != nil { 585 s.writeErrorResponse(w, err) 586 return 587 } 588 defer rc.Close() 589 590 rf, ok := w.(io.ReaderFrom) 591 if ok && runtime.GOOS != "windows" { 592 // Attempt to use splice/sendfile() optimization, A very specific behavior mentioned below is necessary. 593 // See https://github.com/golang/go/blob/f7c5cbb82087c55aa82081e931e0142783700ce8/src/net/sendfile_linux.go#L20 594 // Windows can lock up with this optimization, so we fall back to regular copy. 595 sr, ok := rc.(*sendFileReader) 596 if ok { 597 _, err = rf.ReadFrom(sr.Reader) 598 if !xnet.IsNetworkOrHostDown(err, true) { // do not need to log disconnected clients 599 logger.LogIf(r.Context(), err) 600 } 601 if err == nil || !errors.Is(err, xhttp.ErrNotImplemented) { 602 return 603 } 604 } 605 } // Fallback to regular copy 606 607 _, err = xioutil.Copy(w, rc) 608 if !xnet.IsNetworkOrHostDown(err, true) { // do not need to log disconnected clients 609 logger.LogIf(r.Context(), err) 610 } 611 } 612 613 // ListDirHandler - list a directory. 614 func (s *storageRESTServer) ListDirHandler(ctx context.Context, params *grid.MSS, out chan<- *ListDirResult) *grid.RemoteErr { 615 if !s.checkID(params.Get(storageRESTDiskID)) { 616 return grid.NewRemoteErr(errDiskNotFound) 617 } 618 volume := params.Get(storageRESTVolume) 619 dirPath := params.Get(storageRESTDirPath) 620 origvolume := params.Get(storageRESTOrigVolume) 621 count, err := strconv.Atoi(params.Get(storageRESTCount)) 622 if err != nil { 623 return grid.NewRemoteErr(err) 624 } 625 626 entries, err := s.getStorage().ListDir(ctx, origvolume, volume, dirPath, count) 627 if err != nil { 628 return grid.NewRemoteErr(err) 629 } 630 out <- &ListDirResult{Entries: entries} 631 return nil 632 } 633 634 // DeleteFileHandler - delete a file. 635 func (s *storageRESTServer) DeleteFileHandler(p *DeleteFileHandlerParams) (grid.NoPayload, *grid.RemoteErr) { 636 if !s.checkID(p.DiskID) { 637 return grid.NewNPErr(errDiskNotFound) 638 } 639 return grid.NewNPErr(s.getStorage().Delete(context.Background(), p.Volume, p.FilePath, p.Opts)) 640 } 641 642 // DeleteVersionsErrsResp - collection of delete errors 643 // for bulk version deletes 644 type DeleteVersionsErrsResp struct { 645 Errs []error 646 } 647 648 // DeleteVersionsHandler - delete a set of a versions. 649 func (s *storageRESTServer) DeleteVersionsHandler(w http.ResponseWriter, r *http.Request) { 650 if !s.IsValid(w, r) { 651 return 652 } 653 654 volume := r.Form.Get(storageRESTVolume) 655 totalVersions, err := strconv.Atoi(r.Form.Get(storageRESTTotalVersions)) 656 if err != nil { 657 s.writeErrorResponse(w, err) 658 return 659 } 660 661 versions := make([]FileInfoVersions, totalVersions) 662 decoder := msgpNewReader(r.Body) 663 defer readMsgpReaderPoolPut(decoder) 664 for i := 0; i < totalVersions; i++ { 665 dst := &versions[i] 666 if err := dst.DecodeMsg(decoder); err != nil { 667 s.writeErrorResponse(w, err) 668 return 669 } 670 } 671 672 dErrsResp := &DeleteVersionsErrsResp{Errs: make([]error, totalVersions)} 673 674 setEventStreamHeaders(w) 675 encoder := gob.NewEncoder(w) 676 done := keepHTTPResponseAlive(w) 677 678 opts := DeleteOptions{} 679 errs := s.getStorage().DeleteVersions(r.Context(), volume, versions, opts) 680 done(nil) 681 for idx := range versions { 682 if errs[idx] != nil { 683 dErrsResp.Errs[idx] = StorageErr(errs[idx].Error()) 684 } 685 } 686 encoder.Encode(dErrsResp) 687 } 688 689 // RenameDataHandler - renames a meta object and data dir to destination. 690 func (s *storageRESTServer) RenameDataHandler(p *RenameDataHandlerParams) (*RenameDataResp, *grid.RemoteErr) { 691 if !s.checkID(p.DiskID) { 692 return nil, grid.NewRemoteErr(errDiskNotFound) 693 } 694 695 sign, err := s.getStorage().RenameData(context.Background(), p.SrcVolume, p.SrcPath, p.FI, p.DstVolume, p.DstPath, p.Opts) 696 resp := &RenameDataResp{ 697 Signature: sign, 698 } 699 return resp, grid.NewRemoteErr(err) 700 } 701 702 // RenameFileHandler - rename a file from source to destination 703 func (s *storageRESTServer) RenameFileHandler(p *RenameFileHandlerParams) (grid.NoPayload, *grid.RemoteErr) { 704 if !s.checkID(p.DiskID) { 705 return grid.NewNPErr(errDiskNotFound) 706 } 707 return grid.NewNPErr(s.getStorage().RenameFile(context.Background(), p.SrcVolume, p.SrcFilePath, p.DstVolume, p.DstFilePath)) 708 } 709 710 // CleanAbandonedDataHandler - Clean unused data directories. 711 func (s *storageRESTServer) CleanAbandonedDataHandler(w http.ResponseWriter, r *http.Request) { 712 if !s.IsValid(w, r) { 713 return 714 } 715 volume := r.Form.Get(storageRESTVolume) 716 filePath := r.Form.Get(storageRESTFilePath) 717 if volume == "" || filePath == "" { 718 return // Ignore 719 } 720 keepHTTPResponseAlive(w)(s.getStorage().CleanAbandonedData(r.Context(), volume, filePath)) 721 } 722 723 // closeNotifier is itself a ReadCloser that will notify when either an error occurs or 724 // the Close() function is called. 725 type closeNotifier struct { 726 rc io.ReadCloser 727 done chan struct{} 728 } 729 730 func (c *closeNotifier) Read(p []byte) (n int, err error) { 731 n, err = c.rc.Read(p) 732 if err != nil { 733 if c.done != nil { 734 xioutil.SafeClose(c.done) 735 c.done = nil 736 } 737 } 738 return n, err 739 } 740 741 func (c *closeNotifier) Close() error { 742 if c.done != nil { 743 xioutil.SafeClose(c.done) 744 c.done = nil 745 } 746 return c.rc.Close() 747 } 748 749 // keepHTTPReqResponseAlive can be used to avoid timeouts with long storage 750 // operations, such as bitrot verification or data usage scanning. 751 // Every 10 seconds a space character is sent. 752 // keepHTTPReqResponseAlive will wait for the returned body to be read before starting the ticker. 753 // The returned function should always be called to release resources. 754 // An optional error can be sent which will be picked as text only error, 755 // without its original type by the receiver. 756 // waitForHTTPResponse should be used to the receiving side. 757 func keepHTTPReqResponseAlive(w http.ResponseWriter, r *http.Request) (resp func(error), body io.ReadCloser) { 758 bodyDoneCh := make(chan struct{}) 759 doneCh := make(chan error) 760 ctx := r.Context() 761 go func() { 762 canWrite := true 763 write := func(b []byte) { 764 if canWrite { 765 n, err := w.Write(b) 766 if err != nil || n != len(b) { 767 canWrite = false 768 } 769 } 770 } 771 // Wait for body to be read. 772 select { 773 case <-ctx.Done(): 774 case <-bodyDoneCh: 775 case err := <-doneCh: 776 if err != nil { 777 write([]byte{1}) 778 write([]byte(err.Error())) 779 } else { 780 write([]byte{0}) 781 } 782 xioutil.SafeClose(doneCh) 783 return 784 } 785 defer xioutil.SafeClose(doneCh) 786 // Initiate ticker after body has been read. 787 ticker := time.NewTicker(time.Second * 10) 788 for { 789 select { 790 case <-ticker.C: 791 // Response not ready, write a filler byte. 792 write([]byte{32}) 793 if canWrite { 794 w.(http.Flusher).Flush() 795 } 796 case err := <-doneCh: 797 if err != nil { 798 write([]byte{1}) 799 write([]byte(err.Error())) 800 } else { 801 write([]byte{0}) 802 } 803 ticker.Stop() 804 return 805 } 806 } 807 }() 808 return func(err error) { 809 if doneCh == nil { 810 return 811 } 812 813 // Indicate we are ready to write. 814 doneCh <- err 815 816 // Wait for channel to be closed so we don't race on writes. 817 <-doneCh 818 819 // Clear so we can be called multiple times without crashing. 820 doneCh = nil 821 }, &closeNotifier{rc: r.Body, done: bodyDoneCh} 822 } 823 824 // keepHTTPResponseAlive can be used to avoid timeouts with long storage 825 // operations, such as bitrot verification or data usage scanning. 826 // keepHTTPResponseAlive may NOT be used until the request body has been read, 827 // use keepHTTPReqResponseAlive instead. 828 // Every 10 seconds a space character is sent. 829 // The returned function should always be called to release resources. 830 // An optional error can be sent which will be picked as text only error, 831 // without its original type by the receiver. 832 // waitForHTTPResponse should be used to the receiving side. 833 func keepHTTPResponseAlive(w http.ResponseWriter) func(error) { 834 doneCh := make(chan error) 835 go func() { 836 canWrite := true 837 write := func(b []byte) { 838 if canWrite { 839 n, err := w.Write(b) 840 if err != nil || n != len(b) { 841 canWrite = false 842 } 843 } 844 } 845 defer xioutil.SafeClose(doneCh) 846 ticker := time.NewTicker(time.Second * 10) 847 defer ticker.Stop() 848 for { 849 select { 850 case <-ticker.C: 851 // Response not ready, write a filler byte. 852 write([]byte{32}) 853 if canWrite { 854 w.(http.Flusher).Flush() 855 } 856 case err := <-doneCh: 857 if err != nil { 858 write([]byte{1}) 859 write([]byte(err.Error())) 860 } else { 861 write([]byte{0}) 862 } 863 return 864 } 865 } 866 }() 867 return func(err error) { 868 if doneCh == nil { 869 return 870 } 871 // Indicate we are ready to write. 872 doneCh <- err 873 874 // Wait for channel to be closed so we don't race on writes. 875 <-doneCh 876 877 // Clear so we can be called multiple times without crashing. 878 doneCh = nil 879 } 880 } 881 882 // waitForHTTPResponse will wait for responses where keepHTTPResponseAlive 883 // has been used. 884 // The returned reader contains the payload. 885 func waitForHTTPResponse(respBody io.Reader) (io.Reader, error) { 886 reader := bufio.NewReader(respBody) 887 for { 888 b, err := reader.ReadByte() 889 if err != nil { 890 return nil, err 891 } 892 // Check if we have a response ready or a filler byte. 893 switch b { 894 case 0: 895 return reader, nil 896 case 1: 897 errorText, err := io.ReadAll(reader) 898 if err != nil { 899 return nil, err 900 } 901 return nil, errors.New(string(errorText)) 902 case 32: 903 continue 904 default: 905 return nil, fmt.Errorf("unexpected filler byte: %d", b) 906 } 907 } 908 } 909 910 // httpStreamResponse allows streaming a response, but still send an error. 911 type httpStreamResponse struct { 912 done chan error 913 block chan []byte 914 err error 915 } 916 917 // Write part of the streaming response. 918 // Note that upstream errors are currently not forwarded, but may be in the future. 919 func (h *httpStreamResponse) Write(b []byte) (int, error) { 920 if len(b) == 0 || h.err != nil { 921 // Ignore 0 length blocks 922 return 0, h.err 923 } 924 tmp := make([]byte, len(b)) 925 copy(tmp, b) 926 h.block <- tmp 927 return len(b), h.err 928 } 929 930 // CloseWithError will close the stream and return the specified error. 931 // This can be done several times, but only the first error will be sent. 932 // After calling this the stream should not be written to. 933 func (h *httpStreamResponse) CloseWithError(err error) { 934 if h.done == nil { 935 return 936 } 937 h.done <- err 938 h.err = err 939 // Indicates that the response is done. 940 <-h.done 941 h.done = nil 942 } 943 944 // streamHTTPResponse can be used to avoid timeouts with long storage 945 // operations, such as bitrot verification or data usage scanning. 946 // Every 10 seconds a space character is sent. 947 // The returned function should always be called to release resources. 948 // An optional error can be sent which will be picked as text only error, 949 // without its original type by the receiver. 950 // waitForHTTPStream should be used to the receiving side. 951 func streamHTTPResponse(w http.ResponseWriter) *httpStreamResponse { 952 doneCh := make(chan error) 953 blockCh := make(chan []byte) 954 h := httpStreamResponse{done: doneCh, block: blockCh} 955 go func() { 956 canWrite := true 957 write := func(b []byte) { 958 if canWrite { 959 n, err := w.Write(b) 960 if err != nil || n != len(b) { 961 canWrite = false 962 } 963 } 964 } 965 966 ticker := time.NewTicker(time.Second * 10) 967 defer ticker.Stop() 968 for { 969 select { 970 case <-ticker.C: 971 // Response not ready, write a filler byte. 972 write([]byte{32}) 973 if canWrite { 974 w.(http.Flusher).Flush() 975 } 976 case err := <-doneCh: 977 if err != nil { 978 write([]byte{1}) 979 write([]byte(err.Error())) 980 } else { 981 write([]byte{0}) 982 } 983 xioutil.SafeClose(doneCh) 984 return 985 case block := <-blockCh: 986 var tmp [5]byte 987 tmp[0] = 2 988 binary.LittleEndian.PutUint32(tmp[1:], uint32(len(block))) 989 write(tmp[:]) 990 write(block) 991 if canWrite { 992 w.(http.Flusher).Flush() 993 } 994 } 995 } 996 }() 997 return &h 998 } 999 1000 var poolBuf8k = sync.Pool{ 1001 New: func() interface{} { 1002 b := make([]byte, 8192) 1003 return &b 1004 }, 1005 } 1006 1007 var poolBuf128k = sync.Pool{ 1008 New: func() interface{} { 1009 b := make([]byte, 128<<10) 1010 return b 1011 }, 1012 } 1013 1014 // waitForHTTPStream will wait for responses where 1015 // streamHTTPResponse has been used. 1016 // The returned reader contains the payload and must be closed if no error is returned. 1017 func waitForHTTPStream(respBody io.ReadCloser, w io.Writer) error { 1018 var tmp [1]byte 1019 // 8K copy buffer, reused for less allocs... 1020 bufp := poolBuf8k.Get().(*[]byte) 1021 buf := *bufp 1022 defer poolBuf8k.Put(bufp) 1023 for { 1024 _, err := io.ReadFull(respBody, tmp[:]) 1025 if err != nil { 1026 return err 1027 } 1028 // Check if we have a response ready or a filler byte. 1029 switch tmp[0] { 1030 case 0: 1031 // 0 is unbuffered, copy the rest. 1032 _, err := io.CopyBuffer(w, respBody, buf) 1033 if err == io.EOF { 1034 return nil 1035 } 1036 return err 1037 case 1: 1038 errorText, err := io.ReadAll(respBody) 1039 if err != nil { 1040 return err 1041 } 1042 return errors.New(string(errorText)) 1043 case 2: 1044 // Block of data 1045 var tmp [4]byte 1046 _, err := io.ReadFull(respBody, tmp[:]) 1047 if err != nil { 1048 return err 1049 } 1050 length := binary.LittleEndian.Uint32(tmp[:]) 1051 n, err := io.CopyBuffer(w, io.LimitReader(respBody, int64(length)), buf) 1052 if err != nil { 1053 return err 1054 } 1055 if n != int64(length) { 1056 return io.ErrUnexpectedEOF 1057 } 1058 continue 1059 case 32: 1060 continue 1061 default: 1062 return fmt.Errorf("unexpected filler byte: %d", tmp[0]) 1063 } 1064 } 1065 } 1066 1067 // VerifyFileResp - VerifyFile()'s response. 1068 type VerifyFileResp struct { 1069 Err error 1070 } 1071 1072 // VerifyFileHandler - Verify all part of file for bitrot errors. 1073 func (s *storageRESTServer) VerifyFileHandler(w http.ResponseWriter, r *http.Request) { 1074 if !s.IsValid(w, r) { 1075 return 1076 } 1077 volume := r.Form.Get(storageRESTVolume) 1078 filePath := r.Form.Get(storageRESTFilePath) 1079 1080 if r.ContentLength < 0 { 1081 s.writeErrorResponse(w, errInvalidArgument) 1082 return 1083 } 1084 1085 var fi FileInfo 1086 if err := msgp.Decode(r.Body, &fi); err != nil { 1087 s.writeErrorResponse(w, err) 1088 return 1089 } 1090 1091 setEventStreamHeaders(w) 1092 encoder := gob.NewEncoder(w) 1093 done := keepHTTPResponseAlive(w) 1094 err := s.getStorage().VerifyFile(r.Context(), volume, filePath, fi) 1095 done(nil) 1096 vresp := &VerifyFileResp{} 1097 if err != nil { 1098 vresp.Err = StorageErr(err.Error()) 1099 } 1100 encoder.Encode(vresp) 1101 } 1102 1103 func checkDiskFatalErrs(errs []error) error { 1104 // This returns a common error if all errors are 1105 // same errors, then there is no point starting 1106 // the server. 1107 if countErrs(errs, errUnsupportedDisk) == len(errs) { 1108 return errUnsupportedDisk 1109 } 1110 1111 if countErrs(errs, errDiskAccessDenied) == len(errs) { 1112 return errDiskAccessDenied 1113 } 1114 1115 if countErrs(errs, errFileAccessDenied) == len(errs) { 1116 return errDiskAccessDenied 1117 } 1118 1119 if countErrs(errs, errDiskNotDir) == len(errs) { 1120 return errDiskNotDir 1121 } 1122 1123 if countErrs(errs, errFaultyDisk) == len(errs) { 1124 return errFaultyDisk 1125 } 1126 1127 if countErrs(errs, errXLBackend) == len(errs) { 1128 return errXLBackend 1129 } 1130 1131 return nil 1132 } 1133 1134 // A single function to write certain errors to be fatal 1135 // or informative based on the `exit` flag, please look 1136 // at each implementation of error for added hints. 1137 // 1138 // FIXME: This is an unusual function but serves its purpose for 1139 // now, need to revisit the overall erroring structure here. 1140 // Do not like it :-( 1141 func logFatalErrs(err error, endpoint Endpoint, exit bool) { 1142 switch { 1143 case errors.Is(err, errXLBackend): 1144 logger.Fatal(config.ErrInvalidXLValue(err), "Unable to initialize backend") 1145 case errors.Is(err, errUnsupportedDisk): 1146 var hint string 1147 if endpoint.URL != nil { 1148 hint = fmt.Sprintf("Drive '%s' does not support O_DIRECT flags, MinIO erasure coding requires filesystems with O_DIRECT support", endpoint.Path) 1149 } else { 1150 hint = "Drives do not support O_DIRECT flags, MinIO erasure coding requires filesystems with O_DIRECT support" 1151 } 1152 logger.Fatal(config.ErrUnsupportedBackend(err).Hint(hint), "Unable to initialize backend") 1153 case errors.Is(err, errDiskNotDir): 1154 var hint string 1155 if endpoint.URL != nil { 1156 hint = fmt.Sprintf("Drive '%s' is not a directory, MinIO erasure coding needs a directory", endpoint.Path) 1157 } else { 1158 hint = "Drives are not directories, MinIO erasure coding needs directories" 1159 } 1160 logger.Fatal(config.ErrUnableToWriteInBackend(err).Hint(hint), "Unable to initialize backend") 1161 case errors.Is(err, errDiskAccessDenied): 1162 // Show a descriptive error with a hint about how to fix it. 1163 var username string 1164 if u, err := user.Current(); err == nil { 1165 username = u.Username 1166 } else { 1167 username = "<your-username>" 1168 } 1169 var hint string 1170 if endpoint.URL != nil { 1171 hint = fmt.Sprintf("Run the following command to add write permissions: `sudo chown -R %s %s && sudo chmod u+rxw %s`", 1172 username, endpoint.Path, endpoint.Path) 1173 } else { 1174 hint = fmt.Sprintf("Run the following command to add write permissions: `sudo chown -R %s. <path> && sudo chmod u+rxw <path>`", username) 1175 } 1176 if !exit { 1177 logger.LogOnceIf(GlobalContext, fmt.Errorf("Drive is not writable %s, %s", endpoint, hint), "log-fatal-errs") 1178 } else { 1179 logger.Fatal(config.ErrUnableToWriteInBackend(err).Hint(hint), "Unable to initialize backend") 1180 } 1181 case errors.Is(err, errFaultyDisk): 1182 if !exit { 1183 logger.LogOnceIf(GlobalContext, fmt.Errorf("Drive is faulty at %s, please replace the drive - drive will be offline", endpoint), "log-fatal-errs") 1184 } else { 1185 logger.Fatal(err, "Unable to initialize backend") 1186 } 1187 case errors.Is(err, errDiskFull): 1188 if !exit { 1189 logger.LogOnceIf(GlobalContext, fmt.Errorf("Drive is already full at %s, incoming I/O will fail - drive will be offline", endpoint), "log-fatal-errs") 1190 } else { 1191 logger.Fatal(err, "Unable to initialize backend") 1192 } 1193 default: 1194 if !exit { 1195 logger.LogOnceIf(GlobalContext, fmt.Errorf("Drive %s returned an unexpected error: %w, please investigate - drive will be offline", endpoint, err), "log-fatal-errs") 1196 } else { 1197 logger.Fatal(err, "Unable to initialize backend") 1198 } 1199 } 1200 } 1201 1202 // StatInfoFile returns file stat info. 1203 func (s *storageRESTServer) StatInfoFile(w http.ResponseWriter, r *http.Request) { 1204 if !s.IsValid(w, r) { 1205 return 1206 } 1207 volume := r.Form.Get(storageRESTVolume) 1208 filePath := r.Form.Get(storageRESTFilePath) 1209 glob := r.Form.Get(storageRESTGlob) 1210 done := keepHTTPResponseAlive(w) 1211 stats, err := s.getStorage().StatInfoFile(r.Context(), volume, filePath, glob == "true") 1212 done(err) 1213 if err != nil { 1214 return 1215 } 1216 for _, si := range stats { 1217 msgp.Encode(w, &si) 1218 } 1219 } 1220 1221 // ReadMultiple returns multiple files 1222 func (s *storageRESTServer) ReadMultiple(w http.ResponseWriter, r *http.Request) { 1223 if !s.IsValid(w, r) { 1224 return 1225 } 1226 rw := streamHTTPResponse(w) 1227 defer func() { 1228 if r := recover(); r != nil { 1229 debug.PrintStack() 1230 rw.CloseWithError(fmt.Errorf("panic: %v", r)) 1231 } 1232 }() 1233 1234 var req ReadMultipleReq 1235 mr := msgpNewReader(r.Body) 1236 defer readMsgpReaderPoolPut(mr) 1237 err := req.DecodeMsg(mr) 1238 if err != nil { 1239 rw.CloseWithError(err) 1240 return 1241 } 1242 1243 mw := msgp.NewWriter(rw) 1244 responses := make(chan ReadMultipleResp, len(req.Files)) 1245 var wg sync.WaitGroup 1246 wg.Add(1) 1247 go func() { 1248 defer wg.Done() 1249 for resp := range responses { 1250 err := resp.EncodeMsg(mw) 1251 if err != nil { 1252 rw.CloseWithError(err) 1253 return 1254 } 1255 mw.Flush() 1256 } 1257 }() 1258 err = s.getStorage().ReadMultiple(r.Context(), req, responses) 1259 wg.Wait() 1260 rw.CloseWithError(err) 1261 } 1262 1263 // globalLocalSetDrives is used for local drive as well as remote REST 1264 // API caller for other nodes to talk to this node. 1265 // 1266 // Any updates to this must be serialized via globalLocalDrivesMu (locker) 1267 var globalLocalSetDrives [][][]StorageAPI 1268 1269 // registerStorageRESTHandlers - register storage rpc router. 1270 func registerStorageRESTHandlers(router *mux.Router, endpointServerPools EndpointServerPools, gm *grid.Manager) { 1271 h := func(f http.HandlerFunc) http.HandlerFunc { 1272 return collectInternodeStats(httpTraceHdrs(f)) 1273 } 1274 1275 globalLocalSetDrives = make([][][]StorageAPI, len(endpointServerPools)) 1276 for pool := range globalLocalSetDrives { 1277 globalLocalSetDrives[pool] = make([][]StorageAPI, endpointServerPools[pool].SetCount) 1278 for set := range globalLocalSetDrives[pool] { 1279 globalLocalSetDrives[pool][set] = make([]StorageAPI, endpointServerPools[pool].DrivesPerSet) 1280 } 1281 } 1282 for _, serverPool := range endpointServerPools { 1283 for _, endpoint := range serverPool.Endpoints { 1284 if !endpoint.IsLocal { 1285 continue 1286 } 1287 1288 server := &storageRESTServer{ 1289 endpoint: endpoint, 1290 } 1291 1292 subrouter := router.PathPrefix(path.Join(storageRESTPrefix, endpoint.Path)).Subrouter() 1293 1294 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodHealth).HandlerFunc(h(server.HealthHandler)) 1295 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodAppendFile).HandlerFunc(h(server.AppendFileHandler)) 1296 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadVersion).HandlerFunc(h(server.ReadVersionHandler)) 1297 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadXL).HandlerFunc(h(server.ReadXLHandler)) 1298 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodCreateFile).HandlerFunc(h(server.CreateFileHandler)) 1299 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadFile).HandlerFunc(h(server.ReadFileHandler)) 1300 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadFileStream).HandlerFunc(h(server.ReadFileStreamHandler)) 1301 1302 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteVersions).HandlerFunc(h(server.DeleteVersionsHandler)) 1303 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodVerifyFile).HandlerFunc(h(server.VerifyFileHandler)) 1304 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodStatInfoFile).HandlerFunc(h(server.StatInfoFile)) 1305 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadMultiple).HandlerFunc(h(server.ReadMultiple)) 1306 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodCleanAbandoned).HandlerFunc(h(server.CleanAbandonedDataHandler)) 1307 logger.FatalIf(storageListDirRPC.RegisterNoInput(gm, server.ListDirHandler, endpoint.Path), "unable to register handler") 1308 logger.FatalIf(storageReadAllRPC.Register(gm, server.ReadAllHandler, endpoint.Path), "unable to register handler") 1309 logger.FatalIf(storageWriteAllRPC.Register(gm, server.WriteAllHandler, endpoint.Path), "unable to register handler") 1310 logger.FatalIf(storageRenameFileRPC.Register(gm, server.RenameFileHandler, endpoint.Path), "unable to register handler") 1311 logger.FatalIf(storageRenameDataRPC.Register(gm, server.RenameDataHandler, endpoint.Path), "unable to register handler") 1312 logger.FatalIf(storageDeleteFileRPC.Register(gm, server.DeleteFileHandler, endpoint.Path), "unable to register handler") 1313 logger.FatalIf(storageCheckPartsRPC.Register(gm, server.CheckPartsHandler, endpoint.Path), "unable to register handler") 1314 logger.FatalIf(storageReadVersionRPC.Register(gm, server.ReadVersionHandlerWS, endpoint.Path), "unable to register handler") 1315 logger.FatalIf(storageWriteMetadataRPC.Register(gm, server.WriteMetadataHandler, endpoint.Path), "unable to register handler") 1316 logger.FatalIf(storageUpdateMetadataRPC.Register(gm, server.UpdateMetadataHandler, endpoint.Path), "unable to register handler") 1317 logger.FatalIf(storageDeleteVersionRPC.Register(gm, server.DeleteVersionHandler, endpoint.Path), "unable to register handler") 1318 logger.FatalIf(storageReadXLRPC.Register(gm, server.ReadXLHandlerWS, endpoint.Path), "unable to register handler") 1319 logger.FatalIf(storageNSScannerRPC.RegisterNoInput(gm, server.NSScannerHandler, endpoint.Path), "unable to register handler") 1320 logger.FatalIf(storageDiskInfoRPC.Register(gm, server.DiskInfoHandler, endpoint.Path), "unable to register handler") 1321 logger.FatalIf(storageStatVolRPC.Register(gm, server.StatVolHandler, endpoint.Path), "unable to register handler") 1322 logger.FatalIf(gm.RegisterStreamingHandler(grid.HandlerWalkDir, grid.StreamHandler{ 1323 Subroute: endpoint.Path, 1324 Handle: server.WalkDirHandler, 1325 OutCapacity: 1, 1326 }), "unable to register handler") 1327 1328 createStorage := func(server *storageRESTServer) bool { 1329 xl, err := newXLStorage(endpoint, false) 1330 if err != nil { 1331 // if supported errors don't fail, we proceed to 1332 // printing message and moving forward. 1333 if errors.Is(err, errDriveIsRoot) { 1334 err = fmt.Errorf("major: %v: minor: %v: %w", xl.major, xl.minor, err) 1335 } 1336 logFatalErrs(err, endpoint, false) 1337 return false 1338 } 1339 storage := newXLStorageDiskIDCheck(xl, true) 1340 storage.SetDiskID(xl.diskID) 1341 // We do not have to do SetFormatData() since 'xl' 1342 // already captures formatData cached. 1343 1344 globalLocalDrivesMu.Lock() 1345 defer globalLocalDrivesMu.Unlock() 1346 1347 globalLocalDrives = append(globalLocalDrives, storage) 1348 globalLocalSetDrives[endpoint.PoolIdx][endpoint.SetIdx][endpoint.DiskIdx] = storage 1349 return true 1350 } 1351 1352 if createStorage(server) { 1353 continue 1354 } 1355 1356 // Start async goroutine to create storage. 1357 go func(server *storageRESTServer) { 1358 for { 1359 time.Sleep(3 * time.Second) 1360 if createStorage(server) { 1361 return 1362 } 1363 } 1364 }(server) 1365 1366 } 1367 } 1368 }