Skip to content

Commit

Permalink
fix: Return ReadError where appropriate
Browse files Browse the repository at this point in the history
  • Loading branch information
bodgit committed Nov 9, 2024
1 parent 76fea5a commit 37e5ad0
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 35 deletions.
58 changes: 45 additions & 13 deletions reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (fr *fileReader) Stat() (fs.FileInfo, error) {
return headerFileInfo{&fr.f.FileHeader}, nil
}

func (fr *fileReader) Read(p []byte) (n int, err error) {
func (fr *fileReader) Read(p []byte) (int, error) {
if len(p) == 0 {
return 0, nil
}
Expand All @@ -97,10 +97,22 @@ func (fr *fileReader) Read(p []byte) (n int, err error) {
p = p[0:fr.n]
}

n, err = fr.rc.Read(p)
n, err := fr.rc.Read(p)
fr.n -= int64(n)

return
if err != nil && !errors.Is(err, io.EOF) {
e := &ReadError{
Err: err,
}

if frc, ok := fr.rc.(*folderReadCloser); ok {
e.Encrypted = frc.hasEncryption
}

return n, e
}

return n, err
}

func (fr *fileReader) Close() error {
Expand Down Expand Up @@ -137,18 +149,32 @@ func (f *File) Open() (io.ReadCloser, error) {
return io.NopCloser(bytes.NewReader(nil)), nil
}

var err error

rc, _ := f.zip.pool[f.folder].Get(f.offset)
if rc == nil {
rc, _, err = f.zip.folderReader(f.zip.si, f.folder)
var (
encrypted bool
err error
)

rc, _, encrypted, err = f.zip.folderReader(f.zip.si, f.folder)
if err != nil {
return nil, err
return nil, &ReadError{
Encrypted: encrypted,
Err: err,
}
}
}

if _, err = rc.Seek(f.offset, io.SeekStart); err != nil {
return nil, err
if _, err := rc.Seek(f.offset, io.SeekStart); err != nil {
e := &ReadError{
Err: err,
}

if fr, ok := rc.(*folderReadCloser); ok {
e.Encrypted = fr.hasEncryption
}

return nil, e
}

return &fileReader{
Expand Down Expand Up @@ -264,7 +290,7 @@ func NewReader(r io.ReaderAt, size int64) (*Reader, error) {
return NewReaderWithPassword(r, size, "")
}

func (z *Reader) folderReader(si *streamsInfo, f int) (*folderReadCloser, uint32, error) {
func (z *Reader) folderReader(si *streamsInfo, f int) (*folderReadCloser, uint32, bool, error) {
// Create a SectionReader covering all of the streams data
return si.FolderReader(io.NewSectionReader(z.r, z.start, z.end-z.start), f, z.p)
}
Expand Down Expand Up @@ -417,14 +443,20 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
return errors.New("sevenzip: expected only one folder in header stream")
}

fr, crc, err := z.folderReader(streamsInfo, 0)
fr, crc, encrypted, err := z.folderReader(streamsInfo, 0)
if err != nil {
return err
return &ReadError{
Encrypted: encrypted,
Err: err,
}
}
defer fr.Close()

if header, err = readEncodedHeader(util.ByteReadCloser(fr)); err != nil {
return err
return &ReadError{
Encrypted: fr.hasEncryption,
Err: err,
}
}

if crc != 0 && !util.CRC32Equal(fr.Checksum(), crc) {
Expand Down
57 changes: 35 additions & 22 deletions struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,31 +80,33 @@ func (f *folder) findOutBindPair(i uint64) *bindPair {
return nil
}

func (f *folder) coderReader(readers []io.ReadCloser, coder uint64, password string) (io.ReadCloser, error) {
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, errAlgorithm
return nil, false, errAlgorithm
}

cr, err := dcomp(f.coder[coder].properties, f.size[coder], readers)
if err != nil {
return nil, err
return nil, false, err
}

if crc, ok := cr.(CryptoReadCloser); ok {
crc, ok := cr.(CryptoReadCloser)
if ok {
if err = crc.Password(password); err != nil {
return nil, err
return nil, true, err
}
}

return plumbing.LimitReadCloser(cr, int64(f.size[coder])), nil //nolint:gosec
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
h hash.Hash
wc *plumbing.WriteCounter
size int64
hasEncryption bool
}

func (rc *folderReadCloser) Checksum() []byte {
Expand Down Expand Up @@ -148,12 +150,13 @@ func (rc *folderReadCloser) Size() int64 {
return rc.size
}

func newFolderReadCloser(rc io.ReadCloser, size int64) *folderReadCloser {
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
}
Expand Down Expand Up @@ -235,8 +238,8 @@ func (si *streamsInfo) folderOffset(folder int) int64 {
return int64(si.packInfo.position + offset) //nolint:gosec
}

//nolint:cyclop,funlen
func (si *streamsInfo) FolderReader(r io.ReaderAt, folder int, password string) (*folderReadCloser, uint32, error) {
//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)
Expand All @@ -254,11 +257,14 @@ func (si *streamsInfo) FolderReader(r io.ReaderAt, folder int, password string)
offset += size
}

input, output := uint64(0), uint64(0)
var (
hasEncryption bool
input, output uint64
)

for i, c := range f.coder {
if c.out != 1 {
return nil, 0, errors.New("more than one output stream")
return nil, 0, hasEncryption, errors.New("more than one output stream")
}

for j := input; j < input+c.in; j++ {
Expand All @@ -268,17 +274,24 @@ func (si *streamsInfo) FolderReader(r io.ReaderAt, folder int, password string)

bp := f.findInBindPair(j)
if bp == nil || out[bp.out] == nil {
return nil, 0, errors.New("cannot find bound stream")
return nil, 0, hasEncryption, errors.New("cannot find bound stream")
}

in[j] = out[bp.out]
}

var err error
var (
isEncrypted bool
err error
)

out[output], err = f.coderReader(in[input:input+c.in], uint64(i), password)
out[output], isEncrypted, err = f.coderReader(in[input:input+c.in], uint64(i), password)

Check failure on line 288 in struct.go

View workflow job for this annotation

GitHub Actions / Build and Test (1.21)

G115: integer overflow conversion int -> uint64 (gosec)

Check failure on line 288 in struct.go

View workflow job for this annotation

GitHub Actions / Build and Test (1.22)

G115: integer overflow conversion int -> uint64 (gosec)
if err != nil {
return nil, 0, err
return nil, 0, hasEncryption, err
}

if isEncrypted {
hasEncryption = true
}

input += c.in
Expand All @@ -294,16 +307,16 @@ func (si *streamsInfo) FolderReader(r io.ReaderAt, folder int, password string)
}

if len(unbound) != 1 || out[unbound[0]] == nil {
return nil, 0, errors.New("expecting one unbound output stream")
return nil, 0, hasEncryption, errors.New("expecting one unbound output stream")
}

fr := newFolderReadCloser(out[unbound[0]], int64(f.unpackSize())) //nolint:gosec
fr := newFolderReadCloser(out[unbound[0]], int64(f.unpackSize()), hasEncryption) //nolint:gosec

if si.unpackInfo.digest != nil {
return fr, si.unpackInfo.digest[folder], nil
return fr, si.unpackInfo.digest[folder], hasEncryption, nil
}

return fr, 0, nil
return fr, 0, hasEncryption, nil
}

type filesInfo struct {
Expand Down

0 comments on commit 37e5ad0

Please sign in to comment.