summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjdennis <jdennis@c9f7a03b-bd48-0410-a16d-cbbf54688b0b>2010-11-19 20:36:54 +0000
committerjdennis <jdennis@c9f7a03b-bd48-0410-a16d-cbbf54688b0b>2010-11-19 20:36:54 +0000
commit391e47d2b0006d49395b1906a1f6f469e874dfa2 (patch)
tree59a089453c70709b2bc354cf45818b96e97c43da
parent3295970b2f978951e235bb237912f0f7497b687d (diff)
downloadpki-391e47d2b0006d49395b1906a1f6f469e874dfa2.tar.gz
pki-391e47d2b0006d49395b1906a1f6f469e874dfa2.tar.xz
pki-391e47d2b0006d49395b1906a1f6f469e874dfa2.zip
Reimplement copy_directory, remove_directory
The copy_directory function was losing critical information. It called out to the shell to recursively copy the contents of one directory to another. But this meant we lost track of the files and directories actually being copied, we couldn't log them nor add them to the installation manifest. Now the copy_directory function builds a list of files in the src directory and iteratively copies each file calling into our copy_file function which records the operation and checks for errors. The remove_directory function was an unapologetic sledge hammer, it simply nuked entire trees. Now the function is more sensible, by default it removed one empty directory, or optionally recursively removes all directory contents. Both functions previously had serious implementation mistakes. Both were implemented by calling out to a UNIX shell and invoking a shell command via the Perl backtick operator. The fundamental problems with this were: * UNIX shell commands only work on UNIX * Not all UNIX shell commands are identical * The error detection stragegy was completly broken. It executed the shell command via the backtick operator which returns the stdout of the command and discards stderr. The function would then test to see if the length of stdout was zero to determine if there was an error. If there was no stdout it assumed no errors occurred. This is completely wrong. To test if an error occurs with a shell command one needs to examine the exit status of the command which is impossible to do with the Perl backtick operator. If one wants to test the exit status of a shell command in Perl you must utilize the subprocess features of Perl. The reimplementation eschews the non-portable use of UNIX shell commands in favor of the portable Perl extensions for operating on filesystem directories. git-svn-id: svn+ssh://svn.fedorahosted.org/svn/pki/trunk@1538 c9f7a03b-bd48-0410-a16d-cbbf54688b0b
-rwxr-xr-xpki/base/setup/pkicommon132
1 files changed, 87 insertions, 45 deletions
diff --git a/pki/base/setup/pkicommon b/pki/base/setup/pkicommon
index 127720cb..dc197fa1 100755
--- a/pki/base/setup/pkicommon
+++ b/pki/base/setup/pkicommon
@@ -2878,82 +2878,110 @@ sub move_directory
return 0;
}
-
-# arg0 source directory
-# arg1 destination path
+# arg0 src directory
+# arg1 dst path
# return 1 - successfully copied directory, or
# return 0 - failed copying directory
sub copy_directory
{
- my $source_dir_path = $_[0];
- my $dest_dir_path = $_[1];
+ my($src_dir_path, $dst_dir_path, $uninstall_action) = @_;
+ my($result);
+ my($paths, $path, $src_path, $dst_path);
+ my(%dst_dirs, $dst_dir);
- my $result = 0;
+ $uninstall_action = 'remove' unless defined($uninstall_action);
+ $result = 1;
+
+ $src_dir_path = normalize_path($src_dir_path);
+ $dst_dir_path = normalize_path($dst_dir_path);
- emit("copy_directory(): source=> $source_dir_path dest=> $dest_dir_path \n","debug");
- if (!is_path_valid($source_dir_path)) {
- emit("copy_directory(): illegal source path => $source_dir_path.\n",
+ if ($verbose > 1) {
+ emit(sprintf("%s(%s)\n", (caller(0))[3], join(', ', @_)));
+ }
+
+ emit("copy_directory(): $src_dir_path => $dst_dir_path\n", "debug");
+ if (!is_path_valid($src_dir_path)) {
+ emit("copy_directory(): illegal src path => $src_dir_path.\n",
"error");
return 0;
}
- if (!is_path_valid($dest_dir_path)) {
- emit("copy_directory(): illegal destination path => "
- . "$dest_dir_path.\n",
- "error");
+ if (!is_path_valid($dst_dir_path)) {
+ emit("copy_directory(): illegal dst path ($dst_dir_path)\n", "error");
return 0;
}
- if (!directory_exists($source_dir_path)) {
+ if (!directory_exists($src_dir_path)) {
# Take the case where this directory does not exist
# Just return true
+ emit("copy_directory(): non-existent src path ($src_dir_path)\n", "error");
return 1;
}
- if (!directory_exists($dest_dir_path)) {
- $result = create_directory($dest_dir_path);
- if (!$result) {
- return 0;
- }
+ # Get list of files under the src dir
+ $paths = get_directory_files($src_dir_path, {'strip_dir' => 1});
+
+
+ # Assure each destination directory exists
+ $dst_dirs{$dst_dir_path}++;
+ for $path (@$paths) {
+ $dst_path = "${dst_dir_path}/${path}";
+ $dst_dir = dirname($dst_path);
+ $dst_dirs{$dst_dir}++;
}
- if (!is_directory_empty($source_dir_path)) {
- $result = `cp -fr $source_dir_path/* $dest_dir_path`;
- } else {
- $result = 0;
+ for $dst_dir (keys %dst_dirs) {
+ return 0 if !create_directory($dst_dir);
}
- # System call returns 0 on success.
- if ($result == 0) {
- return 1;
+ # Copy each file
+ for $path (@$paths) {
+ $src_path = "${src_dir_path}/${path}";
+ $dst_path = "${dst_dir_path}/${path}";
+ if (!copy_file($src_path, $dst_path)) {
+ $result = 0;
+ }
}
- emit("copy_directory(): failed copying directory from $source_dir_path "
- . "to $dest_dir_path.\n",
- "error");
+ if (!$result) {
+ emit("copy_directory(): failed $src_dir_path => $dst_dir_path.\n",
+ "error");
+ }
- return 0;
+ return $result;
}
-# arg0 directory
-# return 1 - successfully removed directory, or
-# return 0 - failed removing directory
+# Removes given directory. By default only the directory is removed and
+# only if it is empty. To remove the directory and all of it's contents
+# you must provide the $remove_contents parameter and set it to true,
+# it defaults to false.
+#
+# Return 1 if success, 0 if failure
sub remove_directory
{
- my ($dir) = $_[0];
+ my($dir, $remove_contents) = @_;
+ my($errors, $result);
- emit("remove_directory(): " . $dir . "\n","debug");
- my $result = 0;
+ $remove_contents = 0 unless defined($remove_contents);
+ $result = 1;
+
+ emit("remove_directory(): \"$dir\"\n", "debug");
+ add_install_info($dir, 'dir', 'remove', 'remove');
+
+ if ($dry_run || $verbose > 1) {
+ emit(sprintf("%s(%s)\n", (caller(0))[3], join(', ', @_)));
+ return 1 if ($dry_run);
+ }
if (!is_path_valid($dir)) {
- emit("remove_directory(): specified invalid directory $dir.\n",
+ emit("remove_directory(): specified invalid directory $dir.\n",
"error");
return 0;
}
if ($dir eq "/") {
- emit("remove_directory(): don't even think about removing root!.\n",
+ emit("remove_directory(): don't even think about removing root!.\n",
"error");
return 0;
}
@@ -2962,15 +2990,29 @@ sub remove_directory
return 1;
}
- $result = `rm -rf $dir`;
- if ($result == 0) {
- return 1;
+ if ($remove_contents) {
+ remove_tree($dir, {error => \$errors});
+ if (@$errors) {
+ my($error, $path, $errstr);
+ $result = 0;
+ for $error (@$errors) {
+ $path, $errstr = %$error;
+ if ($path eq '') {
+ emit("remove_directory(): tree=\"$dir\" ($errstr)\n", "error");
+ }
+ else {
+ emit("remove_directory(): tree=\"$dir\" path=\"$path\" ($errstr)\n", "error");
+ }
+ }
+ }
+ } else {
+ if (!rmdir($dir)) {
+ $result = 0;
+ emit("remove_directory(): dir=\"$dir\" ($!) \n", "error");
+ }
}
- emit("remove_directory(): failed to remove directory $dir.\n",
- "error");
-
- return 0;
+ return $result;
}