#!/usr/local/bin/perl
# save_mount.cgi
# Save or create a mount. When saving an existing mount, at lot of different
# things can happen. 

require './mount-lib.pl';
&error_setup($text{'save_err'});
&ReadParse();
$| = 1;

# check inputs
if ($in{type} ne "swap") {
	$in{directory} =~ /^\// ||
		&error(&text('save_edirname', $in{'directory'}));
	if (-r $in{'directory'} && !(-d $in{'directory'})) {
		&error(&text('save_edir', $in{'directory'}));
		}
	# non-existant directories get created later
	}
else {
	# for swap files, set the directory to 'swap'
	$in{directory} = "swap";
	}
&can_edit_fs($in{'directory'}) && !$access{'only'} ||
	&error($text{'edit_ecannot'});
$access{'create'} || defined($in{'old'}) || &error($text{'edit_ecannot2'});

# Get user choices
@mmodes = &mount_modes($in{type});
$msave = ($mmodes[0]==0 ? 0 : $in{msave});
$mnow = ($mmodes[1]==0 ? $msave : $in{mmount});

foreach $f (&files_to_lock()) {
	&lock_file($f);
	}
if (defined($in{old}) && !$access{'simple'}) {
	# Saving an existing mount
	if ($in{temp}) { @mlist = &list_mounted(); }
	else { @mlist = &list_mounts(); }
	@mold = @{$mlist[$in{old}]};

	if (!$mnow && !$in{oldmnow} && !$msave) {
		# Not mounted, so remove from fstab without checking
		$dev = $mold[1];
		}
	else {
		# Changing an existing mount
		$dev = &check_location($in{'type'});
		&parse_options($mold[2], $mold[3]);
		$opts = &check_options($in{'type'}, $dev, $in{'directory'});
		@minfo = ($in{'directory'}, $dev, $in{'type'}, $opts,
			  $mmodes[2] ? $in{'order'} : "-",
			  $in{'msave'}==2||$mmodes[0]==1 ? "yes" : "no");
		}

	# Check for change in device
	if ($mold[1] ne $dev) {
		# Device has changed..check it
		if (!&multiple_mount($minfo[2]) && &get_mounted("*", $dev)>=0) {
			&error(&text('save_ealready', $dev));
			}
		if (!&multiple_mount($minfo[2]) && &get_mount("*", $dev) != -1){
			&error(&text('save_ealready2', $dev));
			}
		$changed = 1;
		}

	# Check for change in directory
	if ($in{type} ne "swap" && $mold[0] ne $in{directory}) {
		# Directory has changed.. check it too
		if (&get_mounted($in{directory}, "*")>=0) {
			&error(&text('save_ealready3', $in{'directory'}));
			}
		if (&get_mount($in{directory}, "*") != -1) {
			&error(&text('save_ealready4', $in{'directory'}));
			}
		$changed = 1;

		if (!(-d $in{directory})) {
			# Create the new directory
			&lock_file($in{directory});
			mkdir($in{directory}, 0755) ||
				&error(&text('save_emkdir',
					     $in{'directory'}, $!));
			&unlock_file($in{directory});
			$made_dir = 1;
			}
		}

	# Check for change in current mount status
	if ($in{'oldmnow'} && $mmodes[3] == 1) {
		# Mounted, and cannot be unmounted
		}
	elsif ($in{'oldmnow'} && !$mnow) {
		# Just been unmounted..
		if ($error = &unmount_dir($mold[0], $mold[1], $in{type})) {
			#if ($error =~ /busy/) {
				# Mount is busy.. most likely because it is
				# currently in use. Offer the user a choice
				# to remove from the mount table only
			#	}
			#else { &error("Unmount failed : $error"); }
			&error(&text('save_eumount', $error));
			}
		@tlog = ( "umount", "dir", $mold[0],
			  { 'dir' => $mold[0], 'dev' => $mold[1],
			    'type' => $mold[2], 'opts' => $mold[3] } );
		}
	elsif ($mnow && !$in{oldmnow}) {
		# Just been mounted..
		if ($error = &mount_dir(@minfo)) {
			&error(&text('save_emount', $error));
			}
		@tlog = ( "mount", "dir", $minfo[0], 
			  { 'dir' => $minfo[0], 'dev' => $minfo[1],
			    'type' => $minfo[2], 'opts' => $minfo[3] } );
		}
	elsif (!$mnow && !$in{oldmnow}) {
		# Not mounted, and doesn't need to be
		}
	elsif (($mold[0] ne $minfo[0] || $mold[1] ne $minfo[1] ||
	       &diff_opts($mold[3], $minfo[3])) && !$in{'perm_only'}) {
		# Need to unmount/mount to apply new options
		if ($error = &unmount_dir($mold[0], $mold[1], $in{type})) {
			if ($error =~ /busy|Invalid argument/ && $msave) {
				# Mount is busy.. most likely because it is
				# currently in use. Offer the user a choice
				# to update only the fstab file, rather than
				# the real mount
				&header($text{'edit_title'}, "");
				print "<hr>\n";
				print &text('save_perm', "<tt>$mold[0]</tt>"),
				      "<p>\n";
				print "<form action=save_mount.cgi>\n";
				print "<input type=hidden name=perm_only ",
				      "value=1>\n";
				foreach $k (keys %in) {
					print "<input type=hidden name=$k ",
					      "value=\"$in{$k}\">\n";
					}
				print "<center><input type=submit ",
				      "value=\"$text{'save_apply'}\">",
				      "</center>\n";
				print "</form>\n";

				print "<hr>\n";
				&footer("", $text{'index_return'});
				exit;
				}
			else { &error(&text('save_eremount', $error)); }
			}
		if ($error = &mount_dir(@minfo)) {
			&error(&text('save_eremount', $error));
			}
		@tlog = ( "remount", "dir", $minfo[0], 
			  { 'dir' => $minfo[0], 'dev' => $minfo[1],
			    'type' => $minfo[2], 'opts' => $minfo[3] } );
		}

	# Check for change in permanence
	if ($in{oldmsave} && !$msave) {
		# Delete from mount table
		&delete_mount($in{old});
		@plog = ( "delete", "dir", $in{'directory'},
			  { 'dir' => $mold[0], 'dev' => $mold[1],
			    'type' => $mold[2], 'opts' => $mold[3] } );
		}
	elsif ($msave && !$in{oldmsave}) {
		# Add to mount table
		&create_mount(@minfo);
		@plog = ( "create", "dir", $in{'directory'},
			  { 'dir' => $minfo[0], 'dev' => $minfo[1],
			    'type' => $minfo[2], 'opts' => $minfo[3] } );
		}
	elsif (!$msave && !$in{oldmsave}) {
		# Not in mount table
		}
	elsif ($mold[0] ne $minfo[0] || $mold[1] ne $minfo[1] ||
	       &diff_opts($mold[3], $minfo[3])) {
		# Apply any changes in mount options
		&change_mount($in{old}, @minfo);
		@plog = ( "modify", "dir", $in{'directory'},
			  { 'dir' => $minfo[0], 'dev' => $minfo[1],
			    'type' => $minfo[2], 'opts' => $minfo[3] } );
		}
	}
