Просмотр исходного кода

dpkg-maintscript-helper: Add new dir_to_symlink command

Closes: #583585
Guillem Jover лет назад: 13
Родитель
Сommit
4b870bcc52
3 измененных файлов с 150 добавлено и 2 удалено
  1. 1 0
      debian/changelog
  2. 33 1
      man/dpkg-maintscript-helper.1
  3. 116 1
      scripts/dpkg-maintscript-helper.sh

+ 1 - 0
debian/changelog

@@ -81,6 +81,7 @@ dpkg (1.17.2) UNRELEASED; urgency=low
     we might get us to read garbage from memory or segfault.
     we might get us to read garbage from memory or segfault.
   * Add new symlink_to_dir command to dpkg-maintscript-helper. Closes: #720712
   * Add new symlink_to_dir command to dpkg-maintscript-helper. Closes: #720712
     Based on a patch by Bastien ROUCARIÈS <roucaries.bastien@gmail.com>.
     Based on a patch by Bastien ROUCARIÈS <roucaries.bastien@gmail.com>.
+  * Add new dir_to_symlink command to dpkg-maintscript-helper. Closes: #583585
 
 
   [ Updated programs translations ]
   [ Updated programs translations ]
   * German (Sven Joachim).
   * German (Sven Joachim).

+ 33 - 1
man/dpkg-maintscript-helper.1

@@ -30,6 +30,8 @@ dpkg\-maintscript\-helper \- works around known dpkg limitations in maintainer s
 \fBmv_conffile\fP \fIold-conffile\fP \fInew-conffile\fP [\fIprior-version\fP [\fIpackage\fP]]
 \fBmv_conffile\fP \fIold-conffile\fP \fInew-conffile\fP [\fIprior-version\fP [\fIpackage\fP]]
 .P
 .P
 \fBsymlink_to_dir\fP \fIpathname\fP \fIold-target\fP [\fIprior-version\fP [\fIpackage\fP]]
 \fBsymlink_to_dir\fP \fIpathname\fP \fIold-target\fP [\fIprior-version\fP [\fIpackage\fP]]
+.P
+\fBdir_to_symlink\fP \fIpathname\fP \fInew-target\fP [\fIprior-version\fP [\fIpackage\fP]]
 .
 .
 .SH DESCRIPTION
 .SH DESCRIPTION
 .P
 .P
@@ -172,13 +174,43 @@ the \fBpostinst\fP removes \fIpathname\fP\fB.dpkg\-backup\fP if
 abort\-upgrade/abort\-install, the \fBpostrm\fP renames
 abort\-upgrade/abort\-install, the \fBpostrm\fP renames
 \fIpathname\fP\fB.dpkg\-backup\fP back to \fIpathname\fP if required.
 \fIpathname\fP\fB.dpkg\-backup\fP back to \fIpathname\fP if required.
 .
 .
+.SS Switching a directory to symlink
+.
+If a real directory is converted to a symlink, you need to make sure
+before unpacking that the directory is removed. This may seem a simple
+change to the \fBpreinst\fP script at first, however that will result
+in some problems in case the directory contains conffiles, pathnames
+owned by other packages, locally created pathnames, or when downgrading
+the package.
+.P
+Graceful switching can be implemented by putting the following shell
+snippet in the \fBpreinst\fP, \fBpostinst\fP and \fBpostrm\fP maintainer
+scripts:
+.P
+    dpkg\-maintscript\-helper dir_to_symlink \\
+        \fIpathname\fP \fInew-target\fP \fIprior-version\fP \fIpackage\fP \-\- "$@"
+.P
+\fIpathname\fP is the name of the of the old directory (the path will be a
+symlink at the end of the installation) and \fInew-target\fP is the target
+of the new symlink at \fIpathname\fP.
+.P
+Current implementation: the \fBpreinst\fP checks if the directory
+exists, does not contain conffiles, pathnames owned by other packages,
+or locally created pathnames, if not then it's left in place, otherwise
+it's renamed to \fIpathname\fP\fB.dpkg\-backup\fP. On configuration,
+the \fBpostinst\fP removes \fIpathname\fP\fB.dpkg\-backup\fP if
+\fIpathname\fP\fB.dpkg\-backup\fP is still a directory. On
+abort\-upgrade/abort\-install, the \fBpostrm\fP renames
+\fIpathname\fP\fB.dpkg\-backup\fP back to \fIpathname\fP if required.
+.
 .SH INTEGRATION IN PACKAGES
 .SH INTEGRATION IN PACKAGES
 .P
 .P
 Given that \fBdpkg\-maintscript\-helper\fP is used in the \fBpreinst\fP,
 Given that \fBdpkg\-maintscript\-helper\fP is used in the \fBpreinst\fP,
 using it unconditionally requires a pre-dependency to ensure that the
 using it unconditionally requires a pre-dependency to ensure that the
 required version of \fBdpkg\fP has been unpacked before. The required version
 required version of \fBdpkg\fP has been unpacked before. The required version
 depends on the command used, for \fBrm_conffile\fP and \fBmv_conffile\fP
 depends on the command used, for \fBrm_conffile\fP and \fBmv_conffile\fP
