@@ -118,7 +118,7 @@ function registerVFS(vfs, name) {
118118 if ( name !== undefined ) {
119119 // What was loaded through the name came from the layer it linked to.
120120 if ( activeNames . has ( name ) ) {
121- purgeLoaderCachesForPrefix ( getVfsRoot ( ) + sep + name ) ;
121+ purgeLoaderCachesForPrefix ( join ( getVfsRoot ( ) , name ) ) ;
122122 }
123123 activeNames . set ( name , vfs [ kLayerId ] ) ;
124124 }
@@ -134,7 +134,7 @@ function deregisterVFS(vfs) {
134134 MapPrototypeForEach ( activeNames , ( target , name ) => {
135135 if ( target === layerId ) {
136136 activeNames . delete ( name ) ;
137- purgeLoaderCachesForPrefix ( getVfsRoot ( ) + sep + name ) ;
137+ purgeLoaderCachesForPrefix ( join ( getVfsRoot ( ) , name ) ) ;
138138 }
139139 } ) ;
140140 purgeLoaderCachesForPrefix ( vfs . mountPoint ) ;
@@ -153,10 +153,14 @@ function deregisterVFS(vfs) {
153153 *
154154 * A mount name is a symbolic link to its layer, so a path that starts with
155155 * one is rewritten to start with the layer's mount point instead, and
156- * `path` is what the layer must be handed. With `followLast` false, a path
157- * that is the name itself is not followed, for the operations that act on
158- * a link rather than on its target. `mountPoint` is the layer's mount point
159- * as the input spells it, by name or by id.
156+ * `path` is what the layer must be handed. `followLink` is the difference
157+ * between stat() and lstat(): a name in the middle of a path is always
158+ * followed, but a path that is the name itself is only followed when
159+ * `followLink` is true. The operations that act on a link rather than on its
160+ * target (lstat, readlink, unlink, rename, ...) pass false, so that they see
161+ * the name as a symbolic link, not as the layer's root directory.
162+ * `mountPoint` is the layer's mount point as the input spells it, by name or
163+ * by id.
160164 *
161165 * A path under the root that no layer serves comes back with `vfs: null`
162166 * rather than as `null`, because the two cases must not be treated alike
@@ -170,15 +174,16 @@ function deregisterVFS(vfs) {
170174 * it rejects the empty device as an invalid package.json, so the loader
171175 * must answer for the whole root.
172176 * @param {string } inputPath
173- * @param {boolean } [followLast]
177+ * @param {boolean } [followLink] Whether a path that is a mount name
178+ * resolves to its layer; false to act on the name's link itself.
174179 * @returns {{
175180 * vfs: object|null,
176181 * path: string,
177182 * normalized: string,
178183 * mountPoint: string|null,
179184 * }|null }
180185 */
181- function resolveVFS ( inputPath , followLast = true ) {
186+ function resolveVFS ( inputPath , followLink = true ) {
182187 const normalized = normalizeMountedPath ( inputPath ) ;
183188 if ( normalized !== normalizedVfsRoot &&
184189 ! StringPrototypeStartsWith ( normalized , normalizedVfsRootPrefix ) ) {
@@ -192,9 +197,12 @@ function resolveVFS(inputPath, followLast = true) {
192197 if ( vfs !== undefined && vfs . shouldHandleNormalized ( normalized ) ) {
193198 return { vfs, path : inputPath , normalized, mountPoint : vfs . mountPoint } ;
194199 }
195- } else if ( followLast || end < normalized . length ) {
200+ } else if ( followLink || end < normalized . length ) {
196201 const layerId = activeNames . get ( segment ) ;
197202 if ( layerId !== undefined ) {
203+ // This runs for every fs call through a name, so the paths are
204+ // concatenated rather than joined: the root is already normalized and
205+ // `segment` is a single segment, so there is nothing to normalize.
198206 const path = normalizedVfsRootPrefix + layerId +
199207 StringPrototypeSlice ( normalized , end ) ;
200208 return {
@@ -214,11 +222,11 @@ function resolveVFS(inputPath, followLast = true) {
214222 * fs functions see the root as a directory, so a path under the root that
215223 * no layer serves is the root file system's to answer.
216224 * @param {string } inputPath
217- * @param {boolean } [followLast]
225+ * @param {boolean } [followLink] See `resolveVFS()`.
218226 * @returns {{ vfs: object, path: string }|null }
219227 */
220- function findVFS ( inputPath , followLast ) {
221- const r = resolveVFS ( inputPath , followLast ) ;
228+ function findVFS ( inputPath , followLink ) {
229+ const r = resolveVFS ( inputPath , followLink ) ;
222230 if ( r === null ) return null ;
223231 if ( r . vfs === null ) r . vfs = rootVFS ;
224232 return r ;
@@ -388,28 +396,28 @@ function vfsRead(path, syscall, fn) {
388396 return findVFSWith ( pathStr , syscall , fn ) ;
389397}
390398
391- function vfsOp ( path , fn , followLast ) {
399+ function vfsOp ( path , fn , followLink ) {
392400 const pathStr = toPathStr ( path ) ;
393401 if ( pathStr !== null ) {
394- const r = findVFS ( pathStr , followLast ) ;
402+ const r = findVFS ( pathStr , followLink ) ;
395403 if ( r !== null ) return fn ( r . vfs , r . path ) ;
396404 }
397405 return undefined ;
398406}
399407
400- function vfsOpVoid ( path , fn , followLast ) {
408+ function vfsOpVoid ( path , fn , followLink ) {
401409 const pathStr = toPathStr ( path ) ;
402410 if ( pathStr !== null ) {
403- const r = findVFS ( pathStr , followLast ) ;
411+ const r = findVFS ( pathStr , followLink ) ;
404412 if ( r !== null ) { fn ( r . vfs , r . path ) ; return true ; }
405413 }
406414 return undefined ;
407415}
408416
409417// Returns the path to hand `srcVfs` for `destPath`, which must be served by
410418// the same VFS as the source.
411- function findSameVFS ( srcPath , destPath , syscall , srcVfs , followLast ) {
412- const r = findVFS ( destPath , followLast ) ;
419+ function findSameVFS ( srcPath , destPath , syscall , srcVfs , followLink ) {
420+ const r = findVFS ( destPath , followLink ) ;
413421 if ( r ?. vfs !== srcVfs ) {
414422 throw createEXDEV ( syscall , srcPath ) ;
415423 }
0 commit comments