Commit ef37bdb7 authored by Joan Lledó's avatar Joan Lledó Committed by Curtis Gedak

Added support to lost data recovery using gpart

parent 9d6a3073
/* Copyright (C) 2010 Joan Lledó
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*
* The dialog for mount old data
* Reads the output of gpart and build the dialog
* */
#ifndef DIALOG_RESCUE_DATA
#define DIALOG_RESCUE_DATA
#include "../include/Device.h"
#include "../include/Partition.h"
#include <gtkmm/dialog.h>
#include <gtkmm/frame.h>
#include <vector>
namespace GParted
{
class Dialog_Rescue_Data : public Gtk::Dialog
{
public:
Dialog_Rescue_Data();
void init_partitions(Device *parentDevice, const Glib::ustring &buff);
std::vector<Partition> get_partitions();
private:
void draw_dialog();
void create_list_of_fs();
bool is_overlaping(int nPart);
void read_partitions_from_buffer();
void check_overlaps(int nPart);
void open_ro_view(Glib::ustring mountPoint);
bool is_inconsistent(const Partition &part);
Device *device; //Parent device
std::vector<Partition> partitions; //Partitions readed from the buffer
std::vector<int> overlappedPartitions;//List of guessed partitions that
//overlap active partitions
Glib::ustring device_path;
Sector device_length;
bool inconsistencies; //If some of the guessed partitions is inconsistent
int sector_size;
std::vector<int>inconsistencies_list; //List of inconsistent partitions
// Output of gpart command
std::istringstream *buffer;
//GUI stuff
Gtk::Frame *frm;
//Callback
void on_view_clicked(int nPart);
};
} //GParted
#endif //DIALOG_RESCUE_DATA
......@@ -38,6 +38,7 @@ public:
void find_supported_filesystems() ;
void set_user_devices( const std::vector<Glib::ustring> & user_devices ) ;
void set_devices( std::vector<Device> & devices ) ;
void guess_partition_table(const Device & device, Glib::ustring &buff);
bool snap_to_cylinder( const Device & device, Partition & partition, Glib::ustring & error ) ;
bool snap_to_mebibyte( const Device & device, Partition & partition, Glib::ustring & error ) ;
......@@ -198,6 +199,7 @@ private:
std::vector<Glib::ustring> device_paths ;
bool probe_devices ;
Glib::ustring thread_status_message; //Used to pass data to show_pulsebar method
Glib::RefPtr<Glib::IOChannel> iocInput, iocOutput; // Used to send data to gpart command
std::map< Glib::ustring, std::vector<Glib::ustring> > mount_info ;
std::map< Glib::ustring, std::vector<Glib::ustring> > fstab_info ;
......
......@@ -4,6 +4,7 @@ EXTRA_DIST = \
Device.h \
Dialog_Base_Partition.h \
Dialog_Disklabel.h \
Dialog_Rescue_Data.h \
Dialog_Partition_Copy.h \
Dialog_Partition_Info.h \
Dialog_Partition_Label.h \
......
......@@ -156,6 +156,7 @@ public:
static void tokenize( const Glib::ustring& str,
std::vector<Glib::ustring>& tokens,
const Glib::ustring& delimiters ) ;
static int convert_to_int(const Glib::ustring & src);
};
......
......@@ -126,6 +126,7 @@ private:
void thread_unmount_partition( bool * succes, Glib::ustring * error ) ;
void thread_mount_partition( Glib::ustring mountpoint, bool * succes, Glib::ustring * error ) ;
void thread_toggle_swap( bool * succes, Glib::ustring * error ) ;
void thread_guess_partition_table();
//signal handlers
void open_operationslist() ;
......@@ -162,6 +163,7 @@ private:
void toggle_swap_mount_state() ;
void activate_mount_partition( unsigned int index ) ;
void activate_disklabel() ;
void activate_attempt_rescue_data();
void activate_manage_flags() ;
void activate_check() ;
void activate_label_partition() ;
......@@ -243,6 +245,7 @@ private:
unsigned short new_count;//new_count keeps track of the new created partitions
FS fs ;
bool OPERATIONSLIST_OPEN ;
Glib::ustring gpart_output;//Output of gpart command
GParted_Core gparted_core ;
std::vector<Gtk::Label *> device_info ;
......
......@@ -11,6 +11,7 @@ src/Dialog_Partition_Resize_Move.cc
src/Dialog_Progress.cc
src/DialogFeatures.cc
src/DialogManageFlags.cc
src/Dialog_Rescue_Data.cc
src/DMRaid.cc
src/GParted_Core.cc
src/HBoxOperations.cc
......
/* Copyright (C) 2010 Joan Lledó
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "../include/Utils.h"
#include "../include/Dialog_Rescue_Data.h"
#include <gtkmm/messagedialog.h>
#include <gtkmm/stock.h>
#include <gtkmm/checkbutton.h>
#include <sstream>
namespace GParted
{
#define tmp_prefix P_tmpdir "/gparted-roview-XXXXXX"
//The constructor creates a empty dialog
Dialog_Rescue_Data::Dialog_Rescue_Data()
{
this ->set_title( _("Search disk for file systems") );
this ->add_button( Gtk::Stock::CLOSE, Gtk::RESPONSE_CLOSE );
}
//getters
std::vector<Partition> Dialog_Rescue_Data::get_partitions()
{
return this->partitions;
}
// Draws the dialog
void Dialog_Rescue_Data::draw_dialog()
{
Glib::ustring *message;
/*TO TRANSLATORS: looks like File systems found on on /dev/sdb */
this ->set_title( String::ucompose( _("File systems found on on %1"), this->device_path ) );
message=new Glib::ustring("<big><b>");
if(!this->inconsistencies)
{
message->append(_("Data found"));
}
else
{
message->append(_("Data found with inconsistencies"));
Glib::ustring msg_label=_("WARNING!: The file systems marked with (!) are inconsistent.");
msg_label.append("\n");
msg_label.append(_("You might encounter errors trying to view these file systems."));
Gtk::Label *inconsis_label=manage(Utils::mk_label(msg_label));
Gdk::Color c( "red" );
inconsis_label->modify_fg(Gtk::STATE_NORMAL, c );
this->get_vbox()->pack_end(*inconsis_label, Gtk::PACK_SHRINK, 5);
}
message->append("</b></big>");
this->get_vbox()->set_spacing(5);
Gtk::Label *msgLbl=Utils::mk_label(*message);
this->get_vbox()->pack_start(*msgLbl, Gtk::PACK_SHRINK);
this->create_list_of_fs();
Glib::ustring info_txt=_("The 'View' buttons create read-only views of each file system.");
info_txt+="\n";
info_txt+=_("All mounted views will be unmounted when you close this dialog.");
Gtk::HBox *infoBox=manage(new Gtk::HBox());
Gtk::Image *infoImg=manage(new Gtk::Image( Gtk::Stock::DIALOG_INFO, Gtk::ICON_SIZE_DIALOG));
Gtk::Label *infoLabel= manage(new Gtk::Label (info_txt));
infoBox->pack_start(*infoImg, Gtk::PACK_SHRINK, 5);
infoBox->pack_start(*infoLabel, Gtk::PACK_SHRINK);
this->get_vbox()->pack_start(*infoBox, Gtk::PACK_SHRINK);
this->show_all_children();
delete message;
}
/*
* Create the list of the filesystems found */
void Dialog_Rescue_Data::create_list_of_fs()
{
Gtk::VBox *vb=manage(new Gtk::VBox());
vb->set_border_width(5);
vb->set_spacing(5);
this->frm=Gtk::manage(new Gtk::Frame(_("File systems")));
this->frm->add(*vb);
for(unsigned int i=0;i<this->partitions.size();i++)
{
if(this->partitions[i].filesystem==GParted::FS_UNALLOCATED
|| this->partitions[i].filesystem==GParted::FS_UNKNOWN
|| this->partitions[i].filesystem==GParted::FS_UNFORMATTED
|| this->partitions[i].filesystem==GParted::FS_EXTENDED
|| this->partitions[i].type==GParted::TYPE_EXTENDED
|| this->partitions[i].type==GParted::TYPE_UNALLOCATED)
{
continue;
}
std::string fs_name=Utils::get_filesystem_string( this->partitions[i].filesystem );
if(this->partitions[i].filesystem==FS_EXT2)
{
fs_name+="/3/4, ReiserFs or XFS";
}
/*TO TRANSLATORS: looks like 1: ntfs (10240 MiB)*/
Gtk::Label *fsLbl= manage(new Gtk::Label( String::ucompose(_("#%1: %2 (%3 MiB)"), i+1, fs_name, (this->partitions[i].get_byte_length()/1024/1024))));
if(this->is_inconsistent(this->partitions[i]))
{
fsLbl->set_label(fsLbl->get_label().append(" (!)"));
}
Gtk::HBox *hb=manage(new Gtk::HBox());
Gtk::Button *btn=manage(new Gtk::Button(_("View")));
btn->signal_clicked().connect(sigc::bind(sigc::mem_fun(this, &Dialog_Rescue_Data::on_view_clicked), i));
hb->pack_start(*fsLbl, Gtk::PACK_SHRINK);
hb->pack_end(*btn, Gtk::PACK_SHRINK);
vb->pack_start(*hb, Gtk::PACK_SHRINK);
}
this->get_vbox()->pack_start(*this->frm, Gtk::PACK_SHRINK);
}
/*
* Callback function for "View" button */
void Dialog_Rescue_Data::on_view_clicked(int nPart)
{
Partition part=this->partitions[nPart];
int initOffset=this->sector_size*part.sector_start;
int numSectors=part.sector_end-part.sector_start+1;
int totalSize=this->sector_size*numSectors;
this->check_overlaps(nPart);
char tmpDir[32]=tmp_prefix;
mkdtemp(tmpDir);
Glib::ustring mountPoint=tmpDir;
Glib::ustring commandLine= String::ucompose("mount -o ro,loop,offset=%1,sizelimit=%2 %3 %4", initOffset, totalSize, this->device_path, mountPoint);
int mountResult=Utils::execute_command(commandLine);
if(mountResult!=0)
{
Glib::ustring error_txt=_("An error occurred while creating the read-only view.");
error_txt+="\n";
error_txt+=_("Either the file system can not be mounted (like swap), or there are inconsistencies or errors in the file system.");
//Dialog information
Gtk::MessageDialog errorDialog(*this, "", true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
errorDialog.set_message(_("Failed creating read-only view"));
errorDialog.set_secondary_text(error_txt);
errorDialog.run();
return;
}
this->open_ro_view(mountPoint);
}
/* Opens the default browser in a directory */
void Dialog_Rescue_Data::open_ro_view(Glib::ustring mountPoint)
{
GError *error = NULL ;
GdkScreen *gscreen = NULL ;
Glib::ustring uri = "file:" + mountPoint ;
gscreen = gdk_screen_get_default() ;
#ifdef HAVE_GTK_SHOW_URI
gtk_show_uri( gscreen, uri .c_str(), gtk_get_current_event_time(), &error ) ;
#else
Glib::ustring command = "gnome-open " + uri ;
gdk_spawn_command_line_on_screen( gscreen, command .c_str(), &error ) ;
#endif
if ( error != NULL )
{
Glib::ustring sec_text(_("Error:"));
sec_text.append("\n");
sec_text.append(error ->message);
sec_text.append("\n");
sec_text.append("\n");
/*TO TRANSLATORS: looks like
* The file system is mounted on:
* /tmp/gparted-roview-Nlhb3R. */
sec_text.append(_("The file system is mounted on:"));
sec_text.append("\n");
sec_text.append("<b>"+mountPoint+"</b>");
Gtk::MessageDialog dialog( *this
, _( "Unable to open the default file manager" )
, false
, Gtk::MESSAGE_ERROR
, Gtk::BUTTONS_OK
, true
) ;
dialog .set_secondary_text( sec_text, true ) ;
dialog .run() ;
}
}
/*
* Checks if a guessed filesystem is overlapping other one and
* shows the dialog to umount it */
void Dialog_Rescue_Data::check_overlaps(int nPart)
{
if(is_overlaping(nPart))
{
Gtk::MessageDialog overlapDialog(*this, "", true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO, true);
overlapDialog.set_message(_("Warning: The detected file system area overlaps with at least one existing partition."));
Glib::ustring sec_text=_("It is recommended that you do not use any overlapping file systems to avoid disturbing existing data.");
sec_text+="\n";
sec_text+=_("Do you want to try to deactivate the following mount points?");
for(unsigned int i=0;i<this->overlappedPartitions.size(); i++)
{
Glib::ustring ovrDevPath=this->device->partitions[i].get_path();
Glib::ustring ovrDevMountPoint=this->device->partitions[i].get_mountpoint();
sec_text+="\n"+ovrDevPath+" mounted on "+ovrDevMountPoint;
}
overlapDialog.set_secondary_text(sec_text);
if(overlapDialog.run()==Gtk::RESPONSE_YES)
{
for(unsigned int i=0;i<this->overlappedPartitions.size(); i++)
{
Glib::ustring mountP=this->device->partitions[i].get_mountpoint();
Glib::ustring commandUmount= "umount "+mountP;
Utils::execute_command(commandUmount);
}
}
}
}
/*
* Reads the output of gpart and sets some private variables */
void Dialog_Rescue_Data::init_partitions(Device *parentDevice, const Glib::ustring & buff)
{
this->device=parentDevice;
this->device_path=parentDevice->get_path();
this->device_length=parentDevice->length;
this->sector_size=parentDevice->sector_size;
this->buffer=new std::istringstream(buff);
this->inconsistencies=false;
std::string line;
while(getline(*this->buffer, line))
{
//If gpart finds inconsistencies, might not be able to mount some partitions
if(line.find("Number of inconsistencies found")!=Glib::ustring::npos)
{
this->inconsistencies=true;
}
//Read the primary partition table
// (gpart only finds primary partitions)
if(line.find("Guessed primary partition table:")!=Glib::ustring::npos)
{
this->read_partitions_from_buffer();
}
}
this->draw_dialog();
}
/* Reads the output of gpart and builds the vector of guessed partitions */
void Dialog_Rescue_Data::read_partitions_from_buffer()
{
this->partitions.clear();
std::string line;
while(getline(*this->buffer, line))
{
if(line.find("Primary partition")!=line.npos)
{
// Parameters of Partition::Set
Partition part;
Glib::ustring dev_path=this->device_path;
Glib::ustring part_path;
int part_num;
PartitionType type=GParted::TYPE_PRIMARY;
FILESYSTEM fs=GParted::FS_UNALLOCATED;
Sector sec_start=0;
Sector sec_end=0;
Byte_Value sec_size=this->sector_size;
// Get the part_num
Glib::ustring num=Utils::regexp_label(line, "^Primary partition\\(+([0-9])+\\)$");
part_num=Utils::convert_to_int(num);
//Get the part_path
part_path=String::ucompose ( "%1%2", this->device_path, num );
while(getline(*this->buffer, line))
{
if(line=="")
{
break;
}
if(line.find("type:")!=Glib::ustring::npos)
{
//Get the filesystem (needed for set the color of the visual partition)
int code=Utils::convert_to_int(Utils::regexp_label(line, "^[\t ]+type: +([0-9]+)+\\(+[a-zA-Z0-9]+\\)\\(+.+\\)$"));
switch (code)
{
case 0x83: //FS code for ext2, reiserfs and xfs
{
fs=GParted::FS_EXT2;
break;
}
case 0x18:
case 0x82:
case 0xB8: //SWAP partition
{
fs=GParted::FS_LINUX_SWAP;
break;
}
case 0x04:
case 0x14:
case 0x86:
case 0xE4: //FAT16
{
fs=GParted::FS_FAT16;
break;
}
case 0x0B:
case 0x0C: //FAT32
{
fs=GParted::FS_FAT32;
break;
}
case 0x07: //NTFS and HPFS
{
fs=GParted::FS_NTFS;
break;
}
case 0x05:
case 0x0F:
case 0x85: //Extended
{
fs=GParted::FS_EXTENDED;
type=GParted::TYPE_EXTENDED;
break;
}
default:
{
fs=GParted::FS_UNKNOWN;
}
}
}
if(line.find("size:")!=Glib::ustring::npos)
{
// Get the start sector
sec_start=Utils::convert_to_int(Utils::regexp_label(line, "^[\t ]+size: [0-9]+mb #s\\(+[0-9]+\\) s\\(+([0-9]+)+\\-+[0-9]+\\)$"));
// Get the end sector
sec_end=Utils::convert_to_int(Utils::regexp_label(line, "^[\t ]+size: [0-9]+mb #s\\(+[0-9]+\\) s\\(+[0-9]+\\-+([0-9]+)+\\)$"));
}
}
//No part found
if(sec_start==0 && sec_end==0)
{
continue;
}
//Swap partitions don't contain data
if(fs==GParted::FS_LINUX_SWAP)
{
continue;
}
part.Set(dev_path, part_path, part_num,
type, fs, sec_start, sec_end, sec_size, false, false);
this->partitions.push_back(part);
}
}
}
/*
* Checks if the guessed partition is overlaping some active partition
*/
bool Dialog_Rescue_Data::is_overlaping(int nPart)
{
bool result=false;
Sector start_sector=this->partitions[nPart].sector_start;
Sector end_sector=this->partitions[nPart].sector_end;
for(unsigned int j=0;j<this->device->partitions.size();j++)
{
//only check if the partition if mounted
if(this->device->partitions[j].get_mountpoint()=="")
{
continue;
}
//If the start sector is inside other partition
if(start_sector>this->device->partitions[j].sector_start &&
start_sector<this->device->partitions[j].sector_end)
{
this->overlappedPartitions.push_back(j);
result=true;
continue;
}
//If the end sector is inside other partition
if(end_sector>this->device->partitions[j].sector_start &&
end_sector<this->device->partitions[j].sector_end)
{
this->overlappedPartitions.push_back(j);
result=true;
continue;
}
//There is a partition between the sectors start and end
//If the end sector is inside other partition
if(this->device->partitions[j].sector_start>start_sector &&
this->device->partitions[j].sector_end<end_sector)
{
this->overlappedPartitions.push_back(j);
result=true;
continue;
}
}
return result;
}
/* Check if a partition is inconsistent */
bool Dialog_Rescue_Data::is_inconsistent(const Partition &part)
{
for(unsigned int i=0;i<this->inconsistencies_list.size();i++)
{
if (part.partition_number==this->inconsistencies_list[i])
{
return true;
}
}
return false;
}
}//GParted
......@@ -350,6 +350,42 @@ void GParted_Core::set_devices( std::vector<Device> & devices )
fstab_info .clear() ;
}
// runs gpart on the specified parameter
void GParted_Core::guess_partition_table(const Device & device, Glib::ustring &buff)
{
int pid, stdoutput, stderror;
std::vector<std::string> argvproc, envpproc;
gunichar tmp;
//Get the char string of the sector_size