home *** CD-ROM | disk | FTP | other *** search
/ Chip: Linux Special / CorelLinux_CHIP.iso / VMware / bin / vmware-mount.pl < prev    next >
Encoding:
Perl Script  |  1999-11-10  |  22.8 KB  |  815 lines

  1. #!/usr/bin/perl -w
  2. #
  3. # Copyright (C) 1999 VMWare, Inc.
  4. # All Rights Reserved
  5. #
  6. # VMware Virtual Hard Disk mount script
  7.  
  8. use strict;
  9.  
  10. # External helper programs
  11. #  mknod binary location
  12. my $mknod;
  13. #  modprobe binary location
  14. my $modprobe;
  15. #  mount binary location
  16. my $mount;
  17. #  umount binary location
  18. my $umount;
  19. #  uname binary location
  20. my $uname;
  21.  
  22. # Path of vmware-loop
  23. my $vmware_loop;
  24.  
  25. # nbd prefix
  26. my $nbd_prefix = "/dev/nb";
  27.  
  28. # Global variables for signal handlers
  29. #  PID of the child process
  30. my $child_pid;
  31. #  Mounted device
  32. my $mount_dev;
  33. #  Mounted directory
  34. my $mount_pt;
  35.  
  36. # Size of a terminal line
  37. my $lineSize = 80;
  38.  
  39. # Convert a string to its equivalent shell representation
  40. sub shell_string {
  41.   my $single_quoted = $_[0];
  42.  
  43.   $single_quoted =~ s/'/'"'"'/g;
  44.   # This comment is a fix for emacs's broken syntax-highlighting code --hpreg
  45.   return '\'' . $single_quoted . '\'';
  46. }
  47.  
  48. # Execute the command passed as an argument
  49. # _without_ interpolating variables (Perl does it by default)
  50. sub direct_command {
  51.   return `$_[0]`;
  52. }
  53.  
  54. # Wordwrap system: append some content to the output
  55. sub append_output {
  56.   my $output = $_[0];
  57.   my $pos = $_[1];
  58.   my $append = $_[2];
  59.  
  60.   $output = $output . $append;
  61.   $pos = ($pos + length($append)) % $lineSize;
  62.  
  63.   return ($output, $pos);
  64. }
  65.  
  66. # Wordwrap system: process the last encountered word
  67. sub process_word {
  68.   my $output = $_[0];
  69.   my $pos = $_[1];
  70.   my $word = $_[2];
  71.  
  72.   if ((($pos + length($word)) >= $lineSize) && (length($word) < $lineSize)) {
  73.     # The word is too large to fit in the end of line, but can fit in a new
  74.     # empty line, let's create one for it
  75.     ($output, $pos) = append_output($output, $pos, "\n");
  76.     $pos = 0;
  77.   }
  78.  
  79.   ($output, $pos) = append_output($output, $pos, $word);
  80.   $word = "";
  81.  
  82.   return ($output, $pos, $word);
  83. }
  84.  
  85. # Wordwrap system: deal with the next character
  86. sub wrap_one_char {
  87.   my $output = $_[0];
  88.   my $pos = $_[1];
  89.   my $word = $_[2];
  90.   my $char = $_[3];
  91.  
  92.   if ($char eq "\n") {
  93.     ($output, $pos, $word) = process_word($output, $pos, $word);
  94.     ($output, $pos) = append_output($output, $pos, "\n");
  95.     $pos = 0;
  96.   } else {
  97.     if (($char eq " ") || ($char eq "-")) {
  98.       ($output, $pos, $word) = process_word($output, $pos, $word);
  99.       ($output, $pos) = append_output($output, $pos, $char);
  100.     } else {
  101.       $word = $word . $char;
  102.     }
  103.   }
  104.  
  105.   return ($output, $pos, $word);
  106. }
  107.  
  108. # Wordwrap system: word-wrap a string
  109. sub wrap {
  110.   my $input = $_[0];
  111.   my $output;
  112.   my $pos;
  113.   my $word;
  114.   my $i;
  115.  
  116.   $output = "";
  117.   $pos = 0;
  118.   $word = "";
  119.   for ($i = 0; $i < length($input); $i++) {
  120.     ($output, $pos, $word) = wrap_one_char($output, $pos, $word, substr($input, $i, 1));
  121.   }
  122.   ($output, $pos, $word) = process_word($output, $pos, $word);
  123.  
  124.   return $output;
  125. }
  126.  
  127. # Build a digital Linux kernel version number
  128. sub Kernel_MakeVersion {
  129.   my $version;
  130.   my $patchLevel;
  131.   my $subLevel;
  132.   ($version, $patchLevel, $subLevel) = @_;
  133.  
  134.   return $version * 65536 + $patchLevel * 256 + $subLevel;
  135. }
  136.  
  137. # Get a clean version number for the running Linux kernel
  138. # Return value is the list of:
  139. #  Complete human readable version
  140. #  Clean x.y.z human readable version
  141. #  Digital version (default result)
  142. sub Kernel_RunningVersion {
  143.   my $fullVersion;
  144.   my $version;
  145.   my $patchLevel;
  146.   my $subLevel;
  147.  
  148.   $fullVersion = direct_command(shell_string($uname) . ' -r');
  149.   chop($fullVersion);
  150.   ($version, $patchLevel, $subLevel) = split(/\./, $fullVersion);
  151.   # Clean the subLevel in case there is an extraversion
  152.   ($subLevel) = split(/[^0-9]/, $subLevel);
  153.  
  154.   return ($fullVersion, $version . '.' . $patchLevel . '.' . $subLevel, Kernel_MakeVersion($version, $patchLevel, $subLevel));
  155. }
  156.  
  157. # Contrary to a popular belief, 'which' is not always a shell builtin command.
  158. # So we can not trust it to determine the location of other binaries.
  159. # Moreover, SuSE 6.1's 'which' is unable to handle program names beginning with
  160. # a '/'...
  161. #
  162. # Return value is the complete path if found, or "" if not found
  163. sub internal_which {
  164.   my $bin = shift;
  165.  
  166.   if (substr($bin, 0, 1) eq '/') {
  167.     # Absolute name
  168.     if ((-f $bin) && (-x $bin)) {
  169.       return $bin;
  170.     }
  171.   } else {
  172.     # Relative name
  173.     my @paths;
  174.     my $path;
  175.  
  176.     if (index($bin, '/') == -1) {
  177.       # There is no other '/' in the name
  178.       @paths = split(':', $ENV{'PATH'});
  179.       foreach $path (@paths) {
  180.     my $fullbin;
  181.  
  182.     $fullbin = $path . '/' . $bin;
  183.     if ((-f $fullbin) && (-x $fullbin)) {
  184.       return $fullbin;
  185.     }
  186.       }
  187.     }
  188.   }
  189.  
  190.   return "";
  191. }
  192.  
  193. # Tell if the user is the super user
  194. sub is_root {
  195.   return $> == 0;
  196. }
  197.  
  198. # Prompt the user for a value, providing a default value
  199. sub query {
  200.   my ($message, $defaultreply) = @_;
  201.   my $reply;
  202.   my $optSpace = substr($message, -1) eq "\n" ? "" : " ";
  203.  
  204.   print "\n";
  205.   if ($defaultreply ne "") {
  206.     print wrap($message . $optSpace . "[" . $defaultreply . "] ");
  207.   } else {
  208.     print wrap($message . $optSpace);
  209.   }
  210.   
  211.   chop($reply = <STDIN>);
  212.   $reply =~ s/^\s*//;            # Kill leading whitespace
  213.   $reply =~ s/\s*$//;            # Kill trailing whitespace
  214.   $reply = $defaultreply if $reply eq "";
  215.   return $reply;
  216. }
  217.  
  218. # Find a suitable temporary directory
  219. sub get_tmpdir {
  220.   return defined($ENV{TMPDIR}) ? $ENV{TMPDIR} : "/tmp";
  221. }
  222.  
  223. # Find a suitable temporary file name
  224. sub get_tmpfile {
  225.   return get_tmpdir() . "/vmware-mount.pl." . $$;
  226. }
  227.  
  228. # Prompts the user if a binary is not found
  229. # Return value is:
  230. #  "": the binary has not been found
  231. #  the binary name if it has been found
  232. sub DoesBinaryExist_Prompt {
  233.   my $bin;
  234.   my $ans;
  235.  
  236.   $bin = $_[0];
  237.   while (internal_which($bin) eq "") {
  238.     $ans = query("Setup is unable to find the " . $bin . " program on your machine. Do you want to specify the location of this program by hand?", "Y");
  239.     if ($ans !~ /^[yY]/) {
  240.       return "";
  241.     }
  242.  
  243.     $bin = query("What is the location of the " . $bin . " program on your machine?", "");
  244.   }
  245.   return $bin;
  246. }
  247.  
  248. # Prompts the user if a file is not readable
  249. # Return value is:
  250. #  "": the file is not readable
  251. #  the file path if it has been found
  252. sub IsFileReadable_Prompt {
  253.   my $bin;
  254.   my $ans;
  255.  
  256.   $bin = $_[0];
  257.   while (not -r $bin) {
  258.     $ans = query("Setup is unable to read the " . $bin . " file. Do you want to specify the location of this file by hand?", "Y");
  259.     if ($ans !~ /^[yY]/) {
  260.       return "";
  261.     }
  262.  
  263.     $bin = query("What is the location of the " . $bin . " file on your machine?", "");
  264.   }
  265.   return $bin;
  266. }
  267.  
  268. # Set up the location of external helpers
  269. sub initialize_external_helpers {
  270.   $mknod = DoesBinaryExist_Prompt("mknod");
  271.   if ($mknod eq "") {
  272.     print STDERR wrap("\nUnable to continue.\n");
  273.     exit(1);
  274.   }
  275.   $modprobe = DoesBinaryExist_Prompt("modprobe");
  276.   if ($modprobe eq "") {
  277.     print STDERR wrap("\nUnable to continue.\n");
  278.     exit(1);
  279.   }
  280.   $mount = DoesBinaryExist_Prompt("mount");
  281.   if ($mount eq "") {
  282.     print STDERR wrap("\nUnable to continue.\n");
  283.     exit(1);
  284.   }
  285.   $umount = DoesBinaryExist_Prompt("umount");
  286.   if ($umount eq "") {
  287.     print STDERR wrap("\nUnable to continue.\n");
  288.     exit(1);
  289.   }
  290.   $uname = DoesBinaryExist_Prompt("uname");
  291.   if ($uname eq "") {
  292.     print STDERR wrap("\nUnable to continue.\n");
  293.     exit(1);
  294.   }
  295. }
  296.  
  297. # Retrieve the path to the vmware-loop binary
  298. sub get_loop_binary {
  299.   my $vmware_config;
  300.   my $line;
  301.   my @parts;
  302.  
  303.   $vmware_config = IsFileReadable_Prompt("/etc/vmware/config");
  304.   if ($vmware_config eq "") {
  305.     print STDERR wrap("\nUnable to find the system wide VMware installation file. You may want to re-install VMware on your computer.\n");
  306.     exit(1);
  307.   }
  308.   $vmware_loop = "";
  309.   open(VMCONF, $vmware_config);
  310.   while (defined($line = <VMCONF>)) {
  311.     chop $line;
  312.     if ($line =~ /^[ \t]*loop\.fullpath[ \t]*=[ \t]*/) {
  313.       @parts = split(/\"/, $line);
  314.       $vmware_loop = $parts[1];
  315.     }
  316.   }
  317.   close(VMCONF);
  318.  
  319.   if ($vmware_loop eq "") {
  320.     print STDERR wrap("\nThis script can not find a correctly formatted loop.fullpath entry in the " . $vmware_config . " file. You may want to re-install VMware on your computer.\n");
  321.     exit(1);
  322.   }
  323. }
  324.  
  325. # Display the splash screen
  326. sub splash_screen {
  327.   my $buildNr = "1.0 " . q$Name: build-364 $;
  328.   $buildNr =~ s/Name: //;
  329.   print "\n"
  330. . "--------------------------------------------\n"
  331. . "VMware for Linux - Virtual Hard Disk Mounter\n"
  332. . "Version: " . $buildNr . "\n"
  333. . "Copyright (C) 1999 VMware, Inc.\n"
  334. . "--------------------------------------------\n";
  335. }
  336.  
  337. # Find if a network block device exists and is a block device
  338. sub does_nbd_exist {
  339.   my $nr = $_[0];
  340.  
  341.   return -b $nbd_prefix . $nr
  342. }
  343.  
  344. # Create a network block device
  345. sub create_nbd {
  346.   my $nr = $_[0];
  347.   my $ans;
  348.   my $begin;
  349.  
  350.   if ($nr == 0) {
  351.     $begin = "There is no Network Block Device defined on this machine.";
  352.   } else {
  353.     $begin = "All Network Block Devices are currently in use.";
  354.   }
  355.   $ans = query($begin . " This script is about to create the " . $nbd_prefix . $nr . " Network Block Device. Continue?", "Y");
  356.   if ($ans !~ /^[yY]/) {
  357.     print STDERR wrap("\nUnable to continue.\n");
  358.     exit(1);
  359.   }
  360.  
  361.   print wrap("\nCreating the " . $nbd_prefix . $nr . " Network Block Device\n");
  362.   if (system(shell_string($mknod) . " " . $nbd_prefix . $nr . " b 43 " . $nr . " > /dev/null")) {
  363.     print STDERR wrap("\nUnable to create the " . $nbd_prefix . $nr . " Network Block Device. You may need to re-run this script as the super user.\n");
  364.     exit(1);
  365.   }
  366.  
  367.   if (not chmod 0600, $nbd_prefix . $nr) {
  368.     print STDERR wrap("\nUnable to change the permission of the " . $nbd_prefix . $nr . " file.\n");
  369.     exit(1);
  370.   }
  371. }
  372.  
  373. # Find if a network block device driver exists
  374. sub does_nbd_driver_exist {
  375.   my $nr = $_[0];
  376.  
  377.   if (open DEV, $nbd_prefix . $nr) {
  378.     print wrap("\nNetwork Block Device driver detected.\n");
  379.     close(DEV);
  380.     return 1;
  381.   }
  382.  
  383.   print wrap("\nNo Network Block Device driver detected.\n");
  384.   return 0;
  385. }
  386.  
  387. # Kill the child
  388. sub forward_kill {
  389.   kill 'USR1', $child_pid;
  390. }
  391.  
  392. # Determine if the mount point is mounted as it should
  393. sub IsMounted {
  394.   my $mount_pid;
  395.   my $line;
  396.   my @parts;
  397.  
  398.   $mount_pid = open MOUNT, shell_string($mount) . " |";
  399.   if (not defined($mount_pid)) {
  400.     print STDERR wrap("\nUnable to invoke " . $mount . ".\n");
  401.     exit(1);
  402.   }
  403.  
  404.   while (defined($line = <MOUNT>)) {
  405.     chop($line);
  406.     @parts = split(/[ \t]+/, $line);
  407.     # Use the device name for the comparison because it can not contain
  408.     # a " " or a trailing "/"
  409.     if ($parts[0] eq ($nbd_prefix . $mount_dev)) {
  410.       return 1;
  411.     }
  412.   }
  413.   close(MOUNT); 
  414.  
  415.   return 0;
  416. }
  417.  
  418. # SIGINT handler
  419. sub sigint_handler {
  420.   my $signame = shift;
  421.  
  422.   if (IsMounted()) {
  423.     # Unmount the nbd before killing the child
  424.     if (system(shell_string($umount) . " " . $nbd_prefix . $mount_dev . " > /dev/null")) {
  425.       print wrap("\nUnable to unmount the Network Block Device on the " . $mount_pt . " mount point. Please make sure that no process uses " . $mount_pt . " as its current directory or is accessing a file under this directory.\n\nUsing another terminal, you can continue to browse your Virtual Hard Disk partition in " . $mount_pt . ". Hit Control-C again in this terminal when done.\n");
  426.       return;
  427.     }
  428.   } else {
  429.     print STDERR wrap("\nIt seems like somebody unmounted the " . $mount_pt . " mount point in my back. I was about to do it anyway.\n");
  430.   }
  431.  
  432.   forward_kill();
  433. }
  434.  
  435. # Retrieve the filesystem of the partition from the partition type and Id
  436. sub get_partition_fs {
  437.   my $tmpfile = $_[0];
  438.   my $part_nb = $_[1];
  439.   my $line;
  440.   my @parts;
  441.   my $offset;
  442.   my $part_type;
  443.   my $part_id;
  444.  
  445.   if (not open(OUTPUT, $tmpfile)) {
  446.     printf STDERR wrap("\nUnable to open the temporary file containing the partition table.\n");
  447.     return "";
  448.   }
  449.  
  450.   $part_type = "";
  451.   $part_id = "";
  452.   $line = <OUTPUT>;
  453.   $line = <OUTPUT>;
  454.   $line = <OUTPUT>;
  455.   while(defined($line = <OUTPUT>)) {
  456.     chop $line;
  457.     @parts = split(/[ \t]+/, $line);
  458.     # In case the line begins with a whitespace
  459.     if ($parts[0] eq "") {
  460.       $offset = 1;
  461.     } else {
  462.       $offset = 0;
  463.     }
  464.     if ($part_nb == $parts[$offset]) {
  465.       $part_type = $parts[$offset + 3];
  466.       $part_id = $parts[$offset + 4];
  467.       last;
  468.     }
  469.   }
  470.  
  471.   close(OUTPUT);
  472.   if (($part_type eq "") || ($part_id eq "")) {
  473.     printf STDERR wrap("\nUnable to retrieve the partition in the partition table.\n");
  474.     return "";
  475.   }
  476.  
  477.   if ($part_type eq "BIOS") {
  478.     if (($part_id eq "1") || ($part_id eq "4") || ($part_id eq "6") || ($part_id eq "B") || ($part_id eq "E") || ($part_id eq "11") || ($part_id eq "14") || ($part_id eq "16") || ($part_id eq "24") || ($part_id eq "C1") || ($part_id eq "C4") || ($part_id eq "C6") || ($part_id eq "F2")) {
  479.       return "msdos";
  480.     }
  481.     if ($part_id eq "C") {
  482.       return "vfat";
  483.     }
  484.     if ($part_id eq "7" || ($part_id eq "17")) {
  485.       return "ntfs";
  486.     }
  487.     if ($part_id eq "4D" || ($part_id eq "4E") || ($part_id eq "4F")) {
  488.       return "qnx4";
  489.     }
  490.     if ($part_id eq "83") {
  491.       return "ext2";
  492.     }
  493.   }
  494.   if ($part_type eq "BSD") {
  495.     if ($part_id eq "7") {
  496.       return "ufs";
  497.     }
  498.     if ($part_id eq "11") {
  499.       return "ext2";
  500.     }
  501.   }
  502.  
  503.   print STDERR wrap("\nUnable to retrieve the filesystem of the partition (the partition type is " . $part_type . " and the partition Id is " . $part_id . "). Please file an incident with VMware at http://www.vmware.com/forms/Incident_Login.cfm by copying this error message.\n");
  504.   return "";
  505. }
  506.  
  507. # Export and mount a partition
  508. sub export_mount {
  509.   my $vhd = $_[0];
  510.   my $part_nb = $_[1];
  511.   my $vfstype = $_[2];
  512.   # $_[3] is the global $mount_pt
  513.   my $options = $_[4];
  514.   my $i;
  515.   my $fuser;
  516.   my $line;
  517.   my $char;
  518.   my $ready;
  519.   my $retval;
  520.   my $tmpfile;
  521.  
  522.   # Find an existing nbd
  523.   for(;;) {
  524.     for($i = 0; $i < 256; $i++) {
  525.       if (does_nbd_exist($i)) {
  526.     last;
  527.       }
  528.     }
  529.     
  530.     if ($i != 256) {
  531.       last;
  532.     }
  533.     print wrap("\nNo Network Block Device detected.\n");
  534.     create_nbd(0);
  535.   }
  536.  
  537.   # Make sure the nbds are supported
  538.   if (not does_nbd_driver_exist($i)) {
  539.     print wrap("\nTrying to load the Network Block Device driver kernel module... ");
  540.     if (system(shell_string($modprobe) . " nbd > /dev/null")) {
  541.       print "Failure.\n";
  542.       print STDERR wrap("\nIf you are not an experimented Linux user, you may want to upgrade your Linux distribution and re-run this script.\n\nIf you are an experimented Linux user, you may want to re-compile your kernel. Use a kernel version 2.1.101 or higher, and set the CONFIG_BLK_DEV_NBD option (which you can find under \"Block devices\"->\"Network block device support\") to \"y\" or \"m\". If you don't want to mess with kmod (the kernel module loader formerly known as kerneld), we recommend to use \"y\". Once you have rebooted your new kernel, re-run this script.\n");
  543.       exit(1);
  544.     }
  545.  
  546.     print "Success.\n";
  547.   }
  548.  
  549.   # Output the partition table in a temporary file (so that it doesn't
  550.   # interfere with user interaction). Do that before mapping the
  551.   # virtual hard disk partition to an nbd to avoid the deadlock
  552.   $tmpfile = get_tmpfile();      
  553.   if (system(shell_string($vmware_loop) . " -q -P " . shell_string($tmpfile) . " " . shell_string($vhd))) {
  554.     print STDERR wrap("\nUnable to invoke " . $vmware_loop . ". You may want to re-install VMware.\n");
  555.     exit(1);
  556.   }
  557.  
  558.   # As of Linux 2.2.9, the nbd driver allows to issue 2 NBD_SET_SOCK ioctls
  559.   # in a row without NBD_CLEAR_SOCK in between. This is not cool because we
  560.   # cannot know if another process has already set up the nbd :( I'm going to
  561.   # submit a patch. In the meantime, I have no other option than adding a race
  562.   # condition by using fuser or lsof :( --hpreg
  563.   $fuser = DoesBinaryExist_Prompt("fuser");
  564.   if ($fuser eq "") {
  565.     $fuser = DoesBinaryExist_Prompt("lsof");
  566.     if ($fuser eq "") {
  567.       print STDERR wrap("\nThis script can not continue.\n");
  568.       exit(1);
  569.     }
  570.   }
  571.   
  572.   # Map the virtual hard disk partition to the first available nbd
  573.   for($i = 0; $i < 256; $i++) {
  574.     if (not does_nbd_exist($i)) {
  575.       create_nbd($i);
  576.     }
  577.  
  578.     if (not system(shell_string($fuser) . " " . $nbd_prefix . $i . " > /dev/null")) {
  579.       print wrap("The " . $nbd_prefix . $i . " Network Block Device is busy.\n");
  580.       next;
  581.     }
  582.  
  583.     select(STDOUT); $| = 1; # Unbuffer
  584.     # Fork a vmware-loop in quiet_mode, and redirect its stdin/stdout to the
  585.     # LOOP filehandle
  586.     $child_pid = open LOOP, shell_string($vmware_loop) . " -q " . shell_string($vhd) . " " . $part_nb . " " . $nbd_prefix . $i . " |";
  587.     if (not defined($child_pid)) {
  588.       print STDERR wrap("\nUnable to invoke " . $vmware_loop . ". You may want to re-install VMware.\n");
  589.       exit(1);
  590.     }
  591.  
  592.     $line = "";
  593.     for (;;) {
  594.       $char = getc(LOOP);
  595.       if (not defined ($char)) {
  596.     # EOF
  597.     $ready = 0;
  598.         last;
  599.       }
  600.  
  601.       # Display the output of the program so that the user can interact with
  602.       # the hints
  603.       print $char;
  604.  
  605.       # Reconstitute lines
  606.       if ($char eq "\n") {
  607.     if ($line =~ /^Client: The partition is now mapped on the $nbd_prefix$i Network Block Device.$/) {
  608.       $ready = 1;
  609.       last;
  610.     }
  611.     $line = "";
  612.       } else {
  613.     $line = $line . $char;
  614.       }
  615.     }
  616.  
  617.     if ($ready == 0) {
  618.       # Child closed the pipe, its exit code is in $?
  619.       # This shouldn't normally happen unless the binary location is wrong
  620.       print wrap("Unable to map the " . $nbd_prefix . $i . " Network Block Device.\n");
  621.     } else {
  622.       last;
  623.     }
  624.   }
  625.   if ($i == 256) {
  626.     print STDERR wrap("\nAll Network Block Devices are in use. Please re-run this script when at least one of them is not busy anymore.\n");
  627.     exit(1);
  628.   }
  629.  
  630.   # Avoid to special case this later
  631.   if (not ($options eq "")) {
  632.     $options = '-o ' . shell_string($options);
  633.   }
  634.  
  635.   # Mount the nbd
  636.   $mount_pt = $_[3];
  637.   $mount_dev = $i;
  638.   if ($vfstype eq "") {
  639.     # First try to autoprobe the type of the filesystem (in case its driver is
  640.     # either non-modular, or modular and already loaded)
  641.     $retval = system(shell_string($mount) . ' ' . $options . ' ' . $nbd_prefix . $mount_dev . ' ' . shell_string($mount_pt) . ' > /dev/null');
  642.     if ($retval) {
  643.       # Second, try to deduce the type from the partition table
  644.       print wrap("\nIf you know the filesystem of the partition you want to mount, you can provide it using the -t command line option. Since you haven't done so, this script is going to try to determine the filesystem of the partition based on the partition type and id.\n");
  645.  
  646.       $vfstype = get_partition_fs($tmpfile, $part_nb);
  647.       if ($vfstype eq "") {
  648.     forward_kill();
  649.     close(LOOP);
  650.     exit(1);
  651.       }      
  652.       
  653.       # Remove the temporary file. Let's be clean
  654.       unlink($tmpfile);
  655.  
  656.       print wrap("\nDetected " . $vfstype . " filesystem type.\n");
  657.       $retval = system(shell_string($mount) . ' ' . $options . ' -t ' . shell_string($vfstype) . ' ' . $nbd_prefix . $mount_dev . ' ' . shell_string($mount_pt) . ' > /dev/null');
  658.     }
  659.   } else {
  660.     # Use the type provided by the user
  661.     $retval = system(shell_string($mount) . ' ' . $options . ' -t ' . shell_string($vfstype) . ' ' . $nbd_prefix . $mount_dev . ' ' . shell_string($mount_pt) . ' > /dev/null');
  662.   }
  663.   if ($retval) {
  664.     print wrap("\nUnable to mount the Network Block Device on the " . $mount_pt . " mount point. Please make sure that nothing is currently using the mount point and that your kernel supports the partition type you want to mount before re-running this script.\n");
  665.     forward_kill();
  666.     close(LOOP);
  667.     exit(1);
  668.   }
  669.   
  670.   # Install the SIGINT handler
  671.   $SIG{INT} = \&sigint_handler;
  672.  
  673.   print wrap("\nUsing another terminal, you can now browse your Virtual Hard Disk partition in " . $mount_pt . ". Hit Control-C in this terminal when done.\n");
  674.  
  675.   for (;;) {
  676.     $char = getc(LOOP);
  677.     if (not defined ($char)) {
  678.       # EOF
  679.       last;
  680.     }
  681.  
  682.     # Display the output of the program so that the user can interact with
  683.     # the hints
  684.     print $char;
  685.   }
  686.  
  687.   # Child has exited (perhaps because we killed it)
  688.   close(LOOP);
  689.  
  690.   # Reset the handler
  691.   $SIG{INT} = 'DEFAULT';
  692. }
  693.  
  694. # Display the partition table of a virtual disk
  695. sub show_partition_table {
  696.   my $vhd = $_[0];
  697.  
  698.   if (system(shell_string($vmware_loop) . " -q -p " . shell_string($vhd))) {
  699.     print STDERR wrap("\nUnable to invoke " . $vmware_loop . ". You may want to re-install VMware.\n");
  700.     exit(1);
  701.   }
  702. }
  703.  
  704. # Display a usage string on stderr, and exit
  705. sub usage {
  706.   print STDERR "\nUsage: " . $0 . "\n" .
  707. "\t-p          : Print the partition table\n" .
  708. "\tdisk        : Name of the Virtual Hard Disk file\n" .
  709. "or\n" .
  710. "\tdisk        : Name of the Virtual Hard Disk file\n" .
  711. "\tpartition   : Number of the partition\n" .
  712. "\t[-t type]   : Partition type\n" .
  713. "\t[-o options]: Partition mount options(s)\n" .
  714. "\tmount-point : Directory where to mount the partition\n";
  715.   exit(1);
  716. }
  717.  
  718. # Where the real work is done
  719. sub main {  
  720.   #  print partition table
  721.   my $print_mode;
  722.   #  filesystem type
  723.   my $type_value;
  724.   #  mount options
  725.   my $options_value;
  726.   #  mandatory arguments
  727.   my @arguments;
  728.  
  729.   my $i;
  730.   my $nb;
  731.  
  732.   # Scan the command line
  733.   @arguments = ();
  734.   $print_mode = 0;
  735.   $type_value = "";
  736.   $options_value = "";
  737.   for ($i = 0; $i < $#ARGV + 1; $i++) {
  738.     if ($ARGV[$i] eq "--") {
  739.       last;
  740.     }
  741.     if ($ARGV[$i] eq "-p") {
  742.       $print_mode = 1;
  743.       next;
  744.     }
  745.     if ($ARGV[$i] eq "-t") {
  746.       $i++;
  747.       if ($i >= $#ARGV + 1) {
  748.     print STDERR wrap("\nThe -t command line option requires a valid filesystem type as argument. See 'man mount' for filesystem types supported by your system.\n");
  749.     usage();
  750.       }
  751.       $type_value = $ARGV[$i];
  752.       next;
  753.     }
  754.     if ($ARGV[$i] eq "-o") {
  755.       $i++;
  756.       if ($i >= $#ARGV + 1) {
  757.     print STDERR wrap("\nThe -o command line option requires at least one valid mount option as argument. See 'man mount' for options supported by your filesystem.\n");
  758.     usage();
  759.       }
  760.       $options_value = $ARGV[$i];
  761.       next;
  762.     }
  763.     push @arguments, $ARGV[$i];
  764.   }
  765.   
  766.   # Check the options validity
  767.   if (($print_mode == 1) && (not ($type_value eq ""))) {
  768.     print STDERR wrap("\nOptions -p and -t are mutually exclusive.\n");
  769.     usage();
  770.   }
  771.   if (($print_mode == 1) && (not ($options_value eq ""))) {
  772.     print STDERR wrap("\nOptions -p and -o are mutually exclusive.\n");
  773.     usage();
  774.   }
  775.  
  776.   # Check the number of mandatory arguments
  777.   if ($print_mode == 0) {
  778.     $nb = 3;
  779.   } else {
  780.     $nb = 1;
  781.   }
  782.   if ($#arguments + 1 != $nb) {
  783.     print STDERR wrap("\nThis script requires " . $nb . " (not " . ($#arguments + 1) . ") mandatory argument(s).\n");
  784.     usage();
  785.   }
  786.  
  787.   splash_screen();
  788.   get_loop_binary();
  789.  
  790.   if ($print_mode == 0) {
  791.     if (not is_root()) {
  792.       print STDERR wrap("\nPlease re-run this script as the super user.\n");
  793.       exit(1);
  794.     }
  795.  
  796.     # Force the path to reduce the risk of using "modified" external helpers
  797.     # If the user has a special system setup, he will be prompted for the
  798.     # proper location anyway
  799.     $ENV{'PATH'} = "/bin:/usr/bin:/sbin:/usr/sbin";
  800.  
  801.     initialize_external_helpers();
  802.     if (Kernel_RunningVersion() < Kernel_MakeVersion(2, 1, 101)) {
  803.       print STDERR wrap("\nYour Linux kernel is too old to run this script. Please boot a Linux kernel version 2.1.101 or higher (including 2.2.x) and re-run this script.\n");
  804.       exit(1);
  805.     }
  806.     export_mount($arguments[0], $arguments[1], $type_value, $arguments[2], $options_value);
  807.   } else {
  808.     show_partition_table($arguments[0]);
  809.   }
  810.   
  811.   exit(0);
  812. }
  813.  
  814. main();
  815.