github.com/artpar/rclone@v1.67.3/backend/union/entry.go (about) 1 package union 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "io" 8 "sync" 9 "time" 10 11 "github.com/artpar/rclone/backend/union/upstream" 12 "github.com/artpar/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 writebackMu sync.Mutex 23 } 24 25 // Directory describes a union Directory 26 // 27 // This is a wrapped object contains all candidates 28 type Directory struct { 29 *upstream.Directory 30 fs *Fs // what this directory is part of 31 cd []upstream.Entry 32 } 33 34 type entry interface { 35 upstream.Entry 36 candidates() []upstream.Entry 37 } 38 39 // Update o with the contents of newO excluding the lock 40 func (o *Object) update(newO *Object) { 41 o.Object = newO.Object 42 o.fs = newO.fs 43 o.co = newO.co 44 } 45 46 // UnWrapUpstream returns the upstream Object that this Object is wrapping 47 func (o *Object) UnWrapUpstream() *upstream.Object { 48 return o.Object 49 } 50 51 // Fs returns the union Fs as the parent 52 func (o *Object) Fs() fs.Info { 53 return o.fs 54 } 55 56 func (o *Object) candidates() []upstream.Entry { 57 return o.co 58 } 59 60 func (d *Directory) candidates() []upstream.Entry { 61 return d.cd 62 } 63 64 // Update in to the object with the modTime given of the given size 65 // 66 // When called from outside an Fs by rclone, src.Size() will always be >= 0. 67 // But for unknown-sized objects (indicated by src.Size() == -1), Upload should either 68 // return an error or update the object properly (rather than e.g. calling panic). 69 func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error { 70 entries, err := o.fs.actionEntries(o.candidates()...) 71 if err == fs.ErrorPermissionDenied { 72 // There are no candidates in this object which can be written to 73 // So attempt to create a new object instead 74 newO, err := o.fs.put(ctx, in, src, false, options...) 75 if err != nil { 76 return err 77 } 78 // Update current object 79 o.update(newO.(*Object)) 80 return nil 81 } else if err != nil { 82 return err 83 } 84 if len(entries) == 1 { 85 obj := entries[0].(*upstream.Object) 86 return obj.Update(ctx, in, src, options...) 87 } 88 // Multi-threading 89 readers, errChan := multiReader(len(entries), in) 90 errs := Errors(make([]error, len(entries)+1)) 91 multithread(len(entries), func(i int) { 92 if o, ok := entries[i].(*upstream.Object); ok { 93 err := o.Update(ctx, readers[i], src, options...) 94 if err != nil { 95 errs[i] = fmt.Errorf("%s: %w", o.UpstreamFs().Name(), err) 96 if len(entries) > 1 { 97 // Drain the input buffer to allow other uploads to continue 98 _, _ = io.Copy(io.Discard, readers[i]) 99 } 100 } 101 } else { 102 errs[i] = fs.ErrorNotAFile 103 } 104 }) 105 errs[len(entries)] = <-errChan 106 return errs.Err() 107 } 108 109 // Remove candidate objects selected by ACTION policy 110 func (o *Object) Remove(ctx context.Context) error { 111 entries, err := o.fs.actionEntries(o.candidates()...) 112 if err != nil { 113 return err 114 } 115 errs := Errors(make([]error, len(entries))) 116 multithread(len(entries), func(i int) { 117 if o, ok := entries[i].(*upstream.Object); ok { 118 err := o.Remove(ctx) 119 if err != nil { 120 errs[i] = fmt.Errorf("%s: %w", o.UpstreamFs().Name(), err) 121 } 122 } else { 123 errs[i] = fs.ErrorNotAFile 124 } 125 }) 126 return errs.Err() 127 } 128 129 // SetModTime sets the metadata on the object to set the modification date 130 func (o *Object) SetModTime(ctx context.Context, t time.Time) error { 131 entries, err := o.fs.actionEntries(o.candidates()...) 132 if err != nil { 133 return err 134 } 135 var wg sync.WaitGroup 136 errs := Errors(make([]error, len(entries))) 137 multithread(len(entries), func(i int) { 138 if o, ok := entries[i].(*upstream.Object); ok { 139 err := o.SetModTime(ctx, t) 140 if err != nil { 141 errs[i] = fmt.Errorf("%s: %w", o.UpstreamFs().Name(), err) 142 } 143 } else { 144 errs[i] = fs.ErrorNotAFile 145 } 146 }) 147 wg.Wait() 148 return errs.Err() 149 } 150 151 // GetTier returns storage tier or class of the Object 152 func (o *Object) GetTier() string { 153 do, ok := o.Object.Object.(fs.GetTierer) 154 if !ok { 155 return "" 156 } 157 return do.GetTier() 158 } 159 160 // ID returns the ID of the Object if known, or "" if not 161 func (o *Object) ID() string { 162 do, ok := o.Object.Object.(fs.IDer) 163 if !ok { 164 return "" 165 } 166 return do.ID() 167 } 168 169 // MimeType returns the content type of the Object if known 170 func (o *Object) MimeType(ctx context.Context) (mimeType string) { 171 if do, ok := o.Object.Object.(fs.MimeTyper); ok { 172 mimeType = do.MimeType(ctx) 173 } 174 return mimeType 175 } 176 177 // SetTier performs changing storage tier of the Object if 178 // multiple storage classes supported 179 func (o *Object) SetTier(tier string) error { 180 do, ok := o.Object.Object.(fs.SetTierer) 181 if !ok { 182 return errors.New("underlying remote does not support SetTier") 183 } 184 return do.SetTier(tier) 185 } 186 187 // Open opens the file for read. Call Close() on the returned io.ReadCloser 188 func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadCloser, error) { 189 // Need some sort of locking to prevent multiple downloads 190 o.writebackMu.Lock() 191 defer o.writebackMu.Unlock() 192 193 // FIXME what if correct object is already in o.co 194 195 newObj, err := o.Object.Writeback(ctx) 196 if err != nil { 197 return nil, err 198 } 199 if newObj != nil { 200 o.Object = newObj 201 o.co = append(o.co, newObj) // FIXME should this append or overwrite or update? 202 } 203 return o.Object.Object.Open(ctx, options...) 204 } 205 206 // ModTime returns the modification date of the directory 207 // It returns the latest ModTime of all candidates 208 func (d *Directory) ModTime(ctx context.Context) (t time.Time) { 209 entries := d.candidates() 210 times := make([]time.Time, len(entries)) 211 multithread(len(entries), func(i int) { 212 times[i] = entries[i].ModTime(ctx) 213 }) 214 for _, ti := range times { 215 if t.Before(ti) { 216 t = ti 217 } 218 } 219 return t 220 } 221 222 // Size returns the size of the directory 223 // It returns the sum of all candidates 224 func (d *Directory) Size() (s int64) { 225 for _, e := range d.candidates() { 226 s += e.Size() 227 } 228 return s 229 } 230 231 // SetMetadata sets metadata for an DirEntry 232 // 233 // It should return fs.ErrorNotImplemented if it can't set metadata 234 func (d *Directory) SetMetadata(ctx context.Context, metadata fs.Metadata) error { 235 entries, err := d.fs.actionEntries(d.candidates()...) 236 if err != nil { 237 return err 238 } 239 var wg sync.WaitGroup 240 errs := Errors(make([]error, len(entries))) 241 multithread(len(entries), func(i int) { 242 if d, ok := entries[i].(*upstream.Directory); ok { 243 err := d.SetMetadata(ctx, metadata) 244 if err != nil { 245 errs[i] = fmt.Errorf("%s: %w", d.UpstreamFs().Name(), err) 246 } 247 } else { 248 errs[i] = fs.ErrorIsFile 249 } 250 }) 251 wg.Wait() 252 return errs.Err() 253 } 254 255 // SetModTime sets the metadata on the DirEntry to set the modification date 256 // 257 // If there is any other metadata it does not overwrite it. 258 func (d *Directory) SetModTime(ctx context.Context, t time.Time) error { 259 entries, err := d.fs.actionEntries(d.candidates()...) 260 if err != nil { 261 return err 262 } 263 var wg sync.WaitGroup 264 errs := Errors(make([]error, len(entries))) 265 multithread(len(entries), func(i int) { 266 if d, ok := entries[i].(*upstream.Directory); ok { 267 err := d.SetModTime(ctx, t) 268 if err != nil { 269 errs[i] = fmt.Errorf("%s: %w", d.UpstreamFs().Name(), err) 270 } 271 } else { 272 errs[i] = fs.ErrorIsFile 273 } 274 }) 275 wg.Wait() 276 return errs.Err() 277 } 278 279 // Check the interfaces are satisfied 280 var ( 281 _ fs.FullObject = (*Object)(nil) 282 _ fs.FullDirectory = (*Directory)(nil) 283 )