storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/storage-rest-server.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2018 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cmd 18 19 import ( 20 "bufio" 21 "encoding/binary" 22 "encoding/gob" 23 "encoding/hex" 24 "errors" 25 "fmt" 26 "io" 27 "io/ioutil" 28 "net/http" 29 "os/user" 30 "path" 31 "strconv" 32 "strings" 33 "time" 34 35 "github.com/tinylib/msgp/msgp" 36 37 jwtreq "github.com/dgrijalva/jwt-go/request" 38 "github.com/gorilla/mux" 39 40 "storj.io/minio/cmd/config" 41 xhttp "storj.io/minio/cmd/http" 42 xjwt "storj.io/minio/cmd/jwt" 43 "storj.io/minio/cmd/logger" 44 xnet "storj.io/minio/pkg/net" 45 ) 46 47 var errDiskStale = errors.New("disk stale") 48 49 // To abstract a disk over network. 50 type storageRESTServer struct { 51 storage *xlStorage 52 } 53 54 func (s *storageRESTServer) WriteErrorResponse(w http.ResponseWriter, err error) { 55 if errors.Is(err, errDiskStale) { 56 w.WriteHeader(http.StatusPreconditionFailed) 57 } else { 58 w.WriteHeader(http.StatusForbidden) 59 } 60 w.Write([]byte(err.Error())) 61 w.(http.Flusher).Flush() 62 } 63 64 // DefaultSkewTime - skew time is 15 minutes between minio peers. 65 const DefaultSkewTime = 15 * time.Minute 66 67 // Authenticates storage client's requests and validates for skewed time. 68 func storageServerRequestValidate(r *http.Request) error { 69 token, err := jwtreq.AuthorizationHeaderExtractor.ExtractToken(r) 70 if err != nil { 71 if err == jwtreq.ErrNoTokenInRequest { 72 return errNoAuthToken 73 } 74 return err 75 } 76 77 claims := xjwt.NewStandardClaims() 78 if err = xjwt.ParseWithStandardClaims(token, claims, []byte(globalActiveCred.SecretKey)); err != nil { 79 return errAuthentication 80 } 81 82 owner := claims.AccessKey == globalActiveCred.AccessKey || claims.Subject == globalActiveCred.AccessKey 83 if !owner { 84 return errAuthentication 85 } 86 87 if claims.Audience != r.URL.RawQuery { 88 return errAuthentication 89 } 90 91 requestTimeStr := r.Header.Get("X-Minio-Time") 92 requestTime, err := time.Parse(time.RFC3339, requestTimeStr) 93 if err != nil { 94 return err 95 } 96 utcNow := UTCNow() 97 delta := requestTime.Sub(utcNow) 98 if delta < 0 { 99 delta *= -1 100 } 101 if delta > DefaultSkewTime { 102 return fmt.Errorf("client time %v is too apart with server time %v", requestTime, utcNow) 103 } 104 105 return nil 106 } 107 108 // IsValid - To authenticate and verify the time difference. 109 func (s *storageRESTServer) IsValid(w http.ResponseWriter, r *http.Request) bool { 110 if s.storage == nil { 111 s.WriteErrorResponse(w, errDiskNotFound) 112 return false 113 } 114 115 if err := storageServerRequestValidate(r); err != nil { 116 s.WriteErrorResponse(w, err) 117 return false 118 } 119 120 diskID := r.URL.Query().Get(storageRESTDiskID) 121 if diskID == "" { 122 // Request sent empty disk-id, we allow the request 123 // as the peer might be coming up and trying to read format.json 124 // or create format.json 125 return true 126 } 127 128 storedDiskID, err := s.storage.GetDiskID() 129 if err != nil { 130 s.WriteErrorResponse(w, err) 131 return false 132 } 133 134 if diskID != storedDiskID { 135 s.WriteErrorResponse(w, errDiskStale) 136 return false 137 } 138 139 // If format.json is available and request sent the right disk-id, we allow the request 140 return true 141 } 142 143 // HealthHandler handler checks if disk is stale 144 func (s *storageRESTServer) HealthHandler(w http.ResponseWriter, r *http.Request) { 145 s.IsValid(w, r) 146 } 147 148 // DiskInfoHandler - returns disk info. 149 func (s *storageRESTServer) DiskInfoHandler(w http.ResponseWriter, r *http.Request) { 150 if !s.IsValid(w, r) { 151 return 152 } 153 info, err := s.storage.DiskInfo(r.Context()) 154 if err != nil { 155 info.Error = err.Error() 156 } 157 defer w.(http.Flusher).Flush() 158 logger.LogIf(r.Context(), msgp.Encode(w, &info)) 159 } 160 161 func (s *storageRESTServer) NSScannerHandler(w http.ResponseWriter, r *http.Request) { 162 if !s.IsValid(w, r) { 163 return 164 } 165 166 setEventStreamHeaders(w) 167 168 var cache dataUsageCache 169 err := cache.deserialize(r.Body) 170 if err != nil { 171 logger.LogIf(r.Context(), err) 172 s.WriteErrorResponse(w, err) 173 return 174 } 175 176 resp := streamHTTPResponse(w) 177 usageInfo, err := s.storage.NSScanner(r.Context(), cache) 178 if err != nil { 179 resp.CloseWithError(err) 180 return 181 } 182 resp.CloseWithError(usageInfo.serializeTo(resp)) 183 } 184 185 // MakeVolHandler - make a volume. 186 func (s *storageRESTServer) MakeVolHandler(w http.ResponseWriter, r *http.Request) { 187 if !s.IsValid(w, r) { 188 return 189 } 190 vars := mux.Vars(r) 191 volume := vars[storageRESTVolume] 192 err := s.storage.MakeVol(r.Context(), volume) 193 if err != nil { 194 s.WriteErrorResponse(w, err) 195 } 196 } 197 198 // MakeVolBulkHandler - create multiple volumes as a bulk operation. 199 func (s *storageRESTServer) MakeVolBulkHandler(w http.ResponseWriter, r *http.Request) { 200 if !s.IsValid(w, r) { 201 return 202 } 203 vars := mux.Vars(r) 204 volumes := strings.Split(vars[storageRESTVolumes], ",") 205 err := s.storage.MakeVolBulk(r.Context(), volumes...) 206 if err != nil { 207 s.WriteErrorResponse(w, err) 208 } 209 } 210 211 // ListVolsHandler - list volumes. 212 func (s *storageRESTServer) ListVolsHandler(w http.ResponseWriter, r *http.Request) { 213 if !s.IsValid(w, r) { 214 return 215 } 216 infos, err := s.storage.ListVols(r.Context()) 217 if err != nil { 218 s.WriteErrorResponse(w, err) 219 return 220 } 221 defer w.(http.Flusher).Flush() 222 logger.LogIf(r.Context(), msgp.Encode(w, VolsInfo(infos))) 223 } 224 225 // StatVolHandler - stat a volume. 226 func (s *storageRESTServer) StatVolHandler(w http.ResponseWriter, r *http.Request) { 227 if !s.IsValid(w, r) { 228 return 229 } 230 vars := mux.Vars(r) 231 volume := vars[storageRESTVolume] 232 info, err := s.storage.StatVol(r.Context(), volume) 233 if err != nil { 234 s.WriteErrorResponse(w, err) 235 return 236 } 237 defer w.(http.Flusher).Flush() 238 logger.LogIf(r.Context(), msgp.Encode(w, &info)) 239 } 240 241 // DeleteVolumeHandler - delete a volume. 242 func (s *storageRESTServer) DeleteVolHandler(w http.ResponseWriter, r *http.Request) { 243 if !s.IsValid(w, r) { 244 return 245 } 246 vars := mux.Vars(r) 247 volume := vars[storageRESTVolume] 248 forceDelete := vars[storageRESTForceDelete] == "true" 249 err := s.storage.DeleteVol(r.Context(), volume, forceDelete) 250 if err != nil { 251 s.WriteErrorResponse(w, err) 252 } 253 } 254 255 // AppendFileHandler - append data from the request to the file specified. 256 func (s *storageRESTServer) AppendFileHandler(w http.ResponseWriter, r *http.Request) { 257 if !s.IsValid(w, r) { 258 return 259 } 260 vars := mux.Vars(r) 261 volume := vars[storageRESTVolume] 262 filePath := vars[storageRESTFilePath] 263 264 buf := make([]byte, r.ContentLength) 265 _, err := io.ReadFull(r.Body, buf) 266 if err != nil { 267 s.WriteErrorResponse(w, err) 268 return 269 } 270 err = s.storage.AppendFile(r.Context(), volume, filePath, buf) 271 if err != nil { 272 s.WriteErrorResponse(w, err) 273 } 274 } 275 276 // CreateFileHandler - fallocate() space for a file and copy the contents from the request. 277 func (s *storageRESTServer) CreateFileHandler(w http.ResponseWriter, r *http.Request) { 278 if !s.IsValid(w, r) { 279 return 280 } 281 vars := mux.Vars(r) 282 volume := vars[storageRESTVolume] 283 filePath := vars[storageRESTFilePath] 284 285 fileSizeStr := vars[storageRESTLength] 286 fileSize, err := strconv.Atoi(fileSizeStr) 287 if err != nil { 288 s.WriteErrorResponse(w, err) 289 return 290 } 291 292 done := keepHTTPResponseAlive(w) 293 done(s.storage.CreateFile(r.Context(), volume, filePath, int64(fileSize), r.Body)) 294 } 295 296 // DeleteVersion delete updated metadata. 297 func (s *storageRESTServer) DeleteVersionHandler(w http.ResponseWriter, r *http.Request) { 298 if !s.IsValid(w, r) { 299 return 300 } 301 vars := mux.Vars(r) 302 volume := vars[storageRESTVolume] 303 filePath := vars[storageRESTFilePath] 304 forceDelMarker, err := strconv.ParseBool(vars[storageRESTForceDelMarker]) 305 if err != nil { 306 s.WriteErrorResponse(w, errInvalidArgument) 307 return 308 } 309 310 if r.ContentLength < 0 { 311 s.WriteErrorResponse(w, errInvalidArgument) 312 return 313 } 314 315 var fi FileInfo 316 if err := msgp.Decode(r.Body, &fi); err != nil { 317 s.WriteErrorResponse(w, err) 318 return 319 } 320 321 err = s.storage.DeleteVersion(r.Context(), volume, filePath, fi, forceDelMarker) 322 if err != nil { 323 s.WriteErrorResponse(w, err) 324 } 325 } 326 327 // ReadVersion read metadata of versionID 328 func (s *storageRESTServer) ReadVersionHandler(w http.ResponseWriter, r *http.Request) { 329 if !s.IsValid(w, r) { 330 return 331 } 332 vars := mux.Vars(r) 333 volume := vars[storageRESTVolume] 334 filePath := vars[storageRESTFilePath] 335 versionID := vars[storageRESTVersionID] 336 readData, err := strconv.ParseBool(vars[storageRESTReadData]) 337 if err != nil { 338 s.WriteErrorResponse(w, err) 339 return 340 } 341 342 fi, err := s.storage.ReadVersion(r.Context(), volume, filePath, versionID, readData) 343 if err != nil { 344 s.WriteErrorResponse(w, err) 345 return 346 } 347 348 logger.LogIf(r.Context(), msgp.Encode(w, &fi)) 349 } 350 351 // WriteMetadata write new updated metadata. 352 func (s *storageRESTServer) WriteMetadataHandler(w http.ResponseWriter, r *http.Request) { 353 if !s.IsValid(w, r) { 354 return 355 } 356 vars := mux.Vars(r) 357 volume := vars[storageRESTVolume] 358 filePath := vars[storageRESTFilePath] 359 360 if r.ContentLength < 0 { 361 s.WriteErrorResponse(w, errInvalidArgument) 362 return 363 } 364 365 var fi FileInfo 366 if err := msgp.Decode(r.Body, &fi); err != nil { 367 s.WriteErrorResponse(w, err) 368 return 369 } 370 371 err := s.storage.WriteMetadata(r.Context(), volume, filePath, fi) 372 if err != nil { 373 s.WriteErrorResponse(w, err) 374 } 375 } 376 377 // UpdateMetadata update new updated metadata. 378 func (s *storageRESTServer) UpdateMetadataHandler(w http.ResponseWriter, r *http.Request) { 379 if !s.IsValid(w, r) { 380 return 381 } 382 vars := mux.Vars(r) 383 volume := vars[storageRESTVolume] 384 filePath := vars[storageRESTFilePath] 385 386 if r.ContentLength < 0 { 387 s.WriteErrorResponse(w, errInvalidArgument) 388 return 389 } 390 391 var fi FileInfo 392 if err := msgp.Decode(r.Body, &fi); err != nil { 393 s.WriteErrorResponse(w, err) 394 return 395 } 396 397 err := s.storage.UpdateMetadata(r.Context(), volume, filePath, fi) 398 if err != nil { 399 s.WriteErrorResponse(w, err) 400 } 401 } 402 403 // WriteAllHandler - write to file all content. 404 func (s *storageRESTServer) WriteAllHandler(w http.ResponseWriter, r *http.Request) { 405 if !s.IsValid(w, r) { 406 return 407 } 408 vars := mux.Vars(r) 409 volume := vars[storageRESTVolume] 410 filePath := vars[storageRESTFilePath] 411 412 if r.ContentLength < 0 { 413 s.WriteErrorResponse(w, errInvalidArgument) 414 return 415 } 416 tmp := make([]byte, r.ContentLength) 417 _, err := io.ReadFull(r.Body, tmp) 418 if err != nil { 419 s.WriteErrorResponse(w, err) 420 return 421 } 422 err = s.storage.WriteAll(r.Context(), volume, filePath, tmp) 423 if err != nil { 424 s.WriteErrorResponse(w, err) 425 } 426 } 427 428 // CheckPartsHandler - check if a file metadata exists. 429 func (s *storageRESTServer) CheckPartsHandler(w http.ResponseWriter, r *http.Request) { 430 if !s.IsValid(w, r) { 431 return 432 } 433 vars := mux.Vars(r) 434 volume := vars[storageRESTVolume] 435 filePath := vars[storageRESTFilePath] 436 437 if r.ContentLength < 0 { 438 s.WriteErrorResponse(w, errInvalidArgument) 439 return 440 } 441 442 var fi FileInfo 443 if err := msgp.Decode(r.Body, &fi); err != nil { 444 s.WriteErrorResponse(w, err) 445 return 446 } 447 448 if err := s.storage.CheckParts(r.Context(), volume, filePath, fi); err != nil { 449 s.WriteErrorResponse(w, err) 450 } 451 } 452 453 // CheckFileHandler - check if a file metadata exists. 454 func (s *storageRESTServer) CheckFileHandler(w http.ResponseWriter, r *http.Request) { 455 if !s.IsValid(w, r) { 456 return 457 } 458 vars := mux.Vars(r) 459 volume := vars[storageRESTVolume] 460 filePath := vars[storageRESTFilePath] 461 462 if err := s.storage.CheckFile(r.Context(), volume, filePath); err != nil { 463 s.WriteErrorResponse(w, err) 464 } 465 } 466 467 // ReadAllHandler - read all the contents of a file. 468 func (s *storageRESTServer) ReadAllHandler(w http.ResponseWriter, r *http.Request) { 469 if !s.IsValid(w, r) { 470 return 471 } 472 vars := mux.Vars(r) 473 volume := vars[storageRESTVolume] 474 filePath := vars[storageRESTFilePath] 475 476 buf, err := s.storage.ReadAll(r.Context(), volume, filePath) 477 if err != nil { 478 s.WriteErrorResponse(w, err) 479 return 480 } 481 w.Header().Set(xhttp.ContentLength, strconv.Itoa(len(buf))) 482 w.Write(buf) 483 w.(http.Flusher).Flush() 484 } 485 486 // ReadFileHandler - read section of a file. 487 func (s *storageRESTServer) ReadFileHandler(w http.ResponseWriter, r *http.Request) { 488 if !s.IsValid(w, r) { 489 return 490 } 491 vars := mux.Vars(r) 492 volume := vars[storageRESTVolume] 493 filePath := vars[storageRESTFilePath] 494 offset, err := strconv.Atoi(vars[storageRESTOffset]) 495 if err != nil { 496 s.WriteErrorResponse(w, err) 497 return 498 } 499 length, err := strconv.Atoi(vars[storageRESTLength]) 500 if err != nil { 501 s.WriteErrorResponse(w, err) 502 return 503 } 504 if offset < 0 || length < 0 { 505 s.WriteErrorResponse(w, errInvalidArgument) 506 return 507 } 508 var verifier *BitrotVerifier 509 if vars[storageRESTBitrotAlgo] != "" { 510 hashStr := vars[storageRESTBitrotHash] 511 var hash []byte 512 hash, err = hex.DecodeString(hashStr) 513 if err != nil { 514 s.WriteErrorResponse(w, err) 515 return 516 } 517 verifier = NewBitrotVerifier(BitrotAlgorithmFromString(vars[storageRESTBitrotAlgo]), hash) 518 } 519 buf := make([]byte, length) 520 _, err = s.storage.ReadFile(r.Context(), volume, filePath, int64(offset), buf, verifier) 521 if err != nil { 522 s.WriteErrorResponse(w, err) 523 return 524 } 525 w.Header().Set(xhttp.ContentLength, strconv.Itoa(len(buf))) 526 w.Write(buf) 527 w.(http.Flusher).Flush() 528 } 529 530 // ReadFileHandler - read section of a file. 531 func (s *storageRESTServer) ReadFileStreamHandler(w http.ResponseWriter, r *http.Request) { 532 if !s.IsValid(w, r) { 533 return 534 } 535 vars := mux.Vars(r) 536 volume := vars[storageRESTVolume] 537 filePath := vars[storageRESTFilePath] 538 offset, err := strconv.Atoi(vars[storageRESTOffset]) 539 if err != nil { 540 s.WriteErrorResponse(w, err) 541 return 542 } 543 length, err := strconv.Atoi(vars[storageRESTLength]) 544 if err != nil { 545 s.WriteErrorResponse(w, err) 546 return 547 } 548 549 rc, err := s.storage.ReadFileStream(r.Context(), volume, filePath, int64(offset), int64(length)) 550 if err != nil { 551 s.WriteErrorResponse(w, err) 552 return 553 } 554 defer rc.Close() 555 556 w.Header().Set(xhttp.ContentLength, strconv.Itoa(length)) 557 if _, err = io.Copy(w, rc); err != nil { 558 if !xnet.IsNetworkOrHostDown(err, true) { // do not need to log disconnected clients 559 logger.LogIf(r.Context(), err) 560 } 561 return 562 } 563 w.(http.Flusher).Flush() 564 } 565 566 // ListDirHandler - list a directory. 567 func (s *storageRESTServer) ListDirHandler(w http.ResponseWriter, r *http.Request) { 568 if !s.IsValid(w, r) { 569 return 570 } 571 vars := mux.Vars(r) 572 volume := vars[storageRESTVolume] 573 dirPath := vars[storageRESTDirPath] 574 count, err := strconv.Atoi(vars[storageRESTCount]) 575 if err != nil { 576 s.WriteErrorResponse(w, err) 577 return 578 } 579 580 entries, err := s.storage.ListDir(r.Context(), volume, dirPath, count) 581 if err != nil { 582 s.WriteErrorResponse(w, err) 583 return 584 } 585 gob.NewEncoder(w).Encode(&entries) 586 w.(http.Flusher).Flush() 587 } 588 589 // DeleteFileHandler - delete a file. 590 func (s *storageRESTServer) DeleteFileHandler(w http.ResponseWriter, r *http.Request) { 591 if !s.IsValid(w, r) { 592 return 593 } 594 vars := mux.Vars(r) 595 volume := vars[storageRESTVolume] 596 filePath := vars[storageRESTFilePath] 597 recursive, err := strconv.ParseBool(vars[storageRESTRecursive]) 598 if err != nil { 599 s.WriteErrorResponse(w, err) 600 return 601 } 602 603 err = s.storage.Delete(r.Context(), volume, filePath, recursive) 604 if err != nil { 605 s.WriteErrorResponse(w, err) 606 } 607 } 608 609 // DeleteVersionsErrsResp - collection of delete errors 610 // for bulk version deletes 611 type DeleteVersionsErrsResp struct { 612 Errs []error 613 } 614 615 // DeleteVersionsHandler - delete a set of a versions. 616 func (s *storageRESTServer) DeleteVersionsHandler(w http.ResponseWriter, r *http.Request) { 617 if !s.IsValid(w, r) { 618 return 619 } 620 621 vars := r.URL.Query() 622 volume := vars.Get(storageRESTVolume) 623 624 totalVersions, err := strconv.Atoi(vars.Get(storageRESTTotalVersions)) 625 if err != nil { 626 s.WriteErrorResponse(w, err) 627 return 628 } 629 630 versions := make([]FileInfo, totalVersions) 631 decoder := msgp.NewReader(r.Body) 632 for i := 0; i < totalVersions; i++ { 633 dst := &versions[i] 634 if err := dst.DecodeMsg(decoder); err != nil { 635 s.WriteErrorResponse(w, err) 636 return 637 } 638 } 639 640 dErrsResp := &DeleteVersionsErrsResp{Errs: make([]error, totalVersions)} 641 642 setEventStreamHeaders(w) 643 encoder := gob.NewEncoder(w) 644 done := keepHTTPResponseAlive(w) 645 errs := s.storage.DeleteVersions(r.Context(), volume, versions) 646 done(nil) 647 for idx := range versions { 648 if errs[idx] != nil { 649 dErrsResp.Errs[idx] = StorageErr(errs[idx].Error()) 650 } 651 } 652 encoder.Encode(dErrsResp) 653 w.(http.Flusher).Flush() 654 } 655 656 // RenameDataHandler - renames a meta object and data dir to destination. 657 func (s *storageRESTServer) RenameDataHandler(w http.ResponseWriter, r *http.Request) { 658 if !s.IsValid(w, r) { 659 return 660 } 661 662 vars := mux.Vars(r) 663 srcVolume := vars[storageRESTSrcVolume] 664 srcFilePath := vars[storageRESTSrcPath] 665 dstVolume := vars[storageRESTDstVolume] 666 dstFilePath := vars[storageRESTDstPath] 667 668 if r.ContentLength < 0 { 669 s.WriteErrorResponse(w, errInvalidArgument) 670 return 671 } 672 673 var fi FileInfo 674 if err := msgp.Decode(r.Body, &fi); err != nil { 675 s.WriteErrorResponse(w, err) 676 return 677 } 678 679 err := s.storage.RenameData(r.Context(), srcVolume, srcFilePath, fi, dstVolume, dstFilePath) 680 if err != nil { 681 s.WriteErrorResponse(w, err) 682 } 683 } 684 685 // RenameFileHandler - rename a file. 686 func (s *storageRESTServer) RenameFileHandler(w http.ResponseWriter, r *http.Request) { 687 if !s.IsValid(w, r) { 688 return 689 } 690 vars := mux.Vars(r) 691 srcVolume := vars[storageRESTSrcVolume] 692 srcFilePath := vars[storageRESTSrcPath] 693 dstVolume := vars[storageRESTDstVolume] 694 dstFilePath := vars[storageRESTDstPath] 695 err := s.storage.RenameFile(r.Context(), srcVolume, srcFilePath, dstVolume, dstFilePath) 696 if err != nil { 697 s.WriteErrorResponse(w, err) 698 } 699 } 700 701 // keepHTTPResponseAlive can be used to avoid timeouts with long storage 702 // operations, such as bitrot verification or data usage scanning. 703 // Every 10 seconds a space character is sent. 704 // The returned function should always be called to release resources. 705 // An optional error can be sent which will be picked as text only error, 706 // without its original type by the receiver. 707 // waitForHTTPResponse should be used to the receiving side. 708 func keepHTTPResponseAlive(w http.ResponseWriter) func(error) { 709 doneCh := make(chan error) 710 go func() { 711 defer close(doneCh) 712 ticker := time.NewTicker(time.Second * 10) 713 for { 714 select { 715 case <-ticker.C: 716 // Response not ready, write a filler byte. 717 w.Write([]byte{32}) 718 w.(http.Flusher).Flush() 719 case err := <-doneCh: 720 if err != nil { 721 w.Write([]byte{1}) 722 w.Write([]byte(err.Error())) 723 } else { 724 w.Write([]byte{0}) 725 } 726 ticker.Stop() 727 return 728 } 729 } 730 }() 731 return func(err error) { 732 if doneCh == nil { 733 return 734 } 735 // Indicate we are ready to write. 736 doneCh <- err 737 738 // Wait for channel to be closed so we don't race on writes. 739 <-doneCh 740 741 // Clear so we can be called multiple times without crashing. 742 doneCh = nil 743 } 744 } 745 746 // waitForHTTPResponse will wait for responses where keepHTTPResponseAlive 747 // has been used. 748 // The returned reader contains the payload. 749 func waitForHTTPResponse(respBody io.Reader) (io.Reader, error) { 750 reader := bufio.NewReader(respBody) 751 for { 752 b, err := reader.ReadByte() 753 if err != nil { 754 return nil, err 755 } 756 // Check if we have a response ready or a filler byte. 757 switch b { 758 case 0: 759 return reader, nil 760 case 1: 761 errorText, err := ioutil.ReadAll(reader) 762 if err != nil { 763 return nil, err 764 } 765 return nil, errors.New(string(errorText)) 766 case 32: 767 continue 768 default: 769 return nil, fmt.Errorf("unexpected filler byte: %d", b) 770 } 771 } 772 } 773 774 // drainCloser can be used for wrapping an http response. 775 // It will drain the body before closing. 776 type drainCloser struct { 777 rc io.ReadCloser 778 } 779 780 // Read forwards the read operation. 781 func (f drainCloser) Read(p []byte) (n int, err error) { 782 return f.rc.Read(p) 783 } 784 785 // Close drains the body and closes the upstream. 786 func (f drainCloser) Close() error { 787 xhttp.DrainBody(f.rc) 788 return nil 789 } 790 791 // httpStreamResponse allows streaming a response, but still send an error. 792 type httpStreamResponse struct { 793 done chan error 794 block chan []byte 795 err error 796 } 797 798 // Write part of the the streaming response. 799 // Note that upstream errors are currently not forwarded, but may be in the future. 800 func (h *httpStreamResponse) Write(b []byte) (int, error) { 801 if len(b) == 0 || h.err != nil { 802 // Ignore 0 length blocks 803 return 0, h.err 804 } 805 tmp := make([]byte, len(b)) 806 copy(tmp, b) 807 h.block <- tmp 808 return len(b), h.err 809 } 810 811 // CloseWithError will close the stream and return the specified error. 812 // This can be done several times, but only the first error will be sent. 813 // After calling this the stream should not be written to. 814 func (h *httpStreamResponse) CloseWithError(err error) { 815 if h.done == nil { 816 return 817 } 818 h.done <- err 819 h.err = err 820 // Indicates that the response is done. 821 <-h.done 822 h.done = nil 823 } 824 825 // streamHTTPResponse can be used to avoid timeouts with long storage 826 // operations, such as bitrot verification or data usage scanning. 827 // Every 10 seconds a space character is sent. 828 // The returned function should always be called to release resources. 829 // An optional error can be sent which will be picked as text only error, 830 // without its original type by the receiver. 831 // waitForHTTPStream should be used to the receiving side. 832 func streamHTTPResponse(w http.ResponseWriter) *httpStreamResponse { 833 doneCh := make(chan error) 834 blockCh := make(chan []byte) 835 h := httpStreamResponse{done: doneCh, block: blockCh} 836 go func() { 837 ticker := time.NewTicker(time.Second * 10) 838 for { 839 select { 840 case <-ticker.C: 841 // Response not ready, write a filler byte. 842 w.Write([]byte{32}) 843 w.(http.Flusher).Flush() 844 case err := <-doneCh: 845 ticker.Stop() 846 defer close(doneCh) 847 if err != nil { 848 w.Write([]byte{1}) 849 w.Write([]byte(err.Error())) 850 } else { 851 w.Write([]byte{0}) 852 } 853 return 854 case block := <-blockCh: 855 var tmp [5]byte 856 tmp[0] = 2 857 binary.LittleEndian.PutUint32(tmp[1:], uint32(len(block))) 858 w.Write(tmp[:]) 859 w.Write(block) 860 w.(http.Flusher).Flush() 861 } 862 } 863 }() 864 return &h 865 } 866 867 // waitForHTTPStream will wait for responses where 868 // streamHTTPResponse has been used. 869 // The returned reader contains the payload and must be closed if no error is returned. 870 func waitForHTTPStream(respBody io.ReadCloser, w io.Writer) error { 871 var tmp [1]byte 872 for { 873 _, err := io.ReadFull(respBody, tmp[:]) 874 if err != nil { 875 return err 876 } 877 // Check if we have a response ready or a filler byte. 878 switch tmp[0] { 879 case 0: 880 // 0 is unbuffered, copy the rest. 881 _, err := io.Copy(w, respBody) 882 respBody.Close() 883 if err == io.EOF { 884 return nil 885 } 886 return err 887 case 1: 888 errorText, err := ioutil.ReadAll(respBody) 889 if err != nil { 890 return err 891 } 892 respBody.Close() 893 return errors.New(string(errorText)) 894 case 3: 895 // gob style is already deprecated, we can remove this when 896 // storage API version will be greater or equal to 23. 897 defer respBody.Close() 898 dec := gob.NewDecoder(respBody) 899 var err error 900 if de := dec.Decode(&err); de == nil { 901 return err 902 } 903 return errors.New("rpc error") 904 case 2: 905 // Block of data 906 var tmp [4]byte 907 _, err := io.ReadFull(respBody, tmp[:]) 908 if err != nil { 909 return err 910 } 911 length := binary.LittleEndian.Uint32(tmp[:]) 912 _, err = io.CopyN(w, respBody, int64(length)) 913 if err != nil { 914 return err 915 } 916 continue 917 case 32: 918 continue 919 default: 920 go xhttp.DrainBody(respBody) 921 return fmt.Errorf("unexpected filler byte: %d", tmp[0]) 922 } 923 } 924 } 925 926 // VerifyFileResp - VerifyFile()'s response. 927 type VerifyFileResp struct { 928 Err error 929 } 930 931 // VerifyFileHandler - Verify all part of file for bitrot errors. 932 func (s *storageRESTServer) VerifyFileHandler(w http.ResponseWriter, r *http.Request) { 933 if !s.IsValid(w, r) { 934 return 935 } 936 vars := mux.Vars(r) 937 volume := vars[storageRESTVolume] 938 filePath := vars[storageRESTFilePath] 939 940 if r.ContentLength < 0 { 941 s.WriteErrorResponse(w, errInvalidArgument) 942 return 943 } 944 945 var fi FileInfo 946 if err := msgp.Decode(r.Body, &fi); err != nil { 947 s.WriteErrorResponse(w, err) 948 return 949 } 950 951 setEventStreamHeaders(w) 952 encoder := gob.NewEncoder(w) 953 done := keepHTTPResponseAlive(w) 954 err := s.storage.VerifyFile(r.Context(), volume, filePath, fi) 955 done(nil) 956 vresp := &VerifyFileResp{} 957 if err != nil { 958 vresp.Err = StorageErr(err.Error()) 959 } 960 encoder.Encode(vresp) 961 w.(http.Flusher).Flush() 962 } 963 964 // A single function to write certain errors to be fatal 965 // or informative based on the `exit` flag, please look 966 // at each implementation of error for added hints. 967 // 968 // FIXME: This is an unusual function but serves its purpose for 969 // now, need to revist the overall erroring structure here. 970 // Do not like it :-( 971 func logFatalErrs(err error, endpoint Endpoint, exit bool) { 972 if errors.Is(err, errMinDiskSize) { 973 logger.Fatal(config.ErrUnableToWriteInBackend(err).Hint(err.Error()), "Unable to initialize backend") 974 } else if errors.Is(err, errUnsupportedDisk) { 975 var hint string 976 if endpoint.URL != nil { 977 hint = fmt.Sprintf("Disk '%s' does not support O_DIRECT flags, MinIO erasure coding requires filesystems with O_DIRECT support", endpoint.Path) 978 } else { 979 hint = "Disks do not support O_DIRECT flags, MinIO erasure coding requires filesystems with O_DIRECT support" 980 } 981 logger.Fatal(config.ErrUnsupportedBackend(err).Hint(hint), "Unable to initialize backend") 982 } else if errors.Is(err, errDiskNotDir) { 983 var hint string 984 if endpoint.URL != nil { 985 hint = fmt.Sprintf("Disk '%s' is not a directory, MinIO erasure coding needs a directory", endpoint.Path) 986 } else { 987 hint = "Disks are not directories, MinIO erasure coding needs directories" 988 } 989 logger.Fatal(config.ErrUnableToWriteInBackend(err).Hint(hint), "Unable to initialize backend") 990 } else if errors.Is(err, errFileAccessDenied) { 991 // Show a descriptive error with a hint about how to fix it. 992 var username string 993 if u, err := user.Current(); err == nil { 994 username = u.Username 995 } else { 996 username = "<your-username>" 997 } 998 var hint string 999 if endpoint.URL != nil { 1000 hint = fmt.Sprintf("Run the following command to add write permissions: `sudo chown -R %s %s && sudo chmod u+rxw %s`", 1001 username, endpoint.Path, endpoint.Path) 1002 } else { 1003 hint = fmt.Sprintf("Run the following command to add write permissions: `sudo chown -R %s. <path> && sudo chmod u+rxw <path>`", username) 1004 } 1005 logger.Fatal(config.ErrUnableToWriteInBackend(err).Hint(hint), "Unable to initialize backend") 1006 } else if errors.Is(err, errFaultyDisk) { 1007 if !exit { 1008 logger.LogIf(GlobalContext, fmt.Errorf("disk is faulty at %s, please replace the drive - disk will be offline", endpoint)) 1009 } else { 1010 logger.Fatal(err, "Unable to initialize backend") 1011 } 1012 } else if errors.Is(err, errDiskFull) { 1013 if !exit { 1014 logger.LogIf(GlobalContext, fmt.Errorf("disk is already full at %s, incoming I/O will fail - disk will be offline", endpoint)) 1015 } else { 1016 logger.Fatal(err, "Unable to initialize backend") 1017 } 1018 } else { 1019 if !exit { 1020 logger.LogIf(GlobalContext, fmt.Errorf("disk returned an unexpected error at %s, please investigate - disk will be offline", endpoint)) 1021 } else { 1022 logger.Fatal(err, "Unable to initialize backend") 1023 } 1024 } 1025 } 1026 1027 // registerStorageRPCRouter - register storage rpc router. 1028 func registerStorageRESTHandlers(router *mux.Router, endpointServerPools EndpointServerPools) { 1029 for _, ep := range endpointServerPools { 1030 for _, endpoint := range ep.Endpoints { 1031 if !endpoint.IsLocal { 1032 continue 1033 } 1034 storage, err := newXLStorage(endpoint) 1035 if err != nil { 1036 // if supported errors don't fail, we proceed to 1037 // printing message and moving forward. 1038 logFatalErrs(err, endpoint, false) 1039 } 1040 1041 server := &storageRESTServer{storage: storage} 1042 1043 subrouter := router.PathPrefix(path.Join(storageRESTPrefix, endpoint.Path)).Subrouter() 1044 1045 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodHealth).HandlerFunc(HTTPTraceHdrs(server.HealthHandler)) 1046 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDiskInfo).HandlerFunc(HTTPTraceHdrs(server.DiskInfoHandler)) 1047 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodNSScanner).HandlerFunc(HTTPTraceHdrs(server.NSScannerHandler)) 1048 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodMakeVol).HandlerFunc(HTTPTraceHdrs(server.MakeVolHandler)).Queries(restQueries(storageRESTVolume)...) 1049 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodMakeVolBulk).HandlerFunc(HTTPTraceHdrs(server.MakeVolBulkHandler)).Queries(restQueries(storageRESTVolumes)...) 1050 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodStatVol).HandlerFunc(HTTPTraceHdrs(server.StatVolHandler)).Queries(restQueries(storageRESTVolume)...) 1051 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteVol).HandlerFunc(HTTPTraceHdrs(server.DeleteVolHandler)).Queries(restQueries(storageRESTVolume)...) 1052 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodListVols).HandlerFunc(HTTPTraceHdrs(server.ListVolsHandler)) 1053 1054 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodAppendFile).HandlerFunc(HTTPTraceHdrs(server.AppendFileHandler)). 1055 Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) 1056 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodWriteAll).HandlerFunc(HTTPTraceHdrs(server.WriteAllHandler)). 1057 Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) 1058 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodWriteMetadata).HandlerFunc(HTTPTraceHdrs(server.WriteMetadataHandler)). 1059 Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) 1060 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodUpdateMetadata).HandlerFunc(HTTPTraceHdrs(server.UpdateMetadataHandler)). 1061 Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) 1062 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteVersion).HandlerFunc(HTTPTraceHdrs(server.DeleteVersionHandler)). 1063 Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTForceDelMarker)...) 1064 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadVersion).HandlerFunc(HTTPTraceHdrs(server.ReadVersionHandler)). 1065 Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTVersionID, storageRESTReadData)...) 1066 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodRenameData).HandlerFunc(HTTPTraceHdrs(server.RenameDataHandler)). 1067 Queries(restQueries(storageRESTSrcVolume, storageRESTSrcPath, 1068 storageRESTDstVolume, storageRESTDstPath)...) 1069 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodCreateFile).HandlerFunc(HTTPTraceHdrs(server.CreateFileHandler)). 1070 Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTLength)...) 1071 1072 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodCheckFile).HandlerFunc(HTTPTraceHdrs(server.CheckFileHandler)). 1073 Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) 1074 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodCheckParts).HandlerFunc(HTTPTraceHdrs(server.CheckPartsHandler)). 1075 Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) 1076 1077 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadAll).HandlerFunc(HTTPTraceHdrs(server.ReadAllHandler)). 1078 Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) 1079 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadFile).HandlerFunc(HTTPTraceHdrs(server.ReadFileHandler)). 1080 Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTOffset, storageRESTLength, storageRESTBitrotAlgo, storageRESTBitrotHash)...) 1081 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodReadFileStream).HandlerFunc(HTTPTraceHdrs(server.ReadFileStreamHandler)). 1082 Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTOffset, storageRESTLength)...) 1083 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodListDir).HandlerFunc(HTTPTraceHdrs(server.ListDirHandler)). 1084 Queries(restQueries(storageRESTVolume, storageRESTDirPath, storageRESTCount)...) 1085 1086 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteVersions).HandlerFunc(HTTPTraceHdrs(server.DeleteVersionsHandler)). 1087 Queries(restQueries(storageRESTVolume, storageRESTTotalVersions)...) 1088 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodDeleteFile).HandlerFunc(HTTPTraceHdrs(server.DeleteFileHandler)). 1089 Queries(restQueries(storageRESTVolume, storageRESTFilePath, storageRESTRecursive)...) 1090 1091 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodRenameFile).HandlerFunc(HTTPTraceHdrs(server.RenameFileHandler)). 1092 Queries(restQueries(storageRESTSrcVolume, storageRESTSrcPath, storageRESTDstVolume, storageRESTDstPath)...) 1093 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodVerifyFile).HandlerFunc(HTTPTraceHdrs(server.VerifyFileHandler)). 1094 Queries(restQueries(storageRESTVolume, storageRESTFilePath)...) 1095 subrouter.Methods(http.MethodPost).Path(storageRESTVersionPrefix + storageRESTMethodWalkDir).HandlerFunc(HTTPTraceHdrs(server.WalkDirHandler)). 1096 Queries(restQueries(storageRESTVolume, storageRESTDirPath, storageRESTRecursive)...) 1097 } 1098 } 1099 }