Permalink
Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upscripts/uncow.py /
Go to file| #!/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 | |