def save_to_block(self, block: int) -> None: """ Set the metadata space of the given block (from 0 to 38, inclusive) to this metadata. """ full_block = FileTable().read_block(block) data_section = full_block[START_OF_CONTENT:] new_bytes = self.form_bytes() + data_section write_block(block, new_bytes) FMLog.success(f"Wrote new metadata to block {block}")
def mkdir(self, path, mode): self.files[path] = dict(st_mode=(S_IFDIR | mode), st_nlink=2, st_size=0, st_ctime=time(), st_mtime=time(), st_atime=time()) name = path mode = self.files[path].get('st_mode') ctime = self.files[path].get('st_ctime') mtime = self.files[path].get('st_mtime') atime = self.files[path].get('st_atime') nlink = self.files[path].get('st_nlink') size = self.files[path].get('st_size') uid = os.getuid() gid = os.getgid() self.chown(path, uid, gid) # get free block for the new created file to save the metadata. num_array = Format.get_free_block(Format, 1) block_num = num_array[0] # set up the location in the disk inode_data = Format.set_inode(Format, name, mode, ctime, mtime, atime, nlink, uid, gid, size, block_num) disktools.write_block(block_num, inode_data) # update the free block bitmap Format.update_bit_map(Format, num_array) # find the parent path and add the nlink by 1. parent_path = Format.find_parent_path(Format, path) self.files[parent_path]['st_nlink'] += 1 Format.update_nlink(Format, parent_path, 1)
def create(self, path, mode): self.files[path] = dict(st_mode=(S_IFREG | mode), st_nlink=1, st_size=0, st_ctime=time(), st_mtime=time(), st_atime=time()) name = path mode = self.files[path].get('st_mode') ctime = self.files[path].get('st_ctime') mtime = self.files[path].get('st_mtime') atime = self.files[path].get('st_atime') nlink = self.files[path].get('st_nlink') size = self.files[path].get('st_size') uid = os.getuid() gid = os.getgid() self.chown(path, uid, gid) # get free block for the new created file to save the metadata. num_array = Format.get_free_block(Format, 1) block_num = num_array[0] # set up the location in the disk inode_data = Format.set_inode(Format, name, mode, ctime, mtime, atime, nlink, uid, gid, size, block_num) disktools.write_block(block_num, inode_data) # update the free block bitmap Format.update_bit_map(Format, num_array) self.fd += 1 return self.fd
def clear_data_block(self, path): for i in range(1, NUM_BLOCKS, 1): data_block = [] num_array = [] clean_block = bytearray([0] * BLOCK_SIZE) block = disktools.read_block(i) if block[NAME_START:NAME_FINISH].decode().rstrip('\x00') == path: block_number = disktools.bytes_to_int( block[LOCATION_START:LOCATION_FINISH]) block_number_bin = bin(block_number) for b in block_number_bin[2:]: num_array.append(int(b)) num_array = list(reversed(num_array)) for idx, val in enumerate(num_array): if val == 1: data_block.append(idx) for data in data_block: disktools.write_block(data, clean_block) self.update_bit_map(self, data_block)
def write_bytes_to_block( self, data: bytearray, overwrite: List[int] = [], print: bool = False ) -> List[int]: # print(overwrite) # print(data) data_as_bytes = data size = len(data_as_bytes) split_blocks = [ data_as_bytes[i * BLOCK_SIZE : (i + 1) * BLOCK_SIZE] for i in range((size + BLOCK_SIZE - 1) // BLOCK_SIZE) ] written_blocks: List[int] = [] def get_block_to_write(level: int) -> int: if level < len(overwrite): return overwrite[level] return self.find_free_block(written_blocks) iterator = 0 for i in split_blocks: free: int = get_block_to_write(iterator) write_block(free, i) written_blocks.append(free) iterator += 1 return written_blocks
def update_mode(self, path, mode): for i in range(1, NUM_BLOCKS, 1): block = disktools.read_block(i) if block[NAME_START:NAME_FINISH].decode().rstrip('\x00') == path: block[MODE_START:MODE_FINISH] = disktools.int_to_bytes(mode, 2) disktools.write_block(i, block) return 0
def update_name(self, path, new_path): for i in range(1, NUM_BLOCKS, 1): block = disktools.read_block(i) if block[NAME_START:NAME_FINISH].decode().rstrip('\x00') == path: block[NAME_START:NAME_FINISH] = new_path.encode() disktools.write_block(i, block) return 0
def update_file_location(self, path, bitmap): for i in range(1, NUM_BLOCKS, 1): block = disktools.read_block(i) if block[NAME_START:NAME_FINISH].decode().rstrip('\x00') == path: block[LOCATION_START:LOCATION_FINISH] = disktools.int_to_bytes( bitmap, 2) disktools.write_block(i, block) return 0
def clear_metadata_block(self, path): for i in range(1, NUM_BLOCKS, 1): block = disktools.read_block(i) if block[NAME_START:NAME_FINISH].decode().rstrip('\x00') == path: num_array = [] clean_block = bytearray([0] * BLOCK_SIZE) disktools.write_block(i, clean_block) num_array.append(i) self.update_bit_map(self, num_array)
def update_owner(self, path, uid, gid): for i in range(1, NUM_BLOCKS, 1): block = disktools.read_block(i) if block[NAME_START:NAME_FINISH].decode().rstrip('\x00') == path: block[UID_START:UID_FINISH] = disktools.int_to_bytes(uid, 2) block[GID_START:GID_FINISH] = disktools.int_to_bytes(gid, 2) disktools.write_block(i, block) return 0
def write_to_table(self, locations: List[int]) -> None: filetable = self.get_filetable() location_len = len(locations) for i in range(location_len): location = locations[i] if i + 1 == location_len: filetable[location] = END_OF_FILE else: filetable[location] = locations[i + 1] to_write = filetable write_block(0, to_write)
def update_bit_map(self, used_block): block = disktools.read_block(0) # get the int number of bitmap bitmap = disktools.bytes_to_int(block[BITMAP_START:BITMAP_FINISH]) for i in used_block: bitmap = bits.toggleBit(bitmap, i) block[BITMAP_START:BITMAP_FINISH] = disktools.int_to_bytes(bitmap, 2) disktools.write_block(0, block) return bitmap
def update_nlink(self, path, update): for i in range(0, NUM_BLOCKS, 1): block = disktools.read_block(i) if block[NAME_START:NAME_FINISH].decode().rstrip('\x00') == path: new_nlink = disktools.bytes_to_int( block[NLINK_START:NLINK_FINISH]) + update block[NLINK_START:NLINK_FINISH] = disktools.int_to_bytes( new_nlink, 1) disktools.write_block(i, block) return 0
def purge_full_file(self, at_location: int) -> None: current_block = at_location filetable_snapshot = self.get_filetable() next_block = filetable_snapshot[at_location] while True: filetable_snapshot[current_block] = FREE_SPACE write_block(current_block, bytearray([0] * BLOCK_SIZE)) current_block = next_block if current_block == END_OF_FILE: break next_block = filetable_snapshot[current_block] write_block(0, filetable_snapshot)
def initial_root_metadata(self): now = time() name = '/' mode = (S_IFDIR | 0o755) ctime = now, mtime = now, atime = now, nlink = 2 uid = os.getuid() gid = os.getgid() size = 0 block_num = 0 inode_data = self.set_inode(self, name, mode, ctime[0], mtime[0], atime[0], nlink, uid, gid, size, block_num) disktools.write_block(0, inode_data)
def write_to_block(self, data: str, metadata: bytearray) -> List[int]: data_as_bytes = metadata + bytearray(data.encode(encoding="ascii")) size = len(data_as_bytes) split_blocks = [ data_as_bytes[i * BLOCK_SIZE : (i + 1) * BLOCK_SIZE] for i in range((size + BLOCK_SIZE - 1) // BLOCK_SIZE) ] written_blocks: List[int] = [] for i in split_blocks: free: int = self.find_free_block(written_blocks) write_block(free, i) written_blocks.append(free) return written_blocks
def write(self, path, data, offset, fh): self.data[path] = ( # make sure the data gets inserted at the right offset self.data[path][:offset].ljust(offset, '\x00'.encode('ascii')) + data # and only overwrites the bytes that data is replacing + self.data[path][offset + len(data):]) self.files[path]['st_size'] = len(self.data[path]) # get the length of input data size = len(self.data[path]) # calculate the number of blocks that need to store the data no_of_blocks = (size // BLOCK_SIZE) + 1 # get the free blocks num_array = Format.get_free_block(Format, no_of_blocks) Format.clear_data_block(Format, path) # set the data block bitmap data_block_bitmap = Format.set_data_block_bitmap(Format, num_array) # get the input data input_data = self.data[path] # save the data to disk into the allocated blocks for i in range(no_of_blocks): if no_of_blocks == 1: disktools.write_block(num_array[i], input_data) else: disktools.write_block(num_array[i], input_data[i * 64:i * 64 + 64]) # Add the data block bitmap into the metadata information Format.update_file_location(Format, path, data_block_bitmap) # update the st_size in the metadata Format.update_size(Format, path, self.files[path]['st_size']) # update the st_mtime in the metadata Format.update_mtime(Format, path, int(time())) # update the free block bitmap Format.update_bit_map(Format, num_array) return len(data)
def initial_free_block_bitmap(self): # initialise the bitmap int value to 0 bitmap = 0 # counter for count the blocks count = 0 for i in range(NUM_BLOCKS): # read each block in the disk block = disktools.read_block(i) # set the value to 0 for the non-free block in bit map if disktools.bytes_to_int(block) != 0: bitmap = bits.clearBit(bitmap, count) count += 1 else: bitmap = bits.setBit(bitmap, count) count += 1 # set the first block to 1 which means it is not free(will store bitmap into block 0). bitmap = bits.clearBit(bitmap, 0) # write the bitmap value at the first two bytes at block 0 bitmap_disk = disktools.read_block(0) bitmap_disk[BITMAP_START:BITMAP_FINISH] = disktools.int_to_bytes( bitmap, 2) disktools.write_block(0, bitmap_disk)
# Filetable initial_table_block = bytearray([FREE_SPACE] * NUM_BLOCKS) initial_table_block[0] = END_OF_FILE initial_table_block[1] = END_OF_FILE FMLog.warn("✔ Created filetable in disk block 0") now = time() dir_meta = (MetadataFactory().set_with_params( LOCATION=0x01, MODE=(S_IFDIR | 0o755), ATIME=int(now), CTIME=int(now), MTIME=int(now), NLINKS=2, GID=os.getgid(), UID=os.getuid(), NAME="FMFS", TYPE=0, SIZE=64, ).construct().form_bytes()) root_dir = bytearray([0] * (BLOCK_SIZE - len(dir_meta))) write_block(0, initial_table_block) write_block(1, dir_meta + root_dir) FMLog.warn("✔ Created root directory in disk block 1") FMLog.trace( "🚀 Installation and initialization of the FM Filesystem is complete!")