scripts/uncow.py at master · stsquad/scripts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| #!/usr/bin/python | |
| # -*- coding: utf-8 -*- | |
| # | |
| # uncow, a simple script to uncow files copied into a fresh btrfs subvolume | |
| # | |
| # (C)opyright 2012 Alex Bennée | |
| # License: GPLv3 | |
| # | |
| # Usage: | |
| # - create a new btrfs subvolume with nodatacow mount option | |
| # - move all the data you want into the new subvolume | |
| # - find <SPEC> | xargs uncow.py | |
| # | |
| # What the script does: | |
| # - create a new file | |
| # - copy the contents of the old file to new | |
| # - rm the old file | |
| # - rename the new file to the old files name | |
| # | |
| import sys,os | |
| import argparse | |
| from uuid import uuid3, NAMESPACE_URL | |
| from shutil import copyfile | |
| from subprocess import check_output,check_call,STDOUT,CalledProcessError | |
| from re import search | |
| from array import array | |
| from fcntl import ioctl | |
| # IOCTL constants, ganked from strace of lsattr/chattr | |
| FS_GET_FLAGS=0x80086601 | |
| FS_SET_FLAGS=0x40086602 | |
| FS_NOWCOW_FL=0x00800000 | |
| checked_dirs=dict() | |
| def check_attributes(verbose, directory): | |
| flags=array('I', [0]) | |
| fd=os.open(directory, os.O_DIRECTORY) | |
| ioctl(fd, FS_GET_FLAGS, flags, 1) | |
| if verbose: print "check_for_nowcow(%s) => %x" % (directory, flags[0]) | |
| os.close(fd) | |
| return flags[0] | |
| def check_and_modify_dir(verbose, directory): | |
| """ | |
| check directory has +C attribute, if not set it | |
| """ | |
| if directory in checked_dirs: | |
| return | |
| flags=check_attributes(verbose, directory) | |
| if flags & FS_NOWCOW_FL: | |
| checked_dirs[directory]=1 | |
| else: | |
| fd=os.open(directory, os.O_DIRECTORY) | |
| new_flags=array('I', [flags|FS_NOWCOW_FL]) | |
| if verbose: print "setting +C/%x for %s" % (new_flags[0], directory) | |
| try: | |
| ioctl(fd, FS_SET_FLAGS, new_flags) | |
| except: | |
| print "Error setting +C for %s" % (directory) | |
| sys.exit(-2) | |
| if verbose: print "new flags now %x" % (check_attributes(verbose, directory)) | |
| os.close(fd) | |
| parser = argparse.ArgumentParser(description='Create new non-COW files with old data.', | |
| epilog='If passed a directory instead of a file it will just make '+ | |
| 'the attributes changes for new files') | |
| parser.add_argument('files', metavar='FILE', nargs='+', help='filepath to uncow') | |
| parser.add_argument('-v', '--verbose', action='store_true', default=False, help="Verbose output") | |
| args = parser.parse_args() | |
| for p in args.files: | |
| absp = os.path.abspath(p) | |
| if os.path.exists(absp): | |
| if os.path.isdir(absp): | |
| check_and_modify_dir(args.verbose, absp) | |
| elif os.path.isfile(absp): | |
| old_filename=os.path.basename(absp) | |
| dirname=os.path.dirname(absp) | |
| check_and_modify_dir(args.verbose, dirname) | |
| new_filename="%s/%s-%s" % (dirname, old_filename, uuid3(NAMESPACE_URL, old_filename)) | |
| try: | |
| if args.verbose: print "creating new file %s" % (new_filename) | |
| copyfile(absp, new_filename) | |
| if args.verbose: print "removing old file %s" % (absp) | |
| os.unlink(absp) | |
| if args.verbose: print "renaming %s to %s" % (new_filename, absp) | |
| os.rename(new_filename, absp) | |
| except: | |
| print "error with %s, %s" % (absp, new_filename) | |
| exit -1 | |
| else: | |
| print "can't operate on things that aren't directories or files (%s)" % (absp) | |
| exit -1 | |