diff options
Diffstat (limited to 'src/s3ql/fsck.py')
-rw-r--r-- | src/s3ql/fsck.py | 113 |
1 files changed, 56 insertions, 57 deletions
diff --git a/src/s3ql/fsck.py b/src/s3ql/fsck.py index 6200759..ed1497d 100644 --- a/src/s3ql/fsck.py +++ b/src/s3ql/fsck.py @@ -8,10 +8,12 @@ This program can be distributed under the terms of the GNU GPLv3. from __future__ import division, print_function, absolute_import from .backends.common import NoSuchObject -from .common import ROOT_INODE, CTRL_INODE, inode_for_path, sha256_fh, get_path +from .common import ROOT_INODE, CTRL_INODE, inode_for_path, sha256_fh, get_path, BUFSIZE from .database import NoSuchRowError from os.path import basename -from s3ql.backends.common import CompressFilter +from random import randint +from .inode_cache import MIN_INODE, MAX_INODE, OutOfInodesError +import apsw import logging import os import re @@ -54,8 +56,7 @@ class Fsck(object): log.info('Creating temporary extra indices...') self.conn.execute('CREATE INDEX tmp1 ON blocks(obj_id)') self.conn.execute('CREATE INDEX tmp2 ON inode_blocks(block_id)') - self.conn.execute('CREATE INDEX tmp3 ON inodes(block_id)') - self.conn.execute('CREATE INDEX tmp4 ON contents(inode)') + self.conn.execute('CREATE INDEX tmp3 ON contents(inode)') try: self.check_foreign_keys() self.check_cache() @@ -71,7 +72,7 @@ class Fsck(object): self.check_keylist() finally: log.info('Dropping temporary indices...') - for idx in ('tmp1', 'tmp2', 'tmp3', 'tmp4'): + for idx in ('tmp1', 'tmp2', 'tmp3'): self.conn.execute('DROP INDEX %s' % idx) def log_error(self, *a, **kw): @@ -150,15 +151,14 @@ class Fsck(object): obj_id = self.conn.rowid('INSERT INTO objects (refcount) VALUES(1)') block_id = self.conn.rowid('INSERT INTO blocks (refcount, hash, obj_id, size) ' 'VALUES(?, ?, ?, ?)', (1, hash_, obj_id, size)) - with self.bucket.open_write('s3ql_data_%d' % obj_id) as dest: + def do_write(obj_fh): fh.seek(0) - shutil.copyfileobj(fh, dest) - - if isinstance(dest, CompressFilter): - obj_size = dest.compr_size - else: - obj_size = fh.tell() - self.conn.execute('UPDATE objects SET compr_size=? WHERE id=?', + shutil.copyfileobj(fh, obj_fh, BUFSIZE) + return obj_fh + + obj_size = self.bucket.perform_write(do_write, 's3ql_data_%d' % obj_id).get_obj_size() + + self.conn.execute('UPDATE objects SET size=? WHERE id=?', (obj_size, obj_id)) else: @@ -166,31 +166,18 @@ class Fsck(object): try: - if blockno == 0: - old_block_id = self.conn.get_val('SELECT block_id FROM inodes ' - 'WHERE id=? AND block_id IS NOT NULL', (inode,)) - else: - old_block_id = self.conn.get_val('SELECT block_id FROM inode_blocks ' - 'WHERE inode=? AND blockno=?', (inode, blockno)) + old_block_id = self.conn.get_val('SELECT block_id FROM inode_blocks ' + 'WHERE inode=? AND blockno=?', (inode, blockno)) except NoSuchRowError: - if blockno == 0: - self.conn.execute('UPDATE inodes SET block_id=? WHERE id=?', - (block_id, inode)) - else: - self.conn.execute('INSERT INTO inode_blocks (block_id, inode, blockno) VALUES(?,?,?)', - (block_id, inode, blockno)) + self.conn.execute('INSERT INTO inode_blocks (block_id, inode, blockno) VALUES(?,?,?)', + (block_id, inode, blockno)) else: - if blockno == 0: - self.conn.execute('UPDATE inodes SET block_id=? WHERE id=?', - (block_id, inode)) - else: - self.conn.execute('UPDATE inode_blocks SET block_id=? WHERE inode=? AND blockno=?', - (block_id, inode, blockno)) + self.conn.execute('UPDATE inode_blocks SET block_id=? WHERE inode=? AND blockno=?', + (block_id, inode, blockno)) # We just decrease the refcount, but don't take any action # because the reference count might be wrong - self.conn.execute('UPDATE blocks SET refcount=refcount-1 WHERE id=?', - (old_block_id,)) + self.conn.execute('UPDATE blocks SET refcount=refcount-1 WHERE id=?', (old_block_id,)) self.unlinked_blocks.add(old_block_id) fh.close() @@ -210,10 +197,9 @@ class Fsck(object): except NoSuchRowError: self.found_errors = True self.log_error("Recreating missing lost+found directory") - inode_l = self.conn.rowid("INSERT INTO inodes (mode,uid,gid,mtime,atime,ctime,refcount) " - "VALUES (?,?,?,?,?,?,?)", - (stat.S_IFDIR | stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR, - os.getuid(), os.getgid(), timestamp, timestamp, timestamp, 1)) + inode_l = self.create_inode(mode=stat.S_IFDIR|stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR, + atime=timestamp, ctime=timestamp, mtime=timestamp, + refcount=1) self.conn.execute("INSERT INTO contents (name_id, inode, parent_inode) VALUES(?,?,?)", (self._add_name(b"lost+found"), inode_l, ROOT_INODE)) @@ -225,10 +211,9 @@ class Fsck(object): '/lost+found/inode-%s*', inode_l) # We leave the old inode unassociated, so that it will be added # to lost+found later on. - inode_l = self.conn.rowid("INSERT INTO inodes (mode,uid,gid,mtime,atime,ctime,refcount) " - "VALUES (?,?,?,?,?,?,?)", - (stat.S_IFDIR | stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR, - os.getuid(), os.getgid(), timestamp, timestamp, timestamp, 1)) + inode_l = self.create_inode(mode=stat.S_IFDIR|stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR, + atime=timestamp, ctime=timestamp, mtime=timestamp, + refcount=1) self.conn.execute('UPDATE contents SET inode=? WHERE name_id=? AND parent_inode=?', (inode_l, name_id, ROOT_INODE)) @@ -287,7 +272,7 @@ class Fsck(object): self.conn.execute(''' INSERT INTO min_sizes (id, min_size) SELECT inode, MAX(blockno * ? + size) - FROM inode_blocks_v JOIN blocks ON block_id == blocks.id + FROM inode_blocks JOIN blocks ON block_id == blocks.id GROUP BY inode''', (self.blocksize,)) self.conn.execute(''' @@ -379,7 +364,7 @@ class Fsck(object): self.conn.execute(''' INSERT INTO refcounts (id, refcount) SELECT block_id, COUNT(blockno) - FROM inode_blocks_v + FROM inode_blocks GROUP BY block_id ''') @@ -407,11 +392,12 @@ class Fsck(object): (id_p, name) = self.resolve_free(b"/lost+found", b"block-%d" % id_) self.log_error("Block %d not referenced, adding as /lost+found/%s", id_, name) timestamp = time.time() - time.timezone - inode = self.conn.rowid(""" - INSERT INTO inodes (mode,uid,gid,mtime,atime,ctime,refcount,block_id) - VALUES (?,?,?,?,?,?,?,?)""", - (stat.S_IFREG | stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR, - os.getuid(), os.getgid(), timestamp, timestamp, timestamp, 1, id_)) + size = self.conn.get_val('SELECT size FROM blocks WHERE id=?', (id_,)) + inode = self.create_inode(mode=stat.S_IFREG|stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR, + mtime=timestamp, atime=timestamp, ctime=timestamp, + refcount=1, size=size) + self.conn.execute('INSERT INTO inode_blocks (inode, blockno, block_id) VALUES(?,?,?)', + (inode, 0, id_)) self.conn.execute("INSERT INTO contents (name_id, inode, parent_inode) VALUES (?,?,?)", (self._add_name(basename(name)), inode, id_p)) self.conn.execute("UPDATE blocks SET refcount=? WHERE id=?", (1, id_)) @@ -426,6 +412,26 @@ class Fsck(object): self.conn.execute('DROP TABLE IF EXISTS wrong_refcounts') + def create_inode(self, mode, uid=os.getuid(), gid=os.getgid(), + mtime=None, atime=None, ctime=None, refcount=None, + size=0): + '''Create inode with id fitting into 32bit''' + + for _ in range(100): + id_ = randint(MIN_INODE, MAX_INODE) + try: + self.conn.execute('INSERT INTO inodes (id, mode,uid,gid,mtime,atime,ctime,' + 'refcount,size) VALUES (?,?,?,?,?,?,?,?,?)', + (id_, mode, uid, gid, mtime, atime, ctime, refcount, size)) + except apsw.ConstraintError: + pass + else: + break + else: + raise OutOfInodesError() + + return id_ + def check_name_refcount(self): """Check name reference counters""" @@ -522,9 +528,7 @@ class Fsck(object): inode, get_path(inode, self.conn)) if (not stat.S_ISREG(mode) and - self.conn.has_val('SELECT 1 FROM inode_blocks WHERE inode=? ' - 'UNION SELECT 1 FROM inodes WHERE id=? ' - 'AND block_id IS NOT NULL', (inode, inode))): + self.conn.has_val('SELECT 1 FROM inode_blocks WHERE inode=?', (inode,))): self.found_errors = True self.log_error('Inode %d (%s) is not a regular file but has data blocks. ' 'This is probably going to confuse your system!', @@ -627,11 +631,7 @@ class Fsck(object): self.log_error("object %s only exists in table but not in bucket, deleting", obj_id) for (id_,) in self.conn.query('SELECT inode FROM inode_blocks JOIN blocks ON block_id = id ' - 'WHERE obj_id=? ' - 'UNION ' - 'SELECT inodes.id FROM inodes JOIN blocks ON block_id = blocks.id ' - 'WHERE obj_id=? AND block_id IS NOT NULL', - (obj_id, obj_id)): + 'WHERE obj_id=? ', (obj_id,)): # Same file may lack several blocks, but we want to move it # only once @@ -654,7 +654,6 @@ class Fsck(object): # Unlink missing blocks for (block_id,) in self.conn.query('SELECT id FROM blocks WHERE obj_id=?', (obj_id,)): self.conn.execute('DELETE FROM inode_blocks WHERE block_id=?', (block_id,)) - self.conn.execute('UPDATE inodes SET block_id = NULL WHERE block_id=?', (block_id,)) self.conn.execute("DELETE FROM blocks WHERE obj_id=?", (obj_id,)) self.conn.execute("DELETE FROM objects WHERE id=?", (obj_id,)) |