github.com/vmware/govmomi@v0.43.0/ovf/importer/archive.go (about) 1 /* 2 Copyright (c) 2024-2024 VMware, Inc. All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package importer 18 19 import ( 20 "archive/tar" 21 "bytes" 22 "context" 23 "errors" 24 "fmt" 25 "io" 26 "net/url" 27 "os" 28 "path" 29 "path/filepath" 30 "strings" 31 32 "github.com/vmware/govmomi/ovf" 33 "github.com/vmware/govmomi/vim25" 34 "github.com/vmware/govmomi/vim25/soap" 35 ) 36 37 func ReadOvf(fpath string, a Archive) ([]byte, error) { 38 r, _, err := a.Open(fpath) 39 if err != nil { 40 return nil, err 41 } 42 defer r.Close() 43 44 return io.ReadAll(r) 45 } 46 47 func ReadEnvelope(data []byte) (*ovf.Envelope, error) { 48 e, err := ovf.Unmarshal(bytes.NewReader(data)) 49 if err != nil { 50 return nil, fmt.Errorf("failed to parse ovf: %s", err) 51 } 52 53 return e, nil 54 } 55 56 type Archive interface { 57 Open(string) (io.ReadCloser, int64, error) 58 } 59 60 type TapeArchive struct { 61 Path string 62 Opener 63 } 64 65 type TapeArchiveEntry struct { 66 io.Reader 67 f io.Closer 68 69 Name string 70 } 71 72 func (t *TapeArchiveEntry) Close() error { 73 return t.f.Close() 74 } 75 76 func (t *TapeArchive) Open(name string) (io.ReadCloser, int64, error) { 77 f, _, err := t.OpenFile(t.Path) 78 if err != nil { 79 return nil, 0, err 80 } 81 82 r := tar.NewReader(f) 83 84 for { 85 h, err := r.Next() 86 if err == io.EOF { 87 break 88 } 89 if err != nil { 90 return nil, 0, err 91 } 92 93 matched, err := path.Match(name, path.Base(h.Name)) 94 if err != nil { 95 return nil, 0, err 96 } 97 98 if matched { 99 return &TapeArchiveEntry{r, f, h.Name}, h.Size, nil 100 } 101 } 102 103 _ = f.Close() 104 105 return nil, 0, os.ErrNotExist 106 } 107 108 type FileArchive struct { 109 Path string 110 Opener 111 } 112 113 func (t *FileArchive) Open(name string) (io.ReadCloser, int64, error) { 114 fpath := name 115 if name != t.Path { 116 index := strings.LastIndex(t.Path, "/") 117 if index != -1 { 118 fpath = t.Path[:index] + "/" + name 119 } 120 } 121 122 return t.OpenFile(fpath) 123 } 124 125 type Opener struct { 126 *vim25.Client 127 } 128 129 func IsRemotePath(path string) bool { 130 if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") { 131 return true 132 } 133 return false 134 } 135 136 func (o Opener) OpenLocal(path string) (io.ReadCloser, int64, error) { 137 f, err := os.Open(filepath.Clean(path)) 138 if err != nil { 139 return nil, 0, err 140 } 141 142 s, err := f.Stat() 143 if err != nil { 144 return nil, 0, err 145 } 146 147 return f, s.Size(), nil 148 } 149 150 func (o Opener) OpenFile(path string) (io.ReadCloser, int64, error) { 151 if IsRemotePath(path) { 152 return o.OpenRemote(path) 153 } 154 return o.OpenLocal(path) 155 } 156 157 func (o Opener) OpenRemote(link string) (io.ReadCloser, int64, error) { 158 if o.Client == nil { 159 return nil, 0, errors.New("remote path not supported") 160 } 161 162 u, err := url.Parse(link) 163 if err != nil { 164 return nil, 0, err 165 } 166 167 return o.Download(context.Background(), u, &soap.DefaultDownload) 168 }