def _create_empty_file_or_folder(self):
        self._fullname = FilePath(self._get_full_name())
        if not self._fullname:
            return

        data_dir = self._cfg.sync_directory if self._cfg else get_data_dir()
        self._in_data_dir = self._fullname in FilePath(data_dir)
        logger.debug("Adding special file %s", self._fullname)
        if self._in_data_dir:
            self._sync.add_special_file(self._fullname,
                                        self._on_special_file_event)

        special_file_created = False
        try:
            if self._is_folder:
                make_dirs(self._fullname, is_folder=True)
            else:
                create_empty_file(self._fullname)
            special_file_created = True
            self._update_spec_files(self._fullname, self._is_folder)
        except Exception as e:
            logger.warning("Can't create file or folder %s. Reason %s",
                           self._fullname, e)

        if not self._in_data_dir and special_file_created:
            self._sync.add_special_file(self._fullname,
                                        self._on_special_file_event)
        elif self._in_data_dir and not special_file_created:
            self._sync.remove_special_file(self._fullname)
Exemple #2
0
    def __init__(self, path_converter, db_file_created_cb=None):
        self._pc = path_converter

        self.possibly_sync_folder_is_removed = Signal()
        self.db_or_disk_full = Signal()

        self._db_file = self._pc.create_abspath('.pvtbox/storage.db')
        logger.debug("DB file: %s", self._db_file)
        new_db_file = not exists(self._db_file)
        if new_db_file and callable(db_file_created_cb):
            db_file_created_cb()

        make_dirs(self._db_file)

        if not new_db_file:
            # Database migration. It can be executed before opening db
            try:
                upgrade_db("storage_db", db_filename=self._db_file)
            except Exception as e:
                remove_file(self._db_file)
                new_db_file = True
                logger.warning(
                    "Can't upgrade storage db. "
                    "Reason: (%s) Creating...", e)
                if callable(db_file_created_cb):
                    db_file_created_cb()

        self._engine = create_engine('sqlite:///{}'.format(
            FilePath(self._db_file)),
                                     connect_args={
                                         'timeout': 60 * 1000,
                                         'check_same_thread': False,
                                     })
        self._engine.pool_timeout = 60 * 60 * 1000
        self._Session = sessionmaker(bind=self._engine)

        Base.metadata.create_all(self._engine, checkfirst=True)

        if new_db_file:
            try:
                stamp_db("storage_db", db_filename=self._db_file)
            except Exception as e:
                logger.error("Error stamping storage db: %s", e)

        self._lock = threading.RLock()
    def _move(self):
        data_dir = self._cfg.sync_directory if self._cfg else get_data_dir()
        downloads_dir = get_downloads_dir(data_dir=data_dir, create=True)
        download_name = op.join(downloads_dir, self._current_share_hash)
        if not self._renew_dest_dir():
            return

        dest_dir = self._dest_dirs.get(self._current_share_hash, data_dir)
        dest_name = op.join(dest_dir, self._current_share_name)
        dest_name = FilePath(dest_name).longpath
        dest_name = get_next_name(dest_name)
        logger.debug("Move '%s' to '%s'", download_name, dest_name)
        try:
            if FilePath(dest_dir) not in FilePath(data_dir):
                make_dirs(dest_name)
            shutil.move(download_name, dest_name)
        except IOError as e:
            logger.warning(
                "Can't move downloaded shared file to %s. "
                "Reason: %s", dest_name, e)
            self.cancel_share_download(self._current_share_name,
                                       folder_deleted=True)
 def _write_opened_port_to_accessible_file(self, port, port_file):
     make_dirs(port_file, False)
     with open(port_file, 'wb') as f:
         f.write('{}'.format(port).encode())
    def migrate(self, old_dir, new_dir):
        logger.info("Starting sync dir migration from %s, to %s",
                    old_dir, new_dir)
        old_dir = FilePath(old_dir).longpath
        new_dir = FilePath(new_dir).longpath
        old_files = get_filelist(old_dir)
        old_dirs = get_dir_list(old_dir)
        total_count = len(old_files) + len(old_dirs) + 1
        progress = 0
        sent_progress = 0
        logger.debug("Migration progress: %s/%s (%s%%)", 0, total_count, sent_progress)
        count = 1

        copied_dirs = []
        copied_files = []

        make_dirs(new_dir, is_folder=True)
        copied_dirs.append(new_dir)
        logger.debug("Migration progress: %s/%s (%s%%)", count, total_count, sent_progress)
        self.progress.emit(sent_progress)

        for dir in old_dirs:
            if self._cancelled.isSet():
                self._delete(dirs=copied_dirs)
                logger.debug("Migration done because cancelled")
                self.done.emit()
                return

            new_dir_path = ensure_unicode(op.join(
                new_dir, op.relpath(dir, start=old_dir)))

            try:
                make_dirs(new_dir_path, is_folder=True)
            except Exception as e:
                logger.error("Make dirs error: %s", e)
                self.failed.emit(str(e))
                self._delete(dirs=copied_dirs)
                return

            copied_dirs.append(new_dir_path)
            count += 1
            progress = int(count / total_count * 100)
            if progress > sent_progress:
                sent_progress = progress
                self.progress.emit(sent_progress)
            logger.debug("Migration progress: %s/%s (%s%%)", count, total_count, sent_progress)

        for file in old_files:
            if self._cancelled.isSet():
                self._delete(dirs=copied_dirs, files=copied_files)
                logger.debug("Migration done because cancelled")
                self.done.emit()
                return

            if file in HIDDEN_FILES:
                continue

            new_file_path = ensure_unicode(op.join(
                new_dir, op.relpath(file, start=old_dir)))

            logger.info("Copying file %s, to %s",
                        file, new_file_path)
            try:
                copy_file(file, new_file_path, preserve_file_date=True)
            except Exception as e:
                logger.error("Copy file error: %s", e)
                self.failed.emit(str(e))
                self._delete(dirs=copied_dirs, files=copied_files)
                return

            copied_files.append(new_file_path)
            count += 1
            progress = int(count / total_count * 100)
            if progress > sent_progress:
                sent_progress = progress
                self.progress.emit(sent_progress)
            logger.debug("Migration progress: %s/%s (%s%%)", count, total_count, sent_progress)

        logger.debug("Saving new config")
        self._cfg.set_settings(dict(sync_directory=FilePath(new_dir)))
        self._cfg.sync()
        logger.info("New config saved")

        logger.debug("Updating shortcuts")
        create_shortcuts(new_dir)
        remove_shortcuts(old_dir)
        logger.debug("Resetting custom folder icons")
        reset_all_custom_folder_icons(old_dir)

        logger.debug("Migration done")
        self.done.emit()

        logger.info("Migration thread end")
