@@ -123,8 +123,6 @@ func New(gwAddr string, pwHashCost, janitorRunInterval int, enableCleanup bool,
123123 return m , nil
124124}
125125
126- var _ publicshare.ClosableManager = (* manager )(nil )
127-
128126type commonConfig struct {
129127 GatewayAddr string `mapstructure:"gateway_addr"`
130128 SharePasswordHashCost int `mapstructure:"password_hash_cost"`
@@ -152,7 +150,7 @@ func (c *commonConfig) init() {
152150 c .SharePasswordHashCost = 11
153151 }
154152 if c .JanitorRunInterval == 0 {
155- c .JanitorRunInterval = 600
153+ c .JanitorRunInterval = 3600 // 1 hour
156154 }
157155}
158156
@@ -174,6 +172,8 @@ type manager struct {
174172 janitorDone chan struct {}
175173}
176174
175+ var _ publicshare.ClosableManager = (* manager )(nil )
176+
177177// init is called at the top of every public method to lazily initialize the
178178// persistence layer. It must not take m.mutex: persistence.Init is already
179179// idempotent and self-synchronized (it returns immediately once the
@@ -262,19 +262,20 @@ func (m *manager) Load(ctx context.Context, shareChan <-chan *publicshare.WithPa
262262 if err != nil {
263263 return err
264264 }
265+ dbCopy := persistence .Copy (db )
265266
266267 for ps := range shareChan {
267268 encShare , err := utils .MarshalProtoV1ToJSON (& ps .PublicShare )
268269 if err != nil {
269270 return err
270271 }
271272
272- db [ps .PublicShare .Id .GetOpaqueId ()] = map [string ]interface {}{
273+ dbCopy [ps .PublicShare .Id .GetOpaqueId ()] = map [string ]interface {}{
273274 "share" : string (encShare ),
274275 "password" : ps .Password ,
275276 }
276277 }
277- return m .persistence .Write (ctx , db )
278+ return m .persistence .Write (ctx , dbCopy )
278279}
279280
280281// CreatePublicShare adds a new entry to manager.shares
@@ -345,17 +346,18 @@ func (m *manager) CreatePublicShare(ctx context.Context, u *user.User, rInfo *pr
345346 if err != nil {
346347 return nil , err
347348 }
349+ dbCopy := persistence .Copy (db )
348350
349- if _ , ok := db [s .Id .GetOpaqueId ()]; ! ok {
350- db [s .Id .GetOpaqueId ()] = map [string ]interface {}{
351+ if _ , ok := dbCopy [s .Id .GetOpaqueId ()]; ! ok {
352+ dbCopy [s .Id .GetOpaqueId ()] = map [string ]interface {}{
351353 "share" : string (encShare ),
352354 "password" : ps .Password ,
353355 }
354356 } else {
355357 return nil , errors .New ("key already exists" )
356358 }
357359
358- err = m .persistence .Write (ctx , db )
360+ err = m .persistence .Write (ctx , dbCopy )
359361 if err != nil {
360362 return nil , err
361363 }
@@ -433,13 +435,14 @@ func (m *manager) UpdatePublicShare(ctx context.Context, u *user.User, req *link
433435 if err != nil {
434436 return nil , err
435437 }
438+ dbCopy := persistence .Copy (db )
436439
437440 encShare , err := utils .MarshalProtoV1ToJSON (share )
438441 if err != nil {
439442 return nil , err
440443 }
441444
442- data , ok := db [share .Id .OpaqueId ].(map [string ]interface {})
445+ data , ok := dbCopy [share .Id .OpaqueId ].(map [string ]interface {})
443446 if ! ok {
444447 data = map [string ]interface {}{}
445448 }
@@ -449,9 +452,9 @@ func (m *manager) UpdatePublicShare(ctx context.Context, u *user.User, req *link
449452 }
450453 data ["share" ] = string (encShare )
451454
452- db [share .Id .OpaqueId ] = data
455+ dbCopy [share .Id .OpaqueId ] = data
453456
454- err = m .persistence .Write (ctx , db )
457+ err = m .persistence .Write (ctx , dbCopy )
455458 if err != nil {
456459 return nil , err
457460 }
@@ -522,10 +525,9 @@ func (m *manager) ListPublicShares(ctx context.Context, u *user.User, filters []
522525
523526 m .mutex .RLock ()
524527
525- // Read returns a copy that shares no mutable state with the persistence
526- // backend (see persistence.Copy), so it's safe to keep using db after
527- // the lock is released below - a concurrent writer can no longer race
528- // what we do with it.
528+ // Ranging over db below happens after the lock is released, which is safe
529+ // because we never mutate it: writers copy before they mutate, and the
530+ // persistence layer publishes a new map instead of changing this one.
529531 db , err := m .persistence .Read (ctx )
530532 if err != nil {
531533 m .mutex .RUnlock ()
@@ -627,10 +629,11 @@ func (m *manager) cleanupExpiredShares() error {
627629 return err
628630 }
629631
630- db , err := m .persistence .Read (ctx )
632+ read , err := m .persistence .Read (ctx )
631633 if err != nil {
632634 return err
633635 }
636+ db := persistence .Copy (read )
634637
635638 var changed bool
636639 for id , v := range db {
@@ -642,9 +645,6 @@ func (m *manager) cleanupExpiredShares() error {
642645 }
643646
644647 if publicshare .IsExpired (& ps ) {
645- // db is our own copy (see persistence.Copy), so deleting the
646- // current entry while ranging over it is safe: single goroutine,
647- // no aliasing with the persistence backend's internal state.
648648 delete (db , id )
649649 changed = true
650650 }
@@ -671,10 +671,11 @@ func (m *manager) RevokePublicShare(ctx context.Context, _ *user.User, ref *link
671671
672672// revokePublicShare doesn't have a lock inside, ensure a lock before call
673673func (m * manager ) revokePublicShare (ctx context.Context , ref * link.PublicShareReference ) error {
674- db , err := m .persistence .Read (ctx )
674+ read , err := m .persistence .Read (ctx )
675675 if err != nil {
676676 return err
677677 }
678+ db := persistence .Copy (read )
678679
679680 switch {
680681 case ref .GetId () != nil && ref .GetId ().OpaqueId != "" :
0 commit comments