github.com/vmware/govmomi@v0.37.1/govc/importx/archive.go (about)

     1  /*
     2  Copyright (c) 2014-2015 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 importx
    18  
    19  import (
    20  	"archive/tar"
    21  	"bytes"
    22  	"context"
    23  	"errors"
    24  	"flag"
    25  	"fmt"
    26  	"io"
    27  	"net/url"
    28  	"os"
    29  	"path"
    30  	"path/filepath"
    31  	"strings"
    32  
    33  	"github.com/vmware/govmomi/ovf"
    34  	"github.com/vmware/govmomi/vapi/library"
    35  	"github.com/vmware/govmomi/vim25"
    36  	"github.com/vmware/govmomi/vim25/soap"
    37  )
    38  
    39  // ArchiveFlag doesn't register any flags;
    40  // only encapsulates some common archive related functionality.
    41  type ArchiveFlag struct {
    42  	Archive
    43  
    44  	manifest map[string]*library.Checksum
    45  }
    46  
    47  func newArchiveFlag(ctx context.Context) (*ArchiveFlag, context.Context) {
    48  	return &ArchiveFlag{}, ctx
    49  }
    50  
    51  func (f *ArchiveFlag) Register(ctx context.Context, fs *flag.FlagSet) {
    52  }
    53  
    54  func (f *ArchiveFlag) Process(ctx context.Context) error {
    55  	return nil
    56  }
    57  
    58  func (f *ArchiveFlag) ReadOvf(fpath string) ([]byte, error) {
    59  	r, _, err := f.Open(fpath)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	defer r.Close()
    64  
    65  	return io.ReadAll(r)
    66  }
    67  
    68  func (f *ArchiveFlag) ReadEnvelope(data []byte) (*ovf.Envelope, error) {
    69  	e, err := ovf.Unmarshal(bytes.NewReader(data))
    70  	if err != nil {
    71  		return nil, fmt.Errorf("failed to parse ovf: %s", err)
    72  	}
    73  
    74  	return e, nil
    75  }
    76  
    77  func (f *ArchiveFlag) readManifest(fpath string) error {
    78  	base := filepath.Base(fpath)
    79  	ext := filepath.Ext(base)
    80  	mfName := strings.Replace(base, ext, ".mf", 1)
    81  
    82  	mf, _, err := f.Open(mfName)
    83  	if err != nil {
    84  		msg := fmt.Sprintf("manifest %q: %s", mf, err)
    85  		fmt.Fprintln(os.Stderr, msg)
    86  		return errors.New(msg)
    87  	}
    88  	f.manifest, err = library.ReadManifest(mf)
    89  	_ = mf.Close()
    90  	return err
    91  }
    92  
    93  type Archive interface {
    94  	Open(string) (io.ReadCloser, int64, error)
    95  }
    96  
    97  type TapeArchive struct {
    98  	Path string
    99  	Opener
   100  }
   101  
   102  type TapeArchiveEntry struct {
   103  	io.Reader
   104  	f io.Closer
   105  
   106  	Name string
   107  }
   108  
   109  func (t *TapeArchiveEntry) Close() error {
   110  	return t.f.Close()
   111  }
   112  
   113  func (t *TapeArchive) Open(name string) (io.ReadCloser, int64, error) {
   114  	f, _, err := t.OpenFile(t.Path)
   115  	if err != nil {
   116  		return nil, 0, err
   117  	}
   118  
   119  	r := tar.NewReader(f)
   120  
   121  	for {
   122  		h, err := r.Next()
   123  		if err == io.EOF {
   124  			break
   125  		}
   126  		if err != nil {
   127  			return nil, 0, err
   128  		}
   129  
   130  		matched, err := path.Match(name, path.Base(h.Name))
   131  		if err != nil {
   132  			return nil, 0, err
   133  		}
   134  
   135  		if matched {
   136  			return &TapeArchiveEntry{r, f, h.Name}, h.Size, nil
   137  		}
   138  	}
   139  
   140  	_ = f.Close()
   141  
   142  	return nil, 0, os.ErrNotExist
   143  }
   144  
   145  type FileArchive struct {
   146  	Path string
   147  	Opener
   148  }
   149  
   150  func (t *FileArchive) Open(name string) (io.ReadCloser, int64, error) {
   151  	fpath := name
   152  	if name != t.Path {
   153  		index := strings.LastIndex(t.Path, "/")
   154  		if index != -1 {
   155  			fpath = t.Path[:index] + "/" + name
   156  		}
   157  	}
   158  
   159  	return t.OpenFile(fpath)
   160  }
   161  
   162  type Opener struct {
   163  	*vim25.Client
   164  }
   165  
   166  func isRemotePath(path string) bool {
   167  	if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") {
   168  		return true
   169  	}
   170  	return false
   171  }
   172  
   173  func (o Opener) OpenLocal(path string) (io.ReadCloser, int64, error) {
   174  	f, err := os.Open(filepath.Clean(path))
   175  	if err != nil {
   176  		return nil, 0, err
   177  	}
   178  
   179  	s, err := f.Stat()
   180  	if err != nil {
   181  		return nil, 0, err
   182  	}
   183  
   184  	return f, s.Size(), nil
   185  }
   186  
   187  func (o Opener) OpenFile(path string) (io.ReadCloser, int64, error) {
   188  	if isRemotePath(path) {
   189  		return o.OpenRemote(path)
   190  	}
   191  	return o.OpenLocal(path)
   192  }
   193  
   194  func (o Opener) OpenRemote(link string) (io.ReadCloser, int64, error) {
   195  	if o.Client == nil {
   196  		return nil, 0, errors.New("remote path not supported")
   197  	}
   198  
   199  	u, err := url.Parse(link)
   200  	if err != nil {
   201  		return nil, 0, err
   202  	}
   203  
   204  	return o.Download(context.Background(), u, &soap.DefaultDownload)
   205  }