6
6
"io"
7
7
"os"
8
8
"path/filepath"
9
- "sort"
10
9
"strings"
11
10
)
12
11
@@ -27,7 +26,6 @@ func (z ZipArchive) Decompress(destination string) error {
27
26
// Struct and slice to collect symlinks and create them after all files have
28
27
// been created
29
28
type header struct {
30
- name string
31
29
linkname string
32
30
path string
33
31
}
@@ -97,7 +95,6 @@ func (z ZipArchive) Decompress(destination string) error {
97
95
// Collect all of the headers for symlinks so that they can be verified
98
96
// after all other files are written
99
97
symlinkHeaders = append (symlinkHeaders , header {
100
- name : f .Name ,
101
98
linkname : string (linkname ),
102
99
path : path ,
103
100
})
@@ -133,42 +130,55 @@ func (z ZipArchive) Decompress(destination string) error {
133
130
}
134
131
}
135
132
136
- // Sort the symlinks so that symlinks of symlinks have their base link
137
- // created before they are created.
138
- //
139
- // For example:
140
- // b-sym -> a-sym/x
141
- // a-sym -> z
142
- // c-sym -> d-sym
143
- // d-sym -> z
144
- //
145
- // Will sort to:
146
- // a-sym -> z
147
- // b-sym -> a-sym/x
148
- // d-sym -> z
149
- // c-sym -> d-sym
150
- sort .Slice (symlinkHeaders , func (i , j int ) bool {
151
- if filepath .Clean (symlinkHeaders [i ].name ) == linknameFullPath (symlinkHeaders [j ].name , symlinkHeaders [j ].linkname ) {
152
- return true
153
- }
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
138
+ }
154
139
155
- if filepath .Clean (symlinkHeaders [j ].name ) == linknameFullPath (symlinkHeaders [i ].name , symlinkHeaders [i ].linkname ) {
156
- return false
157
- }
140
+ // Loop over map until it is empty this is potentially O(infinity) if there
141
+ // is a cyclical link but I like to live on the edge
142
+ for len (symlinkMap ) > 0 {
143
+ // Name the outer loop as an escape hatch
144
+ Builder:
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
+ sln := strings .Split (linkname , "/" )
158
+ for i := 0 ; i < len (sln ); i ++ {
159
+ if _ , ok := symlinkMap [linknameFullPath (path , filepath .Join (sln [:i + 1 ]... ))]; ok {
160
+ continue Builder
161
+ }
162
+ }
158
163
159
- return filepath . Clean ( symlinkHeaders [ i ]. name ) < linknameFullPath ( symlinkHeaders [ j ]. name , symlinkHeaders [ j ]. linkname )
160
- })
164
+ // If the linkname is not an existing link in the symlink table then we
165
+ // can attempt the make the link
161
166
162
- for _ , h := range symlinkHeaders {
163
- // Check to see if the file that will be linked to is valid for symlinking
164
- _ , err := filepath .EvalSymlinks (linknameFullPath (h .path , h .linkname ))
165
- if err != nil {
166
- return fmt .Errorf ("failed to evaluate symlink %s: %w" , h .path , err )
167
- }
167
+ // Check to see if the file that will be linked to is valid for symlinking
168
+ _ , err := filepath .EvalSymlinks (linknameFullPath (path , linkname ))
169
+ if err != nil {
170
+ return fmt .Errorf ("failed to evaluate symlink %s: %w" , path , err )
171
+ }
168
172
169
- err = os .Symlink (h .linkname , h .path )
170
- if err != nil {
171
- return fmt .Errorf ("failed to unzip symlink: %w" , err )
173
+ // Create the symlink
174
+ err = os .Symlink (linkname , path )
175
+ if err != nil {
176
+ return fmt .Errorf ("failed to unzip symlink: %s" , err )
177
+ }
178
+
179
+ // Remove the created symlink from the symlink table so that its
180
+ // dependent symlinks can be created in the next iteration
181
+ delete (symlinkMap , path )
172
182
}
173
183
}
174
184
0 commit comments