func (t *TarArchiveProvider) ExtractReader(dst string, src io.Reader) error { tr := tar.NewReader(src) for { var h *tar.Header h, err := tr.Next() if err == io.EOF { break } if err != nil { return err } fp := filepath.Join(dst, h.Name) if h.FileInfo().IsDir() { os.MkdirAll(fp, 0755) continue } os.MkdirAll(filepath.Dir(fp), 0755) f, err := os.Create(fp) if err != nil { return err } _, err = io.Copy(f, tr) f.Close() if err != nil { return err } os.Chmod(fp, os.FileMode(h.Mode)) } return nil }
// Process impl func (p *ArchiveExtract) Process(hdr *tar.Header, r io.Reader) (*tar.Header, io.Reader, error) { // make sure we have our tempdir if p.workingDir == "" { t, err := ioutil.TempDir(p.tempDir, "tar-") if err != nil { return hdr, r, err } p.workingDir = t } // If a directory make it and continue fpath := filepath.Join(p.workingDir, hdr.Name) if hdr.FileInfo().IsDir() { err := os.MkdirAll(fpath, 0755) return hdr, r, err } // Extract the file! file, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE, hdr.FileInfo().Mode()) if err != nil { return hdr, r, err } defer file.Close() _, err = io.Copy(file, r) if err != nil { return hdr, r, err } return hdr, r, nil }
func extractFile(dir string, header *tar.Header, tarReader io.Reader) error { path := filepath.Join(dir, header.Name) glog.V(3).Infof("Creating %s", path) file, err := os.Create(path) // The file times need to be modified after it's been closed thus this function // is deferred after the file close (LIFO order for defer) defer os.Chtimes(path, time.Now(), header.FileInfo().ModTime()) defer file.Close() if err != nil { return err } glog.V(3).Infof("Extracting/writing %s", path) written, err := io.Copy(file, tarReader) if err != nil { return err } if written != header.Size { return fmt.Errorf("Wrote %d bytes, expected to write %d", written, header.Size) } if runtime.GOOS != "windows" { // Skip chmod if on windows OS return file.Chmod(header.FileInfo().Mode()) } return nil }
func extractTarArchiveFile(header *tar.Header, dest string, input io.Reader) error { filePath := filepath.Join(dest, header.Name) fileInfo := header.FileInfo() if fileInfo.IsDir() { err := os.MkdirAll(filePath, fileInfo.Mode()) if err != nil { return err } } else { err := os.MkdirAll(filepath.Dir(filePath), 0755) if err != nil { return err } if fileInfo.Mode()&os.ModeSymlink != 0 { return os.Symlink(header.Linkname, filePath) } fileCopy, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fileInfo.Mode()) if err != nil { return err } defer fileCopy.Close() _, err = io.Copy(fileCopy, input) if err != nil { return err } } return nil }
func extractFile(archive *tar.Reader, hdr *tar.Header, path string) error { file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, hdr.FileInfo().Mode()) if err != nil { return err } defer file.Close() _, err = io.Copy(file, archive) return err }
// Process impl func (p *ArchiveCheckEmpty) Process(hdr *tar.Header, r io.Reader) (*tar.Header, io.Reader, error) { if p.hasFiles { return hdr, r, nil } if !hdr.FileInfo().IsDir() { p.hasFiles = true } return hdr, r, nil }
func (m *SymlinkWebotsManager) extractFile(v WebotsVersion, h *tar.Header, r io.Reader) error { dest := strings.TrimPrefix(h.Name, "webots/") dest = path.Join(m.workpath, v.String(), dest) if dest == path.Join(m.workpath, v.String()) { return nil } switch h.Typeflag { case tar.TypeReg, tar.TypeRegA: destDir := path.Dir(dest) err := os.MkdirAll(destDir, 0775) if err != nil { return err } f, err := os.Create(dest) if err != nil { return err } _, err = io.Copy(f, r) if err != nil { return err } case tar.TypeDir: err := os.MkdirAll(dest, 0775) if err != nil { return err } case tar.TypeSymlink: destDir := path.Dir(dest) err := os.MkdirAll(destDir, 0775) if err != nil { return err } err = os.Symlink(h.Linkname, dest) if err != nil { return err } return nil default: return fmt.Errorf("Internal error, cannot handle file %s", h.Name) } err := os.Chtimes(dest, time.Now(), h.FileInfo().ModTime()) if err != nil { return err } err = os.Chmod(dest, h.FileInfo().Mode()) if err != nil { return err } return nil }
func untar(tr *tar.Reader, header *tar.Header, dir string) error { switch header.Typeflag { case tar.TypeDir: return os.MkdirAll(filepath.Join(dir, header.Name), 0700) case tar.TypeReg, tar.TypeRegA: return writeFile(filepath.Join(dir, header.Name), tr, header.FileInfo().Mode()) case tar.TypeSymlink: return writeSymlink(filepath.Join(dir, header.Name), header.Linkname) default: return fmt.Errorf("%s has unknown type %v", header.Name, header.Typeflag) } }
func extractTarArchiveFile(header *tar.Header, dest string, input io.Reader) error { home := os.Getenv("HOME") // check if dir has a leading hash maybeHash := filepath.Dir(header.Name) _, err := hex.DecodeString(maybeHash) isHash := err == nil filePath := filepath.Join(dest, header.Name) if isHash { filePath = filepath.Join(home, ".dpm", "workspace", header.Name) } fileInfo := header.FileInfo() if fileInfo.IsDir() { err := os.MkdirAll(filePath, fileInfo.Mode()) if err != nil { return err } } else { // already exist if isHash { if _, err := os.Stat(filePath); err == nil { return nil } } err := os.MkdirAll(filepath.Dir(filePath), 0755) if err != nil { return err } if fileInfo.Mode()&os.ModeSymlink != 0 { return os.Symlink(header.Linkname, filePath) } fileCopy, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, fileInfo.Mode()) if err != nil { return err } defer fileCopy.Close() _, err = io.Copy(fileCopy, input) if err != nil { return err } } return nil }
func (d *dirTarReceiver) OnEntry(tarHeader *tar.Header, tarFileReader io.Reader) error { relativePath := tarHeader.Name if tarHeader.FileInfo().IsDir() { fullDestPath := filepath.Join(d.dir, relativePath) defer os.Chtimes(fullDestPath, tarHeader.AccessTime, tarHeader.ModTime) return os.MkdirAll(fullDestPath, os.FileMode(tarHeader.Mode)) } else { fullDestPath := filepath.Join(d.dir, relativePath) if val, ok := tarHeader.Xattrs["SINGLE_FILE_ONLY"]; ok && val == "1" { fullDestPath = d.dir } parentDir := filepath.Dir(fullDestPath) err := os.MkdirAll(parentDir, os.FileMode(tarHeader.Mode)) if err != nil { return fmt.Errorf("Unable to MkDirAll '%s', error: %s", parentDir, err.Error()) } file, err := os.OpenFile(fullDestPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(tarHeader.Mode)) if err != nil { return fmt.Errorf("Unable to open file '%s', error: %s", fullDestPath, err.Error()) } defer func() { file.Close() os.Chtimes(fullDestPath, tarHeader.AccessTime, tarHeader.ModTime) }() _, err = io_throttler.CopyThrottled(io_throttler.DefaultIOThrottlingBandwidth, file, tarFileReader) if err != nil { return fmt.Errorf("Unable to copy stream to file '%s', error: %s", fullDestPath, err.Error()) } return nil } }
func openTar(za *ZipAssets, tr *tar.Reader) (err error) { var ( hdr *tar.Header fc filecontent ) for { if hdr, err = tr.Next(); err == io.EOF { break } if err != nil { return } if fc.content, err = ioutil.ReadAll(tr); err != nil { return } fc.name = hdr.Name fc.lastModified = hdr.ModTime fc.isDir = hdr.FileInfo().IsDir() za.files[hdr.Name] = &fc } return }
// WriteHeader changes the mode of files and directories inline as a tarfile is // being written func (a ChmodAdapter) WriteHeader(hdr *tar.Header) error { if hdr.FileInfo().Mode()&os.ModeSymlink == 0 { newMode := hdr.Mode &^ 0777 if hdr.FileInfo().IsDir() { newMode |= a.NewDirMode } else if hdr.FileInfo().Mode()&0010 != 0 { // S_IXUSR newMode |= a.NewExecFileMode } else { newMode |= a.NewFileMode } hdr.Mode = newMode } return a.Writer.WriteHeader(hdr) }
// extractFile extracts zip.File to file system. func extractFile(f *tar.Header, tr *tar.Reader, destPath string) error { filePath := path.Join(destPath, f.Name) os.MkdirAll(path.Dir(filePath), os.ModePerm) fw, err := os.Create(filePath) if err != nil { return err } defer fw.Close() if _, err = io.Copy(fw, tr); err != nil { return err } // Skip symbolic links. if f.FileInfo().Mode()&os.ModeSymlink != 0 { return nil } // Set back file information. if err = os.Chtimes(filePath, f.FileInfo().ModTime(), f.FileInfo().ModTime()); err != nil { return err } return os.Chmod(filePath, f.FileInfo().Mode()) }
// extractFile extracts the file described by hdr from the given tarball into // the target directory. // If overwrite is true, existing files will be overwritten. func extractFile(tr *tar.Reader, target string, hdr *tar.Header, overwrite bool, editor FilePermissionsEditor) error { p := filepath.Join(target, hdr.Name) fi := hdr.FileInfo() typ := hdr.Typeflag if overwrite { info, err := os.Lstat(p) switch { case os.IsNotExist(err): case err == nil: // If the old and new paths are both dirs do nothing or // RemoveAll will remove all dir's contents if !info.IsDir() || typ != tar.TypeDir { err := os.RemoveAll(p) if err != nil { return err } } default: return err } } // Create parent dir if it doesn't exist if err := os.MkdirAll(filepath.Dir(p), DEFAULT_DIR_MODE); err != nil { return err } switch { case typ == tar.TypeReg || typ == tar.TypeRegA: f, err := os.OpenFile(p, os.O_CREATE|os.O_RDWR, fi.Mode()) if err != nil { return err } _, err = io.Copy(f, tr) if err != nil { f.Close() return err } f.Close() case typ == tar.TypeDir: if err := os.MkdirAll(p, fi.Mode()); err != nil { return err } dir, err := os.Open(p) if err != nil { return err } if err := dir.Chmod(fi.Mode()); err != nil { dir.Close() return err } dir.Close() case typ == tar.TypeLink: dest := filepath.Join(target, hdr.Linkname) if err := os.Link(dest, p); err != nil { return err } case typ == tar.TypeSymlink: if err := os.Symlink(hdr.Linkname, p); err != nil { return err } case typ == tar.TypeChar: dev := device.Makedev(uint(hdr.Devmajor), uint(hdr.Devminor)) mode := uint32(fi.Mode()) | syscall.S_IFCHR if err := syscall.Mknod(p, mode, int(dev)); err != nil { return err } case typ == tar.TypeBlock: dev := device.Makedev(uint(hdr.Devmajor), uint(hdr.Devminor)) mode := uint32(fi.Mode()) | syscall.S_IFBLK if err := syscall.Mknod(p, mode, int(dev)); err != nil { return err } case typ == tar.TypeFifo: if err := syscall.Mkfifo(p, uint32(fi.Mode())); err != nil { return err } // TODO(jonboulle): implement other modes default: return fmt.Errorf("unsupported type: %v", typ) } if editor != nil { if err := editor(p, hdr.Uid, hdr.Gid, hdr.Typeflag, fi); err != nil { return err } } // Restore entry atime and mtime. // Use special function LUtimesNano not available on go's syscall package because we // have to restore symlink's times and not the referenced file times. ts := HdrToTimespec(hdr) if hdr.Typeflag != tar.TypeSymlink { if err := syscall.UtimesNano(p, ts); err != nil { return err } } else { if err := fileutil.LUtimesNano(p, ts); err != nil && err != ErrNotSupportedPlatform { return err } } return nil }
func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool) error { // hdr.Mode is in linux format, which we can use for sycalls, // but for os.Foo() calls we need the mode converted to os.FileMode, // so use hdrInfo.Mode() (they differ for e.g. setuid bits) hdrInfo := hdr.FileInfo() switch hdr.Typeflag { case tar.TypeDir: // Create directory unless it exists as a directory already. // In that case we just want to merge the two if fi, err := os.Lstat(path); !(err == nil && fi.IsDir()) { if err := os.Mkdir(path, hdrInfo.Mode()); err != nil { return err } } case tar.TypeReg, tar.TypeRegA: // Source is regular file file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode()) if err != nil { return err } if _, err := io.Copy(file, reader); err != nil { file.Close() return err } file.Close() case tar.TypeBlock, tar.TypeChar, tar.TypeFifo: mode := uint32(hdr.Mode & 07777) switch hdr.Typeflag { case tar.TypeBlock: mode |= syscall.S_IFBLK case tar.TypeChar: mode |= syscall.S_IFCHR case tar.TypeFifo: mode |= syscall.S_IFIFO } if err := system.Mknod(path, mode, int(system.Mkdev(hdr.Devmajor, hdr.Devminor))); err != nil { return err } case tar.TypeLink: targetPath := filepath.Join(extractDir, hdr.Linkname) // check for hardlink breakout if !strings.HasPrefix(targetPath, extractDir) { return breakoutError(fmt.Errorf("invalid hardlink %q -> %q", targetPath, hdr.Linkname)) } if err := os.Link(targetPath, path); err != nil { return err } case tar.TypeSymlink: // path -> hdr.Linkname = targetPath // e.g. /extractDir/path/to/symlink -> ../2/file = /extractDir/path/2/file targetPath := filepath.Join(filepath.Dir(path), hdr.Linkname) // the reason we don't need to check symlinks in the path (with FollowSymlinkInScope) is because // that symlink would first have to be created, which would be caught earlier, at this very check: if !strings.HasPrefix(targetPath, extractDir) { return breakoutError(fmt.Errorf("invalid symlink %q -> %q", path, hdr.Linkname)) } if err := os.Symlink(hdr.Linkname, path); err != nil { return err } case tar.TypeXGlobalHeader: logrus.Debugf("PAX Global Extended Headers found and ignored") return nil default: return fmt.Errorf("Unhandled tar header type %d\n", hdr.Typeflag) } if err := os.Lchown(path, hdr.Uid, hdr.Gid); err != nil && Lchown { return err } for key, value := range hdr.Xattrs { if err := system.Lsetxattr(path, key, []byte(value), 0); err != nil { return err } } // There is no LChmod, so ignore mode for symlink. Also, this // must happen after chown, as that can modify the file mode if hdr.Typeflag == tar.TypeLink { if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) { if err := os.Chmod(path, hdrInfo.Mode()); err != nil { return err } } } else if hdr.Typeflag != tar.TypeSymlink { if err := os.Chmod(path, hdrInfo.Mode()); err != nil { return err } } ts := []syscall.Timespec{timeToTimespec(hdr.AccessTime), timeToTimespec(hdr.ModTime)} // syscall.UtimesNano doesn't support a NOFOLLOW flag atm, and if hdr.Typeflag == tar.TypeLink { if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) { if err := system.UtimesNano(path, ts); err != nil && err != system.ErrNotSupportedPlatform { return err } } } else if hdr.Typeflag != tar.TypeSymlink { if err := system.UtimesNano(path, ts); err != nil && err != system.ErrNotSupportedPlatform { return err } } else { if err := system.LUtimesNano(path, ts); err != nil && err != system.ErrNotSupportedPlatform { return err } } return nil }
func unpackFile( path string, targetDir string, header *tar.Header, reader *bufio.Reader) error { info := header.FileInfo() switch header.Typeflag { case tar.TypeDir: // Create dir if it does not exist. Do nothing if it already exists // (we want to merge). existingInfo, err := os.Lstat(path) if err != nil || !existingInfo.IsDir() { err = os.Mkdir(path, info.Mode()) if err != nil { return err } } case tar.TypeReg, tar.TypeRegA: // Regular file. file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, info.Mode()) if err != nil { return err } _, err = io.Copy(file, reader) if err != nil { file.Close() return err } file.Close() case tar.TypeBlock, tar.TypeChar, tar.TypeFifo, tar.TypeXGlobalHeader: // Not supported. Skip. case tar.TypeLink: // Hard link. linkPath := filepath.Join(targetDir, header.Linkname) if !strings.HasPrefix(linkPath, targetDir) { return fmt.Errorf("Invalid hard link pointing outside target dir") } err := os.Link(linkPath, path) if err != nil { return err } case tar.TypeSymlink: linkPath := filepath.Join(filepath.Dir(path), header.Linkname) if !strings.HasPrefix(linkPath, targetDir) { return fmt.Errorf("Invalid sym link pointing outside target dir") } err := os.Symlink(linkPath, path) if err != nil { return err } default: return fmt.Errorf("Unhandled tar header type") } for key, value := range header.Xattrs { err := setxattr(path, key, []byte(value), 0) if err != nil { return err } } return nil }
func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *TarChownOptions, inUserns bool) error { // hdr.Mode is in linux format, which we can use for sycalls, // but for os.Foo() calls we need the mode converted to os.FileMode, // so use hdrInfo.Mode() (they differ for e.g. setuid bits) hdrInfo := hdr.FileInfo() switch hdr.Typeflag { case tar.TypeDir: // Create directory unless it exists as a directory already. // In that case we just want to merge the two if fi, err := os.Lstat(path); !(err == nil && fi.IsDir()) { if err := os.Mkdir(path, hdrInfo.Mode()); err != nil { return err } } case tar.TypeReg, tar.TypeRegA: // Source is regular file file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, hdrInfo.Mode()) if err != nil { return err } if _, err := io.Copy(file, reader); err != nil { file.Close() return err } file.Close() case tar.TypeBlock, tar.TypeChar: if inUserns { // cannot create devices in a userns return nil } // Handle this is an OS-specific way if err := handleTarTypeBlockCharFifo(hdr, path); err != nil { return err } case tar.TypeFifo: // Handle this is an OS-specific way if err := handleTarTypeBlockCharFifo(hdr, path); err != nil { return err } case tar.TypeLink: targetPath := filepath.Join(extractDir, hdr.Linkname) // check for hardlink breakout if !strings.HasPrefix(targetPath, extractDir) { return breakoutError(fmt.Errorf("invalid hardlink %q -> %q", targetPath, hdr.Linkname)) } if err := os.Link(targetPath, path); err != nil { return err } case tar.TypeSymlink: // path -> hdr.Linkname = targetPath // e.g. /extractDir/path/to/symlink -> ../2/file = /extractDir/path/2/file targetPath := filepath.Join(filepath.Dir(path), hdr.Linkname) // the reason we don't need to check symlinks in the path (with FollowSymlinkInScope) is because // that symlink would first have to be created, which would be caught earlier, at this very check: if !strings.HasPrefix(targetPath, extractDir) { return breakoutError(fmt.Errorf("invalid symlink %q -> %q", path, hdr.Linkname)) } if err := os.Symlink(hdr.Linkname, path); err != nil { return err } case tar.TypeXGlobalHeader: logrus.Debug("PAX Global Extended Headers found and ignored") return nil default: return fmt.Errorf("Unhandled tar header type %d\n", hdr.Typeflag) } // Lchown is not supported on Windows. if Lchown && runtime.GOOS != "windows" { if chownOpts == nil { chownOpts = &TarChownOptions{UID: hdr.Uid, GID: hdr.Gid} } if err := os.Lchown(path, chownOpts.UID, chownOpts.GID); err != nil { return err } } var errors []string for key, value := range hdr.Xattrs { if err := system.Lsetxattr(path, key, []byte(value), 0); err != nil { if err == syscall.ENOTSUP { // We ignore errors here because not all graphdrivers support // xattrs *cough* old versions of AUFS *cough*. However only // ENOTSUP should be emitted in that case, otherwise we still // bail. errors = append(errors, err.Error()) continue } return err } } if len(errors) > 0 { logrus.WithFields(logrus.Fields{ "errors": errors, }).Warn("ignored xattrs in archive: underlying filesystem doesn't support them") } // There is no LChmod, so ignore mode for symlink. Also, this // must happen after chown, as that can modify the file mode if err := handleLChmod(hdr, path, hdrInfo); err != nil { return err } aTime := hdr.AccessTime if aTime.Before(hdr.ModTime) { // Last access time should never be before last modified time. aTime = hdr.ModTime } // system.Chtimes doesn't support a NOFOLLOW flag atm if hdr.Typeflag == tar.TypeLink { if fi, err := os.Lstat(hdr.Linkname); err == nil && (fi.Mode()&os.ModeSymlink == 0) { if err := system.Chtimes(path, aTime, hdr.ModTime); err != nil { return err } } } else if hdr.Typeflag != tar.TypeSymlink { if err := system.Chtimes(path, aTime, hdr.ModTime); err != nil { return err } } else { ts := []syscall.Timespec{timeToTimespec(aTime), timeToTimespec(hdr.ModTime)} if err := system.LUtimesNano(path, ts); err != nil && err != system.ErrNotSupportedPlatform { return err } } return nil }
// Processes a single header/body combination from the tar // archive being processed in Extract() above. func (u *Untar) processEntry(header *tar.Header) error { // Check the security of the name being given to us by tar. // If the name contains any bad things then we force // an error in order to protect ourselves. if err := checkName(header.Name); err != nil { return err } // Ensure that the file is allowed against the current whitelist, if one is // specified. if !u.checkEntryAgainstWhitelist(header) { return nil } name := filepath.Join(u.target, header.Name) // resolve the destination and then reset the name based on the resolution destDir, err := u.resolveDestination(filepath.Dir(name)) name = filepath.Join(destDir, filepath.Base(name)) if err != nil { return err } // look at the type to see how we want to remove existing entries switch { case header.Typeflag == tar.TypeDir: // if we are extracting a directory, we want to see if the directory // already exists... if it exists but isn't a directory, we need // to remove it fi, _ := os.Stat(name) if fi != nil { if !fi.IsDir() { os.RemoveAll(name) } } default: os.RemoveAll(name) } // handle individual types switch { case header.Typeflag == tar.TypeDir: // Handle directories // don't return error if it already exists mode := os.FileMode(0755) if u.PreservePermissions { mode = header.FileInfo().Mode() | u.IncludedPermissionMask } // create the directory err := os.MkdirAll(name, mode) if err != nil { return err } // Perform a chmod after creation to ensure modes are applied directly, // regardless of umask. if err := os.Chmod(name, mode); err != nil { return err } case header.Typeflag == tar.TypeSymlink: // Handle symlinks err := checkLinkName(header.Linkname, name, u.target) if err != nil { return err } // have seen links to themselves if name == header.Linkname { break } // make the link if err := os.Symlink(header.Linkname, name); err != nil { return err } case header.Typeflag == tar.TypeLink: // handle creation of hard links if err := checkLinkName(header.Linkname, name, u.target); err != nil { return err } // find the full path, need to ensure it exists link := filepath.Join(u.target, header.Linkname) // do the link... no permissions or owners, those carry over if err := os.Link(link, name); err != nil { return err } case header.Typeflag == tar.TypeReg || header.Typeflag == tar.TypeRegA: flags := os.O_WRONLY | os.O_CREATE | os.O_EXCL // determine the mode to use mode := os.FileMode(0644) if u.PreservePermissions { mode = header.FileInfo().Mode() | u.IncludedPermissionMask } // open the file f, err := os.OpenFile(name, flags, mode) if err != nil { return err } defer f.Close() // Perform a chmod after creation to ensure modes are applied directly, // regardless of umask. if err := os.Chmod(name, mode); err != nil { return err } // SETUID/SETGID needs to be defered... // The standard chown call is after handling the files, since we want to // just have it one place, and after the file exists. However, chown // will clear the setuid/setgid bit on a file. if header.Mode&c_ISUID != 0 { defer lazyChmod(name, os.ModeSetuid) } if header.Mode&c_ISGID != 0 { defer lazyChmod(name, os.ModeSetgid) } // copy the contents n, err := io.Copy(f, u.archive) if err != nil { return err } else if n != header.Size { return fmt.Errorf("Short write while copying file %s", name) } case header.Typeflag == tar.TypeBlock || header.Typeflag == tar.TypeChar || header.Typeflag == tar.TypeFifo: // check to see if the flag to skip character/block devices is set, and // simply return if it is if u.SkipSpecialDevices { return nil } // determine how to OR the mode devmode := uint32(0) switch header.Typeflag { case tar.TypeChar: devmode = syscall.S_IFCHR case tar.TypeBlock: devmode = syscall.S_IFBLK case tar.TypeFifo: devmode = syscall.S_IFIFO } // determine the mode to use mode := os.FileMode(0644) if u.PreservePermissions { mode = header.FileInfo().Mode() | u.IncludedPermissionMask } // syscall to mknod dev := makedev(header.Devmajor, header.Devminor) if err := osMknod(name, devmode|uint32(mode), dev); err != nil { return err } // Perform a chmod after creation to ensure modes are applied directly, // regardless of umask. if err := os.Chmod(name, mode|os.FileMode(devmode)); err != nil { return err } default: return fmt.Errorf("Unrecognized type: %d", header.Typeflag) } // process the uid/gid ownership uid := u.MappedUserID gid := u.MappedGroupID if u.PreserveOwners { if uid, err = u.OwnerMappingFunc(header.Uid); err != nil { return fmt.Errorf("failed to map UID for file: %v", err) } if gid, err = u.GroupMappingFunc(header.Gid); err != nil { return fmt.Errorf("failed to map GID for file: %v", err) } } // apply it switch header.Typeflag { case tar.TypeSymlink: os.Lchown(name, uid, gid) case tar.TypeLink: // don't chown on hard links or symlinks. doing this also removes setuid // from mode and the hard link will already pick up the same owner default: os.Chown(name, uid, gid) } return nil }
// ExtractFile extracts the file described by hdr from the given tarball into // the provided directory. // If overwrite is true, existing files will be overwritten. func ExtractFile(tr *tar.Reader, hdr *tar.Header, dir string, overwrite bool) error { p := filepath.Join(dir, hdr.Name) fi := hdr.FileInfo() typ := hdr.Typeflag if overwrite { info, err := os.Lstat(p) switch { case os.IsNotExist(err): case err == nil: // If the old and new paths are both dirs do nothing or // RemoveAll will remove all dir's contents if !info.IsDir() || typ != tar.TypeDir { err := os.RemoveAll(p) if err != nil { return err } } default: return err } } // Create parent dir if it doesn't exist if err := os.MkdirAll(filepath.Dir(p), DEFAULT_DIR_MODE); err != nil { return err } switch { case typ == tar.TypeReg || typ == tar.TypeRegA: f, err := os.OpenFile(p, os.O_CREATE|os.O_RDWR, fi.Mode()) if err != nil { return err } _, err = io.Copy(f, tr) if err != nil { f.Close() return err } f.Close() case typ == tar.TypeDir: if err := os.MkdirAll(p, fi.Mode()); err != nil { return err } dir, err := os.Open(p) if err != nil { return err } if err := dir.Chmod(fi.Mode()); err != nil { dir.Close() return err } dir.Close() case typ == tar.TypeLink: dest := filepath.Join(dir, hdr.Linkname) if !strings.HasPrefix(dest, dir) { return insecureLinkError(fmt.Errorf("insecure link %q -> %q", p, hdr.Linkname)) } if err := os.Link(dest, p); err != nil { return err } case typ == tar.TypeSymlink: dest := filepath.Join(filepath.Dir(p), hdr.Linkname) if !strings.HasPrefix(dest, dir) { return insecureLinkError(fmt.Errorf("insecure symlink %q -> %q", p, hdr.Linkname)) } if err := os.Symlink(hdr.Linkname, p); err != nil { return err } case typ == tar.TypeChar: dev := makedev(int(hdr.Devmajor), int(hdr.Devminor)) mode := uint32(fi.Mode()) | syscall.S_IFCHR if err := syscall.Mknod(p, mode, dev); err != nil { return err } case typ == tar.TypeBlock: dev := makedev(int(hdr.Devmajor), int(hdr.Devminor)) mode := uint32(fi.Mode()) | syscall.S_IFBLK if err := syscall.Mknod(p, mode, dev); err != nil { return err } case typ == tar.TypeFifo: if err := syscall.Mkfifo(p, uint32(fi.Mode())); err != nil { return err } // TODO(jonboulle): implement other modes default: return fmt.Errorf("unsupported type: %v", typ) } if err := os.Lchown(p, hdr.Uid, hdr.Gid); err != nil { return err } // lchown(2) says that, depending on the linux kernel version, it // can change the file's mode also if executed as root. So call // os.Chmod after it. if typ != tar.TypeSymlink { if err := os.Chmod(p, fi.Mode()); err != nil { return err } } // Restore entry atime and mtime. // Use special function LUtimesNano not available on go's syscall package because we // have to restore symlink's times and not the referenced file times. ts := HdrToTimespec(hdr) if hdr.Typeflag != tar.TypeSymlink { if err := syscall.UtimesNano(p, ts); err != nil { return err } } else { if err := LUtimesNano(p, ts); err != nil && err != ErrNotSupportedPlatform { return err } } return nil }
Expect(outputBuffer).To(test_helpers.Say("Successfully uploaded droplet-name")) Expect(fakeDropletRunner.UploadBitsCallCount()).To(Equal(1)) dropletName, uploadPath := fakeDropletRunner.UploadBitsArgsForCall(0) Expect(dropletName).To(Equal("droplet-name")) Expect(uploadPath).ToNot(BeNil()) Expect(uploadPath).To(HaveSuffix(".tar")) file, err := os.Open(uploadPath) Expect(err).NotTo(HaveOccurred()) tarReader := tar.NewReader(file) var h *tar.Header h, err = tarReader.Next() Expect(err).NotTo(HaveOccurred()) Expect(h.FileInfo().Name()).To(Equal("aaa")) Expect(h.FileInfo().IsDir()).To(BeFalse()) Expect(h.FileInfo().Mode()).To(Equal(os.FileMode(0700))) h, err = tarReader.Next() Expect(err).NotTo(HaveOccurred()) Expect(h.FileInfo().Name()).To(Equal("bbb")) Expect(h.FileInfo().IsDir()).To(BeFalse()) Expect(h.FileInfo().Mode()).To(Equal(os.FileMode(0750))) h, err = tarReader.Next() Expect(err).NotTo(HaveOccurred()) Expect(h.FileInfo().Name()).To(Equal("ccc")) Expect(h.FileInfo().IsDir()).To(BeFalse()) Expect(h.FileInfo().Mode()).To(Equal(os.FileMode(0644)))