Exemplo n.º 1
0
def main():
    global logger, crypt, tardis, args, owMode
    args = parseArgs()
    logger = Util.setupLogging(args.verbose, stream=sys.stderr)

    try:
        password = Util.getPassword(args.password, args.passwordfile, args.passwordprog, prompt="Password for %s: " % (args.client))
        args.password = None
        (tardis, cache, crypt) = Util.setupDataConnection(args.database, args.client, password, args.keys, args.dbname, args.dbdir)

        r = Regenerator.Regenerator(cache, tardis, crypt=crypt)
    except TardisDB.AuthenticationException as e:
        logger.error("Authentication failed.  Bad password")
        #if args.exceptions:
            #logger.exception(e)
        sys.exit(1)
    except Exception as e:
        logger.error("Regeneration failed: %s", e)
        sys.exit(1)

    try:
        bset = False

        if args.date:
            cal = parsedatetime.Calendar()
            (then, success) = cal.parse(args.date)
            if success:
                timestamp = time.mktime(then)
                logger.info("Using time: %s", time.asctime(then))
                bsetInfo = tardis.getBackupSetInfoForTime(timestamp)
                if bsetInfo and bsetInfo['backupset'] != 1:
                    bset = bsetInfo['backupset']
                    logger.debug("Using backupset: %s %d", bsetInfo['name'], bsetInfo['backupset'])
                else:
                    logger.critical("No backupset at date: %s (%s)", args.date, time.asctime(then))
                    sys.exit(1)
            else:
                logger.critical("Could not parse date string: %s", args.date)
                sys.exit(1)
        elif args.backup:
            #bsetInfo = tardis.getBackupSetInfo(args.backup)
            bsetInfo = Util.getBackupSet(tardis, args.backup)
            if bsetInfo:
                bset = bsetInfo['backupset']
            else:
                logger.critical("No backupset at for name: %s", args.backup)
                sys.exit(1)

        outputdir = None
        output    = sys.stdout.buffer
        outname   = None
        linkDB    = None

        owMode    = overwriteNames[args.overwrite]

        if args.output:
            if len(args.files) > 1:
                outputdir = mkOutputDir(args.output)
            elif os.path.isdir(args.output):
                outputdir = args.output
            else:
                outname = args.output
        logger.debug("Outputdir: %s  Outname: %s", outputdir, outname)

        if args.hardlinks:
            linkDB = {}

        #if args.cksum and (args.settime or args.setperm):
            #logger.warning("Unable to set time or permissions on files specified by checksum.")

        permChecker = setupPermissionChecks()

        retcode = 0
        hasher = None

        # do the work here
        if args.cksum:
            for i in args.files:
                try:
                    if args.auth:
                        hasher = Util.getHash(crypt)
                    ckname = i
                    if args.recovername:
                        ckname = recoverName(i)
                    f = r.recoverChecksum(i, args.auth)
                    if f:
                        logger.info("Recovering checksum %s", ckname)
                    # Generate an output name
                        if outname:
                            # Note, this should ONLY be true if only one file
                            output = open(outname,  "wb")
                        elif outputdir:
                            outname = os.path.join(outputdir, ckname)
                            if os.path.exists(outname) and owMode == OW_NEVER:
                                logger.warning("File %s exists.  Skipping", outname)
                                continue
                            logger.debug("Writing output to %s", outname)
                            output = open(outname,  "wb")
                        elif outname:
                            # Note, this should ONLY be true if only one file
                            if os.path.exists(outname) and owMode == OW_NEVER:
                                logger.warning("File %s exists.  Skipping", outname)
                                continue
                            output = file(outname,  "wb")
                        try:
                            x = f.read(64 * 1024)
                            while x:
                                output.write(x)
                                if hasher:
                                    hasher.update(x)
                                x = f.read(64 * 1024)
                        except Exception as e:
                            logger.error("Unable to read file: {}: {}".format(i, repr(e)))
                            raise
                        finally:
                            f.close()
                            if output is not sys.stdout.buffer:
                                output.close()
                        if args.auth:
                            logger.debug("Checking authentication")
                            outname = doAuthenticate(outname, i, hasher.hexdigest())

                except TardisDB.AuthenticationException as e:
                    logger.error("Authentication failed.  Bad password")
                    #if args.exceptions:
                        #logger.exception(e)
                    sys.exit(1)
                except Exception as e:
                    logger.error("Could not recover: %s: %s", i, e)
                    if args.exceptions:
                        logger.exception(e)
                    retcode += 1

        else: # Not checksum, but acutal pathnames
            for i in args.files:
                try:
                    i = os.path.abspath(i)
                    logger.info("Processing %s", Util.shortPath(i))
                    path = None
                    f = None
                    if args.last:
                        (bset, path, name) = findLastPath(i, args.reduce)
                        if bset is None:
                            logger.error("Unable to find a latest version of %s", i)
                            raise Exception("Unable to find a latest version of " + i)
                        logger.info("Found %s in backup set %s", i, name)
                    elif args.reduce:
                        path = Util.reducePath(tardis, bset, i, args.reduce, crypt)
                        logger.debug("Reduced path %s to %s", path, i)
                        if not path:
                            logger.error("Unable to find a compute path for %s", i)
                            raise Exception("Unable to compute path for " + i)
                    else:
                        path = i

                    if args.crypt and crypt:
                        actualPath = crypt.encryptPath(path)
                    else:
                        actualPath = path
                    logger.debug("Actual path is %s -- %s", actualPath, bset)
                    info = tardis.getFileInfoByPath(actualPath, bset)
                    if info:
                        retcode += recoverObject(r, info, bset, outputdir, path, linkDB, name=outname, authenticate=args.auth)
                    else:
                        logger.error("Could not recover info for %s (File not found)", i)
                        retcode += 1
                except TardisDB.AuthenticationException as e:
                    logger.error("Authentication failed.  Bad password")
                    #if args.exceptions:
                        #logger.exception(e)
                    sys.exit(1)
                except Exception as e:
                    logger.error("Could not recover: %s: %s", i, e)
                    if args.exceptions:
                        logger.exception(e)
    except KeyboardInterrupt:
        logger.error("Recovery interupted")
    except TardisDB.AuthenticationException as e:
        logger.error("Authentication failed.  Bad password")
        if args.exceptions:
            logger.exception(e)
    except Exception as e:
        logger.error("Regeneration failed: %s", e)
        if args.exceptions:
            logger.exception(e)

    if errors:
        logger.warning("%d files could not be recovered.")

    return retcode