Exemple #6
0
 def update_file_signature(self, file, signature):
     signature_path = self._pc.create_abspath(file.signature_rel_path)
     make_dirs(signature_path)
     with open(signature_path, 'wb') as f:
         dump(signature, f, protocol=2)
Exemple #7
0
    def _on_upload_task_completed(self, upload_id_str, elapsed, total_str):
        """
        Slot to be called on upload task download completion

        @param upload_id_str ID of upload task [string]
        @param elapsed Time elapsed from download starting (in seconds) [float]
        @param total_str Size of file being downloaded (in bytes) [string]
        """

        upload_id = int(upload_id_str)
        state = self.download_tasks_info[upload_id]['state']

        upload_name = self.download_tasks_info[upload_id]['upload_name']
        if state == 'cancelled':
            logger.debug("Upload task %s cancelled", upload_id)
            self._on_upload_failed(upload_id)
            # Tray notification
            self.upload_cancelled.emit(upload_name)
            return
        elif state == 'paused':
            self.download_tasks_info[upload_id]['elapsed'] += elapsed
            return

        elapsed += self.download_tasks_info[upload_id]['elapsed']
        total = int(total_str)
        bps_avg = int(total / elapsed) if elapsed > 0 else 0
        bps_avg = "{:,}".format(bps_avg)
        logger.info(
            "Upload task ID '%s' complete (downloaded %s bytes in %s seconds"
            "(%s Bps))", upload_id_str, total_str, elapsed, bps_avg)

        # Calculate checksum
        tmp_fn = self.download_tasks_info[upload_id]['tmp_fn']
        checksum = self.download_tasks_info[upload_id]['upload_md5']
        try:
            logger.debug("Calculating checksum for upload task ID '%s'...",
                         upload_id)
            checksum_calculated = hashfile(tmp_fn)
        except Exception as e:
            logger.error("Failed to calculate checksum of '%s' (%s)", tmp_fn,
                         e)
            self._on_upload_failed(upload_id)
            return

        if self._tracker:
            self._tracker.http_download(upload_id, total, elapsed,
                                        checksum_calculated == checksum)

        # Validate checksum
        if checksum_calculated != checksum:
            logger.error("MD5 checkfum of '%s' is '%s' instead of '%s'",
                         tmp_fn, checksum_calculated, checksum)
            self._on_upload_failed(upload_id)
            return

        # Move file to its location
        path = self._check_upload_path(upload_id)
        if path is None:
            return
        path = FilePath(op.join(path, upload_name))
        fullpath = ensure_unicode(op.join(self._cfg.sync_directory, path))
        fullpath = FilePath(fullpath).longpath
        dirname = op.dirname(fullpath)
        if not op.isdir(dirname):
            logger.warning(
                "Destination directory %s"
                "does not exist for upload %s", dirname, fullpath)
            self._on_upload_failed(upload_id)
            return

        try:
            try:
                logger.info("Moving downloaded file '%s' to '%s'...", tmp_fn,
                            fullpath)
                # Create necessary directories
                make_dirs(fullpath)
                # Move file
                shutil.move(src=tmp_fn, dst=fullpath)
            except OSError as e:
                if e.errno != errno.EACCES:
                    raise e
                logger.warning(
                    "Can't move downloaded file '%s' into '%s' (%s)", tmp_fn,
                    dirname, e)
                fullpath = get_next_name(fullpath)
                shutil.move(src=tmp_fn, dst=fullpath)
        except Exception as e:
            logger.error("Failed to move downloaded file '%s' into '%s' (%s)",
                         tmp_fn, dirname, e)
            self._on_upload_failed(upload_id)
            return

        self.download_status.emit(*self._empty_progress,
                                  [{}, {}, [upload_id_str]], {})
        self._on_upload_complete(upload_id)