elsif (defined($in{'old'})) {
	# Doing a simple modification to a mount
	if ($in{temp}) { @mlist = &list_mounted(); }
	else { @mlist = &list_mounts(); }
	@mold = @{$mlist[$in{old}]};

	if ($in{'umount'}) {
		# Just unmount the filesystem
		if ($error = &unmount_dir($mold[0], $mold[1], $in{type})) {
			&error(&text('save_eumount', $error));
			}
		}
	elsif ($in{'mount'}) {
		# Just mount the filesystem
		if ($error = &mount_dir(@mold)) {
			&error(&text('save_emount', $error));
			}
		}
	elsif ($in{'perm'}) {
		# Add to permanent mount list
		&create_mount($mold[0], $mold[1], $mold[2], $mold[3],
			      2, "yes");
		}
	elsif ($in{'delete'}) {
		if ($in{'oldmnow'}) {
			# Unmount first
			if ($error = &unmount_dir($mold[0], $mold[1],
						  $in{type})) {
				&error(&text('save_eumount', $error));
				}
			}
		# Remove from permanent list
		&delete_mount($in{'old'});
		}
	else {
		# Updating the mount in some way ..
		# Check the mount source
		$dev = &check_location($in{'type'});
		&parse_options($mold[2], $mold[3]);
		if (defined($access{'opts'}) &&
		    $access{'opts'} !~ /$in{'type'}/) {
			# Just use existing options
			local @opts;
			foreach $k (keys %options) {
				if ($options{$k} eq '') { push(@opts, $k); }
				else { push(@opts, "$k=$options{$k}"); }
				}
			$opts = @opts ? join(",", @opts) : "-";
			}
		else {
			# Get options from the user
			$opts = &check_options($in{'type'}, $dev,
					       $in{'directory'});
			}
		@minfo = ($in{'directory'}, $dev, $in{'type'}, $opts, 2, 'yes');

		# Check for change in device
		if ($mold[1] ne $dev) {
			# Device has changed..check it
			if (!&multiple_mount($minfo[2]) &&
			    &get_mounted("*", $dev)>=0) {
				&error(&text('save_ealready', $dev));
				}
			if (!&multiple_mount($minfo[2]) &&
			    &get_mount("*", $dev) != -1){
				&error(&text('save_ealready2', $dev));
				}
			$changed = 1;
			}

		# Check for change in directory
		if ($in{type} ne "swap" && $mold[0] ne $in{directory}) {
			# Directory has changed.. check it too
			if (&get_mounted($in{directory}, "*")>=0) {
				&error(&text('save_ealready3',
					     $in{'directory'}));
				}
			if (&get_mount($in{directory}, "*") != -1) {
				&error(&text('save_ealready4',
					     $in{'directory'}));
				}
			$changed = 1;

			if (!(-d $in{directory})) {
				# Create the new directory
				&lock_file($in{directory});
				mkdir($in{directory}, 0755) ||
					&error(&text('save_emkdir',
						     $in{'directory'}, $!));
				&unlock_file($in{directory});
				$made_dir = 1;
				}
			}

		if ($in{'oldmnow'} && ($mold[0] ne $minfo[0] ||
		    $mold[1] ne $minfo[1] || &diff_opts($mold[3], $minfo[3]))) {
			# Need to unmount/mount to apply new options
			if ($error=&unmount_dir($mold[0], $mold[1], $in{type})){
				&error(&text('save_eremount', $error));
				}
			if ($error = &mount_dir(@minfo)) {
				&error(&text('save_eremount', $error));
				}
			}

		if ($in{'oldmsave'}) {
			# Change entry in fstab
			&change_mount($in{'old'}, @minfo);
			}
		}
	}
