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 }