mattermost-community-enterp.../vendor/github.com/bodgit/sevenzip/struct.go
Claude ec1f89217a Merge: Complete Mattermost Server with Community Enterprise
Full Mattermost server source with integrated Community Enterprise features.
Includes vendor directory for offline/air-gapped builds.

Structure:
- enterprise-impl/: Enterprise feature implementations
- enterprise-community/: Init files that register implementations
- enterprise/: Bridge imports (community_imports.go)
- vendor/: All dependencies for offline builds

Build (online):
  go build ./cmd/mattermost

Build (offline/air-gapped):
  go build -mod=vendor ./cmd/mattermost

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 23:59:07 +09:00

468 lines
9.7 KiB
Go

package sevenzip
import (
"bufio"
"errors"
"fmt"
"hash"
"hash/crc32"
"io"
iofs "io/fs"
"path"
"time"
"github.com/bodgit/plumbing"
"github.com/bodgit/sevenzip/internal/util"
)
var (
errAlgorithm = errors.New("sevenzip: unsupported compression algorithm")
errInvalidWhence = errors.New("invalid whence")
errNegativeSeek = errors.New("negative seek")
errSeekBackwards = errors.New("cannot seek backwards")
errSeekEOF = errors.New("cannot seek beyond EOF")
errMultipleOutputStreams = errors.New("more than one output stream")
errNoBoundStream = errors.New("cannot find bound stream")
errNoUnboundStream = errors.New("expecting one unbound output stream")
)
// CryptoReadCloser adds a Password method to decompressors.
type CryptoReadCloser interface {
Password(password string) error
}
type signatureHeader struct {
Signature [6]byte
Major byte
Minor byte
CRC uint32
}
type startHeader struct {
Offset uint64
Size uint64
CRC uint32
}
type packInfo struct {
position uint64
streams uint64
size []uint64
digest []uint32
}
type coder struct {
id []byte
in, out uint64
properties []byte
}
type bindPair struct {
in, out uint64
}
type folder struct {
in, out uint64
packedStreams uint64
coder []*coder
bindPair []*bindPair
size []uint64
packed []uint64
}
func (f *folder) findInBindPair(i uint64) *bindPair {
for _, v := range f.bindPair {
if v.in == i {
return v
}
}
return nil
}
func (f *folder) findOutBindPair(i uint64) *bindPair {
for _, v := range f.bindPair {
if v.out == i {
return v
}
}
return nil
}
func (f *folder) coderReader(readers []io.ReadCloser, coder uint64, password string) (io.ReadCloser, bool, error) {
dcomp := decompressor(f.coder[coder].id)
if dcomp == nil {
return nil, false, errAlgorithm
}
cr, err := dcomp(f.coder[coder].properties, f.size[coder], readers)
if err != nil {
return nil, false, err
}
crc, ok := cr.(CryptoReadCloser)
if ok {
if err = crc.Password(password); err != nil {
return nil, true, fmt.Errorf("sevenzip: error setting password: %w", err)
}
}
return plumbing.LimitReadCloser(cr, int64(f.size[coder])), ok, nil //nolint:gosec
}
type folderReadCloser struct {
io.ReadCloser
h hash.Hash
wc *plumbing.WriteCounter
size int64
hasEncryption bool
}
func (rc *folderReadCloser) Checksum() []byte {
return rc.h.Sum(nil)
}
func (rc *folderReadCloser) Seek(offset int64, whence int) (int64, error) {
var newo int64
switch whence {
case io.SeekStart:
newo = offset
case io.SeekCurrent:
newo = int64(rc.wc.Count()) + offset //nolint:gosec
case io.SeekEnd:
newo = rc.Size() + offset
default:
return 0, errInvalidWhence
}
if newo < 0 {
return 0, errNegativeSeek
}
if uint64(newo) < rc.wc.Count() {
return 0, errSeekBackwards
}
if newo > rc.Size() {
return 0, errSeekEOF
}
if _, err := io.CopyN(io.Discard, rc, newo-int64(rc.wc.Count())); err != nil { //nolint:gosec
return 0, fmt.Errorf("sevenzip: error seeking: %w", err)
}
return newo, nil
}
func (rc *folderReadCloser) Size() int64 {
return rc.size
}
func newFolderReadCloser(rc io.ReadCloser, size int64, hasEncryption bool) *folderReadCloser {
nrc := new(folderReadCloser)
nrc.h = crc32.NewIEEE()
nrc.wc = new(plumbing.WriteCounter)
nrc.ReadCloser = plumbing.TeeReadCloser(rc, io.MultiWriter(nrc.h, nrc.wc))
nrc.size = size
nrc.hasEncryption = hasEncryption
return nrc
}
func (f *folder) unpackSize() uint64 {
if len(f.size) == 0 {
return 0
}
for i := len(f.size) - 1; i >= 0; i-- {
if f.findOutBindPair(uint64(i)) == nil {
return f.size[i]
}
}
return f.size[len(f.size)-1]
}
type unpackInfo struct {
folder []*folder
digest []uint32
}
type subStreamsInfo struct {
streams []uint64
size []uint64
digest []uint32
}
type streamsInfo struct {
packInfo *packInfo
unpackInfo *unpackInfo
subStreamsInfo *subStreamsInfo
}
func (si *streamsInfo) Folders() int {
if si != nil && si.unpackInfo != nil {
return len(si.unpackInfo.folder)
}
return 0
}
func (si *streamsInfo) FileFolderAndSize(file int) (int, uint64, uint32) {
var (
folder int
streams uint64 = 1
crc uint32
)
if si.subStreamsInfo != nil {
total := uint64(0)
for folder, streams = range si.subStreamsInfo.streams {
total += streams
if uint64(file) < total { //nolint:gosec
break
}
}
if len(si.subStreamsInfo.digest) > 0 {
crc = si.subStreamsInfo.digest[file]
}
}
if streams == 1 {
if len(si.unpackInfo.digest) > 0 {
crc = si.unpackInfo.digest[folder]
}
return folder, si.unpackInfo.folder[folder].size[len(si.unpackInfo.folder[folder].coder)-1], crc
}
return folder, si.subStreamsInfo.size[file], crc
}
func (si *streamsInfo) folderOffset(folder int) int64 {
offset := uint64(0)
for i, k := 0, uint64(0); i < folder; i++ {
for j := k; j < k+si.unpackInfo.folder[i].packedStreams; j++ {
offset += si.packInfo.size[j]
}
k += si.unpackInfo.folder[i].packedStreams
}
return int64(si.packInfo.position + offset) //nolint:gosec
}
//nolint:cyclop,funlen,lll
func (si *streamsInfo) FolderReader(r io.ReaderAt, folder int, password string) (*folderReadCloser, uint32, bool, error) {
f := si.unpackInfo.folder[folder]
in := make([]io.ReadCloser, f.in)
out := make([]io.ReadCloser, f.out)
packedOffset := 0
for i := 0; i < folder; i++ {
packedOffset += len(si.unpackInfo.folder[i].packed)
}
offset := int64(0)
for i, input := range f.packed {
size := int64(si.packInfo.size[packedOffset+i]) //nolint:gosec
in[input] = util.NopCloser(bufio.NewReader(io.NewSectionReader(r, si.folderOffset(folder)+offset, size)))
offset += size
}
var (
hasEncryption bool
input, output uint64
)
for i, c := range f.coder {
if c.out != 1 {
return nil, 0, hasEncryption, errMultipleOutputStreams
}
for j := input; j < input+c.in; j++ {
if in[j] != nil {
continue
}
bp := f.findInBindPair(j)
if bp == nil || out[bp.out] == nil {
return nil, 0, hasEncryption, errNoBoundStream
}
in[j] = out[bp.out]
}
var (
isEncrypted bool
err error
)
out[output], isEncrypted, err = f.coderReader(in[input:input+c.in], uint64(i), password) //nolint:gosec
if err != nil {
return nil, 0, hasEncryption, err
}
if isEncrypted {
hasEncryption = true
}
input += c.in
output += c.out
}
unbound := make([]uint64, 0, f.out)
for i := uint64(0); i < f.out; i++ {
if bp := f.findOutBindPair(i); bp == nil {
unbound = append(unbound, i)
}
}
if len(unbound) != 1 || out[unbound[0]] == nil {
return nil, 0, hasEncryption, errNoUnboundStream
}
fr := newFolderReadCloser(out[unbound[0]], int64(f.unpackSize()), hasEncryption) //nolint:gosec
if si.unpackInfo.digest != nil {
return fr, si.unpackInfo.digest[folder], hasEncryption, nil
}
return fr, 0, hasEncryption, nil
}
type filesInfo struct {
file []FileHeader
}
type header struct {
streamsInfo *streamsInfo
filesInfo *filesInfo
}
// FileHeader describes a file within a 7-zip file.
type FileHeader struct {
Name string
Created time.Time
Accessed time.Time
Modified time.Time
Attributes uint32
CRC32 uint32
UncompressedSize uint64
// Stream is an opaque identifier representing the compressed stream
// that contains the file. Any File with the same value can be assumed
// to be stored within the same stream.
Stream int
isEmptyStream bool
isEmptyFile bool
}
// FileInfo returns an [fs.FileInfo] for the FileHeader.
func (h *FileHeader) FileInfo() iofs.FileInfo {
return headerFileInfo{h}
}
type headerFileInfo struct {
fh *FileHeader
}
func (fi headerFileInfo) Name() string { return path.Base(fi.fh.Name) }
func (fi headerFileInfo) Size() int64 { return int64(fi.fh.UncompressedSize) } //nolint:gosec
func (fi headerFileInfo) IsDir() bool { return fi.Mode().IsDir() }
func (fi headerFileInfo) ModTime() time.Time { return fi.fh.Modified.UTC() }
func (fi headerFileInfo) Mode() iofs.FileMode { return fi.fh.Mode() }
func (fi headerFileInfo) Type() iofs.FileMode { return fi.fh.Mode().Type() }
func (fi headerFileInfo) Sys() interface{} { return fi.fh }
func (fi headerFileInfo) Info() (iofs.FileInfo, error) { return fi, nil }
const (
// Unix constants. The specification doesn't mention them,
// but these seem to be the values agreed on by tools.
sIFMT = 0xf000
sIFSOCK = 0xc000
sIFLNK = 0xa000
sIFREG = 0x8000
sIFBLK = 0x6000
sIFDIR = 0x4000
sIFCHR = 0x2000
sIFIFO = 0x1000
sISUID = 0x800
sISGID = 0x400
sISVTX = 0x200
msdosDir = 0x10
msdosReadOnly = 0x01
)
// Mode returns the permission and mode bits for the FileHeader.
func (h *FileHeader) Mode() (mode iofs.FileMode) {
// Prefer the POSIX attributes if they're present
if h.Attributes&0xf0000000 != 0 {
mode = unixModeToFileMode(h.Attributes >> 16)
} else {
mode = msdosModeToFileMode(h.Attributes)
}
return
}
func msdosModeToFileMode(m uint32) (mode iofs.FileMode) {
if m&msdosDir != 0 {
mode = iofs.ModeDir | 0o777
} else {
mode = 0o666
}
if m&msdosReadOnly != 0 {
mode &^= 0o222
}
return mode
}
//nolint:cyclop
func unixModeToFileMode(m uint32) iofs.FileMode {
mode := iofs.FileMode(m & 0o777)
switch m & sIFMT {
case sIFBLK:
mode |= iofs.ModeDevice
case sIFCHR:
mode |= iofs.ModeDevice | iofs.ModeCharDevice
case sIFDIR:
mode |= iofs.ModeDir
case sIFIFO:
mode |= iofs.ModeNamedPipe
case sIFLNK:
mode |= iofs.ModeSymlink
case sIFREG:
// nothing to do
case sIFSOCK:
mode |= iofs.ModeSocket
}
if m&sISGID != 0 {
mode |= iofs.ModeSetgid
}
if m&sISUID != 0 {
mode |= iofs.ModeSetuid
}
if m&sISVTX != 0 {
mode |= iofs.ModeSticky
}
return mode
}