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  }