Handle hostnames with upper-case letters
[webmin.git] / mount / save_mount.cgi
1 #!/usr/local/bin/perl
2 # save_mount.cgi
3 # Save or create a mount. When saving an existing mount, at lot of different
4 # things can happen. 
5
6 require './mount-lib.pl';
7 &error_setup($text{'save_err'});
8 &ReadParse();
9 $| = 1;
10
11 if ($in{'advanced'}) {
12         # Return to mount form, but force advanced mount option mode
13         if ($in{'old'} eq '') {
14                 &redirect("edit_mount.cgi?type=$in{'type'}&advanced=1");
15                 }
16         else {
17                 &redirect("edit_mount.cgi?temp=$in{'temp'}&index=$in{'old'}&advanced=1");
18                 }
19         exit;
20         }
21
22 # check inputs
23 if ($in{type} ne "swap") {
24         if ($in{directory} !~ /^\//) {
25                 if (@access_fs && $in{directory}) {
26                         # Assume relative to allowed dir
27                         $in{directory} = $access_fs[0]."/".$in{directory};
28                         }
29                 else {
30                         &error(&text('save_edirname', $in{'directory'}));
31                         }
32                 }
33         if (-r $in{'directory'} && !(-d $in{'directory'})) {
34                 &error(&text('save_edir', $in{'directory'}));
35                 }
36         # non-existant directories get created later
37         }
38 else {
39         # for swap files, set the directory to 'swap'
40         $in{directory} = "swap";
41         }
42 &can_edit_fs($in{'directory'}, undef, $in{'type'}, undef, 1) &&
43         !$access{'only'} || &error($text{'edit_ecannot'});
44 $access{'create'} || defined($in{'old'}) || &error($text{'edit_ecannot2'});
45
46 # Get user choices
47 @mmodes = &mount_modes($in{type});
48 $msave = ($mmodes[0]==0 ? 0 : $in{msave});
49 $mnow = ($mmodes[1]==0 ? $msave : $in{mmount});
50 $access{'simopts'} = 0 if ($in{'nosimopts'});
51
52 foreach $f (&files_to_lock()) {
53         &lock_file($f);
54         }
55 if (defined($in{old}) && !$access{'simple'}) {
56         # Saving an existing mount
57         if ($in{temp}) { @mlist = &list_mounted(); }
58         else { @mlist = &list_mounts(); }
59         @mold = @{$mlist[$in{old}]};
60
61         if (!$mnow && !$in{oldmnow} && !$msave) {
62                 # Not mounted, so remove from fstab without checking
63                 $dev = $mold[1];
64                 }
65         else {
66                 # Changing an existing mount
67                 $dev = &check_location($in{'type'});
68                 &parse_options($mold[2], $mold[3]);
69                 $opts = &check_options($in{'type'}, $dev, $in{'directory'});
70                 @minfo = ($in{'directory'}, $dev, $in{'type'}, $opts,
71                           $mmodes[2] ? $in{'order'} : "-",
72                           $in{'msave'}==2||$mmodes[0]==1 ? "yes" : "no");
73                 }
74
75         # Check for change in device
76         if ($mold[1] ne $dev) {
77                 # Device has changed..check it
78                 if (!&multiple_mount($minfo[2]) && &get_mounted("*", $dev)>=0) {
79                         &error(&text('save_ealready', $dev));
80                         }
81                 if (!&multiple_mount($minfo[2]) && &get_mount("*", $dev) != -1){
82                         &error(&text('save_ealready2', $dev));
83                         }
84                 $changed = 1;
85                 }
86
87         # Check for change in directory
88         if ($in{type} ne "swap" && $mold[0] ne $in{directory}) {
89                 # Directory has changed.. check it too
90                 if (&get_mounted($in{directory}, "*")>=0) {
91                         &error(&text('save_ealready3', $in{'directory'}));
92                         }
93                 if (&get_mount($in{directory}, "*") != -1) {
94                         &error(&text('save_ealready4', $in{'directory'}));
95                         }
96                 $changed = 1;
97
98                 if (!(-d $in{directory})) {
99                         # Create the new directory
100                         &lock_file($in{directory});
101                         &make_dir($in{directory}, 0755) ||
102                                 &error(&text('save_emkdir',
103                                              $in{'directory'}, $!));
104                         &unlock_file($in{directory});
105                         $made_dir = 1;
106                         }
107                 }
108
109         # Check for change in current mount status
110         if ($in{'oldmnow'} && $mmodes[3] == 1) {
111                 # Mounted, and cannot be unmounted
112                 }
113         elsif ($in{'oldmnow'} && !$mnow) {
114                 # Just been unmounted..
115                 if ($error = &unmount_dir($mold[0], $mold[1], $in{'type'},
116                                           $mold[3], $in{'force'})) {
117                         if (!$in{'force'} &&
118                             $error =~ /busy|Invalid argument/ &&
119                             defined(&can_force_unmount_dir) &&
120                             &can_force_unmount_dir(@mold)) {
121                                 # Mount is busy.. most likely because it is
122                                 # currently in use. Offer the user a choice to
123                                 # forcibly un-mount
124                                 &ui_print_header(undef, $text{'edit_title'}, "");
125                                 print &text('save_force', "<tt>$mold[0]</tt>"),
126                                       "<p>\n";
127                                 print "<form action=save_mount.cgi>\n";
128                                 print "<input type=hidden name=force ",
129                                       "value=1>\n";
130                                 foreach $k (keys %in) {
131                                         print "<input type=hidden name=$k ",
132                                               "value=\"$in{$k}\">\n";
133                                         }
134                                 print "<center>\n";
135                                 print &ui_submit($text{'save_fapply'}),"\n";
136                                 print "</center>\n";
137                                 print "</form>\n";
138
139                                 &ui_print_footer("", $text{'index_return'});
140                                 exit;
141                                 }
142                         else {
143                                 &error(&text('save_eumount', $error));
144                                 }
145                         }
146                 @tlog = ( "umount", "dir", $mold[0],
147                           { 'dir' => $mold[0], 'dev' => $mold[1],
148                             'type' => $mold[2], 'opts' => $mold[3] } );
149                 }
150         elsif ($mnow && !$in{oldmnow}) {
151                 # Just been mounted..
152                 if ($error = &mount_dir(@minfo)) {
153                         &error(&text('save_emount', $error));
154                         }
155                 @tlog = ( "mount", "dir", $minfo[0], 
156                           { 'dir' => $minfo[0], 'dev' => $minfo[1],
157                             'type' => $minfo[2], 'opts' => $minfo[3] } );
158                 }
159         elsif (!$mnow && !$in{oldmnow}) {
160                 # Not mounted, and doesn't need to be
161                 }
162         elsif ($mold[0] eq $minfo[0] && $mold[1] eq $minfo[1] &&
163                &diff_opts($mold[3], $minfo[3]) && !$in{'perm_only'} &&
164                defined(&os_remount_dir)) {
165                 # Only options have changed .. just call remount
166                 if ($error = &remount_dir(@minfo)) {
167                         &error(&text('save_eremount', $error));
168                         }
169                 @tlog = ( "remount", "dir", $minfo[0], 
170                           { 'dir' => $minfo[0], 'dev' => $minfo[1],
171                             'type' => $minfo[2], 'opts' => $minfo[3] } );
172                 }
173         elsif (($mold[0] ne $minfo[0] || $mold[1] ne $minfo[1] ||
174                &diff_opts($mold[3], $minfo[3])) && !$in{'perm_only'}) {
175                 # Need to unmount/mount to apply new options
176                 if ($error = &unmount_dir($mold[0], $mold[1], $in{type})) {
177                         if ($error =~ /busy|Invalid argument/ && $msave) {
178                                 # Mount is busy.. most likely because it is
179                                 # currently in use. Offer the user a choice
180                                 # to update only the fstab file, rather than
181                                 # the real mount
182                                 &ui_print_header(undef, $text{'edit_title'}, "");
183                                 print &text('save_perm', "<tt>$mold[0]</tt>"),
184                                       "<p>\n";
185                                 print "<form action=save_mount.cgi>\n";
186                                 print "<input type=hidden name=perm_only ",
187                                       "value=1>\n";
188                                 foreach $k (keys %in) {
189                                         print "<input type=hidden name=$k ",
190                                               "value=\"$in{$k}\">\n";
191                                         }
192                                 print "<center>\n";
193                                 print &ui_submit($text{'save_apply'}),"\n";
194                                 print "</center>\n";
195                                 print "</form>\n";
196
197                                 &ui_print_footer("", $text{'index_return'});
198                                 exit;
199                                 }
200                         else { &error(&text('save_eremount', $error)); }
201                         }
202                 if ($error = &mount_dir(@minfo)) {
203                         &error(&text('save_eremount', $error));
204                         }
205                 @tlog = ( "remount", "dir", $minfo[0], 
206                           { 'dir' => $minfo[0], 'dev' => $minfo[1],
207                             'type' => $minfo[2], 'opts' => $minfo[3] } );
208                 }
209
210         # Check for change in permanence
211         if ($in{oldmsave} && !$msave) {
212                 # Delete from mount table
213                 &delete_mount($in{old});
214                 @plog = ( "delete", "dir", $in{'directory'},
215                           { 'dir' => $mold[0], 'dev' => $mold[1],
216                             'type' => $mold[2], 'opts' => $mold[3] } );
217                 }
218         elsif ($msave && !$in{oldmsave}) {
219                 # Add to mount table
220                 &create_mount(@minfo);
221                 @plog = ( "create", "dir", $in{'directory'},
222                           { 'dir' => $minfo[0], 'dev' => $minfo[1],
223                             'type' => $minfo[2], 'opts' => $minfo[3] } );
224                 }
225         elsif (!$msave && !$in{oldmsave}) {
226                 # Not in mount table
227                 }
228         elsif ($mold[0] ne $minfo[0] || $mold[1] ne $minfo[1] ||
229                $mold[4] != $minfo[4] || $mold[5] ne $minfo[5] ||
230                &diff_opts($mold[3], $minfo[3])) {
231                 # Apply any changes in mount options
232                 &change_mount($in{old}, @minfo);
233                 @plog = ( "modify", "dir", $in{'directory'},
234                           { 'dir' => $minfo[0], 'dev' => $minfo[1],
235                             'type' => $minfo[2], 'opts' => $minfo[3] } );
236                 }
237
238         # If no longer mounted, remove the dir 
239         if (&get_mounted(@mold) < 0) {
240                 &delete_unmounted(@mold);
241                 }
242         }
243 elsif (defined($in{'old'})) {
244         # Doing a simple modification to a mount
245         if ($in{temp}) {
246                 @mlist = &list_mounted();
247                 $mnow = 1;
248                 }
249         else {
250                 @mlist = &list_mounts();
251                 $msave = 1;
252                 $now = 1 if ($in{'oldmnow'});
253                 }
254         @mold = @{$mlist[$in{old}]};
255
256         if ($in{'umount'}) {
257                 # Just unmount the filesystem
258                 if ($error = &unmount_dir($mold[0], $mold[1], $in{type})) {
259                         &error(&text('save_eumount', $error));
260                         }
261                 &delete_unmounted(@mold);
262                 $mnow = 0;
263                 }
264         elsif ($in{'mount'}) {
265                 # Just mount the filesystem
266                 if ($error = &mount_dir(@mold)) {
267                         &error(&text('save_emount', $error));
268                         }
269                 $mnow = 1;
270                 }
271         elsif ($in{'perm'}) {
272                 # Add to permanent mount list
273                 &create_mount($mold[0], $mold[1], $mold[2], $mold[3],
274                               2, "yes");
275                 $msave = 1;
276                 }
277         elsif ($in{'delete'}) {
278                 if ($in{'oldmnow'}) {
279                         # Unmount first
280                         if ($error = &unmount_dir($mold[0], $mold[1],
281                                                   $in{type})) {
282                                 &error(&text('save_eumount', $error));
283                                 }
284                         $mnow = 0;
285                         }
286                 # Remove from permanent list
287                 &delete_mount($in{'old'});
288                 &delete_unmounted(@mold);
289                 $msave = 0;
290                 }
291         else {
292                 # Updating the mount in some way ..
293                 # Check the mount source
294                 $dev = &check_location($in{'type'});
295                 &parse_options($mold[2], $mold[3]);
296                 if (defined($access{'opts'}) &&
297                     $access{'opts'} !~ /$in{'type'}/) {
298                         # Just use existing options
299                         local @opts;
300                         foreach $k (keys %options) {
301                                 if ($options{$k} eq '') { push(@opts, $k); }
302                                 else { push(@opts, "$k=$options{$k}"); }
303                                 }
304                         $opts = @opts ? join(",", @opts) : "-";
305                         }
306                 else {
307                         # Get options from the user
308                         $opts = &check_options($in{'type'}, $dev,
309                                                $in{'directory'});
310                         }
311                 @minfo = ($in{'directory'}, $dev, $in{'type'}, $opts, 2, 'yes');
312
313                 # Check for change in device
314                 if ($mold[1] ne $dev) {
315                         # Device has changed..check it
316                         if (!&multiple_mount($minfo[2]) &&
317                             &get_mounted("*", $dev)>=0) {
318                                 &error(&text('save_ealready', $dev));
319                                 }
320                         if (!&multiple_mount($minfo[2]) &&
321                             &get_mount("*", $dev) != -1){
322                                 &error(&text('save_ealready2', $dev));
323                                 }
324                         $changed = 1;
325                         }
326
327                 # Check for change in directory
328                 if ($in{type} ne "swap" && $mold[0] ne $in{directory}) {
329                         # Directory has changed.. check it too
330                         if (&get_mounted($in{directory}, "*")>=0) {
331                                 &error(&text('save_ealready3',
332                                              $in{'directory'}));
333                                 }
334                         if (&get_mount($in{directory}, "*") != -1) {
335                                 &error(&text('save_ealready4',
336                                              $in{'directory'}));
337                                 }
338                         $changed = 1;
339
340                         if (!(-d $in{directory})) {
341                                 # Create the new directory
342                                 &lock_file($in{directory});
343                                 &make_dir($in{directory}, 0755) ||
344                                         &error(&text('save_emkdir',
345                                                      $in{'directory'}, $!));
346                                 &unlock_file($in{directory});
347                                 $made_dir = 1;
348                                 }
349                         }
350
351                 if ($in{'oldmnow'} && ($mold[0] ne $minfo[0] ||
352                     $mold[1] ne $minfo[1] || &diff_opts($mold[3], $minfo[3]))) {
353                         # Need to unmount/mount to apply new options
354                         if ($error=&unmount_dir($mold[0], $mold[1], $in{type})){
355                                 &error(&text('save_eremount', $error));
356                                 }
357                         if ($error = &mount_dir(@minfo)) {
358                                 &error(&text('save_eremount', $error));
359                                 }
360                         }
361
362                 if ($in{'oldmsave'}) {
363                         # Change entry in fstab
364                         &change_mount($in{'old'}, @minfo);
365                         }
366                 }
367         }
368 elsif ($access{'simple'}) {
369         # Create and mounting from the simple interface
370         $dev = &check_location($in{type});
371         if (!defined($access{'opts'}) || $access{'opts'} =~ /$in{'type'}/) {
372                 $opts = &check_options($in{'type'}, $dev, $in{'directory'});
373                 }
374         else {
375                 $opts = "-";
376                 }
377         @minfo = ($in{directory}, $dev, $in{type}, $opts, 2, 'yes');
378
379         # Check if the device is in use
380         if (!&multiple_mount($minfo[2]) && &get_mounted("*", $dev)>=0) {
381                 &error(&text('save_ealready', $dev));
382                 }
383         if (!&multiple_mount($minfo[2]) && &get_mount("*", $dev) != -1) {
384                 &error(&text('save_ealready2', $dev));
385                 }
386
387         # Check if the directory is in use
388         if ($in{type} ne "swap") {
389                 if (&get_mounted($in{directory}, "*")>=0) {
390                         &error(&text('save_ealready2', $in{'directory'}));
391                         }
392                 if (&get_mount($in{directory}, "*") != -1) {
393                         &error(&text('save_ealready3', $in{'directory'}));
394                         }
395                 }
396
397         # Create the directory
398         if ($in{type} ne "swap" && !(-d $in{directory})) {
399                 &lock_file($in{directory});
400                 &make_dir($in{directory}, 0755) ||
401                   &error(&text('save_emkdir', $in{'directory'}, $!));
402                 &unlock_file($in{directory});
403                 $made_dir = 1;
404                 }
405
406         # Mount and save
407         if ($error = &mount_dir($minfo[0], $minfo[1],
408                                 $minfo[2], $minfo[3])) {
409                 if ($made_dir) { rmdir($in{directory}); }
410                 &error(&text('save_emount', $error));
411                 }
412         $mnow = 1;
413         if ($mmodes[0]) {
414                 &create_mount(@minfo);
415                 $msave = 1;
416                 }
417         }
418 else {
419         # Creating a new mount, complex interface
420         $dev = &check_location($in{type});
421         &parse_options($minfo[3]);
422         $opts = &check_options($in{type}, $dev, $in{'directory'});
423         @minfo = ($in{directory}, $dev, $in{type}, $opts,
424                   $mmodes[2] ? $in{order} : "-",
425                   $in{msave}==2||$mmodes[0]==1 ? "yes" : "no");
426
427         # Check if anything is being done
428         if (!$msave && !$mnow) {
429                 &error($text{'save_enone'});
430                 }
431
432         # Check if the device is in use
433         if (!&multiple_mount($minfo[2]) && &get_mounted("*", $dev)>=0) {
434                 &error(&text('save_ealready', $dev));
435                 }
436         if (!&multiple_mount($minfo[2]) && &get_mount("*", $dev) != -1) {
437                 &error(&text('save_ealready2', $dev));
438                 }
439
440         # Check if the directory is in use
441         if ($in{type} ne "swap") {
442                 if (&get_mounted($in{directory}, "*")>=0) {
443                         &error(&text('save_ealready2', $in{'directory'}));
444                         }
445                 if (&get_mount($in{directory}, "*") != -1) {
446                         &error(&text('save_ealready3', $in{'directory'}));
447                         }
448                 }
449
450         # Create the directory
451         if ($in{type} ne "swap" && !(-d $in{directory})) {
452                 &lock_file($in{directory});
453                 &make_dir($in{directory}, 0755) ||
454                   &error(&text('save_emkdir', $in{'directory'}, $!));
455                 &unlock_file($in{directory});
456                 $made_dir = 1;
457                 }
458
459         # If mounting now, attempt to do it
460         if ($mnow) {
461                 # If the mount fails, give up totally
462                 if ($error = &mount_dir($minfo[0], $minfo[1],
463                                         $minfo[2], $minfo[3])) {
464                         if ($made_dir) { rmdir($in{directory}); }
465                         &error(&text('save_emount', $error));
466                         }
467                 @tlog = ( "mount", "dir", $in{'directory'},
468                           { 'dir' => $minfo[0], 'dev' => $minfo[1],
469                             'type' => $minfo[2], 'opts' => $minfo[3] } );
470                 }
471
472         # If saving, save now
473         if ($msave) {
474                 &create_mount(@minfo);
475                 @plog = ( "create", "dir", $in{'directory'},
476                           { 'dir' => $minfo[0], 'dev' => $minfo[1],
477                             'type' => $minfo[2], 'opts' => $minfo[3] } );
478                 }
479         }
480 foreach $f (&files_to_lock()) {
481         &unlock_file($f);
482         }
483 &webmin_log(@plog) if (@plog);
484 %tpmap = ( 'create', 'mount',  'delete', 'umount',  'modify', 'remount' );
485 if (@tlog && $tpmap{$plog[0]} ne $tlog[0]) {
486         &webmin_log(@tlog);
487         }
488
489 # Mark this mount and owned by this current user
490 $users = &get_filesystem_users();
491 if ($msave || $mnow) {
492         $users->{$in{'directory'}} ||= $remote_user;
493         }
494 else {
495         delete($users->{$in{'directory'}});
496         }
497 &save_filesystem_users($users);
498
499 &redirect($in{'return'});
500
501 # undo_changes
502 # Put back any changes to the fstab file
503 sub undo_changes
504 {
505 if ($in{temp} && $in{mboot}) {
506         # a mount was made permanent.. undo by deleting it
507         &delete_mount($idx);
508         }
509 elsif (!$in{temp} && !$in{mboot}) {
510         # a permanent mount was made temporary.. undo by making it permanent
511         &create_mount(@mold);
512         }
513 elsif ($in{mboot}) {
514         # some mount options were changed.. undo by changing back
515         &change_mount($in{old}, @mold);
516         }
517 if ($made_dir) {
518         # A directory for mounting was created.. delete it
519         rmdir($in{directory});
520         }
521 }
522
523 # diff_opts(string1, string2)
524 sub diff_opts
525 {
526 local $o1 = join(",", sort { $a cmp $b } split(/,/, $_[0]));
527 local $o2 = join(",", sort { $a cmp $b } split(/,/, $_[1]));
528 return $o1 ne $o2;
529 }
530