github.com/10XDev/rclone@v1.52.3-0.20200626220027-16af9ab76b2a/backend/union/entry.go (about)

     1  package union
     2  
     3  import (
     4  	"bufio"
     5  	"context"
     6  	"io"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/pkg/errors"
    11  	"github.com/rclone/rclone/backend/union/upstream"
    12  	"github.com/rclone/rclone/fs"
    13  )
    14  
    15  // Object describes a union Object
    16  //
    17  // This is a wrapped object which returns the Union Fs as its parent
    18  type Object struct {
    19  	*upstream.Object
    20  	fs *Fs // what this object is part of
    21  	co []upstream.Entry
    22  }
    23  
    24  // Directory describes a union Directory
    25  //
    26  // This is a wrapped object contains all candidates
    27  type Directory struct {
    28  	*upstream.Directory
    29  	cd []upstream.Entry
    30  }
    31  
    32  type entry interface {
    33  	upstream.Entry
    34  	candidates() []upstream.Entry
    35  }
    36  
    37  // UnWrap returns the Object that this Object is wrapping or
    38  // nil if it isn't wrapping anything
    39  func (o *Object) UnWrap() *upstream.Object {
    40  	return o.Object
    41  }
    42  
    43  // Fs returns the union Fs as the parent
    44  func (o *Object) Fs() fs.Info {
    45  	return o.fs
    46  }
    47  
    48  func (o *Object) candidates() []upstream.Entry {
    49  	return o.co
    50  }
    51  
    52  func (d *Directory) candidates() []upstream.Entry {
    53  	return d.cd
    54  }
    55  
    56  // Update in to the object with the modTime given of the given size
    57  //
    58  // When called from outside an Fs by rclone, src.Size() will always be >= 0.
    59  // But for unknown-sized objects (indicated by src.Size() == -1), Upload should either
    60  // return an error or update the object properly (rather than e.g. calling panic).
    61  func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
    62  	entries, err := o.fs.actionEntries(o.candidates()...)
    63  	if err != nil {
    64  		return err
    65  	}
    66  	if len(entries) == 1 {
    67  		obj := entries[0].(*upstream.Object)
    68  		return obj.Update(ctx, in, src, options...)
    69  	}
    70  	// Get multiple reader
    71  	readers := make([]io.Reader, len(entries))
    72  	writers := make([]io.Writer, len(entries))
    73  	errs := Errors(make([]error, len(entries)+1))
    74  	for i := range entries {
    75  		r, w := io.Pipe()
    76  		bw := bufio.NewWriter(w)
    77  		readers[i], writers[i] = r, bw
    78  		defer func() {
    79  			err := w.Close()
    80  			if err != nil {
    81  				panic(err)
    82  			}
    83  		}()
    84  	}
    85  	go func() {
    86  		mw := io.MultiWriter(writers...)
    87  		es := make([]error, len(writers)+1)
    88  		_, es[len(es)-1] = io.Copy(mw, in)
    89  		for i, bw := range writers {
    90  			es[i] = bw.(*bufio.Writer).Flush()
    91  		}
    92  		errs[len(entries)] = Errors(es).Err()
    93  	}()
    94  	// Multi-threading
    95  	multithread(len(entries), func(i int) {
    96  		if o, ok := entries[i].(*upstream.Object); ok {
    97  			err := o.Update(ctx, readers[i], src, options...)
    98  			errs[i] = errors.Wrap(err, o.UpstreamFs().Name())
    99  		} else {
   100  			errs[i] = fs.ErrorNotAFile
   101  		}
   102  	})
   103  	return errs.Err()
   104  }
   105  
   106  // Remove candidate objects selected by ACTION policy
   107  func (o *Object) Remove(ctx context.Context) error {
   108  	entries, err := o.fs.actionEntries(o.candidates()...)
   109  	if err != nil {
   110  		return err
   111  	}
   112  	errs := Errors(make([]error, len(entries)))
   113  	multithread(len(entries), func(i int) {
   114  		if o, ok := entries[i].(*upstream.Object); ok {
   115  			err := o.Remove(ctx)
   116  			errs[i] = errors.Wrap(err, o.UpstreamFs().Name())
   117  		} else {
   118  			errs[i] = fs.ErrorNotAFile
   119  		}
   120  	})
   121  	return errs.Err()
   122  }
   123  
   124  // SetModTime sets the metadata on the object to set the modification date
   125  func (o *Object) SetModTime(ctx context.Context, t time.Time) error {
   126  	entries, err := o.fs.actionEntries(o.candidates()...)
   127  	if err != nil {
   128  		return err
   129  	}
   130  	var wg sync.WaitGroup
   131  	errs := Errors(make([]error, len(entries)))
   132  	multithread(len(entries), func(i int) {
   133  		if o, ok := entries[i].(*upstream.Object); ok {
   134  			err := o.SetModTime(ctx, t)
   135  			errs[i] = errors.Wrap(err, o.UpstreamFs().Name())
   136  		} else {
   137  			errs[i] = fs.ErrorNotAFile
   138  		}
   139  	})
   140  	wg.Wait()
   141  	return errs.Err()
   142  }
   143  
   144  // ModTime returns the modification date of the directory
   145  // It returns the latest ModTime of all candidates
   146  func (d *Directory) ModTime(ctx context.Context) (t time.Time) {
   147  	entries := d.candidates()
   148  	times := make([]time.Time, len(entries))
   149  	multithread(len(entries), func(i int) {
   150  		times[i] = entries[i].ModTime(ctx)
   151  	})
   152  	for _, ti := range times {
   153  		if t.Before(ti) {
   154  			t = ti
   155  		}
   156  	}
   157  	return t
   158  }
   159  
   160  // Size returns the size of the directory
   161  // It returns the sum of all candidates
   162  func (d *Directory) Size() (s int64) {
   163  	for _, e := range d.candidates() {
   164  		s += e.Size()
   165  	}
   166  	return s
   167  }