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  }