@@ -23,14 +23,6 @@ func NewZipArchive(inputReader io.Reader) ZipArchive {
23
23
// Decompress reads from ZipArchive and writes files into the destination
24
24
// specified.
25
25
func (z ZipArchive ) Decompress (destination string ) error {
26
- // Struct and slice to collect symlinks and create them after all files have
27
- // been created
28
- type header struct {
29
- linkname string
30
- path string
31
- }
32
-
33
- var symlinkHeaders []header
34
26
35
27
// Use an os.File to buffer the zip contents. This is needed because
36
28
// zip.NewReader requires an io.ReaderAt so that it can jump around within
@@ -51,6 +43,7 @@ func (z ZipArchive) Decompress(destination string) error {
51
43
return fmt .Errorf ("failed to create zip reader: %w" , err )
52
44
}
53
45
46
+ var symlinks []symlink
54
47
for _ , f := range zr .File {
55
48
// Clean the name in the header to prevent './filename' being stripped to
56
49
// 'filename' also to skip if the destination it the destination directory
@@ -94,9 +87,9 @@ func (z ZipArchive) Decompress(destination string) error {
94
87
95
88
// Collect all of the headers for symlinks so that they can be verified
96
89
// after all other files are written
97
- symlinkHeaders = append (symlinkHeaders , header {
98
- linkname : string (linkname ),
99
- path : path ,
90
+ symlinks = append (symlinks , symlink {
91
+ name : string (linkname ),
92
+ path : path ,
100
93
})
101
94
102
95
default :
@@ -130,69 +123,22 @@ func (z ZipArchive) Decompress(destination string) error {
130
123
}
131
124
}
132
125
133
- // Create a map of all of the symlink names and where they are pointing to to
134
- // act as a quasi-graph
135
- symlinkMap := map [string ]string {}
136
- for _ , h := range symlinkHeaders {
137
- symlinkMap [filepath .Clean (h .path )] = h .linkname
126
+ symlinks , err = sortSymlinks (symlinks )
127
+ if err != nil {
128
+ return err
138
129
}
139
130
140
- // Iterate over the symlink map for every link that is found this ensures
141
- // that all symlinks that can be created will be created and any that are
142
- // left over are cyclically dependent
143
- maxIterations := len (symlinkMap )
144
- for i := 0 ; i < maxIterations ; i ++ {
145
- for path , linkname := range symlinkMap {
146
- // Check to see if the linkname lies on the path of another symlink in
147
- // the table or if it is another symlink in the table
148
- //
149
- // Example:
150
- // path = dir/file
151
- // a-symlink -> dir
152
- // b-symlink -> a-symlink
153
- // c-symlink -> a-symlink/file
154
- //
155
- // If there is a match either of the symlink or it is on the path then
156
- // skip the creation of this symlink for now
157
- shouldSkipLink := func () bool {
158
- sln := strings .Split (linkname , "/" )
159
- for j := 0 ; j < len (sln ); j ++ {
160
- if _ , ok := symlinkMap [linknameFullPath (path , filepath .Join (sln [:j + 1 ]... ))]; ok {
161
- return true
162
- }
163
- }
164
- return false
165
- }
166
-
167
- if shouldSkipLink () {
168
- continue
169
- }
170
-
171
- // If the linkname is not an existing link in the symlink table then we
172
- // can attempt the make the link
173
-
174
- // Check to see if the file that will be linked to is valid for symlinking
175
- _ , err := filepath .EvalSymlinks (linknameFullPath (path , linkname ))
176
- if err != nil {
177
- return fmt .Errorf ("failed to evaluate symlink %s: %w" , path , err )
178
- }
179
-
180
- // Create the symlink
181
- err = os .Symlink (linkname , path )
182
- if err != nil {
183
- return fmt .Errorf ("failed to unzip symlink: %s" , err )
184
- }
185
-
186
- // Remove the created symlink from the symlink table so that its
187
- // dependent symlinks can be created in the next iteration
188
- delete (symlinkMap , path )
131
+ for _ , link := range symlinks {
132
+ // Check to see if the file that will be linked to is valid for symlinking
133
+ _ , err := filepath .EvalSymlinks (linknameFullPath (link .path , link .name ))
134
+ if err != nil {
135
+ return fmt .Errorf ("failed to evaluate symlink %s: %w" , link .path , err )
189
136
}
190
- }
191
137
192
- // Check to see if there are any symlinks left in the map which would
193
- // indicate a cyclical dependency
194
- if len ( symlinkMap ) > 0 {
195
- return fmt . Errorf ( "failed: max iterations reached: this symlink graph contains a cycle" )
138
+ err = os . Symlink ( link . name , link . path )
139
+ if err != nil {
140
+ return fmt . Errorf ( "failed to unzip symlink: %s" , err )
141
+ }
196
142
}
197
143
198
144
return nil
0 commit comments