Skip to content

Commit 1cfbc7e

Browse files
committed
Fix rename behavior, including addition of a missing EXDEV
1 parent 335fe73 commit 1cfbc7e

File tree

2 files changed

+32
-29
lines changed

2 files changed

+32
-29
lines changed

src/vfs/promises.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -530,22 +530,23 @@ export async function rename(this: V_Context, oldPath: fs.PathLike, newPath: fs.
530530
newPath = normalizePath(newPath);
531531
const src = resolveMount(oldPath, this);
532532
const dst = resolveMount(newPath, this);
533+
534+
if (src.fs !== dst.fs) {
535+
throw new ErrnoError(Errno.EXDEV, `cross-device link not permitted, rename '${oldPath}' -> '${newPath}'`);
536+
}
537+
533538
if (checkAccess && !(await stat.call(this, dirname(oldPath))).hasAccess(constants.W_OK, this)) {
534539
throw ErrnoError.With('EACCES', oldPath, 'rename');
535540
}
541+
536542
try {
537-
if (src.mountPoint == dst.mountPoint) {
538-
await src.fs.rename(src.path, dst.path);
539-
emitChange(this, 'rename', oldPath.toString());
540-
emitChange(this, 'change', newPath.toString());
541-
return;
542-
}
543-
await writeFile.call(this, newPath, await readFile(oldPath));
544-
await unlink.call(this, oldPath);
545-
emitChange(this, 'rename', oldPath.toString());
543+
await src.fs.rename(src.path, dst.path);
546544
} catch (e) {
547545
throw fixError(e as ErrnoError, { [src.path]: oldPath, [dst.path]: newPath });
548546
}
547+
548+
emitChange(this, 'rename', oldPath.toString());
549+
emitChange(this, 'change', newPath.toString());
549550
}
550551
rename satisfies typeof promises.rename;
551552

src/vfs/sync.ts

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,25 @@ export function renameSync(this: V_Context, oldPath: fs.PathLike, newPath: fs.Pa
2626
newPath = normalizePath(newPath);
2727
const oldMount = resolveMount(oldPath, this);
2828
const newMount = resolveMount(newPath, this);
29-
if (checkAccess && !statSync.call<V_Context, Parameters<fs.StatSyncFn>, Stats>(this, dirname(oldPath)).hasAccess(constants.W_OK, this)) {
29+
30+
const oldStats = statSync.call<V_Context, Parameters<fs.StatSyncFn>, Stats>(this, dirname(oldPath));
31+
32+
if (checkAccess && !oldStats.hasAccess(constants.W_OK, this)) {
3033
throw ErrnoError.With('EACCES', oldPath, 'rename');
3134
}
32-
try {
33-
if (oldMount === newMount) {
34-
oldMount.fs.renameSync(oldMount.path, newMount.path);
35-
emitChange(this, 'rename', oldPath.toString());
36-
emitChange(this, 'change', newPath.toString());
37-
return;
38-
}
3935

40-
writeFileSync.call(this, newPath, readFileSync(oldPath));
41-
unlinkSync.call(this, oldPath);
42-
emitChange(this, 'rename', oldPath.toString());
36+
if (oldMount.fs !== newMount.fs) {
37+
throw new ErrnoError(Errno.EXDEV, `cross-device link not permitted, rename '${oldPath}' -> '${newPath}'`);
38+
}
39+
40+
try {
41+
oldMount.fs.renameSync(oldMount.path, newMount.path);
4342
} catch (e) {
4443
throw fixError(e as ErrnoError, { [oldMount.path]: oldPath, [newMount.path]: newPath });
4544
}
45+
46+
emitChange(this, 'rename', oldPath.toString());
47+
emitChange(this, 'change', newPath.toString());
4648
}
4749
renameSync satisfies typeof fs.renameSync;
4850

@@ -205,14 +207,6 @@ export function lopenSync(this: V_Context, path: fs.PathLike, flag: string, mode
205207
return toFD(_openSync.call(this, path, { flag, mode, preserveSymlinks: true }));
206208
}
207209

208-
function _readFileSync(this: V_Context, path: fs.PathOrFileDescriptor, flag: string, preserveSymlinks: boolean): Uint8Array {
209-
using file = typeof path == 'number' ? fromFD(this, path) : _openSync.call(this, path.toString(), { flag, mode: 0o644, preserveSymlinks });
210-
const { size } = file.stat();
211-
const data = new Uint8Array(size);
212-
file.read(data, 0, size, 0);
213-
return data;
214-
}
215-
216210
/**
217211
* Synchronously reads the entire contents of a file.
218212
* @option encoding The string encoding for the file contents. Defaults to `null`.
@@ -231,7 +225,15 @@ export function readFileSync(this: V_Context, path: fs.PathOrFileDescriptor, _op
231225
if (flag & constants.O_WRONLY) {
232226
throw new ErrnoError(Errno.EINVAL, 'Flag passed to readFile must allow for reading');
233227
}
234-
const data: Buffer = Buffer.from(_readFileSync.call(this, path, options.flag, false));
228+
229+
using file =
230+
typeof path == 'number'
231+
? fromFD(this, path)
232+
: _openSync.call(this, path.toString(), { flag: options.flag, mode: 0o644, preserveSymlinks: false });
233+
const { size } = file.stat();
234+
const data = Buffer.alloc(size);
235+
file.read(data, 0, size, 0);
236+
235237
return options.encoding ? data.toString(options.encoding) : data;
236238
}
237239
readFileSync satisfies typeof fs.readFileSync;

0 commit comments

Comments
 (0)