Exemple #1
0
    def create_file(self, path: str, is_dir=False) -> int:
        """Create a new file or directory
        """
        dirname, fullname = os.path.split(path)

        file_dir = self.find_dir(dirname)
        if file_dir is None:
            raise FATException(f'{path} directory does not exist')

        filename, extension = os.path.splitext(fullname)
        extension = extension.lstrip('.')

        # does the file already exist?
        if file_dir.find_entry(filename, extension):
            raise FATException(f'{path} file already exists')

        # find a free cluster for the new file
        file_cluster = self.find_free_cluster()
        if file_cluster == const.EOF:
            raise FATException('There is no free space')
        self.entries[file_cluster] = const.EOC

        # create file entry
        additional_entry_options = {}
        if is_dir:
            additional_entry_options['attributes'] = const.FileAttributes.DIRECTORY
        file_dir.add_entry(filename, extension, file_cluster, **additional_entry_options)
        self.write_file(file_dir.cluster_number, file_dir.serialize())

        return file_cluster
Exemple #2
0
 def _validation(self):
     if self.filename and len(self.filename) > 8:
         raise FATException(
             f'Validation error: filename is too long: {len(self.filename)}'
         )
     if self.extension and len(self.extension) > 3:
         raise FATException(
             f'Validation error: extension is too long: {len(self.extension)}'
         )
     if self.is_dir() and self.extension:
         raise FATException(
             'Validation error: directory has to have not specified extension'
         )
Exemple #3
0
    def find_file(self, path: str) -> int:
        dirname, fullname = os.path.split(path)
        file_dir = self.find_dir(dirname)
        if file_dir is None:
            raise FATException(f'{path} directory does not exist')

        filename, extension = os.path.splitext(fullname)
        extension = extension.lstrip('.')

        file_dir_entry = file_dir.find_entry(filename, extension)
        if not file_dir_entry:
            raise FATException(f'{path} file does not exist')

        if file_dir_entry.is_dir():
            raise FATException(f'{path} is a directory. Use `find_dir` instead.')

        return file_dir_entry.first_file_cluster
Exemple #4
0
    def find_free_cluster(self, start_index: int=const.FAT_CLUSTER_TO_USE_FROM) -> int or const.EOF:
        """Try to find a free cluster, returns EOF if no any
        """
        if not self.is_cluster_number(start_index):
            raise FATException('Incorrect cluster position')

        for index, entry in enumerate(self.entries[start_index:], start=start_index):
            if entry == const.FAT_ENTRY_EMPTY:
                return index
        return const.EOF
Exemple #5
0
    def parse(self, data: bytes):
        if len(data) != self.SIZE:
            raise FATException(f'data has to have length={self.SIZE}')

        (self.filename, self.extension, self.attributes, high_first_cluster,
         low_first_cluster) = struct.unpack(self.FORMAT, data)

        self.filename = self.filename.rstrip(b'\x00').decode()
        self.extension = self.extension.rstrip(b'\x00').decode()
        self.first_file_cluster = (
            high_first_cluster << 16) | low_first_cluster
Exemple #6
0
    def read_file(self, file_number: int) -> bytearray:
        if not self.is_cluster_number(file_number):
            raise FATException(f'Incorrect file_number: {file_number}')

        file_content = bytearray()

        fat_number = file_number
        with open(self.volume_path, 'rb') as volume:
            while self.is_cluster_number(fat_number):
                fat_entry = self.entries[fat_number]
                if fat_entry == const.FAT_ENTRY_EMPTY:
                    raise FATException(f'Cluster is empty for the fat_number={fat_number}, file_number={file_number}')

                # read cluster data
                cluster_position = self.get_cluster_position(fat_number)
                volume.seek(cluster_position)
                cluster_value = volume.read(self.cluster_size)
                file_content.extend(cluster_value)

                # go to the next cluster in the chain
                fat_number = fat_entry

        return file_content
Exemple #7
0
    def write_file(self, file_number: int, data: bytes):
        if not self.is_cluster_number(file_number):
            raise FATException(f'Incorrect file_number: {file_number}')

        # TODO: oprimize it for contiguous clusters (don't need to split by chunks)

        # rewrire all clusters
        fat_number = file_number
        prev_number = None
        with open(self.volume_path, 'r+b') as volume:
            for data_chunk in split_by_chunks(data, self.cluster_size):
                # there's no enough size in the file? -> add a new cluster to the file
                if fat_number == const.EOC:
                    fat_number = self.find_free_cluster(prev_number)
                    if fat_number == const.EOF:
                        fat_number = self.find_free_cluster()
                        if fat_number == const.EOF:
                            raise FATException('There is no free space')
                    self.entries[prev_number] = fat_number

                fat_entry = self.entries[fat_number]
                if fat_entry == const.FAT_ENTRY_EMPTY:
                    raise FATException(f'Cluster is empty for the fat_number={fat_number}, file_number={file_number}')

                # write data
                cluster_position = self.get_cluster_position(fat_number)
                volume.seek(cluster_position)
                volume.write(data_chunk)

                # go to the next cluster in the chain
                prev_number = fat_number
                fat_number = fat_entry

        # free not used clusters
        # TODO
        pass
Exemple #8
0
    def find_dir(self, path: str) -> DirectoryTable or None:
        if not path.startswith('/'):
            raise FATException('path has to be absolute.')

        if len(path.strip()) == 1:
            return self.root

        _, *dirs = path.split('/')
        current_dir = self.root
        for directory in dirs:
            for dir_entry in current_dir.entries:
                if dir_entry.is_dir() and dir_entry.filename == directory:
                    current_dir = self.read_dir(dir_entry.first_file_cluster)
                    break
            # directory doesn't have the next subdirectory in the path
            else:
                return None

        return current_dir
Exemple #9
0
 def get_cluster_position(self, number: int):
     """Get offset of a cluster in the volume by its entry number
     """
     if not self.is_cluster_number(number):
         raise FATException('Incorrect cluster position')
     return self.cluster_size * number