github.com/vmware/govmomi@v0.51.0/ovf/importer/archive.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package importer 6 7 import ( 8 "archive/tar" 9 "bytes" 10 "context" 11 "errors" 12 "fmt" 13 "io" 14 "net/url" 15 "os" 16 "path" 17 "path/filepath" 18 "strings" 19 20 "github.com/vmware/govmomi/ovf" 21 "github.com/vmware/govmomi/vim25" 22 "github.com/vmware/govmomi/vim25/soap" 23 ) 24 25 func ReadOvf(fpath string, a Archive) ([]byte, error) { 26 r, _, err := a.Open(fpath) 27 if err != nil { 28 return nil, err 29 } 30 defer r.Close() 31 32 return io.ReadAll(r) 33 } 34 35 func ReadEnvelope(data []byte) (*ovf.Envelope, error) { 36 e, err := ovf.Unmarshal(bytes.NewReader(data)) 37 if err != nil { 38 return nil, fmt.Errorf("failed to parse ovf: %s", err) 39 } 40 41 return e, nil 42 } 43 44 type Archive interface { 45 Open(string) (io.ReadCloser, int64, error) 46 } 47 48 type TapeArchive struct { 49 Path string 50 Opener 51 } 52 53 type TapeArchiveEntry struct { 54 io.Reader 55 f io.Closer 56 57 Name string 58 } 59 60 func (t *TapeArchiveEntry) Close() error { 61 return t.f.Close() 62 } 63 64 func (t *TapeArchive) Open(name string) (io.ReadCloser, int64, error) { 65 f, _, err := t.OpenFile(t.Path) 66 if err != nil { 67 return nil, 0, err 68 } 69 70 r := tar.NewReader(f) 71 72 for { 73 h, err := r.Next() 74 if err == io.EOF { 75 break 76 } 77 if err != nil { 78 return nil, 0, err 79 } 80 81 matched, err := path.Match(name, path.Base(h.Name)) 82 if err != nil { 83 return nil, 0, err 84 } 85 86 if matched { 87 return &TapeArchiveEntry{r, f, h.Name}, h.Size, nil 88 } 89 } 90 91 _ = f.Close() 92 93 return nil, 0, os.ErrNotExist 94 } 95 96 type FileArchive struct { 97 Path string 98 Opener 99 } 100 101 func (t *FileArchive) Open(name string) (io.ReadCloser, int64, error) { 102 fpath := name 103 if name != t.Path { 104 if IsRemotePath(t.Path) { 105 index := strings.LastIndex(t.Path, "/") 106 if index != -1 { 107 fpath = t.Path[:index] + "/" + name 108 } 109 } else if !filepath.IsAbs(name) { 110 fpath = filepath.Join(filepath.Dir(t.Path), name) 111 } 112 } 113 114 return t.OpenFile(fpath) 115 } 116 117 type Opener struct { 118 *vim25.Client 119 } 120 121 func IsRemotePath(path string) bool { 122 if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") { 123 return true 124 } 125 return false 126 } 127 128 func (o Opener) OpenLocal(path string) (io.ReadCloser, int64, error) { 129 f, err := os.Open(filepath.Clean(path)) 130 if err != nil { 131 return nil, 0, err 132 } 133 134 s, err := f.Stat() 135 if err != nil { 136 return nil, 0, err 137 } 138 139 return f, s.Size(), nil 140 } 141 142 func (o Opener) OpenFile(path string) (io.ReadCloser, int64, error) { 143 if IsRemotePath(path) { 144 return o.OpenRemote(path) 145 } 146 return o.OpenLocal(path) 147 } 148 149 func (o Opener) OpenRemote(link string) (io.ReadCloser, int64, error) { 150 if o.Client == nil { 151 return nil, 0, errors.New("remote path not supported") 152 } 153 154 u, err := url.Parse(link) 155 if err != nil { 156 return nil, 0, err 157 } 158 159 return o.Download(context.Background(), u, &soap.DefaultDownload) 160 }