elsif ($access{'simple'}) {
	# Create and mounting from the simple interface
	$dev = &check_location($in{type});
	if (!defined($access{'opts'}) || $access{'opts'} =~ /$in{'type'}/) {
		$opts = &check_options($in{'type'}, $dev, $in{'directory'});
		}
	else {
		$opts = "-";
		}
	@minfo = ($in{directory}, $dev, $in{type}, $opts, 2, 'yes');

	# Check if the device is in use
	if (!&multiple_mount($minfo[2]) && &get_mounted("*", $dev)>=0) {
		&error(&text('save_ealready', $dev));
		}
	if (!&multiple_mount($minfo[2]) && &get_mount("*", $dev) != -1) {
		&error(&text('save_ealready2', $dev));
		}

	# Check if the directory is in use
	if ($in{type} ne "swap") {
		if (&get_mounted($in{directory}, "*")>=0) {
			&error(&text('save_ealready2', $in{'directory'}));
			}
		if (&get_mount($in{directory}, "*") != -1) {
			&error(&text('save_ealready3', $in{'directory'}));
			}
		}

	# Create the directory
	if ($in{type} ne "swap" && !(-d $in{directory})) {
		&lock_file($in{directory});
		mkdir($in{directory}, 0755) ||
		  &error(&text('save_emkdir', $in{'directory'}, $!));
		&unlock_file($in{directory});
		$made_dir = 1;
		}

	# Mount and save
	if ($error = &mount_dir($minfo[0], $minfo[1],
				$minfo[2], $minfo[3])) {
		if ($made_dir) { rmdir($in{directory}); }
		&error(&text('save_emount', $error));
		}
	&create_mount(@minfo) if ($mmodes[0]);
	}
