Skip to content

Commit 1cbb684

Browse files
nvinsonChangaco
authored andcommitted
add support for digest API
Signed-off-by: Nicholas Vinson <nvinson234@gmail.com>
1 parent b313c1d commit 1cbb684

2 files changed

Lines changed: 118 additions & 1 deletion

File tree

libarchive/entry.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ def modify(self, header_codec=None, **attributes):
8686
rdev (int | Tuple[int, int]): device number, if the file is a device
8787
rdevmajor (int): major part of the device number
8888
rdevminor (int): minor part of the device number
89+
md5Digest (bytes): MD5 digest
90+
rmd160Digest (bytes): RMD160 digest
91+
sha1Digest (bytes): SHA1 digest
92+
sha256Digest (bytes): SHA256 digest
93+
sha384Digest (bytes): SHA384 digest
94+
sha512Digest (bytes): SHA512 digest
8995
"""
9096
if header_codec:
9197
self.header_codec = header_codec
@@ -433,6 +439,81 @@ def rdevminor(self):
433439
def rdevminor(self, value):
434440
ffi.entry_set_rdevminor(self._entry_p, value)
435441

442+
@property
443+
def md5Digest(self):
444+
return self._digest(ffi.ARCHIVE_ENTRY_DIGEST_MD5)
445+
446+
@md5Digest.setter
447+
def md5Digest(self, value):
448+
self._set_digest(ffi.ARCHIVE_ENTRY_DIGEST_MD5, value)
449+
450+
@property
451+
def rmd160Digest(self):
452+
return self._digest(ffi.ARCHIVE_ENTRY_DIGEST_RMD160)
453+
454+
@rmd160Digest.setter
455+
def rmd160Digest(self, value):
456+
self._set_digest(ffi.ARCHIVE_ENTRY_DIGEST_RMD160, value)
457+
458+
@property
459+
def sha1Digest(self):
460+
return self._digest(ffi.ARCHIVE_ENTRY_DIGEST_SHA1)
461+
462+
@sha1Digest.setter
463+
def sha1Digest(self, value):
464+
self._set_digest(ffi.ARCHIVE_ENTRY_DIGEST_SHA1, value)
465+
466+
@property
467+
def sha256Digest(self):
468+
return self._digest(ffi.ARCHIVE_ENTRY_DIGEST_SHA256)
469+
470+
@sha256Digest.setter
471+
def sha256Digest(self, value):
472+
self._set_digest(ffi.ARCHIVE_ENTRY_DIGEST_SHA256, value)
473+
474+
@property
475+
def sha384Digest(self):
476+
return self._digest(ffi.ARCHIVE_ENTRY_DIGEST_SHA384)
477+
478+
@sha384Digest.setter
479+
def sha384Digest(self, value):
480+
self._set_digest(ffi.ARCHIVE_ENTRY_DIGEST_SHA384, value)
481+
482+
@property
483+
def sha512Digest(self):
484+
return self._digest(ffi.ARCHIVE_ENTRY_DIGEST_SHA512)
485+
486+
@sha512Digest.setter
487+
def sha512Digest(self, value):
488+
self._set_digest(ffi.ARCHIVE_ENTRY_DIGEST_SHA512, value)
489+
490+
def _digest(self, digestType):
491+
try:
492+
ptr = ffi.entry_digest(self._entry_p, digestType)
493+
if ptr:
494+
return bytes(ptr[:ffi._DIGEST_LENGTHS[digestType - 1]])
495+
except AttributeError:
496+
raise NotImplementedError(f"the libarchive being used (version "
497+
f"{ffi.version_number()} path "
498+
f"{ffi.libarchive_path}) doesn't "
499+
f"support read-only digest APIs")
500+
return None
501+
502+
def _set_digest(self, digestType, value):
503+
try:
504+
digestLen = ffi._DIGEST_LENGTHS[digestType - 1]
505+
if len(value) != digestLen:
506+
raise ValueError(f"Invalid input digest Expected {digestLen} "
507+
f"bytes. Got {len(value)}.")
508+
buffer = (digestLen * ffi.c_ubyte)(*value)
509+
ffi.entry_set_digest(self._entry_p, digestType, buffer)
510+
except AttributeError:
511+
raise NotImplementedError(f"the libarchive being used (version "
512+
f"{ffi.version_number()} path "
513+
f"{ffi.libarchive_path}) doesn't support "
514+
f"writable digest APIs")
515+
return None
516+
436517

437518
class ConsumedArchiveEntry(ArchiveEntry):
438519

libarchive/ffi.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from ctypes import (
22
c_char_p, c_int, c_uint, c_long, c_longlong, c_size_t, c_int64,
3-
c_void_p, c_wchar_p, CFUNCTYPE, POINTER,
3+
c_ubyte, c_void_p, c_wchar_p, CFUNCTYPE, POINTER,
44
)
55

66
try:
@@ -365,3 +365,39 @@ def get_write_filter_function(filter_name):
365365
f"the libarchive being used (version {version_number()}, "
366366
f"path {libarchive_path}) doesn't support encryption"
367367
)
368+
369+
# archive digest API
370+
try:
371+
ffi('entry_digest', [c_archive_entry_p, c_int], POINTER(c_ubyte))
372+
373+
ARCHIVE_ENTRY_DIGEST_MD5 = 1
374+
ARCHIVE_ENTRY_DIGEST_RMD160 = 2
375+
ARCHIVE_ENTRY_DIGEST_SHA1 = 3
376+
ARCHIVE_ENTRY_DIGEST_SHA256 = 4
377+
ARCHIVE_ENTRY_DIGEST_SHA384 = 5
378+
ARCHIVE_ENTRY_DIGEST_SHA512 = 6
379+
380+
_DIGEST_LENGTHS = [
381+
16, # MD5
382+
20, # RMD160
383+
20, # SHA1
384+
32, # SHA256
385+
48, # SHA384
386+
64, # SHA512
387+
]
388+
389+
except AttributeError:
390+
logger.info(
391+
f"the libarchive being used (version {version_number()}, "
392+
f"path {libarchive_path}) doesn't support read-only message digest API"
393+
)
394+
395+
try:
396+
ffi('entry_set_digest',
397+
[ctypes.c_void_p, ctypes.c_int, ctypes.POINTER(ctypes.c_ubyte)],
398+
ctypes.c_int)
399+
except AttributeError:
400+
logger.info(
401+
f"the libarchive being used (version {version_number()}, "
402+
f"path {libarchive_path}) doesn't support mutable message digest API"
403+
)

0 commit comments

Comments
 (0)