mirror of
https://github.com/navidrome/navidrome.git
synced 2026-08-01 07:21:17 +00:00
fix(album): serialize cover upload/delete to close shared-file races
The shared-file handling is check-then-act (CountByImage, then remove/write): two concurrent deletes on albums sharing one file could both see refs>1, both skip removal, and both clear their rows — leaving the file orphaned with no remaining reference for the purge GC to find. Serialize album image operations behind a mutex; they are rare, admin-gated actions.
This commit is contained in:
parent
b0aff3c9d6
commit
a79e670740
@ -32,6 +32,8 @@ func (api *Router) addAlbumRoute(r chi.Router) {
|
||||
|
||||
func (api *Router) uploadAlbumImage() http.HandlerFunc {
|
||||
return handleImageUpload(func(ctx context.Context, reader io.Reader, ext string) error {
|
||||
api.albumImgOps.Lock()
|
||||
defer api.albumImgOps.Unlock()
|
||||
albumID := chi.URLParamFromCtx(ctx, "id")
|
||||
al, err := api.ds.Album(ctx).Get(albumID)
|
||||
if err != nil {
|
||||
@ -77,6 +79,8 @@ func (api *Router) albumImagePathToRemove(ctx context.Context, al *model.Album)
|
||||
|
||||
func (api *Router) deleteAlbumImage() http.HandlerFunc {
|
||||
return handleImageDelete(func(ctx context.Context) error {
|
||||
api.albumImgOps.Lock()
|
||||
defer api.albumImgOps.Unlock()
|
||||
albumID := chi.URLParamFromCtx(ctx, "id")
|
||||
al, err := api.ds.Album(ctx).Get(albumID)
|
||||
if err != nil {
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"html"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/deluan/rest"
|
||||
@ -45,6 +46,9 @@ type Router struct {
|
||||
maintenance core.Maintenance
|
||||
pluginManager PluginManager
|
||||
imgUpload core.ImageUploadService
|
||||
// Serializes album image check-and-act sequences: shared-file ref-counting is
|
||||
// check-then-act, and concurrent requests could orphan or clobber a shared file.
|
||||
albumImgOps sync.Mutex
|
||||
}
|
||||
|
||||
func New(ds model.DataStore, share core.Share, playlists playlistsvc.Playlists, insights metrics.Insights, libraryService core.Library, userService core.User, maintenance core.Maintenance, pluginManager PluginManager, imgUpload core.ImageUploadService) *Router {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user