github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/cmds/core/cpio/cpio_test.go (about)

     1  // Copyright 2020 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"os"
    11  	"path/filepath"
    12  	"runtime"
    13  	"testing"
    14  )
    15  
    16  type dirEnt struct {
    17  	Name     string
    18  	Type     string
    19  	Content  string
    20  	Target   string
    21  	FileInfo os.FileInfo
    22  }
    23  
    24  func TestCpio(t *testing.T) {
    25  	debug = t.Logf
    26  	// Create a temporary directory
    27  	tempDir := t.TempDir()
    28  
    29  	targets := []dirEnt{
    30  		{Name: "file1", Type: "file", Content: "Hello World"},
    31  		{Name: "file2", Type: "file", Content: ""},
    32  		{Name: "directory1", Type: "dir"},
    33  	}
    34  	if runtime.GOOS != "plan9" {
    35  		targets = append(targets, []dirEnt{
    36  			{Name: "hardlinked", Type: "hardlink", Target: "file1", Content: "Hello World"},
    37  			{Name: "hardlinkedtofile2", Type: "hardlink", Target: "file2"},
    38  		}...)
    39  	}
    40  	for _, ent := range targets {
    41  		name := filepath.Join(tempDir, ent.Name)
    42  		switch ent.Type {
    43  		case "dir":
    44  			err := os.Mkdir(name, os.FileMode(0o700))
    45  			if err != nil {
    46  				t.Fatalf("cannot create test directory: %v", err)
    47  			}
    48  
    49  		case "file":
    50  			f, err := os.Create(name)
    51  			if err != nil {
    52  				t.Fatalf("cannot create test file: %v", err)
    53  			}
    54  			defer f.Close()
    55  			_, err = f.WriteString(ent.Content)
    56  			if err != nil {
    57  				t.Fatal(err)
    58  			}
    59  
    60  		case "hardlink":
    61  			target := filepath.Join(tempDir, ent.Target)
    62  			err := os.Link(target, name)
    63  			if err != nil {
    64  				t.Fatalf("cannot create hard link: %v", err)
    65  			}
    66  		}
    67  	}
    68  
    69  	// Now that the temporary directory structure is complete, populate
    70  	// the FileInfo for each target. This needs to happen in a second
    71  	// pass because of the link count.
    72  	for key, ent := range targets {
    73  		var err error
    74  		name := filepath.Join(tempDir, ent.Name)
    75  		targets[key].FileInfo, err = os.Stat(name)
    76  		if err != nil {
    77  			t.Fatalf("cannot stat temporary dirent: %v", err)
    78  		}
    79  	}
    80  
    81  	inputFile, err := os.CreateTemp(tempDir, "")
    82  	if err != nil {
    83  		t.Fatalf("%v", err)
    84  	}
    85  
    86  	for _, ent := range targets {
    87  		name := filepath.Join(tempDir, ent.Name)
    88  		if _, err := fmt.Fprintln(inputFile, name); err != nil {
    89  			t.Fatalf("failed to write file path %v to input file: %v", ent.Name, err)
    90  		}
    91  	}
    92  	inputFile.Seek(0, 0)
    93  
    94  	archive := &bytes.Buffer{}
    95  	err = run([]string{"o"}, inputFile, archive, true, "newc")
    96  	if err != nil {
    97  		t.Fatalf("failed to build archive from filepaths: %v", err)
    98  	}
    99  
   100  	// Cpio can't read from a non-seekable input (e.g. a pipe) in input mode.
   101  	// Write the archive to a file instead.
   102  	archiveFile, err := os.CreateTemp(tempDir, "archive.cpio")
   103  	if err != nil {
   104  		t.Fatal(err)
   105  	}
   106  
   107  	if _, err := archiveFile.Write(archive.Bytes()); err != nil {
   108  		t.Fatal(err)
   109  	}
   110  
   111  	// Extract to a new directory
   112  	tempExtractDir := t.TempDir()
   113  
   114  	out := &bytes.Buffer{}
   115  	// Change directory back afterwards to not interfer with the subsequent tests
   116  	wd, err := os.Getwd()
   117  	if err != nil {
   118  		t.Fatalf("Could not get current working directory: %v", err)
   119  	}
   120  	defer os.Chdir(wd)
   121  
   122  	err = os.Chdir(tempExtractDir)
   123  	if err != nil {
   124  		t.Fatalf("Change to extraction directory %v failed: %#v", tempExtractDir, err)
   125  	}
   126  
   127  	err = run([]string{"i"}, archiveFile, out, true, "newc")
   128  	if err != nil {
   129  		t.Fatalf("Extraction failed:\n%#v\n%v\n", out, err)
   130  	}
   131  
   132  	for _, ent := range targets {
   133  		name := filepath.Join(tempExtractDir, tempDir, ent.Name)
   134  
   135  		newFileInfo, err := os.Stat(name)
   136  		if err != nil {
   137  			t.Error(err)
   138  			continue
   139  		}
   140  		checkFileInfo(t, &ent, newFileInfo)
   141  
   142  		if ent.Type != "dir" {
   143  			content, err := os.ReadFile(name)
   144  			if err != nil {
   145  				t.Error(err)
   146  			}
   147  			if string(content) != ent.Content {
   148  				t.Errorf("File %s has mismatched contents", ent.Name)
   149  			}
   150  		}
   151  	}
   152  }
   153  
   154  func TestDirectoryHardLink(t *testing.T) {
   155  
   156  	// Open an archive containing two directories with the same inode (0).
   157  	// We're trying to test if having the same inode will trigger a hard link.
   158  	archiveFile, err := os.Open("testdata/dir-hard-link.cpio")
   159  	if err != nil {
   160  		t.Fatal(err)
   161  	}
   162  
   163  	// Change directory back afterwards to not interfer with the subsequent tests
   164  	wd, err := os.Getwd()
   165  	if err != nil {
   166  		t.Fatalf("Could not get current working directory: %v", err)
   167  	}
   168  	defer os.Chdir(wd)
   169  	tempExtractDir := t.TempDir()
   170  	err = os.Chdir(tempExtractDir)
   171  	if err != nil {
   172  		t.Fatalf("Change to dir %v failed: %v", tempExtractDir, err)
   173  	}
   174  
   175  	want := &bytes.Buffer{}
   176  	err = run([]string{"i"}, archiveFile, want, true, "newc")
   177  
   178  	if err != nil {
   179  		t.Fatalf("Extraction failed:\n%v\n%v\n", want, err)
   180  	}
   181  
   182  }