Exemple #8
0
def logging_setup(loglevel, logfilename=None, copies_logging=True):
    """
    Configures logging module

    @param loglevel Log level to be used [str]
    @param logfilename Name of file to save log into [str]
    """

    if set_verbose and loglevel == 'DEBUG':
        loglevel = VERBOSE

    config = load_config()
    set_root_directory(config.sync_directory)

    copies_file_prefix = 'copies_'
    if not logfilename:
        logfilename = time.strftime('%Y%m%d_%H%M%S.log')
        logfilename = get_bases_filename(root_directory, logfilename)

    copies_logs = sorted(glob.glob(
        get_bases_filename(
            root_directory,
            copies_file_prefix + '[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]'
            '_[0-9][0-9][0-9][0-9][0-9][0-9]*.log')),
                         reverse=True)
    if copies_logs:
        copies_filename = copies_logs[0]
    else:
        log_dir, log_file = split(logfilename)
        copies_filename = join(log_dir, copies_file_prefix + log_file)

    cfg = {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {
            'logfile': {
                'format':
                '[%(asctime)s %(levelname)s %(name)s:%(lineno)d] %(threadName)s(%(thread)d): %(message)s',  # noqa
            },
            'console': {
                'format':
                '[%(asctime)s %(levelname)s %(name)s:%(lineno)d] %(threadName)s(%(thread)d): %(message)s',  # noqa
            },
        },
        'handlers': {
            'console': {
                'class': 'logging.StreamHandler',
                'formatter': 'console',
                'stream': sys.stdout,
                'level': loglevel,
                'filters': ['console_filter'],
            },
            'file': {
                'formatter': 'logfile',
                'class': 'common.logging_setup.EconoRotatingFileHandler',
                'filename': logfilename,
                'logsCount': DEFAULT_LOGS_COUNT,
                'level': loglevel,
            },
        },
        'filters': {
            'console_filter': {
                '()': ConsoleFilter
            }
        },
        'loggers': {
            # for any logger
            '': {
                'handlers': [
                    'console',
                    'file',
                ],
                'level': loglevel,
            },
        },
    }

    if copies_logging:
        cfg['handlers']['copies_file'] = {
            'formatter': 'logfile',
            'class': 'common.logging_setup.EconoRotatingFileHandler',
            'filename': copies_filename,
            'logsCount': DEFAULT_COPIES_LOGS_COUNT,
            'file_name_prefix': copies_file_prefix,
            'level': loglevel,
        }
        cfg['loggers']['copies_logger'] = {
            'handlers': [
                'copies_file',
            ],
            'level': loglevel,
            'propagate': False,
        }

    make_dirs(logfilename, is_folder=False)

    logging.raiseExceptions = False

    logging.config.dictConfig(cfg)
