github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/backend/union/policy/newest.go (about)

     1  package policy
     2  
     3  import (
     4  	"context"
     5  	"path"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/rclone/rclone/backend/union/upstream"
    10  	"github.com/rclone/rclone/fs"
    11  )
    12  
    13  func init() {
    14  	registerPolicy("newest", &Newest{})
    15  }
    16  
    17  // Newest policy picks the file / directory with the largest mtime
    18  // It implies the existence of a path
    19  type Newest struct {
    20  	EpAll
    21  }
    22  
    23  func (p *Newest) newest(ctx context.Context, upstreams []*upstream.Fs, filePath string) (*upstream.Fs, error) {
    24  	var wg sync.WaitGroup
    25  	ufs := make([]*upstream.Fs, len(upstreams))
    26  	mtimes := make([]time.Time, len(upstreams))
    27  	for i, u := range upstreams {
    28  		wg.Add(1)
    29  		i, u := i, u // Closure
    30  		go func() {
    31  			defer wg.Done()
    32  			rfs := u.RootFs
    33  			remote := path.Join(u.RootPath, filePath)
    34  			if e := findEntry(ctx, rfs, remote); e != nil {
    35  				ufs[i] = u
    36  				mtimes[i] = e.ModTime(ctx)
    37  			}
    38  		}()
    39  	}
    40  	wg.Wait()
    41  	maxMtime := time.Time{}
    42  	var newestFs *upstream.Fs
    43  	for i, u := range ufs {
    44  		if u != nil && mtimes[i].After(maxMtime) {
    45  			maxMtime = mtimes[i]
    46  			newestFs = u
    47  		}
    48  	}
    49  	if newestFs == nil {
    50  		return nil, fs.ErrorObjectNotFound
    51  	}
    52  	return newestFs, nil
    53  }
    54  
    55  func (p *Newest) newestEntries(entries []upstream.Entry) (upstream.Entry, error) {
    56  	var wg sync.WaitGroup
    57  	mtimes := make([]time.Time, len(entries))
    58  	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    59  	defer cancel()
    60  	for i, e := range entries {
    61  		wg.Add(1)
    62  		i, e := i, e // Closure
    63  		go func() {
    64  			defer wg.Done()
    65  			mtimes[i] = e.ModTime(ctx)
    66  		}()
    67  	}
    68  	wg.Wait()
    69  	maxMtime := time.Time{}
    70  	var newestEntry upstream.Entry
    71  	for i, t := range mtimes {
    72  		if t.After(maxMtime) {
    73  			maxMtime = t
    74  			newestEntry = entries[i]
    75  		}
    76  	}
    77  	if newestEntry == nil {
    78  		return nil, fs.ErrorObjectNotFound
    79  	}
    80  	return newestEntry, nil
    81  }
    82  
    83  // Action category policy, governing the modification of files and directories
    84  func (p *Newest) Action(ctx context.Context, upstreams []*upstream.Fs, path string) ([]*upstream.Fs, error) {
    85  	if len(upstreams) == 0 {
    86  		return nil, fs.ErrorObjectNotFound
    87  	}
    88  	upstreams = filterRO(upstreams)
    89  	if len(upstreams) == 0 {
    90  		return nil, fs.ErrorPermissionDenied
    91  	}
    92  	u, err := p.newest(ctx, upstreams, path)
    93  	return []*upstream.Fs{u}, err
    94  }
    95  
    96  // ActionEntries is ACTION category policy but receiving a set of candidate entries
    97  func (p *Newest) ActionEntries(entries ...upstream.Entry) ([]upstream.Entry, error) {
    98  	if len(entries) == 0 {
    99  		return nil, fs.ErrorObjectNotFound
   100  	}
   101  	entries = filterROEntries(entries)
   102  	if len(entries) == 0 {
   103  		return nil, fs.ErrorPermissionDenied
   104  	}
   105  	e, err := p.newestEntries(entries)
   106  	return []upstream.Entry{e}, err
   107  }
   108  
   109  // Create category policy, governing the creation of files and directories
   110  func (p *Newest) Create(ctx context.Context, upstreams []*upstream.Fs, path string) ([]*upstream.Fs, error) {
   111  	if len(upstreams) == 0 {
   112  		return nil, fs.ErrorObjectNotFound
   113  	}
   114  	upstreams = filterNC(upstreams)
   115  	if len(upstreams) == 0 {
   116  		return nil, fs.ErrorPermissionDenied
   117  	}
   118  	u, err := p.newest(ctx, upstreams, path+"/..")
   119  	return []*upstream.Fs{u}, err
   120  }
   121  
   122  // CreateEntries is CREATE category policy but receiving a set of candidate entries
   123  func (p *Newest) CreateEntries(entries ...upstream.Entry) ([]upstream.Entry, error) {
   124  	if len(entries) == 0 {
   125  		return nil, fs.ErrorObjectNotFound
   126  	}
   127  	entries = filterNCEntries(entries)
   128  	if len(entries) == 0 {
   129  		return nil, fs.ErrorPermissionDenied
   130  	}
   131  	e, err := p.newestEntries(entries)
   132  	return []upstream.Entry{e}, err
   133  }
   134  
   135  // Search category policy, governing the access to files and directories
   136  func (p *Newest) Search(ctx context.Context, upstreams []*upstream.Fs, path string) (*upstream.Fs, error) {
   137  	if len(upstreams) == 0 {
   138  		return nil, fs.ErrorObjectNotFound
   139  	}
   140  	return p.newest(ctx, upstreams, path)
   141  }
   142  
   143  // SearchEntries is SEARCH category policy but receiving a set of candidate entries
   144  func (p *Newest) SearchEntries(entries ...upstream.Entry) (upstream.Entry, error) {
   145  	if len(entries) == 0 {
   146  		return nil, fs.ErrorObjectNotFound
   147  	}
   148  	return p.newestEntries(entries)
   149  }