github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/decomposedfs/tree/propagator/sync.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package propagator 20 21 import ( 22 "context" 23 "errors" 24 "os" 25 "strconv" 26 "time" 27 28 "github.com/cs3org/reva/v2/pkg/appctx" 29 "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata" 30 "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/metadata/prefixes" 31 "github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node" 32 "github.com/rogpeppe/go-internal/lockedfile" 33 "github.com/rs/zerolog" 34 ) 35 36 // SyncPropagator implements synchronous treetime & treesize propagation 37 type SyncPropagator struct { 38 treeSizeAccounting bool 39 treeTimeAccounting bool 40 lookup node.PathLookup 41 } 42 43 // NewSyncPropagator returns a new AsyncPropagator instance 44 func NewSyncPropagator(treeSizeAccounting, treeTimeAccounting bool, lookup node.PathLookup) SyncPropagator { 45 return SyncPropagator{ 46 treeSizeAccounting: treeSizeAccounting, 47 treeTimeAccounting: treeTimeAccounting, 48 lookup: lookup, 49 } 50 } 51 52 // Propagate triggers a propagation 53 func (p SyncPropagator) Propagate(ctx context.Context, n *node.Node, sizeDiff int64) error { 54 ctx, span := tracer.Start(ctx, "Propagate") 55 defer span.End() 56 sublog := appctx.GetLogger(ctx).With(). 57 Str("method", "sync.Propagate"). 58 Str("spaceid", n.SpaceID). 59 Str("nodeid", n.ID). 60 Int64("sizeDiff", sizeDiff). 61 Logger() 62 63 if !p.treeTimeAccounting && (!p.treeSizeAccounting || sizeDiff == 0) { 64 // no propagation enabled 65 sublog.Debug().Msg("propagation disabled or nothing to propagate") 66 return nil 67 } 68 69 // is propagation enabled for the parent node? 70 root := n.SpaceRoot 71 72 // use a sync time and don't rely on the mtime of the current node, as the stat might not change when a rename happened too quickly 73 sTime := time.Now().UTC() 74 75 // we loop until we reach the root 76 var ( 77 err error 78 stop bool 79 ) 80 81 for err == nil && !stop && n.ID != root.ID { 82 n, stop, err = p.propagateItem(ctx, n, sTime, sizeDiff, sublog) 83 } 84 85 if err != nil { 86 sublog.Error().Err(err).Msg("error propagating") 87 return err 88 } 89 return nil 90 } 91 92 func (p SyncPropagator) propagateItem(ctx context.Context, n *node.Node, sTime time.Time, sizeDiff int64, log zerolog.Logger) (*node.Node, bool, error) { 93 log.Debug().Msg("propagating") 94 95 attrs := node.Attributes{} 96 97 var f *lockedfile.File 98 // lock parent before reading treesize or tree time 99 100 _, subspan := tracer.Start(ctx, "lockedfile.OpenFile") 101 parentFilename := p.lookup.MetadataBackend().LockfilePath(n.ParentPath()) 102 f, err := lockedfile.OpenFile(parentFilename, os.O_RDWR|os.O_CREATE, 0600) 103 subspan.End() 104 if err != nil { 105 log.Error().Err(err). 106 Str("parent filename", parentFilename). 107 Msg("Propagation failed. Could not open metadata for parent with lock.") 108 return nil, true, err 109 } 110 // always log error if closing node fails 111 defer func() { 112 // ignore already closed error 113 cerr := f.Close() 114 if err == nil && cerr != nil && !errors.Is(cerr, os.ErrClosed) { 115 err = cerr // only overwrite err with en error from close if the former was nil 116 } 117 }() 118 119 if n, err = n.Parent(ctx); err != nil { 120 log.Error().Err(err). 121 Msg("Propagation failed. Could not read parent node.") 122 return n, true, err 123 } 124 125 if !n.HasPropagation(ctx) { 126 log.Debug().Str("attr", prefixes.PropagationAttr).Msg("propagation attribute not set or unreadable, not propagating") 127 // if the attribute is not set treat it as false / none / no propagation 128 return n, true, nil 129 } 130 131 log = log.With().Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Logger() 132 133 if p.treeTimeAccounting { 134 // update the parent tree time if it is older than the nodes mtime 135 updateSyncTime := false 136 137 var tmTime time.Time 138 tmTime, err = n.GetTMTime(ctx) 139 switch { 140 case err != nil: 141 // missing attribute, or invalid format, overwrite 142 log.Debug().Err(err). 143 Msg("could not read tmtime attribute, overwriting") 144 updateSyncTime = true 145 case tmTime.Before(sTime): 146 log.Debug(). 147 Time("tmtime", tmTime). 148 Time("stime", sTime). 149 Msg("parent tmtime is older than node mtime, updating") 150 updateSyncTime = true 151 default: 152 log.Debug(). 153 Time("tmtime", tmTime). 154 Time("stime", sTime). 155 Dur("delta", sTime.Sub(tmTime)). 156 Msg("parent tmtime is younger than node mtime, not updating") 157 } 158 159 if updateSyncTime { 160 // update the tree time of the parent node 161 attrs.SetString(prefixes.TreeMTimeAttr, sTime.UTC().Format(time.RFC3339Nano)) 162 } 163 164 attrs.SetString(prefixes.TmpEtagAttr, "") 165 } 166 167 // size accounting 168 if p.treeSizeAccounting && sizeDiff != 0 { 169 var newSize uint64 170 171 // read treesize 172 treeSize, err := n.GetTreeSize(ctx) 173 switch { 174 case metadata.IsAttrUnset(err): 175 // fallback to calculating the treesize 176 log.Warn().Msg("treesize attribute unset, falling back to calculating the treesize") 177 newSize, err = calculateTreeSize(ctx, p.lookup, n.InternalPath()) 178 if err != nil { 179 return n, true, err 180 } 181 case err != nil: 182 log.Error().Err(err). 183 Msg("Faild to propagate treesize change. Error when reading the treesize attribute from parent") 184 return n, true, err 185 case sizeDiff > 0: 186 newSize = treeSize + uint64(sizeDiff) 187 case uint64(-sizeDiff) > treeSize: 188 // The sizeDiff is larger than the current treesize. Which would result in 189 // a negative new treesize. Something must have gone wrong with the accounting. 190 // Reset the current treesize to 0. 191 log.Error().Uint64("treeSize", treeSize).Int64("sizeDiff", sizeDiff). 192 Msg("Error when updating treesize of parent node. Updated treesize < 0. Reestting to 0") 193 newSize = 0 194 default: 195 newSize = treeSize - uint64(-sizeDiff) 196 } 197 198 // update the tree size of the node 199 attrs.SetString(prefixes.TreesizeAttr, strconv.FormatUint(newSize, 10)) 200 log.Debug().Uint64("newSize", newSize).Msg("updated treesize of parent node") 201 } 202 203 if err = n.SetXattrsWithContext(ctx, attrs, false); err != nil { 204 log.Error().Err(err).Msg("Failed to update extend attributes of parent node") 205 return n, true, err 206 } 207 208 return n, false, nil 209 }