github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/pkg/cpio/fs_unix.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  	"io/ioutil"
    11  	"os"
    12  	"path/filepath"
    13  	"syscall"
    14  
    15  	"github.com/u-root/u-root/pkg/uio"
    16  	"golang.org/x/sys/unix"
    17  )
    18  
    19  // Linux mode_t bits.
    20  const (
    21  	modeTypeMask    = 0170000
    22  	modeSocket      = 0140000
    23  	modeSymlink     = 0120000
    24  	modeFile        = 0100000
    25  	modeBlock       = 0060000
    26  	modeDir         = 0040000
    27  	modeChar        = 0020000
    28  	modeFIFO        = 0010000
    29  	modeSUID        = 0004000
    30  	modeSGID        = 0002000
    31  	modeSticky      = 0001000
    32  	modePermissions = 0000777
    33  )
    34  
    35  var modeMap = map[uint64]os.FileMode{
    36  	modeSocket:  os.ModeSocket,
    37  	modeSymlink: os.ModeSymlink,
    38  	modeFile:    0,
    39  	modeBlock:   os.ModeDevice,
    40  	modeDir:     os.ModeDir,
    41  	modeChar:    os.ModeCharDevice,
    42  	modeFIFO:    os.ModeNamedPipe,
    43  }
    44  
    45  // setModes sets the modes, changing the easy ones first and the harder ones last.
    46  // In this way, we set as much as we can before bailing out.
    47  // N.B.: if you set something with S_ISUID, then change the owner,
    48  // the kernel (Linux, OSX, etc.) clears S_ISUID (a good idea). So, the simple thing:
    49  // Do the chmod operations in order of difficulty, and give up as soon as we fail.
    50  // Set the basic permissions -- not including SUID, GUID, etc.
    51  // Set the times
    52  // Set the owner
    53  // Set ALL the mode bits, in case we need to do SUID, etc. If we could not
    54  // set the owner, we won't even try this operation of course, so we won't
    55  // have SUID incorrectly set for the wrong user.
    56  func setModes(r Record) error {
    57  	if err := os.Chmod(r.Name, toFileMode(r)&os.ModePerm); err != nil {
    58  		return err
    59  	}
    60  	/*if err := os.Chtimes(r.Name, time.Time{}, time.Unix(int64(r.MTime), 0)); err != nil {
    61  		return err
    62  	}*/
    63  	if err := os.Chown(r.Name, int(r.UID), int(r.GID)); err != nil {
    64  		return err
    65  	}
    66  	if err := os.Chmod(r.Name, toFileMode(r)); err != nil {
    67  		return err
    68  	}
    69  	return nil
    70  }
    71  
    72  func toFileMode(r Record) os.FileMode {
    73  	m := os.FileMode(perm(r))
    74  	if r.Mode&unix.S_ISUID != 0 {
    75  		m |= os.ModeSetuid
    76  	}
    77  	if r.Mode&unix.S_ISGID != 0 {
    78  		m |= os.ModeSetgid
    79  	}
    80  	if r.Mode&unix.S_ISVTX != 0 {
    81  		m |= os.ModeSticky
    82  	}
    83  	return m
    84  }
    85  
    86  func perm(r Record) uint32 {
    87  	return uint32(r.Mode) & modePermissions
    88  }
    89  
    90  func dev(r Record) int {
    91  	return int(r.Rmajor<<8 | r.Rminor)
    92  }
    93  
    94  func linuxModeToFileType(m uint64) (os.FileMode, error) {
    95  	if t, ok := modeMap[m&modeTypeMask]; ok {
    96  		return t, nil
    97  	}
    98  	return 0, fmt.Errorf("invalid file type %#o", m&modeTypeMask)
    99  }
   100  
   101  // CreateFile creates a local file for f relative to the current working
   102  // directory.
   103  //
   104  // CreateFile will attempt to set all metadata for the file, including
   105  // ownership, times, and permissions.
   106  func CreateFile(f Record) error {
   107  	return CreateFileInRoot(f, ".", true)
   108  }
   109  
   110  // CreateFileInRoot creates a local file for f relative to rootDir.
   111  //
   112  // It will attempt to set all metadata for the file, including ownership,
   113  // times, and permissions. If these fail, it only returns an error if
   114  // forcePriv is true.
   115  //
   116  // Block and char device creation will only return error if forcePriv is true.
   117  func CreateFileInRoot(f Record, rootDir string, forcePriv bool) error {
   118  	m, err := linuxModeToFileType(f.Mode)
   119  	if err != nil {
   120  		return err
   121  	}
   122  
   123  	f.Name = filepath.Clean(filepath.Join(rootDir, f.Name))
   124  	dir := filepath.Dir(f.Name)
   125  	// The problem: many cpio archives do not specify the directories and
   126  	// hence the permissions. They just specify the whole path.  In order
   127  	// to create files in these directories, we have to make them at least
   128  	// mode 755.
   129  	if _, err := os.Stat(dir); os.IsNotExist(err) && len(dir) > 0 {
   130  		if err := os.MkdirAll(dir, 0755); err != nil {
   131  			return fmt.Errorf("CreateFileInRoot %q: %v", f.Name, err)
   132  		}
   133  	}
   134  
   135  	switch m {
   136  	case os.ModeSocket, os.ModeNamedPipe:
   137  		return fmt.Errorf("%q: type %v: cannot create IPC endpoints", f.Name, m)
   138  
   139  	case os.ModeSymlink:
   140  		content, err := ioutil.ReadAll(uio.Reader(f))
   141  		if err != nil {
   142  			return err
   143  		}
   144  		return os.Symlink(string(content), f.Name)
   145  
   146  	case os.FileMode(0):
   147  		nf, err := os.Create(f.Name)
   148  		if err != nil {
   149  			return err
   150  		}
   151  		defer nf.Close()
   152  		if _, err := io.Copy(nf, uio.Reader(f)); err != nil {
   153  			return err
   154  		}
   155  
   156  	case os.ModeDir:
   157  		if err := os.MkdirAll(f.Name, toFileMode(f)); err != nil {
   158  			return err
   159  		}
   160  
   161  	case os.ModeDevice:
   162  		if err := mknod(f.Name, perm(f)|syscall.S_IFBLK, dev(f)); err != nil && forcePriv {
   163  			return err
   164  		}
   165  
   166  	case os.ModeCharDevice:
   167  		if err := mknod(f.Name, perm(f)|syscall.S_IFCHR, dev(f)); err != nil && forcePriv {
   168  			return err
   169  		}
   170  
   171  	default:
   172  		return fmt.Errorf("%v: Unknown type %#o", f.Name, m)
   173  	}
   174  
   175  	if err := setModes(f); err != nil && forcePriv {
   176  		return err
   177  	}
   178  	return nil
   179  }
   180  
   181  // Inumber and devnumbers are unique to Unix-like
   182  // operating systems. You can not uniquely disambiguate a file in a
   183  // Unix system with just an inumber, you need a device number too.
   184  // To handle hard links (unique to Unix) we need to figure out if a
   185  // given file has been seen before. To do this we see if a file has the
   186  // same [dev,ino] tuple as one we have seen. If so, we won't bother
   187  // reading it in.
   188  
   189  type devInode struct {
   190  	dev uint64
   191  	ino uint64
   192  }
   193  
   194  // A Recorder is a structure that contains variables used to calculate
   195  // file parameters such as inode numbers for a CPIO file. The life-time
   196  // of a Record structure is meant to be the same as the construction of a
   197  // single CPIO archive. Do not reuse between CPIOs if you don't know what
   198  // you're doing.
   199  type Recorder struct {
   200  	inodeMap map[devInode]Info
   201  	inumber  uint64
   202  }
   203  
   204  // Certain elements of the file can not be set by cpio:
   205  // the Inode #
   206  // the Dev
   207  // maintaining these elements leaves us with a non-reproducible
   208  // output stream. In this function, we figure out what inumber
   209  // we need to use, and clear out anything we can.
   210  // We always zero the Dev.
   211  // We try to find the matching inode. If found, we use its inumber.
   212  // If not, we get a new inumber for it and save the inode away.
   213  // This eliminates two of the messier parts of creating reproducible
   214  // output streams.
   215  func (r *Recorder) inode(i Info) (Info, bool) {
   216  	d := devInode{dev: i.Dev, ino: i.Ino}
   217  	i.Dev = 0
   218  
   219  	if d, ok := r.inodeMap[d]; ok {
   220  		i.Ino = d.Ino
   221  		return i, true
   222  	}
   223  
   224  	i.Ino = r.inumber
   225  	r.inumber++
   226  	r.inodeMap[d] = i
   227  
   228  	return i, false
   229  }
   230  
   231  // GetRecord returns a cpio Record for the given path on the local file system.
   232  //
   233  // GetRecord does not follow symlinks. If path is a symlink, the record
   234  // returned will reflect that symlink.
   235  func (r *Recorder) GetRecord(path string) (Record, error) {
   236  	fi, err := os.Lstat(path)
   237  	if err != nil {
   238  		return Record{}, err
   239  	}
   240  
   241  	sys := fi.Sys().(*syscall.Stat_t)
   242  	info, done := r.inode(sysInfo(path, sys))
   243  
   244  	switch fi.Mode() & os.ModeType {
   245  	case 0: // Regular file.
   246  		if done {
   247  			return Record{Info: info}, nil
   248  		}
   249  		return Record{Info: info, ReaderAt: uio.NewLazyFile(path)}, nil
   250  
   251  	case os.ModeSymlink:
   252  		linkname, err := os.Readlink(path)
   253  		if err != nil {
   254  			return Record{}, err
   255  		}
   256  		return StaticRecord([]byte(linkname), info), nil
   257  
   258  	default:
   259  		return StaticRecord(nil, info), nil
   260  	}
   261  }
   262  
   263  // NewRecorder creates a new Recorder.
   264  //
   265  // A recorder is a structure that contains variables used to calculate
   266  // file parameters such as inode numbers for a CPIO file. The life-time
   267  // of a Record structure is meant to be the same as the construction of a
   268  // single CPIO archive. Do not reuse between CPIOs if you don't know what
   269  // you're doing.
   270  func NewRecorder() *Recorder {
   271  	return &Recorder{make(map[devInode]Info), 0}
   272  }