else {
	# Creating a new mount
	$dev = &check_location($in{type});
	&parse_options($minfo[3]);
	$opts = &check_options($in{type}, $dev, $in{'directory'});
	@minfo = ($in{directory}, $dev, $in{type}, $opts,
		  $mmodes[2] ? $in{order} : "-",
		  $in{msave}==2||$mmodes[0]==1 ? "yes" : "no");

	# Check if anything is being done
	if (!$msave && !$mnow) {
		&error($text{'save_enone'});
		}

	# Check if the device is in use
	if (!&multiple_mount($minfo[2]) && &get_mounted("*", $dev)>=0) {
		&error(&text('save_ealready', $dev));
		}
	if (!&multiple_mount($minfo[2]) && &get_mount("*", $dev) != -1) {
		&error(&text('save_ealready2', $dev));
		}

	# Check if the directory is in use
	if ($in{type} ne "swap") {
		if (&get_mounted($in{directory}, "*")>=0) {
			&error(&text('save_ealready2', $in{'directory'}));
			}
		if (&get_mount($in{directory}, "*") != -1) {
			&error(&text('save_ealready3', $in{'directory'}));
			}
		}

	# Create the directory
	if ($in{type} ne "swap" && !(-d $in{directory})) {
		&lock_file($in{directory});
		mkdir($in{directory}, 0755) ||
		  &error(&text('save_emkdir', $in{'directory'}, $!));
		&unlock_file($in{directory});
		$made_dir = 1;
		}

	# If mounting now, attempt to do it
	if ($mnow) {
		# If the mount fails, give up totally
		if ($error = &mount_dir($minfo[0], $minfo[1],
					$minfo[2], $minfo[3])) {
			if ($made_dir) { rmdir($in{directory}); }
			&error(&text('save_emount', $error));
			}
		@tlog = ( "mount", "dir", $in{'directory'},
			  { 'dir' => $minfo[0], 'dev' => $minfo[1],
			    'type' => $minfo[2], 'opts' => $minfo[3] } );
		}

	# If saving, save now
	if ($msave) {
		&create_mount(@minfo);
		@plog = ( "create", "dir", $in{'directory'},
			  { 'dir' => $minfo[0], 'dev' => $minfo[1],
			    'type' => $minfo[2], 'opts' => $minfo[3] } );
		}
	}
foreach $f (&files_to_lock()) {
	&unlock_file($f);
	}
&webmin_log(@plog) if (@plog);
%tpmap = ( 'create', 'mount',  'delete', 'umount',  'modify', 'remount' );
if (@tlog && $tpmap{$plog[0]} ne $tlog[0]) {
	&webmin_log(@tlog);
	}
&redirect($in{'return'});


# undo_changes
# Put back any changes to the fstab file
sub undo_changes
{
if ($in{temp} && $in{mboot}) {
	# a mount was made permanent.. undo by deleting it
	&delete_mount($idx);
	}
elsif (!$in{temp} && !$in{mboot}) {
	# a permanent mount was made temporary.. undo by making it permanent
	&create_mount(@mold);
	}
elsif ($in{mboot}) {
	# some mount options were changed.. undo by changing back
	&change_mount($in{old}, @mold);
	}
if ($made_dir) {
	# A directory for mounting was created.. delete it
	rmdir($in{directory});
	}
}

# diff_opts(string1, string2)
sub diff_opts
{
local $o1 = join(",", sort { $a cmp $b } split(/,/, $_[0]));
local $o2 = join(",", sort { $a cmp $b } split(/,/, $_[1]));
return $o1 ne $o2;
}

