Skip to content

[BUG] Invalid hard links created with prefix option #284

@sushain97

Description

@sushain97

What / Why

When the prefix option to create is used and at least two of the provided files are hard links to each other, the hard links within the resulting tar file are invalid.

When

Hard links are encountered in the files to pack.

Where

We encountered this issue when using npm pack since it uses this library under the hood: https://github.com/npm/cli/blob/v6.14.7/lib/pack.js#L143-L158.

How

The hard link handling is slightly buggy: https://github.com/npm/node-tar/blob/97c46fcee7e4e7849ea3432086c4537fb6197025/lib/write-entry.js#L202-L210.

Current Behavior

Creates an invalid tarball.

Steps to Reproduce

See https://replit.com/@sushain97/GleamingAdolescentMarketing#index.js.

const tar = require('tar');
const child_process = require('child_process');
const fs = require('fs');

(async () => {
  fs.writeFileSync('foo', '');
  fs.linkSync('foo', 'bar');

  await tar.create(
    {
      prefix: 'package/',
      file: 'output.tar.gz',
    },
    ['foo', 'bar'],
  )

  fs.unlinkSync('foo');
  fs.unlinkSync('bar');

  console.log(child_process.execSync('tar tvf output.tar.gz', {encoding: 'utf-8'}));
  child_process.execSync('tar xf output.tar.gz');
})()

Output:

-rw-r--r-- 1000/1000         0 2021-08-04 04:39 package/foo
hrw-r--r-- 1000/1000         0 2021-08-04 04:39 package/bar link to foo

tar: package/bar: Cannot hard link to 'foo': No such file or directory
tar: Exiting with failure status due to previous errors
(node:590) UnhandledPromiseRejectionWarning: Error: Command failed: tar xf output.tar.gz
tar: package/bar: Cannot hard link to 'foo': No such file or directory
tar: Exiting with failure status due to previous errors

    at checkExecSyncError (child_process.js:635:11)
    at Object.execSync (child_process.js:671:15)
    at /home/runner/GleamingAdolescentMarketing/index.js:21:17
(node:590) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:590) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Expected Behavior

The tarball should be

-rw-r--r-- 1000/1000         0 2021-08-04 04:39 package/foo
hrw-r--r-- 1000/1000         0 2021-08-04 04:39 package/bar link to package/foo

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions