github.com/matrixorigin/matrixone@v0.7.0/pkg/fileservice/local_fs.go (about) 1 // Copyright 2022 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package fileservice 16 17 import ( 18 "bytes" 19 "context" 20 "errors" 21 "io" 22 "io/fs" 23 "os" 24 pathpkg "path" 25 "path/filepath" 26 "sort" 27 "strings" 28 "sync" 29 30 "github.com/matrixorigin/matrixone/pkg/common/moerr" 31 "github.com/matrixorigin/matrixone/pkg/logutil" 32 "go.uber.org/zap" 33 ) 34 35 // LocalFS is a FileService implementation backed by local file system 36 type LocalFS struct { 37 name string 38 rootPath string 39 40 sync.RWMutex 41 dirFiles map[string]*os.File 42 43 memCache *MemCache 44 } 45 46 var _ FileService = new(LocalFS) 47 48 const ( 49 localFSSentinelFileName = ".thisisalocalfileservicedir" 50 ) 51 52 func NewLocalFS( 53 name string, 54 rootPath string, 55 memCacheCapacity int64, 56 ) (*LocalFS, error) { 57 58 // ensure dir 59 f, err := os.Open(rootPath) 60 if os.IsNotExist(err) { 61 // not exists, create 62 err := os.MkdirAll(rootPath, 0755) 63 if err != nil { 64 return nil, err 65 } 66 err = os.WriteFile(filepath.Join(rootPath, localFSSentinelFileName), nil, 0644) 67 if err != nil { 68 return nil, err 69 } 70 71 } else if err != nil { 72 // stat error 73 return nil, err 74 75 } else { 76 // existed, check if a real file service dir 77 defer f.Close() 78 entries, err := f.ReadDir(1) 79 if len(entries) == 0 { 80 if errors.Is(err, io.EOF) { 81 // empty dir, ok 82 } else if err != nil { 83 // ReadDir error 84 return nil, err 85 } 86 } else { 87 // not empty, check sentinel file 88 _, err := os.Stat(filepath.Join(rootPath, localFSSentinelFileName)) 89 if os.IsNotExist(err) { 90 return nil, moerr.NewInternalErrorNoCtx("%s is not a file service dir", rootPath) 91 } else if err != nil { 92 return nil, err 93 } 94 } 95 } 96 97 // create tmp dir 98 if err := os.MkdirAll(filepath.Join(rootPath, ".tmp"), 0755); err != nil { 99 return nil, err 100 } 101 102 fs := &LocalFS{ 103 name: name, 104 rootPath: rootPath, 105 dirFiles: make(map[string]*os.File), 106 } 107 if memCacheCapacity > 0 { 108 fs.memCache = NewMemCache(memCacheCapacity) 109 logutil.Info("fileservice: cache initialized", zap.Any("fs-name", name), zap.Any("capacity", memCacheCapacity)) 110 } 111 112 return fs, nil 113 } 114 115 func (l *LocalFS) Name() string { 116 return l.name 117 } 118 119 func (l *LocalFS) Write(ctx context.Context, vector IOVector) error { 120 select { 121 case <-ctx.Done(): 122 return ctx.Err() 123 default: 124 } 125 126 path, err := ParsePathAtService(vector.FilePath, l.name) 127 if err != nil { 128 return err 129 } 130 nativePath := l.toNativeFilePath(path.File) 131 132 // check existence 133 _, err = os.Stat(nativePath) 134 if err == nil { 135 // existed 136 return moerr.NewFileAlreadyExistsNoCtx(path.File) 137 } 138 139 return l.write(ctx, vector) 140 } 141 142 func (l *LocalFS) write(ctx context.Context, vector IOVector) error { 143 select { 144 case <-ctx.Done(): 145 return ctx.Err() 146 default: 147 } 148 149 path, err := ParsePathAtService(vector.FilePath, l.name) 150 if err != nil { 151 return err 152 } 153 nativePath := l.toNativeFilePath(path.File) 154 155 // sort 156 sort.Slice(vector.Entries, func(i, j int) bool { 157 return vector.Entries[i].Offset < vector.Entries[j].Offset 158 }) 159 160 // size 161 var size int64 162 if len(vector.Entries) > 0 { 163 last := vector.Entries[len(vector.Entries)-1] 164 size = int64(last.Offset + last.Size) 165 } 166 167 // write 168 f, err := os.CreateTemp( 169 filepath.Join(l.rootPath, ".tmp"), 170 "*.tmp", 171 ) 172 if err != nil { 173 return err 174 } 175 fileWithChecksum := NewFileWithChecksum(f, _BlockContentSize) 176 n, err := io.Copy(fileWithChecksum, newIOEntriesReader(ctx, vector.Entries)) 177 if err != nil { 178 return err 179 } 180 if n != size { 181 sizeUnknown := false 182 for _, entry := range vector.Entries { 183 if entry.Size < 0 { 184 sizeUnknown = true 185 break 186 } 187 } 188 if !sizeUnknown { 189 return moerr.NewSizeNotMatchNoCtx(path.File) 190 } 191 } 192 if err := f.Sync(); err != nil { 193 return err 194 } 195 if err := f.Close(); err != nil { 196 return err 197 } 198 199 // ensure parent dir 200 parentDir, _ := filepath.Split(nativePath) 201 err = l.ensureDir(parentDir) 202 if err != nil { 203 return err 204 } 205 206 // move 207 if err := os.Rename(f.Name(), nativePath); err != nil { 208 return err 209 } 210 211 if err := l.syncDir(parentDir); err != nil { 212 return err 213 } 214 215 return nil 216 } 217 218 func (l *LocalFS) Read(ctx context.Context, vector *IOVector) (err error) { 219 select { 220 case <-ctx.Done(): 221 return ctx.Err() 222 default: 223 } 224 225 if len(vector.Entries) == 0 { 226 return moerr.NewEmptyVectorNoCtx() 227 } 228 229 if l.memCache != nil { 230 if err := l.memCache.Read(ctx, vector); err != nil { 231 return err 232 } 233 defer func() { 234 if err != nil { 235 return 236 } 237 err = l.memCache.Update(ctx, vector) 238 }() 239 } 240 241 if err := l.read(ctx, vector); err != nil { 242 return err 243 } 244 245 return nil 246 } 247 248 func (l *LocalFS) read(ctx context.Context, vector *IOVector) error { 249 if vector.allDone() { 250 return nil 251 } 252 253 path, err := ParsePathAtService(vector.FilePath, l.name) 254 if err != nil { 255 return err 256 } 257 nativePath := l.toNativeFilePath(path.File) 258 259 file, err := os.Open(nativePath) 260 if os.IsNotExist(err) { 261 return moerr.NewFileNotFoundNoCtx(path.File) 262 } 263 if err != nil { 264 return err 265 } 266 defer file.Close() 267 268 for i, entry := range vector.Entries { 269 if entry.Size == 0 { 270 return moerr.NewEmptyRangeNoCtx(path.File) 271 } 272 273 if entry.done { 274 continue 275 } 276 277 if entry.WriterForRead != nil { 278 fileWithChecksum := NewFileWithChecksum(file, _BlockContentSize) 279 280 if entry.Offset > 0 { 281 _, err := fileWithChecksum.Seek(int64(entry.Offset), io.SeekStart) 282 if err != nil { 283 return err 284 } 285 } 286 r := (io.Reader)(fileWithChecksum) 287 if entry.Size > 0 { 288 r = io.LimitReader(r, int64(entry.Size)) 289 } 290 291 if entry.ToObject != nil { 292 r = io.TeeReader(r, entry.WriterForRead) 293 cr := &countingReader{ 294 R: r, 295 } 296 obj, size, err := entry.ToObject(cr, nil) 297 if err != nil { 298 return err 299 } 300 vector.Entries[i].Object = obj 301 vector.Entries[i].ObjectSize = size 302 if entry.Size > 0 && cr.N != entry.Size { 303 return moerr.NewUnexpectedEOFNoCtx(path.File) 304 } 305 306 } else { 307 n, err := io.Copy(entry.WriterForRead, r) 308 if err != nil { 309 return err 310 } 311 if entry.Size > 0 && n != int64(entry.Size) { 312 return moerr.NewUnexpectedEOFNoCtx(path.File) 313 } 314 } 315 316 } else if entry.ReadCloserForRead != nil { 317 file, err := os.Open(nativePath) 318 if os.IsNotExist(err) { 319 return moerr.NewFileNotFoundNoCtx(path.File) 320 } 321 if err != nil { 322 return err 323 } 324 fileWithChecksum := NewFileWithChecksum(file, _BlockContentSize) 325 326 if entry.Offset > 0 { 327 _, err := fileWithChecksum.Seek(int64(entry.Offset), io.SeekStart) 328 if err != nil { 329 return err 330 } 331 } 332 r := (io.Reader)(fileWithChecksum) 333 if entry.Size > 0 { 334 r = io.LimitReader(r, int64(entry.Size)) 335 } 336 337 if entry.ToObject == nil { 338 *entry.ReadCloserForRead = &readCloser{ 339 r: r, 340 closeFunc: file.Close, 341 } 342 343 } else { 344 buf := new(bytes.Buffer) 345 *entry.ReadCloserForRead = &readCloser{ 346 r: io.TeeReader(r, buf), 347 closeFunc: func() error { 348 defer file.Close() 349 obj, size, err := entry.ToObject(buf, buf.Bytes()) 350 if err != nil { 351 return err 352 } 353 vector.Entries[i].Object = obj 354 vector.Entries[i].ObjectSize = size 355 return nil 356 }, 357 } 358 } 359 360 } else { 361 fileWithChecksum := NewFileWithChecksum(file, _BlockContentSize) 362 363 if entry.Offset > 0 { 364 _, err := fileWithChecksum.Seek(int64(entry.Offset), io.SeekStart) 365 if err != nil { 366 return err 367 } 368 } 369 r := (io.Reader)(fileWithChecksum) 370 if entry.Size > 0 { 371 r = io.LimitReader(r, int64(entry.Size)) 372 } 373 374 if entry.Size < 0 { 375 data, err := io.ReadAll(r) 376 if err != nil { 377 return err 378 } 379 entry.Data = data 380 entry.Size = int64(len(data)) 381 382 } else { 383 if int64(len(entry.Data)) < entry.Size { 384 entry.Data = make([]byte, entry.Size) 385 } 386 n, err := io.ReadFull(r, entry.Data) 387 if err != nil { 388 return err 389 } 390 if int64(n) != entry.Size { 391 return moerr.NewUnexpectedEOFNoCtx(path.File) 392 } 393 } 394 395 if err := entry.setObjectFromData(); err != nil { 396 return err 397 } 398 399 vector.Entries[i] = entry 400 401 } 402 403 } 404 405 return nil 406 407 } 408 409 func (l *LocalFS) List(ctx context.Context, dirPath string) (ret []DirEntry, err error) { 410 select { 411 case <-ctx.Done(): 412 return nil, ctx.Err() 413 default: 414 } 415 416 path, err := ParsePathAtService(dirPath, l.name) 417 if err != nil { 418 return nil, err 419 } 420 nativePath := l.toNativeFilePath(path.File) 421 422 f, err := os.Open(nativePath) 423 if os.IsNotExist(err) { 424 err = nil 425 return 426 } 427 if err != nil { 428 return nil, err 429 } 430 defer f.Close() 431 432 entries, err := f.ReadDir(-1) 433 for _, entry := range entries { 434 name := entry.Name() 435 if strings.HasPrefix(name, ".") { 436 continue 437 } 438 info, err := entry.Info() 439 if err != nil { 440 return nil, err 441 } 442 fileSize := info.Size() 443 nBlock := ceilingDiv(fileSize, _BlockSize) 444 contentSize := fileSize - _ChecksumSize*nBlock 445 446 isDir, err := entryIsDir(nativePath, name, info) 447 if err != nil { 448 return nil, err 449 } 450 ret = append(ret, DirEntry{ 451 Name: name, 452 IsDir: isDir, 453 Size: contentSize, 454 }) 455 } 456 457 sort.Slice(ret, func(i, j int) bool { 458 return ret[i].Name < ret[j].Name 459 }) 460 461 if err != nil { 462 return ret, err 463 } 464 465 return 466 } 467 468 func (l *LocalFS) StatFile(ctx context.Context, filePath string) (*DirEntry, error) { 469 select { 470 case <-ctx.Done(): 471 return nil, ctx.Err() 472 default: 473 } 474 475 path, err := ParsePathAtService(filePath, l.name) 476 if err != nil { 477 return nil, err 478 } 479 nativePath := l.toNativeFilePath(path.File) 480 481 stat, err := os.Stat(nativePath) 482 if os.IsNotExist(err) { 483 return nil, moerr.NewFileNotFound(ctx, filePath) 484 } 485 486 if stat.IsDir() { 487 return nil, moerr.NewFileNotFound(ctx, filePath) 488 } 489 490 fileSize := stat.Size() 491 nBlock := ceilingDiv(fileSize, _BlockSize) 492 contentSize := fileSize - _ChecksumSize*nBlock 493 494 return &DirEntry{ 495 Name: pathpkg.Base(filePath), 496 IsDir: false, 497 Size: contentSize, 498 }, nil 499 } 500 501 func (l *LocalFS) Delete(ctx context.Context, filePaths ...string) error { 502 select { 503 case <-ctx.Done(): 504 return ctx.Err() 505 default: 506 } 507 508 for _, filePath := range filePaths { 509 if err := l.deleteSingle(ctx, filePath); err != nil { 510 return err 511 } 512 } 513 return nil 514 } 515 516 func (l *LocalFS) deleteSingle(ctx context.Context, filePath string) error { 517 path, err := ParsePathAtService(filePath, l.name) 518 if err != nil { 519 return err 520 } 521 nativePath := l.toNativeFilePath(path.File) 522 523 _, err = os.Stat(nativePath) 524 if os.IsNotExist(err) { 525 return moerr.NewFileNotFoundNoCtx(path.File) 526 } 527 if err != nil { 528 return err 529 } 530 531 err = os.Remove(nativePath) 532 if err != nil { 533 return err 534 } 535 536 parentDir, _ := filepath.Split(nativePath) 537 err = l.syncDir(parentDir) 538 if err != nil { 539 return err 540 } 541 542 return nil 543 } 544 545 func (l *LocalFS) ensureDir(nativePath string) error { 546 nativePath = filepath.Clean(nativePath) 547 if nativePath == "" { 548 return nil 549 } 550 551 // check existence by l.dirFiles 552 l.RLock() 553 _, ok := l.dirFiles[nativePath] 554 if ok { 555 // dir existed 556 l.RUnlock() 557 return nil 558 } 559 l.RUnlock() 560 561 // check existence by fstat 562 _, err := os.Stat(nativePath) 563 if err == nil { 564 // existed 565 return nil 566 } 567 568 // ensure parent 569 parent, _ := filepath.Split(nativePath) 570 if parent != nativePath { 571 if err := l.ensureDir(parent); err != nil { 572 return err 573 } 574 } 575 576 // create 577 if err := os.Mkdir(nativePath, 0755); err != nil { 578 return err 579 } 580 581 // sync parent dir 582 if err := l.syncDir(parent); err != nil { 583 return err 584 } 585 586 return nil 587 } 588 589 func (l *LocalFS) syncDir(nativePath string) error { 590 l.Lock() 591 f, ok := l.dirFiles[nativePath] 592 if !ok { 593 var err error 594 f, err = os.Open(nativePath) 595 if err != nil { 596 l.Unlock() 597 return err 598 } 599 l.dirFiles[nativePath] = f 600 } 601 l.Unlock() 602 if err := f.Sync(); err != nil { 603 return err 604 } 605 return nil 606 } 607 608 func (l *LocalFS) toNativeFilePath(filePath string) string { 609 return filepath.Join(l.rootPath, toOSPath(filePath)) 610 } 611 612 var _ MutableFileService = new(LocalFS) 613 614 func (l *LocalFS) NewMutator(filePath string) (Mutator, error) { 615 path, err := ParsePathAtService(filePath, l.name) 616 if err != nil { 617 return nil, err 618 } 619 nativePath := l.toNativeFilePath(path.File) 620 f, err := os.OpenFile(nativePath, os.O_RDWR, 0644) 621 if os.IsNotExist(err) { 622 return nil, moerr.NewFileNotFoundNoCtx(path.File) 623 } 624 return &LocalFSMutator{ 625 osFile: f, 626 fileWithChecksum: NewFileWithChecksum(f, _BlockContentSize), 627 }, nil 628 } 629 630 type LocalFSMutator struct { 631 osFile *os.File 632 fileWithChecksum *FileWithChecksum[*os.File] 633 } 634 635 func (l *LocalFSMutator) Mutate(ctx context.Context, entries ...IOEntry) error { 636 return l.mutate(ctx, 0, entries...) 637 } 638 639 func (l *LocalFSMutator) Append(ctx context.Context, entries ...IOEntry) error { 640 offset, err := l.fileWithChecksum.Seek(0, io.SeekEnd) 641 if err != nil { 642 return err 643 } 644 return l.mutate(ctx, offset, entries...) 645 } 646 647 func (l *LocalFSMutator) mutate(ctx context.Context, baseOffset int64, entries ...IOEntry) error { 648 select { 649 case <-ctx.Done(): 650 return ctx.Err() 651 default: 652 } 653 654 // write 655 for _, entry := range entries { 656 657 if entry.ReaderForWrite != nil { 658 // seek and copy 659 _, err := l.fileWithChecksum.Seek(entry.Offset+baseOffset, 0) 660 if err != nil { 661 return err 662 } 663 n, err := io.Copy(l.fileWithChecksum, entry.ReaderForWrite) 664 if err != nil { 665 return err 666 } 667 if int64(n) != entry.Size { 668 return moerr.NewSizeNotMatchNoCtx("") 669 } 670 671 } else { 672 // WriteAt 673 n, err := l.fileWithChecksum.WriteAt(entry.Data, int64(entry.Offset+baseOffset)) 674 if err != nil { 675 return err 676 } 677 if int64(n) != entry.Size { 678 return moerr.NewSizeNotMatchNoCtx("") 679 } 680 } 681 } 682 683 return nil 684 } 685 686 func (l *LocalFSMutator) Close() error { 687 // sync 688 if err := l.osFile.Sync(); err != nil { 689 return err 690 } 691 692 // close 693 if err := l.osFile.Close(); err != nil { 694 return err 695 } 696 697 return nil 698 } 699 700 var _ ReplaceableFileService = new(LocalFS) 701 702 func (l *LocalFS) Replace(ctx context.Context, vector IOVector) error { 703 return l.write(ctx, vector) 704 } 705 706 var _ CachingFileService = new(LocalFS) 707 708 func (l *LocalFS) FlushCache() { 709 if l.memCache != nil { 710 l.memCache.Flush() 711 } 712 } 713 714 func (l *LocalFS) CacheStats() *CacheStats { 715 if l.memCache != nil { 716 return l.memCache.CacheStats() 717 } 718 return nil 719 } 720 721 func entryIsDir(path string, name string, entry fs.FileInfo) (bool, error) { 722 if entry.IsDir() { 723 return true, nil 724 } 725 if entry.Mode().Type()&fs.ModeSymlink > 0 { 726 stat, err := os.Stat(filepath.Join(path, name)) 727 if err != nil { 728 return false, err 729 } 730 return entryIsDir(path, name, stat) 731 } 732 return false, nil 733 }