#!/usr/local/bin/perl
# save_export.cgi
# Save or create an export

require './bsdexports-lib.pl';
&ReadParse();
$whatfailed = "Failed to save export";

# Validate and save inputs
@dl = split(/\s+/, $in{'dirs'});
foreach $d (@dl) {
	(-d $d) || &error("'$d' is not a directory or does not exist");
	}
@dl || &error("You must enter at least one directory to export");
if ($in{'alldirs'}) {
	@dl == 1 || &error("Only one directory can be given if the ",
			   "'export subdirectories' option is chosen");
	if (&root_dir($dl[0]) ne $dl[0]) {
		&error("When using the 'export subdirectories' option, the ",
		       "exported directory must be the root of a ",
		       "<i>local</i> filesystem");
		}
	}
$exp{'dirs'} = \@dl;
@devno = map { (stat($_))[0] } @dl;
$exp{'alldirs'} = $in{'alldirs'};
$exp{'ro'} = $in{'ro'};
$exp{'kerb'} = $in{'kerb'};

if (!$in{'maproot_def'}) {
	$in{'maproot'} =~ /^-?\d+$/ || defined(getpwnam($in{'maproot'})) ||
		&error("'$in{'maproot'}' is not a valid user or UID");
	if ($in{'maprootg_def'}) {
		@rgl = split(/\s+/, $in{'maprootg'});
		foreach $g (@rgl) {
			$g =~ /^-?\d+$/ || defined(getgrnam($g)) ||
				&error("'$g' is not a valid group or GID");
			}
		$exp{'maproot'} = "$in{'maproot'}:".join(':', @rgl);
		}
	else { $exp{'maproot'} = $in{'maproot'}; }
	}

if (!$in{'mapall_def'}) {
	$in{'mapall'} =~ /^-?\d+$/ || defined(getpwnam($in{'mapall'})) ||
		&error("'$in{'mapall'}' is not a valid user or UID");
	if ($in{'mapallg_def'}) {
		@rgl = split(/\s+/, $in{'mapallg'});
		foreach $g (@agl) {
			$g =~ /^-?\d+$/ || defined(getgrnam($g)) ||
				&error("'$g' is not a valid group or GID");
			}
		$exp{'mapall'} = "$in{'mapall'}:".join(':', @agl);
		}
	else { $exp{'mapall'} = $in{'mapall'}; }
	}

if (!$in{'maproot_def'} + !$in{'mapall_def'} + $in{'kerb'} > 1) {
	&error("The map root users, map all users and kerberos options ",
	       "are mutually exclusive");
	}

if ($in{'cmode'} == 0) {
	# Exporting to a list of hosts
	@hl = split(/\s+/, $in{'hosts'});
	@hl || &error("You must enter at least on host to export to");
	$exp{'hosts'} = \@hl;
	foreach $h (@hl) {
		$ip = gethostbyname($h);
		if ($ip) { push(@iplist, $ip); }
		}
	}
else {
	# Exporting to a subnet
	&check_ipaddress($in{'network'}) ||
		&error("'$in{'network'}' is not a valid network");
	&check_ipaddress($in{'mask'}) ||
		&error("'$in{'mask'}' is not a valid netmask");
	$exp{'network'} = $in{'network'};
	$exp{'mask'} = $in{'mask'};
	}

# Check for an export to the same host on the same local filesystem
&lock_file($config{'exports_file'});
@exps = &list_exports();
for($i=0; $i<@exps; $i++) {
	next if (defined($in{'index'}) && $in{'index'} == $i);
	$samefs = 0;
	foreach $d (@{$exps[$i]->{'dirs'}}) {
		if (&indexof((stat($d))[0], @devno) >= 0) {
			# Same filesystem as this export..
			$samefs = $d;
			}
		}
	next if (!$samefs);
	foreach $h (@{$exps[$i]->{'hosts'}}) {
		$ip = gethostbyname($h);
		if ($ip && &indexof($ip, @iplist) >= 0) {
			# Another export on this filesystem is to the same host
			&error("The directory <tt>$samefs</tt> is already ",
			       "being exported to <tt>$h</tt>. FreeBSD does ",
			       "not allow directories on the same ",
			       "<i>local</i> filesystem to be exported to the ",
			       "same host separately");
			}
		}
	if ($exp{'mask'} && $exp{'mask'} eq $exps[$i]->{'mask'} &&
			    $exp{'network'} eq $exps[$i]->{'network'}) {
		# Another export on this filesystem to the same net
		&error("The directory <tt>$samefs</tt> is already ",
		       "being exported to <tt>$exp{'network'}</tt>. FreeBSD ",
		       "does not allow directories on the same ",
		       "<i>local</i> filesystem to be exported to the ",
		       "same network separately");
		}
	}

if (defined($in{'index'})) {
	$old = $exps[$in{'index'}];
	$exp{'public'} = $old->{'public'};
	$exp{'webnfs'} = $old->{'webnfs'};
	$exp{'index'} = $old->{'index'};
	&modify_export($old, \%exp);
	}
else {
	&create_export(\%exp);
	}
&unlock_file($config{'exports_file'});
&redirect("");

# root_dir(path)
# Returns the root directory of the filesystem some path is in
sub root_dir
{
my $dir = $_[0];
my @pst = stat($dir);
while(1) {
	if ($dir eq "/") { return "/"; }
	my $lastdir = $dir;
	$dir =~ s/\/[^\/]+$//g;
	if ($dir eq "") { $dir = "/"; }
	my @ust = stat($dir);
	if ($ust[0] != $pst[0]) { return $lastdir; }
	}
}

