class StorageInode(object): blocksize = 64 * 1024 # 64 KB #blocksize = 1024*1024 # 1024 KB default_meta = ('mode: 0100644\n' 'nlink: 1\n' 'uid: 0\n' 'gid: 0\n' 'size: 0\n') int_meta = ('nlink', 'uid', 'gid', 'size') oct_meta = ('mode', ) def __init__(self, name, tree, storage): self.name = name self.tree = tree self.storage = storage self.tt = TreeTree(tree, prefix='bt') log.debug('Loaded inode %r', name) def _read_meta(self): try: meta_blob = self.tree['meta'] except KeyError: meta_raw = self.default_meta else: meta_raw = meta_blob.data return dict( line.split(': ', 1) for line in meta_raw.strip().split('\n')) def _write_meta(self, meta_data): meta_raw = ''.join('%s: %s\n' % (key, value) for key, value in sorted(meta_data.items())) self.tree.new_blob('meta').data = meta_raw self.storage._autocommit() def __getitem__(self, key): value = self._read_meta()[key] if key in self.oct_meta: value = int(value, base=8) elif key in self.int_meta: value = int(value) return value def __setitem__(self, key, value): if key in self.oct_meta: value = '0%o' % value elif key in self.int_meta: value = '%d' % value else: raise NotImplementedError meta_data = self._read_meta() meta_data[key] = value self._write_meta(meta_data) def read_block(self, n): block_name = str(n) log.debug('Reading block %r of inode %r', block_name, self.name) try: block = self.tt[block_name] except KeyError: return '' else: return block.data def write_block(self, n, data): block_name = str(n) log.debug('Writing block %r of inode %r', block_name, self.name) try: block = self.tt[block_name] except KeyError: block = self.tt.new_blob(block_name) block.data = data self.storage._autocommit() def delete_block(self, n): block_name = str(n) log.debug('Removing block %r of inode %r', block_name, self.name) del self.tt[block_name] self.storage._autocommit() def read_data(self, offset, length): end = offset + length eof = self['size'] if end > eof: end = eof length = end - offset if length <= 0: return '' first_block = offset / self.blocksize last_block = end / self.blocksize output = StringIO() for n_block in range(first_block, last_block + 1): block_offset = n_block * self.blocksize fragment_offset = 0 if n_block == first_block: fragment_offset = offset - block_offset fragment_end = self.blocksize if n_block == last_block: fragment_end = end - block_offset block_data = self.read_block(n_block) fragment = block_data[fragment_offset:fragment_end] assert len(fragment) == fragment_end - fragment_offset output.write(fragment) output = output.getvalue() assert len(output) == length return output def write_data(self, data, offset): current_size = self['size'] if current_size < offset: self.truncate(offset) log.info('Inode %s writing %d bytes at offset %d', repr(self.name), len(data), offset) end = offset + len(data) first_block = offset / self.blocksize last_block = end / self.blocksize for n_block in range(first_block, last_block + 1): block_offset = n_block * self.blocksize insert_offset = 0 if n_block == first_block: insert_offset = offset - block_offset insert_end = self.blocksize if n_block == last_block: insert_end = end - block_offset data_start = block_offset + insert_offset - offset data_end = block_offset + insert_end - offset log.debug( 'Updating inode %d between (%d, %d) ' 'with data slice between (%d, %d)', n_block, insert_offset, insert_end, data_start, data_end) current_data = self.read_block(n_block) datafile = StringIO() datafile.write(current_data) datafile.seek(insert_offset) datafile.write(data[data_start:data_end]) self.write_block(n_block, datafile.getvalue()) if end > current_size: self['size'] = end def truncate(self, new_size): log.info("Truncating inode %s, new size %d", repr(self.name), new_size) current_size = self['size'] if current_size < new_size: # TODO: avoid creating one big string self.write_data('\0' * (new_size - current_size), current_size) elif current_size > new_size: first_block = new_size / self.blocksize last_block = current_size / self.blocksize truncate_offset = new_size % self.blocksize for n_block in range(first_block, last_block + 1): if n_block == first_block and truncate_offset > 0: old_data = self.read_block(n_block) self.write_block(n_block, old_data[:truncate_offset]) else: self.delete_block(n_block) self['size'] = new_size def unlink(self): log.info('Unlinking inode %r', self.name) nlink = self['nlink'] - 1 if nlink > 0: log.info('Links remaining for inode %r: %d', self.name, nlink) self['nlink'] = nlink else: log.info('Links remaining for inode %r: 0; removing.', self.name) self.storage._remove_inode(self.name) self.tree.remove() self.storage._autocommit()
class StorageInode(object): blocksize = 64*1024 # 64 KB #blocksize = 1024*1024 # 1024 KB default_meta = ('mode: 0100644\n' 'nlink: 1\n' 'uid: 0\n' 'gid: 0\n' 'size: 0\n') int_meta = ('nlink', 'uid', 'gid', 'size') oct_meta = ('mode',) def __init__(self, name, tree, storage): self.name = name self.tree = tree self.storage = storage self.tt = TreeTree(tree, prefix='bt') log.debug('Loaded inode %r', name) def _read_meta(self): try: meta_blob = self.tree['meta'] except KeyError: meta_raw = self.default_meta else: meta_raw = meta_blob.data return dict(line.split(': ', 1) for line in meta_raw.strip().split('\n')) def _write_meta(self, meta_data): meta_raw = ''.join('%s: %s\n' % (key, value) for key, value in sorted(meta_data.items())) self.tree.new_blob('meta').data = meta_raw self.storage._autocommit() def __getitem__(self, key): value = self._read_meta()[key] if key in self.oct_meta: value = int(value, base=8) elif key in self.int_meta: value = int(value) return value def __setitem__(self, key, value): if key in self.oct_meta: value = '0%o' % value elif key in self.int_meta: value = '%d' % value else: raise NotImplementedError meta_data = self._read_meta() meta_data[key] = value self._write_meta(meta_data) def read_block(self, n): block_name = str(n) log.debug('Reading block %r of inode %r', block_name, self.name) try: block = self.tt[block_name] except KeyError: return '' else: return block.data def write_block(self, n, data): block_name = str(n) log.debug('Writing block %r of inode %r', block_name, self.name) try: block = self.tt[block_name] except KeyError: block = self.tt.new_blob(block_name) block.data = data self.storage._autocommit() def delete_block(self, n): block_name = str(n) log.debug('Removing block %r of inode %r', block_name, self.name) del self.tt[block_name] self.storage._autocommit() def read_data(self, offset, length): end = offset + length eof = self['size'] if end > eof: end = eof length = end - offset if length <= 0: return '' first_block = offset / self.blocksize last_block = end / self.blocksize output = StringIO() for n_block in range(first_block, last_block+1): block_offset = n_block * self.blocksize fragment_offset = 0 if n_block == first_block: fragment_offset = offset - block_offset fragment_end = self.blocksize if n_block == last_block: fragment_end = end - block_offset block_data = self.read_block(n_block) fragment = block_data[fragment_offset:fragment_end] assert len(fragment) == fragment_end - fragment_offset output.write(fragment) output = output.getvalue() assert len(output) == length return output def write_data(self, data, offset): current_size = self['size'] if current_size < offset: self.truncate(offset) log.info('Inode %s writing %d bytes at offset %d', repr(self.name), len(data), offset) end = offset + len(data) first_block = offset / self.blocksize last_block = end / self.blocksize for n_block in range(first_block, last_block+1): block_offset = n_block * self.blocksize insert_offset = 0 if n_block == first_block: insert_offset = offset - block_offset insert_end = self.blocksize if n_block == last_block: insert_end = end - block_offset data_start = block_offset + insert_offset - offset data_end = block_offset + insert_end - offset log.debug('Updating inode %d between (%d, %d) ' 'with data slice between (%d, %d)', n_block, insert_offset, insert_end, data_start, data_end) current_data = self.read_block(n_block) datafile = StringIO() datafile.write(current_data) datafile.seek(insert_offset) datafile.write(data[data_start:data_end]) self.write_block(n_block, datafile.getvalue()) if end > current_size: self['size'] = end def truncate(self, new_size): log.info("Truncating inode %s, new size %d", repr(self.name), new_size) current_size = self['size'] if current_size < new_size: # TODO: avoid creating one big string self.write_data('\0' * (new_size - current_size), current_size) elif current_size > new_size: first_block = new_size / self.blocksize last_block = current_size / self.blocksize truncate_offset = new_size % self.blocksize for n_block in range(first_block, last_block+1): if n_block == first_block and truncate_offset > 0: old_data = self.read_block(n_block) self.write_block(n_block, old_data[:truncate_offset]) else: self.delete_block(n_block) self['size'] = new_size def unlink(self): log.info('Unlinking inode %r', self.name) nlink = self['nlink'] - 1 if nlink > 0: log.info('Links remaining for inode %r: %d', self.name, nlink) self['nlink'] = nlink else: log.info('Links remaining for inode %r: 0; removing.', self.name) self.storage._remove_inode(self.name) self.tree.remove() self.storage._autocommit()