[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: "goals" for slink: FHS



"Santiago" == Santiago Vila <sanvila@unex.es> writes:
> >     Santiago> All the stuff in /usr/info should have to be mv'd into
> >     Santiago> /usr/share/info before the symlink is created. This
> >     Santiago> would be very tricky.
> > 
> > Assuming that /usr/share/{info,man} doesn't yet exist, [...]

Santiago Vila <sanvila@unex.es> wrote:
> You can't assume that. You shouldn't assume that.
> 
> We have told our users it is safe again to install some packages from
> unstable. There is not need to complicate things more than they are.

Are you saying that some packages have gone ahead and put stuff
into /usr/share/{doc,info,man}/ even though it's not yet policy?

If so, I'll have to modify the script (see attached).

> Please, let's /usr/share/man and /usr/man both to exist in the
> same system, as we already did with /usr/doc/copyright/* and
> /usr/doc/<package>/copyright files. No need to move anything.

No programs cared where the copyright files were, so these are not really
analogous situations.

Anyways, I've thrown together the beginnings of a rather paranoid script
which attempts to manage the transition.  It would be invoked as follows:

mv2 /usr/doc /usr/share/doc
mv2 /usr/man /usr/share/man
mv2 /usr/info /usr/share/info

I believe that for this transition it will only stop on an error condition
if the system administrator has done something rather unusual.  I don't
think I'm too optimistic in thinking that the admin should be able to
figure out what that thing is.  However, comments are welcome.

I'm imagining that this would be part of the preinst script for some
essential package, perhaps one named "fhs".  Every fhs compliant package
would have to depend on this.  [Note that this dependency scheme is much
simpler than what would be required if we had to update a whole bunch
of packages to support the new locations.]

You've suggested that we have some packages that would interfere with
this script.  I'm not sure why this would be the case -- for example,
man won't work on such manpages (at least not till it's upgraded),
and info wouldn't work either.  Can you point me at these scripts?

-- 
Raul
#!/usr/bin/perl
use File::Basename;

=head1 NAME

mv2 - move directory and replace with symlink

=head1 SYNOPSIS

mv2 source target

=head1 DESCRIPTION

This utility relocates directories, leaving at the old location
a symbolic link leading to the new location.   Both source and
target must be absolute references, and neither may contain "..".
The directory which will contain the target must already exist.

There are several cases:

(0) source is a symbolic link pointing to target and target is a
directory.  Nothing is done, program terminates successfully.

(1) The target directory exists and the source directory is missing.
Here, a symbolic link is inserted.

(2) Both source and target would be on the same partition, source
is a directory and target does not exist.  Here, source is renamed
and a symbolic link is left.

(3) Source and target would be on different partitions.  Each would be
entirely contained within a single partition.  Source is a directory
and target does not yet exist.  Here, the target partition is checked to
ensure that enough space exists for the source hierarchy.  A warning is
issued, and the source heirarchy is copied (using cp -a) to the target
location.  On completion, the source hierarchy is renamed to DELETEME
at the root of the source partition, the symbolic link is placed, and
the old hierarchy is removed (or, as much as possible: directories in
use will be left behind).  [You must be root for this.]

If other cases are encountered, a descriptive warning is issued
and a non-zero exit status is returned.

=head1 SEE ALSO

L<cp(1)>,
L<mv(1)>

=head1 NOTES

This utility was designed to re-arrange directory hierarchies from
within dpkg, without damaging the system.

=cut

# unpack arguments
die "Usage: $ARGV0 sourcedir target\n" if 2 != @ARGV;

sub clean_path {
	# deal with various forms of pathological silliness in absolute path
	local ($_)= @_;
	die "$_ is not absolute\n" unless m-^/-;
	die "$_ contains ..\n" if m-/\.\.(/|$)-;
	while (s-/\.(/|$)-/-g) {}
	s-/+-/-g;
	s-/$--g;
	return $_;
}

$src= clean_path shift @ARGV;
$dst= clean_path shift @ARGV;

# determine what final symbolic link will look like
@src= split '/', $src;
@dst= split '/', $dst;
$common='';
while (defined($s= shift @src) and defined($d= shift @dst)) {
	$common.= "$s/" if $s eq $d
}

if ('/' eq $common) {
	$link= $dst
} else {
	my $prefix= substr($src, length($common))."/";
	$prefix =~s-[^/]+-..-g;
	$prefix =~s-\.\./--;
	my $suffix= substr $dst, length($common);
	$link= "$prefix$suffix";
}

# generic failure message, to orient casual users
$cant= "Can't relocate $src to $dst";

sub finish {
	-d $dst or die <<dead;
$cant, $dst is not a directory!
\nThis should never have happened, but it did.  Presumably this means that
some kind of race condition occurred.  Please investigate carefully.
dead
	-e $src and die <<dead;
$cant, $src still exists!
\nThis should never have happened, but it did.  Presumably this means that
some kind of race condition occurred.  Please investigate carefully.
dead
	symlink $link, $src;
	exit;
}

# check target location
if (-d $dst) {
	finish if ! -e $src;
	if (-l $src) {
		my ($srcdev, $srcino)= stat "$src/.";
		my ($dstdev, $dstino)= stat "$dst/.";
		finish if $srcdev == $dstdev && $srcino == $dstino;
		die <<dead
$cant, $dst is already a directory, and
$src is already a symbolic link, but 
$src does not lead to $dst!
\nPerhaps $src needs to be replaced with a proper symlink?
dead
	}
	die <<dead if -d $src;
$cant, $dst already exists
\nYou have a couple of options:
(1) You can remove $dst and re-run this routine, or
(2) you can read the manual page on mv2 (or read the documentation
    embedded in the routine itself if the manual pages are unavailable)
    and do the steps manually.
	die <<dead;
$cant, $dst already exists and is not a directory
\nPerhaps $dst needs to be erased?
dead
}

# $dst does not exist, and $src is a directory
######################################################################
# easy case?
rename $src, $dst and finish;

# ok, they're probably on different partitions, let's see if we
# can do it the hard way.

# is $dst a reasonable location?
$dstparent= dirname $dst;
die "$cant, $dstparent does not exist\n" unless -e $dstparent;

sub quote {
	local ($_)= @_;
	s/\\/\\\\/g;
	s/"/\\"/g;
	return "\"$_\"";
}

$q_src= quote $src;
$src_space= (split /\s+/, `du -s $q_src`)[0];
$src_mnt= (split /\s+/, (`df $q_src`)[1])[5];

$q_dstp= quote $dstparent;
$dst_space= (split /\s+/, (`df $q_dstp`)[1])[3];

if ($src_space < $dst_space) {
	die "$cant, you must be root\n" if $>;
	$DELETEME= quote clean_path "$src_mnt/DELETEME";
	END{defined $DELETEME and system "rm -rf $DELETEME"}
	system "rm -rf $DELETEME";
	system "mkdir $DELETEME" and die <<dead;
$cant, for some reason I can't create the $DELETEME directory
dead
	die "$cant, race condition involving $dstparent\n" unless -e $dstparent;
	die "$cant, race condition involving $dst\n" if -e $dst;
	print STDERR "Copying contents of $src (${src_space}k) to $dst (${dst_space}k avail)\n";
	if (system "cp -a $q_src $q_dstp") {
		# oops...
		print STDERR "Copy failed, removing bogus $dst [press ENTER to continue]\n";
		scalar <STDIN>;
		$q_dst= quote $dst;
		system "rm -rf $q_dst";
		die <<dead;
$cant, something went wrong when trying to copy everything.
\nSee above for details.
dead
	}
	system "mv $q_src $DELETEME"  and die <<dead;
$cant, sorry, for some reason I can't set aside the original source
directory.  You probably now have identical copies at both locations,
you'll probably want to remove one.  If you remove $src, leave a symbolic
link pointing to $dst ($link).
dead
	symlink $link, $src or die <<dead;
$cant, sorry, for some reason I can't create a symbolic link
making the new copy at $dst also be visible at $src.  I'm not
sure how to best fix this.  Good luck.
dead
} elsif ($src_space == $dst_space) {
	die <<dead;
$cant, there might be enough space to copy everything,
there might not.  Without a safety margin it's not safe for me to try.
If you want to do this manually, read the mv2 manual page for details
of what's supposed to happen.  Otherwise, maybe you can do something to
make more space available.
dead
} else {
	die <<dead;
$cant, not enough space: need $src_space, have $dst_space
\nPerhaps you can do something to make more space available?
dead
}

Reply to: