From fe9cabedf0292a2b09de16e272b968b42c0ac7fe Mon Sep 17 00:00:00 2001 From: Alexey Shmalko Date: Sun, 17 Jul 2016 17:46:23 +0300 Subject: etc: remove obsolete directories This patch adds handling of a directory becoming a symlink in /etc. Before this patch, the directory wasn't removed and then symlinking failed, which caused directory not being updated at all. The idea for the patch goes to @abbradar at https://github.com/NixOS/nixpkgs/issues/16978#issuecomment-232921903: > A heuristic idea for this -- a function `isStatic :: Path -> Bool`: > > * if path `/etc/foo` is a file, return True iff it's a symlink to `/etc/static/foo`. > * if path is a directory, return True iff for all items in it `isStatic` is True. > > On any conflicts, if old path is static, it's safe to replace and/or > delete stale. Otherwise make a backup and notify the user via a > journal entry and console output. The only difference here -- it will not replace user configs. This also fixes https://github.com/NixOS/nixpkgs/issues/16978. --- nixos/modules/system/etc/setup-etc.pl | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'nixos/modules/system') diff --git a/nixos/modules/system/etc/setup-etc.pl b/nixos/modules/system/etc/setup-etc.pl index 89a49b972ff9..efda74161ff8 100644 --- a/nixos/modules/system/etc/setup-etc.pl +++ b/nixos/modules/system/etc/setup-etc.pl @@ -22,6 +22,33 @@ sub atomicSymlink { # current configuration. atomicSymlink $etc, $static or die; +# Returns 1 if the argument points to the files in /etc/static. That +# means either argument is a symlink to a file in /etc/static or a +# directory with all children being static. +sub isStatic { + my $path = shift; + + if (-l $path) { + my $target = readlink $path; + return substr($target, 0, length "/etc/static/") eq "/etc/static/"; + } + + if (-d $path) { + opendir DIR, "$path" or return 0; + my @names = readdir DIR or die; + closedir DIR; + + foreach my $name (@names) { + next if $name eq "." || $name eq ".."; + unless (isStatic("$path/$name")) { + return 0; + } + } + return 1; + } + + return 0; +} # Remove dangling symlinks that point to /etc/static. These are # configuration files that existed in a previous configuration but not @@ -63,6 +90,16 @@ sub link { my $target = "/etc/$fn"; File::Path::make_path(dirname $target); $created{$fn} = 1; + + # Rename doesn't work if target is directory. + if (-l $_ && -d $target) { + if (isStatic $target) { + rmtree $target or warn; + } else { + warn "$target directory contains user files. Symlinking may fail."; + } + } + if (-e "$_.mode") { my $mode = read_file("$_.mode"); chomp $mode; if ($mode eq "direct-symlink") { -- cgit 1.4.1