-it is 1.15.7.2, for \fBsymlink_to_dir\fP it is 1.17.2:
+it is 1.15.7.2, for \fBsymlink_to_dir\fP and \fBdir_to_symlink\fP
+it is 1.17.2:
 .P
 .P
     \fBPre\-Depends:\fP dpkg (>= 1.17.2)
     \fBPre\-Depends:\fP dpkg (>= 1.17.2)
 .P
 .P

+ 116 - 1
scripts/dpkg-maintscript-helper.sh

@@ -295,6 +295,115 @@ symlink_to_dir() {
 	esac
 	esac
 }
 }
 
 
+##
+## Functions to replace a directory with a symlink
+##
+dir_to_symlink() {
+	local PATHNAME="$1"
+	local SYMLINK_TARGET="$2"
+	local LASTVERSION="$3"
+	local PACKAGE="$4"
+
+	if [ "$LASTVERSION" = "--" ]; then
+		LASTVERSION=""
+		PACKAGE="$DPKG_MAINTSCRIPT_PACKAGE${DPKG_MAINTSCRIPT_ARCH:+:$DPKG_MAINTSCRIPT_ARCH}"
+	fi
+	if [ "$PACKAGE" = "--" -o -z "$PACKAGE" ]; then
+		PACKAGE="$DPKG_MAINTSCRIPT_PACKAGE${DPKG_MAINTSCRIPT_ARCH:+:$DPKG_MAINTSCRIPT_ARCH}"
+	fi
+
+	# Skip remaining parameters up to --
+	while [ "$1" != "--" -a $# -gt 0 ]; do shift; done
+	[ $# -gt 0 ] || badusage "missing arguments after --"
+	shift
+
+	[ -n "$DPKG_MAINTSCRIPT_NAME" ] || \
+		error "environment variable DPKG_MAINTSCRIPT_NAME is required"
+	[ -n "$PACKAGE" ] || error "cannot identify the package"
+	[ -n "$PATHNAME" ] || error "directory parameter is missing"
+	[ -n "$SYMLINK_TARGET" ] || error "new symlink target is missing"
+	[ -n "$LASTVERSION" ] || error "last version is missing"
+	[ -n "$1" ] || error "maintainer script parameters are missing"
+
+	debug "Executing $0 dir_to_symlink in $DPKG_MAINTSCRIPT_NAME" \
+	      "of $DPKG_MAINTSCRIPT_PACKAGE"
+	debug "PATHNAME=$PATHNAME SYMLINK_TARGET=$SYMLINK_TARGET" \
+	      "PACKAGE=$PACKAGE LASTVERSION=$LASTVERSION ACTION=$1 PARAM=$2"
+
+	case "$DPKG_MAINTSCRIPT_NAME" in
+	preinst)
+		if [ "$1" = "install" -o "$1" = "upgrade" ] &&
+		   [ -n "$2" ] && [ -d "$PATHNAME" ] &&
+		   dpkg --compare-versions "$2" le-nl "$LASTVERSION"; then
+			prepare_dir_to_symlink "$PACKAGE" "$PATHNAME"
+		fi
+		;;
+	postinst)
+		if [ "$1" = "configure" ] &&
+		   [ -d "${PATHNAME}.dpkg-backup" ] && [ -h "$PATHNAME" ] &&
+		   [ "$(readlink -f $PATHNAME)" = "$SYMLINK_TARGET" ] &&
+		    dpkg --compare-versions "$2" le-nl "$LASTVERSION"; then
+			# By now, dpkg will have updated the symlink to point
+			# to the new location, but we are left behind the old
+			# files owned by this package in the backup directory,
+			# just remove it.
+			rm -rf "${PATHNAME}.dpkg-backup"
+		fi
+		;;
+	postrm)
+		if [ "$1" = "purge" ] && [ -d "${PATHNAME}.dpkg-backup" ]; then
+		    rm -rf "${PATHNAME}.dpkg-backup"
+		fi
+		if [ "$1" = "abort-install" -o "$1" = "abort-upgrade" ] &&
+		   [ -n "$2" ] &&
+		   [ -d "${PATHNAME}.dpkg-backup" ] && [ -h "$PATHNAME" ] &&
+		   [ "$(readlink -f $PATHNAME)" = "$SYMLINK_TARGET" ] &&
+		   dpkg --compare-versions "$2" le-nl "$LASTVERSION"; then
+			echo "Restoring backup of $PATHNAME ..."
+			rm -f "$PATHNAME"
+			mv "${PATHNAME}.dpkg-backup" "$PATHNAME"
+		fi
+		;;
+	*)
+		debug "$0 dir_to_symlink not required in $DPKG_MAINTSCRIPT_NAME"
+		;;
+	esac
+}
+
+prepare_dir_to_symlink()
+{
+	local PACKAGE="$1"
+	local PATHNAME="$2"
+
+	# If there are conffiles we should not perform the switch.
+	if dpkg-query -W -f='${Conffiles}' "$PACKAGE" | \
+	   grep -q "$PATHNAME/."; then
+		error "directory '$PATHNAME' contains conffiles," \
+		      "cannot switch to symlink"
+	fi
+
+	# If there are locally created files or files owned by another package
+	# we should not perform the switch.
+	find "$PATHNAME" -print0 | xargs -0 -n1 sh -c '
+		package="$1"
+		file="$2"
+		if ! dpkg-query -L "$package" | grep -q -x "$file"; then
+			return 1
+		fi
+		return 0
+	' subcommand "$PACKAGE" || \
+		error "directory '$PATHNAME' contains files not owned by" \
+		      "package $PACKAGE, cannot switch to symlink"
+
+	# Move the directory aside and make a temporary symlink to reduce the
+	# time the contents are not available. dpkg will not be able to remove
+	# the old files from the backup directory after unpack, because it
+	# will have updated the symlink to point to the new location already,
+	# we'll remove them ourselves later on.
+	mv -f "$PATHNAME" "${PATHNAME}.dpkg-backup"
+	ln -s "${PATHNAME}.dpkg-backup" "$PATHNAME"
+}
+
 # Common functions
 # Common functions
 ensure_package_owns_file() {
 ensure_package_owns_file() {
 	local PACKAGE="$1"
 	local PACKAGE="$1"
@@ -338,6 +447,9 @@ Commands:
   symlink_to_dir <pathname> <old-symlink-target> [<last-version> [<package>]]
   symlink_to_dir <pathname> <old-symlink-target> [<last-version> [<package>]]
 	Replace a symlink with a directory. Must be called in preinst,
 	Replace a symlink with a directory. Must be called in preinst,
 	postinst and postrm.
 	postinst and postrm.
+  dir_to_symlink <pathname> <new-symlink-target> [<last-version> [<package>]]
+	Replace a directory with a symlink. Must be called in preinst,
+	postinst and postrm.
   help
   help
 	Display this usage information.
 	Display this usage information.
 END
 END
@@ -362,7 +474,7 @@ shift
 case "$command" in
 case "$command" in
 supports)
 supports)
 	case "$1" in
 	case "$1" in
-	rm_conffile|mv_conffile|symlink_to_dir)
+	rm_conffile|mv_conffile|symlink_to_dir|dir_to_symlink)
 		code=0
 		code=0
 		;;
 		;;
 	*)
 	*)
@@ -388,6 +500,9 @@ mv_conffile)
 symlink_to_dir)
 symlink_to_dir)
 	symlink_to_dir "$@"
 	symlink_to_dir "$@"
 	;;
 	;;
+dir_to_symlink)
+	dir_to_symlink "$@"
+	;;
 --help|help|-?)
 --help|help|-?)
 	usage
 	usage
 	;;
 	;;