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  }