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  )