AvianFlu / ncp

Asynchronous recursive file copying with Node.js.
MIT License
680 stars 103 forks source link

Race condition when 2 calls copying files to the same nonexistent directory #141

Open Soulike opened 2 years ago

Soulike commented 2 years ago

This may be a same bug as #125.

The test case:

const tmpDir = path.join(os.tmpdir(), 'testFolder');

fs.rmSync(tmpDir, {recursive: true, force: true});

// operation A
ncp('./bin', tmpDir, function (err)
{
    if (err)
    {
        return console.error(err);
    }
    console.log('done!');
});

// operation B
ncp('./lib', tmpDir, function (err)
{
    if (err)
    {
        return console.error(err);
    }
    console.log('done!');
});

The execution result is:

[
  [Error: EEXIST: file already exists, mkdir '/tmp/testFolder'] {
    errno: -17,
    code: 'EEXIST',
    syscall: 'mkdir',
    path: '/tmp/testFolder'
  }
]
done!

Both operation A and B first checks the existence of tmpDir and thought tmpDir does not exist. Then both of them will try to create it. One of A and B will success and the other will fail.

The root cause is that the asynchronous call sequence isWritable() -> rmFile() may be violated by another call to ncp()

https://github.com/AvianFlu/ncp/blob/6820b0fbe3f7400fdb283c741ef88b6d8e2d4994/lib/ncp.js#L221-L229

https://github.com/AvianFlu/ncp/blob/6820b0fbe3f7400fdb283c741ef88b6d8e2d4994/lib/ncp.js#L148-L151