Exemplo n.º 2
0
def recoverObject(regenerator, info, bset, outputdir, path, linkDB, name=None, authenticate=True):
    """
    Main recovery routine.  Recover an object, based on the info object, and put it in outputdir.
    """
    retCode = 0
    outname = None
    skip = False
    hasher = None
    try:
        if info:
            realname = info['name']
            if args.crypt and crypt:
                realname = crypt.decryptFilename(realname)

            if name:
                # This should only happen only one file specified.
                outname = name
            elif outputdir:
                outname = os.path.abspath(os.path.join(outputdir, realname))

            if outname and not checkOverwrite(outname, info):
                skip = True
                try:
                    logger.warning("Skipping existing file: %s %s", Util.shortPath(path), notSame(path, outname, '(' + Util.shortPath(outname) + ')'))
                except Exception:
                    pass

            # First, determine if we're in a linking situation
            if linkDB is not None and info['nlinks'] > 1 and not info['dir']:
                key = (info['inode'], info['device'])
                if key in linkDB:
                    logger.info("Linking %s to %s", outname, linkDB[key])
                    os.link(linkDB[key], outname)
                    skip = True
                else:
                    linkDB[key] = outname

            # If it's a directory, create the directory, and recursively process it
            if info['dir']:
                if not outname:
                    #logger.error("Cannot regenerate directory %s without outputdir specified", path)
                    raise Exception("Cannot regenerate directory %s without outputdir specified" % (path))

                try:
                    logger.info("Processing directory %s", Util.shortPath(path))
                except Exception:
                    pass

                contents = list(tardis.readDirectory((info['inode'], info['device']), bset))

                # Make sure an output directory is specified (really only useful at the top level)
                if not os.path.exists(outname):
                    os.mkdir(outname)

                dirInode = (info['inode'], info['device'])
                # For each file in the directory, regenerate it.
                for i in contents:
                    name = i['name']
                    # Get the Info
                    childInfo = tardis.getFileInfoByName(name, dirInode, bset)

                    # Decrypt filename, and make it UTF-8.
                    if args.crypt and crypt:
                        name = crypt.decryptFilename(name)
                    else:
                        name = name

                    # Recurse into the child, if it exists.
                    if childInfo:
                        try:
                            if args.recurse or not childInfo['dir']:
                                recoverObject(regenerator, childInfo, bset, outname, os.path.join(path, name), linkDB, authenticate=authenticate)
                        except Exception as e:
                            logger.error("Could not recover %s in %s", name, path)
                            if args.exceptions:
                                logger.exception(e)
                    else:
                        retCode += 1
            elif not skip:
                myname = outname if outname else "stdout"
                logger.info("Recovering file %s %s", Util.shortPath(path), notSame(path, myname, " => " + Util.shortPath(myname)))

                checksum = info['checksum']
                i = regenerator.recoverChecksum(checksum, authenticate)

                if i:
                    if authenticate:
                        hasher = Util.getHash(crypt)

                    if info['link']:
                        # read and make a link
                        i.seek(0)
                        x = i.read(16 * 1024)
                        if outname:
                            os.symlink(x, outname)
                        else:
                            logger.warning("No name specified for link: %s", x)
                        if hasher:
                            hasher.update(x)
                    else:
                        if outname:
                            # Generate an output name
                            logger.debug("Writing output to %s", outname)
                            output = open(outname,  "wb")
                        else:
                            output = sys.stdout.buffer
                        try:
                            x = i.read(16 * 1024)
                            while x:
                                output.write(x)
                                if hasher:
                                    hasher.update(x)
                                x = i.read(16 * 1024)
                        except Exception as e:
                            logger.error("Unable to read file: {}: {}".format(i, repr(e)))
                            raise
                        finally:
                            i.close()
                            if output is not sys.stdout.buffer:
                                output.close()

                        if authenticate:
                            outname = doAuthenticate(outname, checksum, hasher.hexdigest())

            if outname and not skip:
                if args.setperm:
                    try:
                        logger.debug("Setting permissions on %s to %o", outname, info['mode'])
                        os.chmod(outname, info['mode'])
                    except Exception as e:
                        logger.warning("Unable to set permissions for %s", outname)
                    try:
                        # Change the group, then the owner.
                        # Change the group first, as only root can change owner, and that might fail.
                        os.chown(outname, -1, info['gid'])
                        os.chown(outname, info['uid'], -1)
                    except Exception as e:
                        logger.warning("Unable to set owner and group of %s", outname)
                if args.settime:
                    try:
                        logger.debug("Setting times on %s to %d %d", outname, info['atime'], info['mtime'])
                        os.utime(outname, (info['atime'], info['mtime']))
                    except Exception as e:
                        logger.warning("Unable to set times on %s", outname)

                if args.setattrs and 'attr' in info and info['attr']:
                    try:
                        f = regenerator.recoverChecksum(info['attr'], authenticate)
                        xattrs = json.loads(f.read())
                        x = xattr.xattr(outname)
                        for attr in xattrs.keys():
                            value = base64.b64decode(xattrs[attr])
                            try:
                                x.set(attr, value)
                            except IOError:
                                logger.warning("Unable to set extended attribute %s on %s", attr, outname)
                    except Exception as e:
                        logger.warning("Unable to process extended attributes for %s", outname)
                if args.setacl and 'acl' in info and info['acl']:
                    try:
                        f = regenerator.recoverChecksum(info['acl'], authenticate)
                        acl = json.loads(f.read())
                        a = posix1e.ACL(text=acl)
                        a.applyto(outname)
                    except Exception as e:
                        logger.warning("Unable to process extended attributes for %s", outname)
    except Exception as e:
        logger.error("Recovery of %s failed. %s", outname, e)
        if args.exceptions:
            logger.exception(e)
        retCode += 1

    return retCode