@@ -2701,3 +2701,182 @@ t.test('using strip option when top level file exists', t => {
2701
2701
check ( t , path )
2702
2702
} )
2703
2703
} )
2704
+
2705
+ t . test ( 'handle EPERMs when creating symlinks' , t => {
2706
+ // https://github.com/npm/node-tar/issues/265
2707
+ const msg = 'You do not have sufficient privilege to perform this operation.'
2708
+ const er = Object . assign ( new Error ( msg ) , {
2709
+ code : 'EPERM' ,
2710
+ } )
2711
+ t . teardown ( mutateFS . fail ( 'symlink' , er ) )
2712
+ const data = makeTar ( [
2713
+ {
2714
+ path : 'x' ,
2715
+ type : 'Directory' ,
2716
+ } ,
2717
+ {
2718
+ path : 'x/y' ,
2719
+ type : 'File' ,
2720
+ size : 'hello, world' . length ,
2721
+ } ,
2722
+ 'hello, world' ,
2723
+ {
2724
+ path : 'x/link1' ,
2725
+ type : 'SymbolicLink' ,
2726
+ linkpath : './y' ,
2727
+ } ,
2728
+ {
2729
+ path : 'x/link2' ,
2730
+ type : 'SymbolicLink' ,
2731
+ linkpath : './y' ,
2732
+ } ,
2733
+ {
2734
+ path : 'x/link3' ,
2735
+ type : 'SymbolicLink' ,
2736
+ linkpath : './y' ,
2737
+ } ,
2738
+ {
2739
+ path : 'x/z' ,
2740
+ type : 'File' ,
2741
+ size : 'hello, world' . length ,
2742
+ } ,
2743
+ 'hello, world' ,
2744
+ '' ,
2745
+ '' ,
2746
+ ] )
2747
+
2748
+ const dir = path . resolve ( unpackdir , 'eperm-symlinks' )
2749
+ mkdirp . sync ( `${ dir } /sync` )
2750
+ mkdirp . sync ( `${ dir } /async` )
2751
+
2752
+ const check = path => {
2753
+ t . match ( WARNINGS , [
2754
+ [ 'TAR_ENTRY_ERROR' , msg ] ,
2755
+ [ 'TAR_ENTRY_ERROR' , msg ] ,
2756
+ [ 'TAR_ENTRY_ERROR' , msg ] ,
2757
+ ] , 'got expected warnings' )
2758
+ t . equal ( WARNINGS . length , 3 )
2759
+ WARNINGS . length = 0
2760
+ t . equal ( fs . readFileSync ( `${ path } /x/y` , 'utf8' ) , 'hello, world' )
2761
+ t . equal ( fs . readFileSync ( `${ path } /x/z` , 'utf8' ) , 'hello, world' )
2762
+ t . throws ( ( ) => fs . statSync ( `${ path } /x/link1` ) , { code : 'ENOENT' } )
2763
+ t . throws ( ( ) => fs . statSync ( `${ path } /x/link2` ) , { code : 'ENOENT' } )
2764
+ t . throws ( ( ) => fs . statSync ( `${ path } /x/link3` ) , { code : 'ENOENT' } )
2765
+ }
2766
+
2767
+ const WARNINGS = [ ]
2768
+ const u = new Unpack ( {
2769
+ cwd : `${ dir } /async` ,
2770
+ onwarn : ( code , msg , er ) => WARNINGS . push ( [ code , msg ] ) ,
2771
+ } )
2772
+ u . on ( 'end' , ( ) => {
2773
+ check ( `${ dir } /async` )
2774
+ const u = new UnpackSync ( {
2775
+ cwd : `${ dir } /sync` ,
2776
+ onwarn : ( code , msg , er ) => WARNINGS . push ( [ code , msg ] ) ,
2777
+ } )
2778
+ u . end ( data )
2779
+ check ( `${ dir } /sync` )
2780
+ t . end ( )
2781
+ } )
2782
+ u . end ( data )
2783
+ } )
2784
+
2785
+ t . test ( 'close fd when error writing' , t => {
2786
+ const data = makeTar ( [
2787
+ {
2788
+ type : 'Directory' ,
2789
+ path : 'x' ,
2790
+ } ,
2791
+ {
2792
+ type : 'File' ,
2793
+ size : 1 ,
2794
+ path : 'x/y' ,
2795
+ } ,
2796
+ '.' ,
2797
+ '' ,
2798
+ '' ,
2799
+ ] )
2800
+ t . teardown ( mutateFS . fail ( 'write' , new Error ( 'nope' ) ) )
2801
+ const CLOSES = [ ]
2802
+ const OPENS = { }
2803
+ const { open} = require ( 'fs' )
2804
+ t . teardown ( ( ) => fs . open = open )
2805
+ fs . open = ( ...args ) => {
2806
+ const cb = args . pop ( )
2807
+ args . push ( ( er , fd ) => {
2808
+ OPENS [ args [ 0 ] ] = fd
2809
+ cb ( er , fd )
2810
+ } )
2811
+ return open . call ( fs , ...args )
2812
+ }
2813
+ t . teardown ( mutateFS . mutateArgs ( 'close' , ( [ fd ] ) => {
2814
+ CLOSES . push ( fd )
2815
+ return [ fd ]
2816
+ } ) )
2817
+ const WARNINGS = [ ]
2818
+ const dir = path . resolve ( unpackdir , 'close-on-write-error' )
2819
+ mkdirp . sync ( dir )
2820
+ const unpack = new Unpack ( {
2821
+ cwd : dir ,
2822
+ onwarn : ( code , msg ) => WARNINGS . push ( [ code , msg ] ) ,
2823
+ } )
2824
+ unpack . on ( 'end' , ( ) => {
2825
+ for ( const [ path , fd ] of Object . entries ( OPENS ) )
2826
+ t . equal ( CLOSES . includes ( fd ) , true , 'closed fd for ' + path )
2827
+ t . end ( )
2828
+ } )
2829
+ unpack . end ( data )
2830
+ } )
2831
+
2832
+ t . test ( 'close fd when error setting mtime' , t => {
2833
+ const data = makeTar ( [
2834
+ {
2835
+ type : 'Directory' ,
2836
+ path : 'x' ,
2837
+ } ,
2838
+ {
2839
+ type : 'File' ,
2840
+ size : 1 ,
2841
+ path : 'x/y' ,
2842
+ atime : new Date ( '1979-07-01T19:10:00.000Z' ) ,
2843
+ ctime : new Date ( '2011-03-27T22:16:31.000Z' ) ,
2844
+ mtime : new Date ( '2011-03-27T22:16:31.000Z' ) ,
2845
+ } ,
2846
+ '.' ,
2847
+ '' ,
2848
+ '' ,
2849
+ ] )
2850
+ // have to clobber these both, because we fall back
2851
+ t . teardown ( mutateFS . fail ( 'futimes' , new Error ( 'nope' ) ) )
2852
+ t . teardown ( mutateFS . fail ( 'utimes' , new Error ( 'nooooope' ) ) )
2853
+ const CLOSES = [ ]
2854
+ const OPENS = { }
2855
+ const { open} = require ( 'fs' )
2856
+ t . teardown ( ( ) => fs . open = open )
2857
+ fs . open = ( ...args ) => {
2858
+ const cb = args . pop ( )
2859
+ args . push ( ( er , fd ) => {
2860
+ OPENS [ args [ 0 ] ] = fd
2861
+ cb ( er , fd )
2862
+ } )
2863
+ return open . call ( fs , ...args )
2864
+ }
2865
+ t . teardown ( mutateFS . mutateArgs ( 'close' , ( [ fd ] ) => {
2866
+ CLOSES . push ( fd )
2867
+ return [ fd ]
2868
+ } ) )
2869
+ const WARNINGS = [ ]
2870
+ const dir = path . resolve ( unpackdir , 'close-on-futimes-error' )
2871
+ mkdirp . sync ( dir )
2872
+ const unpack = new Unpack ( {
2873
+ cwd : dir ,
2874
+ onwarn : ( code , msg ) => WARNINGS . push ( [ code , msg ] ) ,
2875
+ } )
2876
+ unpack . on ( 'end' , ( ) => {
2877
+ for ( const [ path , fd ] of Object . entries ( OPENS ) )
2878
+ t . equal ( CLOSES . includes ( fd ) , true , 'closed fd for ' + path )
2879
+ t . end ( )
2880
+ } )
2881
+ unpack . end ( data )
2882
+ } )
0 commit comments