Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move operation error "EXDEV: cross-device link not permitted, rename" #515

Open
stanleyxu2005 opened this issue Jan 18, 2021 · 6 comments
Open

Comments

@stanleyxu2005
Copy link

stanleyxu2005 commented Jan 18, 2021

I ran into problem with moving a folder for a physical drive to another (mounted) drive.
My application is running at D:\.
fsn.move('source\dir', 'T:\target\dir')

Error: EXDEV: cross-device link not permitted, rename 'D:\source\dir' -> 'T:\target\dir' 
{
  errno: -4037,
  code: 'EXDEV',
  syscall: 'rename',
  path: 'D:\source\dir',
  dest: 'T:\target\dir'
}

The workaround is
fsn.move(path.resolve('source\dir'), 'T:\target\dir')
Here is my pull request

I checked the implementation of fsn.move, it seems source and destination are resolved on the fly, but not everywhere. Is there a particular reason for that?

export async function move(source: string, destination: string, options: MoveOptions = {}): Promise<void> {
const overwrite = options.overwrite || false;
if (resolve(source) === resolve(destination)) return fsp.access(source);
const myStat = await fsp.lstat(source);
if (myStat.isDirectory() && isSrcKid(source, destination)) {
throw new Error('FS-NEXTRA: Moving a parent directory into a child will result in an infinite loop.');
}
await mkdirs(dirname(destination));
if (overwrite) {
await remove(destination);
} else if (await pathExists(destination)) {
throw new Error('FS-NEXTRA: Destination already exists.');
}
try {
return await fsp.rename(source, destination);

stanleyxu2005 added a commit to stanleyxu2005/fs-nextra that referenced this issue Jan 18, 2021
…oblem that source directory is not a full path that might cause a cross-device link error)
@bdistin
Copy link
Owner

bdistin commented Feb 19, 2021

I have no idea how you were able to trigger that error because cross-device/EXDEV moves are handled with this code:

if (err.code === 'EXDEV') {
const opts = {
overwrite,
errorOnExist: true
};
await copy(source, destination, opts);
return remove(source);
}

Resolving the path, other than checking to see if the source and destination are the same, shouldn't have any relationship with EXDEV.

@stanleyxu2005
Copy link
Author

stanleyxu2005 commented Feb 20, 2021

I generally report this issue when I move a file from drive D to drive T (which is my mounted RAMDISK).
Anyway, if paths are resolved before calling fsn.move, it will not show error.

@bdistin
Copy link
Owner

bdistin commented Feb 20, 2021

Do you have a node version and a stack trace? It's one thing for the return await not being caught by the try-catch in move. It's another thing for a potential bug with copy, which your patch could only ever partially solve.

@stanleyxu2005
Copy link
Author

stanleyxu2005 commented Feb 21, 2021

I'm using nodejs v12.20.1 and (last LTS version supported by Windows 7)

Today I tried to provide more details, however I cannot reproduce the EXDEV error, but see another one. The initial propose of using this function is just to move a folder (including all children) to another. Maybe my usage is not right?

Preparation:
Create a folder temp and create an empty file abc.txt in it

Test 1: Move folder to same drive
fsn.move('./temp/', 'c:/fsntest')
It passed

Test 2: Move folder to another physical drive
fsn.move('./temp/', 'd:/fsntest')

Promise { }
Error: FS-NEXTRA: Copying a parent directory into a child will result in an infinite loop.
at copyDirectory (D:\fsntest\node_modules\fs-nextra\dist\nextra\copy.js:62:15)
at startCopy (D:\fsntest\node_modules\fs-nextra\dist\nextra\copy.js:56:15)
at async Object.copy [as default] (D:\fsntest\node_modules\fs-nextra\dist\nextra\copy.js:25:9)
at async Object.move (D:\fsntest\node_modules\fs-nextra\dist\nextra\move.js:42:13)

Test 3: Move folder to another virtual (mounted) drive
fsn.move('./temp/', 't:/fsntest')

Promise { }
Error: FS-NEXTRA: Copying a parent directory into a child will result in an infinite loop.
at copyDirectory (D:\fsntest\node_modules\fs-nextra\dist\nextra\copy.js:62:15)
at startCopy (D:\fsntest\node_modules\fs-nextra\dist\nextra\copy.js:56:15)
at async Object.copy [as default] (D:\fsntest\node_modules\fs-nextra\dist\nextra\copy.js:25:9)
at async Object.move (D:\fsntest\node_modules\fs-nextra\dist\nextra\move.js:42:13)

@bdistin
Copy link
Owner

bdistin commented Feb 24, 2021

Ok, so that lines up with what I had said previously. A potential bug in copy.

So this line is throwing: https://github.com/bdistin/fs-nextra/blob/master/src/nextra/copy.ts#L86

Can you throw a console.log above that isSrcKid check (in your node_modules\fs-nextra\dist\nextra\copy.js where you are testing this) to test what the resolved mySource and target are. They get path resolved in resolveCopyOptions; then directories are built off of the source and target (in startCopy) so you get the same folder name in the target directory. After that, it iterates through the directory and copies all items (files, directories, and symlinks) inside that directory to the directory just made.

@stanleyxu2005
Copy link
Author

stanleyxu2005 commented Feb 28, 2021

Here is the details before isSrcKid check

 {
  mySource: './temp',
  stats: Stats {
    dev: 4240084544,
    mode: 16822,
    nlink: 1,
    uid: 0,
    gid: 0,
    rdev: 0,
    blksize: 4096,
    ino: 6192449488425088,
    size: 0,
    blocks: 0,
    atimeMs: 1613893809781.817,
    mtimeMs: 1613893809781.817,
    ctimeMs: 1613893987043.3264,
    birthtimeMs: 1613893726676.264,
    atime: 2021-02-21T07:50:09.782Z,
    mtime: 2021-02-21T07:50:09.782Z,
    ctime: 2021-02-21T07:53:07.043Z,
    birthtime: 2021-02-21T07:48:46.676Z
  },
  target: './temp',
  options: {
    currentPath: 'D:\\fsntest\\temp',
    targetPath: 't:\\fsntest',
    filter: [Function],
    overwrite: false,
    preserveTimestamps: false,
    errorOnExist: true
  }
}

It looks like isSrcKid checks source with target without resolving the full path.

Note that I believe there are 2 bugs in my usage.

  1. The new issue we are check stacktrace: Error: FS-NEXTRA: Copying a parent directory into a child will result in an infinite loop.
  2. Once this issue is identified and fixed, I will further look into the original one. EXDEV: cross-device link not permitted, rename. That one requires more setup. I would rather discuss these issues one by one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants