github.com/whamcloud/lemur@v0.0.0-20190827193804-4655df8a52af/dmplugin/dmio/action.go (about) 1 package dmio 2 3 import ( 4 "bufio" 5 "io" 6 "os" 7 8 lustre "github.com/intel-hpdd/go-lustre" 9 "github.com/intel-hpdd/lemur/dmplugin" 10 "github.com/pkg/errors" 11 ) 12 13 // BufferSize is the buffered reader size 14 var BufferSize = 1024 * 1024 15 16 type ( 17 statter interface { 18 Stat() (os.FileInfo, error) 19 } 20 21 writerWriterAt interface { 22 io.WriterAt 23 io.Writer 24 } 25 26 // ActionReader wraps an io.SectionReader and also implements 27 // io.Closer by closing the embedded io.Closer. 28 ActionReader struct { 29 sr *io.SectionReader 30 closer io.Closer 31 } 32 33 // BufferedActionReader wraps a buffered ActionReader and 34 // also implements io.Closer by closing the embedded io.Closer. 35 BufferedActionReader struct { 36 br *bufio.Reader 37 closer io.Closer 38 } 39 40 // ActionWriter wraps an io.WriterAt but imposes a base offset 41 // determined by the action's extent. 42 ActionWriter struct { 43 baseOffset int64 44 wwa writerWriterAt 45 statter statter 46 closer io.Closer 47 } 48 ) 49 50 // Close calls the embedded io.Closer's Close() 51 func (ar *ActionReader) Close() error { 52 return ar.closer.Close() 53 } 54 55 // Read calls the embedded *io.SectionReader's Read() 56 func (ar *ActionReader) Read(p []byte) (int, error) { 57 return ar.sr.Read(p) 58 } 59 60 // Seek calls the embedded *io.SectionReader's Seek() 61 func (ar *ActionReader) Seek(offset int64, whence int) (int64, error) { 62 return ar.sr.Seek(offset, whence) 63 } 64 65 // Close calls the embedded io.Closer's Close() 66 func (bar *BufferedActionReader) Close() error { 67 return bar.closer.Close() 68 } 69 70 // Read calls the embedded *bufio.Reader's Read() 71 func (bar *BufferedActionReader) Read(p []byte) (int, error) { 72 return bar.br.Read(p) 73 } 74 75 // Close calls the embedded io.Closer's Close() 76 func (aw *ActionWriter) Close() error { 77 return aw.closer.Close() 78 } 79 80 // WriteAt calls the embedded io.WriterAt's WriteAt(), with the 81 // offset adjusted by the base offset. 82 func (aw *ActionWriter) WriteAt(p []byte, off int64) (int, error) { 83 return aw.wwa.WriteAt(p, aw.baseOffset+off) 84 } 85 86 // Write calls the embedded io.Writer's Write(). 87 func (aw *ActionWriter) Write(p []byte) (int, error) { 88 return aw.wwa.Write(p) 89 } 90 91 // Stat calls the embedded statter's Stat(). 92 func (aw *ActionWriter) Stat() (os.FileInfo, error) { 93 return aw.statter.Stat() 94 } 95 96 // ActualLength returns the length embedded in the action if it is not 97 // Inf (i.e. when it's an extent). Otherwise, interpret it as EOF 98 // and stat the actual file to determine the length on disk. 99 func ActualLength(action dmplugin.Action, fp statter) (int64, error) { 100 var length int64 101 if action.Length() == lustre.MaxExtentLength { 102 fi, err := fp.Stat() 103 if err != nil { 104 return 0, errors.Wrap(err, "stat failed") 105 } 106 107 length = fi.Size() - int64(action.Offset()) 108 } else { 109 // TODO: Sanity check length + offset with actual file size? 110 length = int64(action.Length()) 111 } 112 return length, nil 113 } 114 115 // NewBufferedActionReader returns a *BufferedActionReader for the supplied 116 // action. 117 func NewBufferedActionReader(action dmplugin.Action) (*BufferedActionReader, int64, error) { 118 ar, length, err := NewActionReader(action) 119 if err != nil { 120 return nil, 0, errors.Wrapf(err, "Failed to create ActionReader from %s", action) 121 } 122 123 return &BufferedActionReader{ 124 br: bufio.NewReaderSize(ar, BufferSize), 125 closer: ar, 126 }, length, nil 127 } 128 129 // NewActionReader returns an *ActionReader for the supplied action. 130 func NewActionReader(action dmplugin.Action) (*ActionReader, int64, error) { 131 src, err := os.Open(action.PrimaryPath()) 132 if err != nil { 133 return nil, 0, errors.Wrapf(err, "Failed to open %s for read", action.PrimaryPath()) 134 } 135 136 length, err := ActualLength(action, src) 137 if err != nil { 138 return nil, 0, errors.Wrapf(err, "Could not determine extent length for %s", action) 139 } 140 141 return &ActionReader{ 142 sr: io.NewSectionReader(src, int64(action.Offset()), length), 143 closer: src, 144 }, length, nil 145 } 146 147 // NewActionWriter returns an *ActionWriter for the supplied action. 148 func NewActionWriter(action dmplugin.Action) (*ActionWriter, error) { 149 dst, err := os.OpenFile(action.WritePath(), os.O_WRONLY, 0644) 150 if err != nil { 151 return nil, errors.Wrapf(err, "Failed to open %s for write", action.WritePath()) 152 } 153 154 // Set up for simple Write() 155 dst.Seek(int64(action.Offset()), 0) 156 157 return &ActionWriter{ 158 baseOffset: int64(action.Offset()), 159 wwa: dst, 160 statter: dst, 161 closer: dst, 162 }, nil 163 }