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 }