From e9b97378cf0a6c70fc3ba375a62463e945baca9a Mon Sep 17 00:00:00 2001 From: Alex Potsides Date: Thu, 17 Oct 2019 15:42:46 +0100 Subject: [PATCH] fix: fix ls crash (#2546) * sanitises input to replace multiple slashes with single slashes * removes trailing slashes * uses `0` for padding when `link.depth` results in negative padding N.b will be resolved in a better way by ipfs/js-ipfs-unixfs-exporter#24 --- src/cli/commands/ls.js | 13 +++++++- test/cli/ls.js | 67 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 71 insertions(+), 9 deletions(-) diff --git a/src/cli/commands/ls.js b/src/cli/commands/ls.js index cbac9a049c..c79eaeee04 100644 --- a/src/cli/commands/ls.js +++ b/src/cli/commands/ls.js @@ -48,6 +48,14 @@ module.exports = { const multihashWidth = Math.max.apply(null, links.map((file) => file.hash.length)) const sizeWidth = Math.max.apply(null, links.map((file) => String(file.size).length)) + // replace multiple slashes + key = key.replace(/\/(\/+)/g, '/') + + // strip trailing flash + if (key.endsWith('/')) { + key = key.replace(/(\/+)$/, '') + } + let pathParts = key.split('/') if (key.startsWith('/ipfs/')) { @@ -56,7 +64,10 @@ module.exports = { links.forEach(link => { const fileName = link.type === 'dir' ? `${link.name || ''}/` : link.name - const padding = link.depth - pathParts.length + + // todo: fix this by resolving https://github.com/ipfs/js-ipfs-unixfs-exporter/issues/24 + const padding = Math.max(link.depth - pathParts.length, 0) + print( rightpad(link.hash, multihashWidth + 1) + rightpad(link.size || '-', sizeWidth + 1) + diff --git a/test/cli/ls.js b/test/cli/ls.js index 3aca1f5a4a..72230cb33f 100644 --- a/test/cli/ls.js +++ b/test/cli/ls.js @@ -26,14 +26,65 @@ describe('ls', () => runOnAndOff((thing) => { }) }) - it('prints nothing for non-existant hashes', function () { - // If the daemon is off, ls should fail - // If the daemon is on, ls should search until it hits a timeout - return Promise.race([ - ipfs.fail('ls QmYmW4HiZhotsoSqnv2o1oSssvkRM8b9RweBoH7ao5nki2'), - new Promise((resolve, reject) => setTimeout(resolve, 4000)) - ]) - .catch(() => expect.fail(0, 1, 'Should have thrown or timedout')) + it('supports a trailing slash', async function () { + this.timeout(20 * 1000) + const out = await ipfs('ls Qmaj2NmcyAXT8dFmZRRytE12wpcaHADzbChKToMEjBsj5Z/') + expect(out).to.eql( + 'QmamKEPmEH9RUsqRQsfNf5evZQDQPYL9KXg1ADeT7mkHkT - blocks/\n' + + 'QmPkWYfSLCEBLZu7BZt4kigGDMe3cpogMbeVf97gN2xJDN 3928 config\n' + + 'QmUqyZtPmsRy1U5Mo8kz2BAMmk1hfJ7yW1KAFTMB2odsFv - datastore/\n' + + 'QmUhUuiTKkkK8J6JZ9zmj8iNHPuNfGYcszgRumzhHBxEEU - init-docs/\n' + + 'QmR56UJmAaZLXLdTT1ALrE9vVqV8soUEekm9BMd4FnuYqV 2 version\n' + ) + }) + + it('supports multiple trailing slashes', async function () { + this.timeout(20 * 1000) + const out = await ipfs('ls Qmaj2NmcyAXT8dFmZRRytE12wpcaHADzbChKToMEjBsj5Z///') + expect(out).to.eql( + 'QmamKEPmEH9RUsqRQsfNf5evZQDQPYL9KXg1ADeT7mkHkT - blocks/\n' + + 'QmPkWYfSLCEBLZu7BZt4kigGDMe3cpogMbeVf97gN2xJDN 3928 config\n' + + 'QmUqyZtPmsRy1U5Mo8kz2BAMmk1hfJ7yW1KAFTMB2odsFv - datastore/\n' + + 'QmUhUuiTKkkK8J6JZ9zmj8iNHPuNfGYcszgRumzhHBxEEU - init-docs/\n' + + 'QmR56UJmAaZLXLdTT1ALrE9vVqV8soUEekm9BMd4FnuYqV 2 version\n' + ) + }) + + it('supports multiple intermediate slashes', async function () { + this.timeout(20 * 1000) + const out = await ipfs('ls Qmaj2NmcyAXT8dFmZRRytE12wpcaHADzbChKToMEjBsj5Z///init-docs') + expect(out).to.eql( + 'QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V 1677 about\n' + + 'QmYCvbfNbCwFR45HiNP45rwJgvatpiW38D961L5qAhUM5Y 189 contact\n' + + 'QmegvLXxpVKiZ4b57Xs1syfBVRd8CbucVHAp7KpLQdGieC - docs/\n' + + 'QmY5heUM5qgRubMDD1og9fhCPA6QdkMp3QCwd4s7gJsyE7 311 help\n' + + 'QmdncfsVm2h5Kqq9hPmU7oAVX2zTSVP3L869tgTbPYnsha 1717 quick-start\n' + + 'QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB 1091 readme\n' + + 'QmTumTjvcYCAvRRwQ8sDRxh8ezmrcr88YFU7iYNroGGTBZ 1016 security-notes\n' + + 'QmciSU8hfpAXKjvK5YLUSwApomGSWN5gFbP4EpDAEzu2Te - tour/\n' + ) + }) + + it('supports recursive listing through intermediate directories', async function () { + this.timeout(20 * 1000) + const out = await ipfs('ls -r Qmaj2NmcyAXT8dFmZRRytE12wpcaHADzbChKToMEjBsj5Z/blocks/CIQLB') + expect(out).to.eql( + 'QmQ8ag7ysVyCMzJGFjxrUStwWtniQ69c7G9aezbmsKeNYD 10849 CIQLBK52T5EHVHZY5URTG5JS3JCUJDQM2DRB5RVF33DCUUOFJNGVDUI.data\n' + + 'QmaSjzSSRanYzRGPXQY6m5SWfSkkfcnzNkurJEQc4chPJx 10807 CIQLBS5HG4PRCRQ7O4EBXFD5QN6MTI5YBYMCVQJDXPKCOVR6RMLHZFQ.data\n' + ) + }) + + it('prints nothing for non-existant hashes', async function () { + if (thing.on) { + // If the daemon is on, ls should search until it hits a timeout + await Promise.race([ + ipfs('ls QmYmW4HiZhotsoSqnv2o1oSssvkRM8b9RweBoH7ao5nki2'), + delay(4000) + ]) + } else { + // If the daemon is off, ls should fail + await ipfs.fail('ls QmYmW4HiZhotsoSqnv2o1oSssvkRM8b9RweBoH7ao5nki2') + } }) it('adds a header, -v', function () {