You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							99 lines
						
					
					
						
							2.2 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							99 lines
						
					
					
						
							2.2 KiB
						
					
					
				| package sftpd | |
| 
 | |
| import ( | |
| 	"fmt" | |
| 	"io" | |
| 	"sync" | |
| 
 | |
| 	"github.com/seaweedfs/seaweedfs/weed/filer" | |
| 	filer_pb "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" | |
| 	"github.com/seaweedfs/seaweedfs/weed/sftpd/utils" | |
| ) | |
| 
 | |
| type SeaweedFileReaderAt struct { | |
| 	fs         *SftpServer | |
| 	entry      *filer_pb.Entry | |
| 	reader     io.ReadSeeker | |
| 	mu         sync.Mutex | |
| 	bufferSize int | |
| 	cache      *utils.LruCache | |
| 	fileSize   int64 | |
| } | |
| 
 | |
| func NewSeaweedFileReaderAt(fs *SftpServer, entry *filer_pb.Entry) *SeaweedFileReaderAt { | |
| 	return &SeaweedFileReaderAt{ | |
| 		fs:         fs, | |
| 		entry:      entry, | |
| 		bufferSize: 5 * 1024 * 1024,       // 5MB | |
| 		cache:      utils.NewLRUCache(10), // Max 10 chunks = ~50MB | |
| 		fileSize:   int64(entry.Attributes.FileSize), | |
| 	} | |
| } | |
| 
 | |
| func (ra *SeaweedFileReaderAt) ReadAt(p []byte, off int64) (n int, err error) { | |
| 	ra.mu.Lock() | |
| 	defer ra.mu.Unlock() | |
| 
 | |
| 	if off >= ra.fileSize { | |
| 		return 0, io.EOF | |
| 	} | |
| 
 | |
| 	remaining := len(p) | |
| 	readOffset := off | |
| 	totalRead := 0 | |
| 
 | |
| 	for remaining > 0 && readOffset < ra.fileSize { | |
| 		bufferKey := (readOffset / int64(ra.bufferSize)) * int64(ra.bufferSize) | |
| 		bufferOffset := int(readOffset - bufferKey) | |
| 
 | |
| 		buffer, ok := ra.cache.Get(bufferKey) | |
| 		if !ok { | |
| 			readSize := ra.bufferSize | |
| 			if bufferKey+int64(readSize) > ra.fileSize { | |
| 				readSize = int(ra.fileSize - bufferKey) | |
| 			} | |
| 
 | |
| 			if ra.reader == nil { | |
| 				r := filer.NewFileReader(ra.fs, ra.entry) | |
| 				if rs, ok := r.(io.ReadSeeker); ok { | |
| 					ra.reader = rs | |
| 				} else { | |
| 					return 0, fmt.Errorf("reader is not seekable") | |
| 				} | |
| 			} | |
| 
 | |
| 			if _, err := ra.reader.Seek(bufferKey, io.SeekStart); err != nil { | |
| 				return 0, fmt.Errorf("seek error: %w", err) | |
| 			} | |
| 
 | |
| 			buffer = make([]byte, readSize) | |
| 			readBytes, err := io.ReadFull(ra.reader, buffer) | |
| 			if err != nil && err != io.ErrUnexpectedEOF { | |
| 				return 0, fmt.Errorf("read error: %w", err) | |
| 			} | |
| 			buffer = buffer[:readBytes] | |
| 			ra.cache.Put(bufferKey, buffer) | |
| 		} | |
| 
 | |
| 		toCopy := len(buffer) - bufferOffset | |
| 		if toCopy > remaining { | |
| 			toCopy = remaining | |
| 		} | |
| 		if toCopy <= 0 { | |
| 			break | |
| 		} | |
| 
 | |
| 		copy(p[totalRead:], buffer[bufferOffset:bufferOffset+toCopy]) | |
| 		totalRead += toCopy | |
| 		readOffset += int64(toCopy) | |
| 		remaining -= toCopy | |
| 	} | |
| 
 | |
| 	if totalRead == 0 { | |
| 		return 0, io.EOF | |
| 	} | |
| 	if totalRead < len(p) { | |
| 		return totalRead, io.EOF | |
| 	} | |
| 	return totalRead, nil | |
| }
 |