class VaultFileKey(os.PathLike): """ HGI vault file key properties """ # NOTE This is implemented in a separate class to keep that part of # the logic outside VaultFile and to decouple it from the filesystem _delimiter: T.ClassVar[str] = "-" _prefix: T.Optional[T.Path] # inode prefix path, without the LSB _suffix: str # LSB and encoded basename suffix name def __init__(self, path: T.Path, inode: T.Optional[int] = None, max_file_name_length: int = _default_max_name_length) -> None: """ Construct the key from a path and (optional) inode @param path Path to construct from @param inode inode ID to construct from (defaults to inode of path) @param max_file_name The maximum length of a filename. Defaults to current directory, however can be passed in for each path being added to the Vault """ # Use the path's inode, if one is not explicitly provided if inode is None: inode = file.inode_id(path) # The byte-padded hexadecimal representation of the inode ID if len(inode_hex := f"{inode:x}") % 2: inode_hex = f"0{inode_hex}" # Chunk the inode ID into 8-bit segments chunks = [inode_hex[i:i + 2] for i in range(0, len(inode_hex), 2)] # inode ID, without the least significant byte, if it exists self._prefix = None if len(chunks) > 1: self._prefix = T.Path(*chunks[:-1]) # inode ID LSB, delimiter, and the base64 encoding of the path. # If the relative file path is too long, we split it by max file name length # and save each part as a directory until we get to a final file encoded_path = base64.encode(path) max_file_name_length -= 3 self._suffix = chunks[-1] + self._delimiter + str( T.Path(*[ encoded_path[i:i + max_file_name_length] for i in range(0, len(encoded_path), max_file_name_length) ]))
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/ """ import unittest from core import typing as T from core.utils import base64 from .utils import VFK, VFK_k _DUMMY = T.Path("foo/bar/quux") _B64_DUMMY = base64.encode(_DUMMY) _DUMMY_LONG = T.Path('this/path/is/going/to/be/much/much/much/much/much/much/' 'much/much/much/much/much/much/much/much/much/much/much/much/much/much/much' '/much/much/much/much/much/much/much/much/much/much/much/much/much/much/' 'much/much/much/much/much/much/much/much/much/longer/than/two/hundred/and/' 'fifty/five/characters') _B64_DUMMY_LONG = base64.encode(_DUMMY_LONG) # Assuming test is run on a filesystem (such as Linux) where NAME_MAX = 255. # If NAME_MAX != 255, these tests for long and really long relative paths # would fail. _B64_DUMMY_LONG_FIRST_PART = _B64_DUMMY_LONG[0:252] _B64_DUMMY_LONG_SECOND_PART = _B64_DUMMY_LONG[252:] _DUMMY_LONGEST = T.Path('this/path/is/going/to/be/much/much/much/much/much/much' '/much/much/much/much/much/much/much/much/much/much/much/much/much/much/'
def test_altchars(self): self.assertEqual(base64.encode(b"\xfa"), "+g==") self.assertEqual(base64.encode(b"\xff"), "_w==") self.assertEqual(base64.decode("+g=="), b"\xfa") self.assertEqual(base64.decode("_w=="), b"\xff")
def test_encode(self): self.assertEqual(base64.encode("foo"), "Zm9v") self.assertEqual(base64.encode(b"foo"), "Zm9v") self.assertEqual(base64.encode(_DUMMY("foo")), "Zm9v")
def _base64_prefix(path: T.Path) -> str: # Find the common base64 prefix of a path to optimise searching bare = base64.encode(path) slashed = base64.encode(f"{path}/") return commonprefix([bare, slashed])