S3QL / src / s3ql /

''' - this file is part of S3QL (

Copyright (C) 2008-2009 Nikolaus Rath <>

This program can be distributed under the terms of the GNU GPLv3.

from .logging import logging, setup_logging, QuietError
from .common import assert_fs_owner, PICKLE_PROTOCOL
from .parse_args import ArgumentParser
import llfuse
import os
import pickle
import stat
import sys
import textwrap

log = logging.getLogger(__name__)

def parse_args(args):
    '''Parse command line'''

    parser = ArgumentParser(
        Replicates the contents of the directory <source> in the
        directory <target>. <source> has to be an existing directory and
        <target>  must not exist. Both directories have to be within
        the same S3QL file system.

        The replication will not take any additional space. Only if one
        of directories is modified later on, the modified data will take
        additional storage space.


    parser.add_argument('source', help='source directory',
                        type=(lambda x: x.rstrip('/')))
    parser.add_argument('target', help='target directory',
                        type=(lambda x: x.rstrip('/')))

    options = parser.parse_args(args)

    return options

def main(args=None):
    '''Efficiently copy a directory tree'''

    if args is None:
        args = sys.argv[1:]

    options = parse_args(args)

    if not os.path.exists(options.source):
        raise QuietError('Source directory %r does not exist' % options.source)

    if os.path.exists(
        raise QuietError('Target directory %r must not yet exist.' %

    parent = os.path.dirname(os.path.abspath(
    if not os.path.exists(parent):
        raise QuietError('Target parent %r does not exist' % parent)

    fstat_s = os.stat(options.source)
    fstat_p = os.stat(parent)
    if not stat.S_ISDIR(fstat_s.st_mode):
        raise QuietError('Source %r is not a directory' % options.source)

    if not stat.S_ISDIR(fstat_p.st_mode):
        raise QuietError('Target parent %r is not a directory' % parent)

    if fstat_p.st_dev != fstat_s.st_dev:
        raise QuietError('Source and target are not on the same file system.')

    if os.path.ismount(options.source):
        raise QuietError('%s is a mount point.' % options.source)
    ctrlfile = assert_fs_owner(options.source)
    except PermissionError:
        raise QuietError('No permission to create target directory')

    fstat_t = os.stat(
    llfuse.setxattr(ctrlfile, 'copy', pickle.dumps((fstat_s.st_ino, fstat_t.st_ino),

if __name__ == '__main__':