@@ -49,6 +49,15 @@ function makeNonEmptyDirectory(depth, files, folders, dirname, createSymLinks) {
49
49
path . join ( dirname , `link-${ depth } -bad` ) ,
50
50
'file'
51
51
) ;
52
+
53
+ // Symlinks that form a loop
54
+ [ [ 'a' , 'b' ] , [ 'b' , 'a' ] ] . forEach ( ( [ x , y ] ) => {
55
+ fs . symlinkSync (
56
+ `link-${ depth } -loop-${ x } ` ,
57
+ path . join ( dirname , `link-${ depth } -loop-${ y } ` ) ,
58
+ 'file'
59
+ ) ;
60
+ } ) ;
52
61
}
53
62
54
63
// File with a name that looks like a glob
@@ -88,7 +97,7 @@ function removeAsync(dir) {
88
97
89
98
// Attempted removal should fail now because the directory is gone.
90
99
fs . rm ( dir , common . mustCall ( ( err ) => {
91
- assert . strictEqual ( err . syscall , 'stat ' ) ;
100
+ assert . strictEqual ( err . syscall , 'lstat ' ) ;
92
101
} ) ) ;
93
102
} ) ) ;
94
103
} ) ) ;
@@ -137,6 +146,48 @@ function removeAsync(dir) {
137
146
fs . rmSync ( filePath , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
138
147
}
139
148
} ) ) ;
149
+
150
+ // Should delete a valid symlink
151
+ const linkTarget = path . join ( tmpdir . path , 'link-target-async.txt' ) ;
152
+ fs . writeFileSync ( linkTarget , '' ) ;
153
+ const validLink = path . join ( tmpdir . path , 'valid-link-async' ) ;
154
+ fs . symlinkSync ( linkTarget , validLink ) ;
155
+ fs . rm ( validLink , common . mustNotMutateObjectDeep ( { recursive : true } ) , common . mustCall ( ( err ) => {
156
+ try {
157
+ assert . strictEqual ( err , null ) ;
158
+ assert . strictEqual ( fs . existsSync ( validLink ) , false ) ;
159
+ } finally {
160
+ fs . rmSync ( linkTarget , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
161
+ fs . rmSync ( validLink , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
162
+ }
163
+ } ) ) ;
164
+
165
+ // Should delete an invalid symlink
166
+ const invalidLink = path . join ( tmpdir . path , 'invalid-link-async' ) ;
167
+ fs . symlinkSync ( 'definitely-does-not-exist-async' , invalidLink ) ;
168
+ fs . rm ( invalidLink , common . mustNotMutateObjectDeep ( { recursive : true } ) , common . mustCall ( ( err ) => {
169
+ try {
170
+ assert . strictEqual ( err , null ) ;
171
+ assert . strictEqual ( fs . existsSync ( invalidLink ) , false ) ;
172
+ } finally {
173
+ fs . rmSync ( invalidLink , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
174
+ }
175
+ } ) ) ;
176
+
177
+ // Should delete a symlink that is part of a loop
178
+ const loopLinkA = path . join ( tmpdir . path , 'loop-link-async-a' ) ;
179
+ const loopLinkB = path . join ( tmpdir . path , 'loop-link-async-b' ) ;
180
+ fs . symlinkSync ( loopLinkA , loopLinkB ) ;
181
+ fs . symlinkSync ( loopLinkB , loopLinkA ) ;
182
+ fs . rm ( loopLinkA , common . mustNotMutateObjectDeep ( { recursive : true } ) , common . mustCall ( ( err ) => {
183
+ try {
184
+ assert . strictEqual ( err , null ) ;
185
+ assert . strictEqual ( fs . existsSync ( loopLinkA ) , false ) ;
186
+ } finally {
187
+ fs . rmSync ( loopLinkA , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
188
+ fs . rmSync ( loopLinkB , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
189
+ }
190
+ } ) ) ;
140
191
}
141
192
142
193
// Removing a .git directory should not throw an EPERM.
@@ -168,7 +219,7 @@ if (isGitPresent) {
168
219
} , {
169
220
code : 'ENOENT' ,
170
221
name : 'Error' ,
171
- message : / ^ E N O E N T : n o s u c h f i l e o r d i r e c t o r y , s t a t /
222
+ message : / ^ E N O E N T : n o s u c h f i l e o r d i r e c t o r y , l s t a t /
172
223
} ) ;
173
224
174
225
// Should delete a file
@@ -181,6 +232,39 @@ if (isGitPresent) {
181
232
fs . rmSync ( filePath , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
182
233
}
183
234
235
+ // Should delete a valid symlink
236
+ const linkTarget = path . join ( tmpdir . path , 'link-target.txt' ) ;
237
+ fs . writeFileSync ( linkTarget , '' ) ;
238
+ const validLink = path . join ( tmpdir . path , 'valid-link' ) ;
239
+ fs . symlinkSync ( linkTarget , validLink ) ;
240
+ try {
241
+ fs . rmSync ( validLink ) ;
242
+ } finally {
243
+ fs . rmSync ( linkTarget , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
244
+ fs . rmSync ( validLink , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
245
+ }
246
+
247
+ // Should delete an invalid symlink
248
+ const invalidLink = path . join ( tmpdir . path , 'invalid-link' ) ;
249
+ fs . symlinkSync ( 'definitely-does-not-exist' , invalidLink ) ;
250
+ try {
251
+ fs . rmSync ( invalidLink ) ;
252
+ } finally {
253
+ fs . rmSync ( invalidLink , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
254
+ }
255
+
256
+ // Should delete a symlink that is part of a loop
257
+ const loopLinkA = path . join ( tmpdir . path , 'loop-link-a' ) ;
258
+ const loopLinkB = path . join ( tmpdir . path , 'loop-link-b' ) ;
259
+ fs . symlinkSync ( loopLinkA , loopLinkB ) ;
260
+ fs . symlinkSync ( loopLinkB , loopLinkA ) ;
261
+ try {
262
+ fs . rmSync ( loopLinkA ) ;
263
+ } finally {
264
+ fs . rmSync ( loopLinkA , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
265
+ fs . rmSync ( loopLinkB , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
266
+ }
267
+
184
268
// Should accept URL
185
269
const fileURL = pathToFileURL ( path . join ( tmpdir . path , 'rm-file.txt' ) ) ;
186
270
fs . writeFileSync ( fileURL , '' ) ;
@@ -195,7 +279,7 @@ if (isGitPresent) {
195
279
fs . rmSync ( dir , { recursive : true } ) ;
196
280
197
281
// Attempted removal should fail now because the directory is gone.
198
- assert . throws ( ( ) => fs . rmSync ( dir ) , { syscall : 'stat ' } ) ;
282
+ assert . throws ( ( ) => fs . rmSync ( dir ) , { syscall : 'lstat ' } ) ;
199
283
}
200
284
201
285
// Removing a .git directory should not throw an EPERM.
@@ -222,7 +306,7 @@ if (isGitPresent) {
222
306
await fs . promises . rm ( dir , common . mustNotMutateObjectDeep ( { recursive : true } ) ) ;
223
307
224
308
// Attempted removal should fail now because the directory is gone.
225
- await assert . rejects ( fs . promises . rm ( dir ) , { syscall : 'stat ' } ) ;
309
+ await assert . rejects ( fs . promises . rm ( dir ) , { syscall : 'lstat ' } ) ;
226
310
227
311
// Should fail if target does not exist
228
312
await assert . rejects ( fs . promises . rm (
@@ -231,7 +315,7 @@ if (isGitPresent) {
231
315
) , {
232
316
code : 'ENOENT' ,
233
317
name : 'Error' ,
234
- message : / ^ E N O E N T : n o s u c h f i l e o r d i r e c t o r y , s t a t /
318
+ message : / ^ E N O E N T : n o s u c h f i l e o r d i r e c t o r y , l s t a t /
235
319
} ) ;
236
320
237
321
// Should not fail if target does not exist and force option is true
@@ -247,6 +331,39 @@ if (isGitPresent) {
247
331
fs . rmSync ( filePath , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
248
332
}
249
333
334
+ // Should delete a valid symlink
335
+ const linkTarget = path . join ( tmpdir . path , 'link-target-prom.txt' ) ;
336
+ fs . writeFileSync ( linkTarget , '' ) ;
337
+ const validLink = path . join ( tmpdir . path , 'valid-link-prom' ) ;
338
+ fs . symlinkSync ( linkTarget , validLink ) ;
339
+ try {
340
+ await fs . promises . rm ( validLink ) ;
341
+ } finally {
342
+ fs . rmSync ( linkTarget , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
343
+ fs . rmSync ( validLink , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
344
+ }
345
+
346
+ // Should delete an invalid symlink
347
+ const invalidLink = path . join ( tmpdir . path , 'invalid-link-prom' ) ;
348
+ fs . symlinkSync ( 'definitely-does-not-exist-prom' , invalidLink ) ;
349
+ try {
350
+ await fs . promises . rm ( invalidLink ) ;
351
+ } finally {
352
+ fs . rmSync ( invalidLink , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
353
+ }
354
+
355
+ // Should delete a symlink that is part of a loop
356
+ const loopLinkA = path . join ( tmpdir . path , 'loop-link-prom-a' ) ;
357
+ const loopLinkB = path . join ( tmpdir . path , 'loop-link-prom-b' ) ;
358
+ fs . symlinkSync ( loopLinkA , loopLinkB ) ;
359
+ fs . symlinkSync ( loopLinkB , loopLinkA ) ;
360
+ try {
361
+ await fs . promises . rm ( loopLinkA ) ;
362
+ } finally {
363
+ fs . rmSync ( loopLinkA , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
364
+ fs . rmSync ( loopLinkB , common . mustNotMutateObjectDeep ( { force : true } ) ) ;
365
+ }
366
+
250
367
// Should accept URL
251
368
const fileURL = pathToFileURL ( path . join ( tmpdir . path , 'rm-promises-file.txt' ) ) ;
252
369
fs . writeFileSync ( fileURL , '' ) ;
0 commit comments