From 853858825b7cbfd9a9a2344ee632e4e756703145 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sun, 15 May 2022 09:51:08 -0500 Subject: [PATCH] Fix:File permissions on cache dirs and cache images, Fix:Db delete read stream closing before write stream resulting in deletes sometimes not happening --- server/managers/CacheManager.js | 42 ++++++++++++++++++++++++++++++--- server/njodb/njodb.js | 28 ++++++++-------------- server/utils/ffmpegHelpers.js | 3 --- 3 files changed, 49 insertions(+), 24 deletions(-) diff --git a/server/managers/CacheManager.js b/server/managers/CacheManager.js index 3de493b5..c406206c 100644 --- a/server/managers/CacheManager.js +++ b/server/managers/CacheManager.js @@ -1,6 +1,7 @@ const Path = require('path') const fs = require('fs-extra') const stream = require('stream') +const filePerms = require('../utils/filePerms') const Logger = require('../Logger') const { resizeImage } = require('../utils/ffmpegHelpers') @@ -9,6 +10,34 @@ class CacheManager { this.CachePath = Path.join(global.MetadataPath, 'cache') this.CoverCachePath = Path.join(this.CachePath, 'covers') this.ImageCachePath = Path.join(this.CachePath, 'images') + + this.cachePathsExist = false + } + + async ensureCachePaths() { // Creates cache paths if necessary and sets owner and permissions + if (this.cachePathsExist) return + + var pathsCreated = false + if (!(await fs.pathExists(this.CachePath))) { + await fs.mkdir(this.CachePath) + pathsCreated = true + } + + if (!(await fs.pathExists(this.CoverCachePath))) { + await fs.mkdir(this.CoverCachePath) + pathsCreated = true + } + + if (!(await fs.pathExists(this.ImageCachePath))) { + await fs.mkdir(this.ImageCachePath) + pathsCreated = true + } + + if (pathsCreated) { + await filePerms.setDefault(this.CachePath) + } + + this.cachePathsExist = true } async handleCoverCache(res, libraryItem, options = {}) { @@ -34,7 +63,7 @@ class CacheManager { } // Write cache - await fs.ensureDir(this.CoverCachePath) + await this.ensureCachePaths() if (!libraryItem.media.coverPath || !await fs.pathExists(libraryItem.media.coverPath)) { return res.sendStatus(404) @@ -43,6 +72,9 @@ class CacheManager { let writtenFile = await resizeImage(libraryItem.media.coverPath, path, width, height) if (!writtenFile) return res.sendStatus(400) + // Set owner and permissions of cache image + await filePerms.setDefault(path) + var readStream = fs.createReadStream(writtenFile) readStream.pipe(res) } @@ -57,7 +89,8 @@ class CacheManager { async purgeEntityCache(entityId, cachePath) { // If purgeAll has been called... The cover cache directory no longer exists - await fs.ensureDir(cachePath) + await this.ensureCachePaths() + return Promise.all((await fs.readdir(cachePath)).reduce((promises, file) => { if (file.startsWith(entityId)) { Logger.debug(`[CacheManager] Going to purge ${file}`); @@ -109,11 +142,14 @@ class CacheManager { } // Write cache - await fs.ensureDir(this.ImageCachePath) + await this.ensureCachePaths() let writtenFile = await resizeImage(author.imagePath, path, width, height) if (!writtenFile) return res.sendStatus(400) + // Set owner and permissions of cache image + await filePerms.setDefault(path) + var readStream = fs.createReadStream(writtenFile) readStream.pipe(res) } diff --git a/server/njodb/njodb.js b/server/njodb/njodb.js index 92b89edf..e8639aa0 100644 --- a/server/njodb/njodb.js +++ b/server/njodb/njodb.js @@ -480,7 +480,6 @@ const updateStoreData = async (store, match, update, tempstore, lockoptions) => release = await lock(store, lockoptions); - // console.log('Start updateStoreData for tempstore', tempstore, 'real store', store) const handlerResults = await new Promise((resolve, reject) => { const writer = createWriteStream(tempstore); @@ -490,14 +489,11 @@ const updateStoreData = async (store, match, update, tempstore, lockoptions) => // Reader was opening and closing before writer ever opened const reader = createInterface({ input: createReadStream(store), crlfDelay: Infinity }); - // console.log('Writer opened for tempstore', tempstore) reader.on("line", record => { handler.next(record, writer) }); - reader.on("close", () => { - // console.log('Closing reader for store', store) writer.end(); resolve(handler.return()); }); @@ -505,12 +501,7 @@ const updateStoreData = async (store, match, update, tempstore, lockoptions) => reader.on("error", error => reject(error)); }); - // writer.on('close', () => { - // console.log('Writer closed for tempstore', tempstore) - // }) - writer.on("error", error => reject(error)); - }); results = Object.assign({ store: store, tempstore: tempstore }, handlerResults); @@ -579,26 +570,27 @@ const updateStoreDataSync = (store, match, update, tempstore) => { const deleteStoreData = async (store, match, tempstore, lockoptions) => { let release, results; - release = await lock(store, lockoptions); const handlerResults = await new Promise((resolve, reject) => { - const reader = createInterface({ input: createReadStream(store), crlfDelay: Infinity }); const writer = createWriteStream(tempstore); const handler = Handler("delete", match); writer.on("open", () => { + // Create reader after writer opens otherwise the reader can sometimes close before the writer opens + const reader = createInterface({ input: createReadStream(store), crlfDelay: Infinity }); + reader.on("line", record => handler.next(record, writer)); + + reader.on("close", () => { + writer.end(); + resolve(handler.return()); + }); + + reader.on("error", error => reject(error)); }); writer.on("error", error => reject(error)); - - reader.on("close", () => { - writer.end(); - resolve(handler.return()); - }); - - reader.on("error", error => reject(error)); }); results = Object.assign({ store: store, tempstore: tempstore }, handlerResults); diff --git a/server/utils/ffmpegHelpers.js b/server/utils/ffmpegHelpers.js index a2de5175..46625d05 100644 --- a/server/utils/ffmpegHelpers.js +++ b/server/utils/ffmpegHelpers.js @@ -120,9 +120,6 @@ module.exports.extractCoverArt = extractCoverArt //This should convert based on the output file extension as well async function resizeImage(filePath, outputPath, width, height) { - var dirname = Path.dirname(outputPath); - await fs.ensureDir(dirname); - return new Promise((resolve) => { var ffmpeg = Ffmpeg(filePath) ffmpeg.addOption(['-vf', `scale=${width || -1}:${height || -1}`])