gitlab.com/apertussolutions/u-root@v7.0.0+incompatible/pkg/cpio/fs_plan9.go (about)

     1  // Copyright 2013-2017 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 cpio
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"path/filepath"
    12  	"time"
    13  
    14  	"github.com/u-root/u-root/pkg/ls"
    15  	"github.com/u-root/u-root/pkg/uio"
    16  )
    17  
    18  // A Recorder is a structure that contains variables used to calculate
    19  // file parameters such as inode numbers for a CPIO file. The life-time
    20  // of a Record structure is meant to be the same as the construction of a
    21  // single CPIO archive. Do not reuse between CPIOs if you don't know what
    22  // you're doing.
    23  type Recorder struct {
    24  	inumber uint64
    25  }
    26  
    27  var modeMap = map[uint64]os.FileMode{
    28  	modeFile: 0,
    29  	modeDir:  os.ModeDir,
    30  }
    31  
    32  func unixModeToFileType(m uint64) (os.FileMode, error) {
    33  	if t, ok := modeMap[m&modeTypeMask]; ok {
    34  		return t, nil
    35  	}
    36  	return 0, fmt.Errorf("invalid file type %#o", m&modeTypeMask)
    37  }
    38  
    39  func toFileMode(r Record) os.FileMode {
    40  	return os.FileMode(perm(r))
    41  }
    42  
    43  // setModes sets the modes.
    44  func setModes(r Record) error {
    45  	if err := os.Chmod(r.Name, toFileMode(r)&os.ModePerm); err != nil {
    46  		return err
    47  	}
    48  	return nil
    49  }
    50  
    51  func perm(r Record) uint32 {
    52  	return uint32(r.Mode) & modePermissions
    53  }
    54  
    55  func dev(r Record) int {
    56  	return int(r.Rmajor<<8 | r.Rminor)
    57  }
    58  
    59  // CreateFile creates a local file for f relative to the current working
    60  // directory.
    61  //
    62  // CreateFile will attempt to set all metadata for the file, including
    63  // ownership, times, and permissions.
    64  func CreateFile(f Record) error {
    65  	return CreateFileInRoot(f, ".", true)
    66  }
    67  
    68  // CreateFileInRoot creates a local file for f relative to rootDir.
    69  func CreateFileInRoot(f Record, rootDir string, forcePriv bool) error {
    70  	m, err := unixModeToFileType(f.Mode)
    71  	if err != nil {
    72  		return err
    73  	}
    74  
    75  	f.Name = filepath.Clean(filepath.Join(rootDir, f.Name))
    76  	dir := filepath.Dir(f.Name)
    77  	// The problem: many cpio archives do not specify the directories and
    78  	// hence the permissions. They just specify the whole path.  In order
    79  	// to create files in these directories, we have to make them at least
    80  	// mode 755.
    81  	if _, err := os.Stat(dir); os.IsNotExist(err) && len(dir) > 0 {
    82  		if err := os.MkdirAll(dir, 0755); err != nil {
    83  			return fmt.Errorf("CreateFileInRoot %q: %v", f.Name, err)
    84  		}
    85  	}
    86  
    87  	switch m {
    88  	case os.FileMode(0):
    89  		nf, err := os.Create(f.Name)
    90  		if err != nil {
    91  			return err
    92  		}
    93  		defer nf.Close()
    94  		if _, err := io.Copy(nf, uio.Reader(f)); err != nil {
    95  			return err
    96  		}
    97  
    98  	case os.ModeDir:
    99  		if err := os.MkdirAll(f.Name, toFileMode(f)); err != nil {
   100  			return err
   101  		}
   102  
   103  	default:
   104  		return fmt.Errorf("%v: Unknown type %#o", f.Name, m)
   105  	}
   106  
   107  	if err := setModes(f); err != nil && forcePriv {
   108  		return err
   109  	}
   110  	return nil
   111  }
   112  
   113  func (r *Recorder) inode(i Info) Info {
   114  	i.Ino = r.inumber
   115  	r.inumber++
   116  	return i
   117  }
   118  
   119  // GetRecord returns a cpio Record for the given path on the local file system.
   120  //
   121  // GetRecord does not follow symlinks. If path is a symlink, the record
   122  // returned will reflect that symlink.
   123  func (r *Recorder) GetRecord(path string) (Record, error) {
   124  	fi, err := os.Stat(path)
   125  	if err != nil {
   126  		return Record{}, err
   127  	}
   128  	info := r.inode(sysInfo(path, fi))
   129  	switch fi.Mode() & os.ModeType {
   130  	case 0: // Regular file.
   131  		return Record{Info: info, ReaderAt: uio.NewLazyFile(path)}, nil
   132  	default:
   133  		return StaticRecord(nil, info), nil
   134  	}
   135  }
   136  
   137  // NewRecorder creates a new Recorder.
   138  //
   139  // A recorder is a structure that contains variables used to calculate
   140  // file parameters such as inode numbers for a CPIO file. The life-time
   141  // of a Record structure is meant to be the same as the construction of a
   142  // single CPIO archive. Do not reuse between CPIOs if you don't know what
   143  // you're doing.
   144  func NewRecorder() *Recorder {
   145  	return &Recorder{}
   146  }
   147  
   148  // LSInfoFromRecord converts a Record to be usable with the ls package for
   149  // listing files.
   150  func LSInfoFromRecord(rec Record) ls.FileInfo {
   151  	mode := modeFromLinux(rec.Mode)
   152  	return ls.FileInfo{
   153  		Name:  rec.Name,
   154  		Mode:  mode,
   155  		UID:   fmt.Sprint("%d", rec.UID),
   156  		Size:  int64(rec.FileSize),
   157  		MTime: time.Unix(int64(rec.MTime), 0).UTC(),
   158  	}
   159  }