Current File : //usr/share/webmin/mount/save_mount.cgi |
#!/usr/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 for redirect to proc module to list processes on the FS
if ($in{'lsof'}) {
&redirect("../proc/index_search.cgi?mode=3&fs=".&urlize($in{'lsoffs'}));
return;
}
# check inputs
if ($in{type} ne "swap") {
if ($in{directory} !~ /^\//) {
if (@access_fs && $in{directory}) {
# Assume relative to allowed dir
$in{directory} = $access_fs[0]."/".$in{directory};
}
else {
&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'}, undef, $in{'type'}, undef, 1) &&
!$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})) {
# 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});
&make_dir($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'},
$mold[3], $in{'force'})) {
if (!$in{'force'} &&
$error =~ /busy|Invalid argument/ &&
defined(&can_force_unmount_dir) &&
&can_force_unmount_dir(@mold)) {
# Mount is busy.. most likely because it is
# currently in use. Offer the user a choice to
# forcibly un-mount
&ui_print_header(undef, $text{'edit_title'}, "");
print &text('save_force', "<tt>$mold[0]</tt>"),
"<p>\n";
print "<form action=save_mount.cgi>\n";
print "<input type=hidden name=force ",
"value=1>\n";
foreach $k (keys %in) {
print "<input type=hidden name=$k ",
"value=\"$in{$k}\">\n";
}
print "<center>\n";
print &ui_submit($text{'save_fapply'}),"\n";
print "</center>\n";
print "</form>\n";
&ui_print_footer("", $text{'index_return'});
exit;
}
else {
&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] eq $minfo[0] && $mold[1] eq $minfo[1] &&
&diff_opts($mold[3], $minfo[3]) && !$in{'perm_only'} &&
defined(&os_remount_dir)) {
# Only options have changed .. just call remount
if ($error = &remount_dir(@minfo)) {
&error(&text('save_eremount', $error));
}
@tlog = ( "remount", "dir", $minfo[0],
{ 'dir' => $minfo[0], 'dev' => $minfo[1],
'type' => $minfo[2], 'opts' => $minfo[3] } );
}
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
&ui_print_header(undef, $text{'edit_title'}, "");
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>\n";
print &ui_submit($text{'save_apply'}),"\n";
print "</center>\n";
print "</form>\n";
&ui_print_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] ||
$mold[4] != $minfo[4] || $mold[5] ne $minfo[5] ||
&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] } );
}
# If no longer mounted, remove the dir
if (&get_mounted(@mold) < 0) {
&delete_unmounted(@mold);
}
}
elsif (defined($in{'old'})) {
# Doing a simple modification to a mount
if ($in{temp}) {
@mlist = &list_mounted();
$mnow = 1;
}
else {
@mlist = &list_mounts();
$msave = 1;
$now = 1 if ($in{'oldmnow'});
}
@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));
}
&delete_unmounted(@mold);
$mnow = 0;
}
elsif ($in{'mount'}) {
# Just mount the filesystem
if ($error = &mount_dir(@mold)) {
&error(&text('save_emount', $error));
}
$mnow = 1;
}
elsif ($in{'perm'}) {
# Add to permanent mount list
&create_mount($mold[0], $mold[1], $mold[2], $mold[3],
2, "yes");
$msave = 1;
}
elsif ($in{'delete'}) {
if ($in{'oldmnow'}) {
# Unmount first
if ($error = &unmount_dir($mold[0], $mold[1],
$in{type})) {
&error(&text('save_eumount', $error));
}
$mnow = 0;
}
# Remove from permanent list
&delete_mount($in{'old'});
&delete_unmounted(@mold);
$msave = 0;
}
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});
&make_dir($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);
}
}
}
else {
# Creating a new mount, complex interface
$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});
&make_dir($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);
}
# Mark this mount and owned by this current user
$users = &get_filesystem_users();
if ($msave || $mnow) {
$users->{$in{'directory'}} ||= $remote_user;
}
else {
delete($users->{$in{'directory'}});
}
&save_filesystem_users($users);
&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;
}