diff --git a/data/helpers.d/filesystem b/data/helpers.d/filesystem index 087a9e944..9fd631e05 100644 --- a/data/helpers.d/filesystem +++ b/data/helpers.d/filesystem @@ -1,4 +1,4 @@ -CAN_BIND=1 +CAN_BIND=${CAN_BIND:-1} # Bind a directory or copy it on error # @@ -7,25 +7,36 @@ CAN_BIND=1 # | arg: destdir - mountpoint or destination directory # | arg: as_root - 1 to execute commands as root ynh_bind_or_cp() { - SRCDIR=$1 - DESTDIR=$2 - SUDO_CMD="sudo" - [[ "$3" != "1" ]] && SUDO_CMD="" + local SRCDIR=$1 + local DESTDIR=$2 + local SUDO_CMD= + [[ "${3}" = "1" ]] && SUDO_CMD="sudo" - if [[ $CAN_BIND == 1 ]]; then - $SUDO_CMD mkdir -p $DESTDIR - $SUDO_CMD mount --bind "$SRCDIR" "$DESTDIR" - if [[ $? == 0 ]]; then - for m in $(mount | grep " $SRCDIR" | awk '{ print $3 }'); do - $SUDO_CMD mount --bind "$m" "${DESTDIR}${m#${SRCDIR}}" - done - return + [[ ! -f "${DESTDIR}" ]] || { + echo "Destination directory '${DESTDIR}' already exists" >&2 + return 1 + } + + # attempt to bind mounting the directory + if [[ "${CAN_BIND}" = "1" ]]; then + eval $SUDO_CMD mkdir -p "${DESTDIR}" + + if sudo mount --rbind "${SRCDIR}" "${DESTDIR}"; then + return 0 + else + CAN_BIND=0 + echo "Bind mounting seems to be disabled on your system." + echo "You have maybe to check your apparmor configuration." fi - echo "Error: bind mounting seems to be disabled on your system." - echo "You have maybe to check your apparmor configuration." - CAN_BIND=0 + + # delete mountpoint directory safely + mountpoint -q "${DESTDIR}" && sudo umount -R "${DESTDIR}" + eval $SUDO_CMD rm -rf "${DESTDIR}" fi - $SUDO_CMD cp -r "$SRCDIR" "$DESTDIR" + + # ... or just copy the directory + eval $SUDO_CMD mkdir -p $(dirname "${DESTDIR}") + eval $SUDO_CMD cp -a "${SRCDIR}" "${DESTDIR}" } # Create a directory under /tmp diff --git a/src/yunohost/backup.py b/src/yunohost/backup.py index 4c3ca3fb6..17d0064dd 100644 --- a/src/yunohost/backup.py +++ b/src/yunohost/backup.py @@ -73,6 +73,7 @@ def backup_create(name=None, description=None, output_directory=None, """ # TODO: Add a 'clean' argument to clean output directory tmp_dir = None + env_var = {} # Validate what to backup if ignore_hooks and ignore_apps: @@ -110,9 +111,13 @@ def backup_create(name=None, description=None, output_directory=None, raise MoulinetteError(errno.EIO, m18n.n('backup_output_directory_not_empty')) - # Define temporary directory + # Do not compress, so set temporary directory to output one and + # disable bind mounting to prevent data loss in case of a rm + # See: https://dev.yunohost.org/issues/298 if no_compress: + logger.debug('bind mounting will be disabled') tmp_dir = output_directory + env_var['CAN_BIND'] = 0 else: output_directory = archives_path @@ -159,7 +164,8 @@ def backup_create(name=None, description=None, output_directory=None, if not hooks or hooks_filtered: logger.info(m18n.n('backup_running_hooks')) - ret = hook_callback('backup', hooks_filtered, args=[tmp_dir]) + ret = hook_callback('backup', hooks_filtered, args=[tmp_dir], + env=env_var) if ret['succeed']: info['hooks'] = ret['succeed'] @@ -216,8 +222,9 @@ def backup_create(name=None, description=None, output_directory=None, subprocess.call(['install', '-Dm555', app_script, tmp_script]) # Prepare env. var. to pass to script - env_dict = {} - app_id, app_instance_nb = _parse_app_instance_name(app_instance_name) + app_id, app_instance_nb = _parse_app_instance_name( + app_instance_name) + env_dict = env_var.copy() env_dict["YNH_APP_ID"] = app_id env_dict["YNH_APP_INSTANCE_NAME"] = app_instance_name env_dict["YNH_APP_INSTANCE_NUMBER"] = str(app_instance_nb)