From 52307fad5d95b9bd1b2f25837e5027921e29b6d7 Mon Sep 17 00:00:00 2001 From: Espen Hovlandsdal Date: Mon, 6 Sep 2021 17:21:43 +0200 Subject: [PATCH] Resolve paths before comparing input/output destination (#2878) This fixes an issue where if you try to write to the same destination as the input file but you are not using absolute (or the same relative path) for both the input and output, sharp/vips might produce errors such as: someFile.jpg: unable to open for write unix error: No such file or directory --- lib/output.js | 3 ++- test/unit/io.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/lib/output.js b/lib/output.js index da50505af..201faec61 100644 --- a/lib/output.js +++ b/lib/output.js @@ -1,5 +1,6 @@ 'use strict'; +const path = require('path'); const is = require('./is'); const sharp = require('./sharp'); @@ -59,7 +60,7 @@ function toFile (fileOut, callback) { let err; if (!is.string(fileOut)) { err = new Error('Missing output file path'); - } else if (this.options.input.file === fileOut) { + } else if (is.string(this.options.input.file) && path.resolve(this.options.input.file) === path.resolve(fileOut)) { err = new Error('Cannot use same file for input and output'); } else if (this.options.formatOut === 'input' && fileOut.toLowerCase().endsWith('.gif') && !this.constructor.format.magick.output.file) { err = errMagickSave; diff --git a/test/unit/io.js b/test/unit/io.js index b0847fd76..f5320dab5 100644 --- a/test/unit/io.js +++ b/test/unit/io.js @@ -1,6 +1,7 @@ 'use strict'; const fs = require('fs'); +const path = require('path'); const assert = require('assert'); const rimraf = require('rimraf'); @@ -316,6 +317,48 @@ describe('Input/output', function () { }); }); + it('Fail when output File is input File (relative output, absolute input)', function (done) { + const relativePath = path.relative(process.cwd(), fixtures.inputJpg); + sharp(fixtures.inputJpg).toFile(relativePath, function (err) { + assert(err instanceof Error); + assert.strictEqual('Cannot use same file for input and output', err.message); + done(); + }); + }); + + it('Fail when output File is input File via Promise (relative output, absolute input)', function (done) { + const relativePath = path.relative(process.cwd(), fixtures.inputJpg); + sharp(fixtures.inputJpg).toFile(relativePath).then(function (data) { + assert(false); + done(); + }).catch(function (err) { + assert(err instanceof Error); + assert.strictEqual('Cannot use same file for input and output', err.message); + done(); + }); + }); + + it('Fail when output File is input File (relative input, absolute output)', function (done) { + const relativePath = path.relative(process.cwd(), fixtures.inputJpg); + sharp(relativePath).toFile(fixtures.inputJpg, function (err) { + assert(err instanceof Error); + assert.strictEqual('Cannot use same file for input and output', err.message); + done(); + }); + }); + + it('Fail when output File is input File via Promise (relative input, absolute output)', function (done) { + const relativePath = path.relative(process.cwd(), fixtures.inputJpg); + sharp(relativePath).toFile(fixtures.inputJpg).then(function (data) { + assert(false); + done(); + }).catch(function (err) { + assert(err instanceof Error); + assert.strictEqual('Cannot use same file for input and output', err.message); + done(); + }); + }); + it('Fail when output File is empty', function (done) { sharp(fixtures.inputJpg).toFile('', function (err) { assert(err instanceof Error);