Exemple #9
0
    def create_patch(cls,
                     modify_file,
                     root,
                     old_blocks_hashes=None,
                     new_blocks_hashes=None,
                     old_file_hash=None,
                     new_file_hash=None,
                     uuid=None,
                     blocksize=SIGNATURE_BLOCK_SIZE):
        def get_patch_filename(suffix):
            return os.path.join(
                get_patches_dir(root), 'patches',
                str(old_file_hash) + str(new_file_hash) + suffix)

        patch_data_file = get_patch_filename('.patch_data')

        # Create directory structure to store patch file
        make_dirs(patch_data_file)

        with open(modify_file, 'rb') as handle_file, \
                open(patch_data_file, 'wb') as data_file:
            blocks = SortedDict()
            patch = dict()
            new_blocks_hashes_search = dict()
            if old_blocks_hashes:
                old_blocks_hashes_search = \
                    dict((value, key) for key, value in
                         old_blocks_hashes.items())
            else:
                old_blocks_hashes_search = dict()
            if new_blocks_hashes is None:
                new_blocks_hashes = cls.block_checksum(filepath=modify_file,
                                                       blocksize=blocksize)
            for new_offset, new_hash in new_blocks_hashes.items():
                clone_block_offset = new_blocks_hashes_search.get(
                    new_hash, None)
                from_patch = clone_block_offset is not None
                clone_block_offset = clone_block_offset if from_patch \
                    else old_blocks_hashes_search.get(new_hash, None)
                if clone_block_offset is None:
                    data_file_offset = data_file.tell()
                    data = cls.get_data(handle=handle_file,
                                        size=blocksize,
                                        offset=new_offset)
                    data_file.write(data)
                    data_size = data_file.tell() - data_file_offset
                    blocks[new_offset] = dict(
                        new=True,
                        hash=new_hash,
                        offset=data_file_offset,
                        data_size=data_size,
                    )
                    new_blocks_hashes_search[new_hash] = new_offset
                else:
                    blocks[new_offset] = dict(new=False,
                                              hash=new_hash,
                                              offset=clone_block_offset,
                                              from_patch=from_patch)

        patch['old_hash'] = old_file_hash
        if new_file_hash is None:
            new_file_hash = Rsync.hash_from_block_checksum(new_blocks_hashes)
        patch['new_hash'] = new_file_hash

        info = cls.getfileinfo(modify_file)
        patch['blocks'] = blocks
        patch['time_modify'] = info.st_mtime
        patch['size'] = info.st_size
        patch['blocksize'] = blocksize

        patch_info_file = get_patch_filename('.patch_info')

        with open(patch_info_file, 'w') as info_file:
            json.dump(patch, info_file)

        if uuid is not None:
            patch_archive_file = op.join(get_patches_dir(root, create=True),
                                         uuid)
        else:
            patch_archive_file = get_patch_filename('.patch')

        with tarfile.open(patch_archive_file, 'w') as archive:
            archive.add(patch_info_file, arcname='info')
            archive.add(patch_data_file, arcname='data')
        remove_file(patch_info_file)
        remove_file(patch_data_file)

        patch['archive_file'] = patch_archive_file
        patch['archive_size'] = os.stat(patch_archive_file).st_size
        return patch