diff --git a/.gitignore b/.gitignore index 286cf05..f166069 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ -build/ -dist/ +src/build/ +src/dist/ +src/carbono.egg-info/ *.swp *.mo *.pyc diff --git a/README.md b/README.md index 28c1c4f..c46956c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -carbono +arbono ======= carbono is a hard disk imaging and recovery application. Carbono @@ -15,7 +15,8 @@ Testing License ------- -carbono is distributed under the terms of the GNU General Public License, version 2. +carbono is distributed under the terms of the GNU General Public License, +version 2. See the [COPYING][4] file for more information. Contributor list @@ -40,3 +41,4 @@ features. [3]: https://github.com/umago/carbono/blob/master/TODO [4]: https://github.com/umago/carbono/blob/master/COPYING [5]: http://unetbootin.sourceforge.net/ + diff --git a/build/.gitkeep b/build/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/carbono/utils.py b/carbono/utils.py deleted file mode 100644 index 1e019e9..0000000 --- a/carbono/utils.py +++ /dev/null @@ -1,182 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 -# Copyright (C) 2010 Lucas Alvares Gomes -# -# 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . - -import subprocess -import tempfile -import multiprocessing -import random -import errno -import os - -from threading import Thread, Event -from os.path import realpath -from carbono.exception import * - -class Timer(Thread): - def __init__(self, callback, timeout=2): - Thread.__init__(self) - self.callback = callback - self.timeout = timeout - self.event = Event() - - def run(self): - while not self.event.is_set(): - self.callback() - self.event.wait(self.timeout) - - def stop(self): - self.event.set() - - -class RunCmd: - def __init__(self, cmd): - self.cmd = cmd - self.stdout = None - self.stdin = None - self.stderr = None - - def run(self): - self.process = subprocess.Popen(self.cmd, shell=True, - stdout=subprocess.PIPE, - stdin=subprocess.PIPE, - stderr=subprocess.PIPE) - self.stdout = self.process.stdout - self.stdin = self.process.stdin - self.stderr = self.process.stderr - - def wait(self): - if hasattr(self, "process"): - self.process.wait() - return self.process.returncode - - def stop(self): - if hasattr(self, "process"): - try: - self.process.kill() - except OSError, e: - if e.errno == errno.ESRCH: - pass - - -def run_simple_command(cmd): - """ """ - p = subprocess.Popen(cmd, shell=True, - stdout=subprocess.PIPE, - stdin=subprocess.PIPE, - stderr=subprocess.PIPE) - p.wait() - return p.returncode - -def random_string(length=5): - return ''.join([random.choice(tempfile._RandomNameSequence.characters) \ - for i in range(length)]) - -def adjust_path(path): - """ """ - path = realpath(path) - if not path[-1] == '/': - path += '/' - return path - -def make_temp_dir(): - """ """ - return adjust_path(tempfile.mkdtemp()) - -def get_parent_path(path): - num = -1 - while True: - try: - int(path[num]) - num -= 1 - except ValueError: - return path[:num+1] - -def singleton(cls): - instance_list = list() - def getinstance(): - if not len(instance_list): - instance_list.append(cls()) - return instance_list[0] - return getinstance - -def available_processors(): - return multiprocessing.cpu_count() - -def is_hyperthreading(): - with open("/proc/cpuinfo", "r") as f: - for line in f.readlines(): - if line.startswith("flags"): - if "ht" in line.split(): - return True - break - return False - -def available_memory(percent=100): - free = 0 - with open("/proc/meminfo", 'r') as f: - for line in f: - if line.startswith("MemFree:"): - free = int(line.split()[1]) * 1024 - break - - if percent < 100: - free = (free * percent) / 100 - - return free - -def get_cdrom_device(): - device = None - with open("/proc/sys/dev/cdrom/info", 'r') as f: - for line in f: - if line.startswith("drive name:"): - try: - device = "/dev/" + line.split()[2] - except IndexError: - break - return device - -def which(program): - def is_exe(fpath): - return os.path.exists(fpath) and os.access(fpath, os.X_OK) - - fpath, fname = os.path.split(program) - if fpath: - if is_exe(program): - return program - else: - for path in os.environ["PATH"].split(os.pathsep): - exe_file = os.path.join(path, program) - if is_exe(exe_file): - return exe_file - - raise CommandNotFound("{0}: command not found".\ - format(program)) - -def sync(): - run_simple_command("sync") - -def is_mounted(device): - with open("/etc/mtab", 'r') as f: - for line in f: - if line.find(device) > -1: - return True - return False - -def check_if_root(): - if os.getuid() == 0: - return True - return False diff --git a/setup.py b/setup.py deleted file mode 100755 index 120fefa..0000000 --- a/setup.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/python -# coding: utf-8 -# Copyright (C) 2011 Lucas Alvares Gomes -# -# 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . - -from distutils.core import setup -from carbono.config import get_version - -DEPENDS = "python2.7, partclone, ntfsprogs, btrfs-tools, e2fsprogs, genisoimage" -DESC = "A hard disk imaging and recovery application" - -setup(name = "carbono", - version = get_version(), - author = "Lucas Alvares Gomes", - author_email = "lucasagomes@gmail.com", - url = "http://umago.info/carbono", - description = DESC, - license = "GNU GPLv2", - packages = ["carbono","carbono.buffer_manager", - "carbono.filesystem", "carbono.ui", - "carbono.image_reader"], - scripts = ["scripts/carbono"], - ) - diff --git a/COPYING b/src/COPYING similarity index 100% rename from COPYING rename to src/COPYING diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..28c1c4f --- /dev/null +++ b/src/README.md @@ -0,0 +1,42 @@ +carbono +======= + +carbono is a hard disk imaging and recovery application. Carbono +optimize the resulting image saving and compacting only the used blocks in the +hard disk. Multiprocessing is used to deal with performance problems. + +Testing +------- + + WARNING: + DO NOT TEST CARBONO IN YOUR MAIN SYSTEM UNLESS YOU KNOW WHAT YOU ARE DOING. + ITS HIGHLY RECOMMENDED USING VIRTUAL MACHINES WHEN TESTING CARBONO! NEVER FORGET IT! + +License +------- + +carbono is distributed under the terms of the GNU General Public License, version 2. +See the [COPYING][4] file for more information. + +Contributor list +---------------- + +Lucas Alvares Gomes (aka umago) + +Contributing +------------ + +1. Fork it +2. Create a branch (`git checkout -b `) +3. Commit your changes (`git commit -am "Added ..."`) +4. Push to the branch (`git push origin `) +5. Create an [Issue][1] with a link to your branch + +Please take a look at [TODO][3] file to see bugs and not-implemented-yet +features. + +[1]: http://github.com/umago/carbono/issues +[2]: http://umago.info/carbono +[3]: https://github.com/umago/carbono/blob/master/TODO +[4]: https://github.com/umago/carbono/blob/master/COPYING +[5]: http://unetbootin.sourceforge.net/ diff --git a/TODO b/src/TODO similarity index 100% rename from TODO rename to src/TODO diff --git a/carbono/__init__.py b/src/carbono/__init__.py similarity index 100% rename from carbono/__init__.py rename to src/carbono/__init__.py diff --git a/src/carbono/boot_manager/__init__.py b/src/carbono/boot_manager/__init__.py new file mode 100644 index 0000000..5b6789c --- /dev/null +++ b/src/carbono/boot_manager/__init__.py @@ -0,0 +1,11 @@ +#!/usr/bin/python +# coding: utf-8 + +# Author: MStech +# Bruno Fernandes Casella +from carbono.boot_manager.boot_manager import BootManager +#from carbono.boot_manager.grub_legacy import GrubLegacy +#from carbono.boot_manager.utils import Utils +#from carbono.boot_manager.disk_utils import DiskUtils +#from carbono.boot_manager.grub2 import Grub2 +#from carbono.boot_manager.syslinux import Syslinux diff --git a/src/carbono/boot_manager/boot_manager.py b/src/carbono/boot_manager/boot_manager.py new file mode 100644 index 0000000..352c949 --- /dev/null +++ b/src/carbono/boot_manager/boot_manager.py @@ -0,0 +1,101 @@ +#!/usr/bin/python +# coding: utf-8 + +# Author: MStech +# Bruno Fernandes Casella + +from carbono.boot_manager.grub_legacy import GrubLegacy +from carbono.boot_manager.grub2 import Grub2 +from carbono.boot_manager.syslinux import Syslinux +from carbono.boot_manager.disk_utils import * +from carbono.boot_manager.utils_misc import * +from carbono.log import log +#from shutil import move as moveFile +#from shutil import copy2 as copyFile + +class BootManager: + MOUNT_OPTIONS = "" + GRUB2_OPTIONS = "" + + def __init__(self, source_device, boot_man="grub", make_config_file=False, chroot_option=False): + self.device = source_device.strip() + self.boot_manager = boot_man.lower() + self.boot_manager = self.boot_manager.strip() + self.chroot_option = chroot_option + self.mounted_devices = [] + self.real_root = None + self.dev_boot_tuples = None + self.make_config_file = make_config_file + + def install_boot_manager(self, boot_only_dev=None, dev_boot_tuples=None): + #dev_boot_tuples --> lista de duplas do tipo (particao_boot_sistema1, particao_sistema1) + + self.dev_boot_tuples = dev_boot_tuples + directory = mount_partition(self.device) + boot_folder = directory+"boot" + + if (boot_only_dev is not None) and (boot_only_dev): + mount_partition(boot_only_dev,directory+"boot") + + if "grub2" in self.boot_manager: + boot_opt = Grub2() + if self.chroot_option: + directory,self.real_root = chroot(directory) + if self.make_config_file: + #on live cd, this only works under chroot + if not self.chroot_option: + directory,self.real_root = chroot(directory) + if boot_opt.build_grub2_menu(directory): + log.warning("Grub2 config file generated") + if boot_opt.install_grub2(self.device, directory): + log.warning("Grub2 successfully installed") + else: + log.error("Installing Grub2 failed") + + elif "grub" in self.boot_manager: + boot_opt=GrubLegacy() + + if self.chroot_option: + directory,self.real_root = chroot(directory) + boot_folder="/boot" + version=grub_version() + if ("grub-legacy" in version) and version: + if self.make_config_file: + boot_opt.build_grub_legacy_menu(boot_folder,dev_boot_tuples) + if boot_opt.install_grub_legacy(self.device, directory): + log.warning("Grub-Legacy successfully installed") + else: + log.error("Installing Grub-Legacy failed") + else: + boot_opt = Grub2() + log.error("Grub-legacy is not installed in {0}".format(self.device)) + log.warning("INSTALING GRUB2 INSTEAD") + if self.make_config_file: + if boot_opt.build_grub2_menu(directory): + log.warning("Grub2 config file generated") + if boot_opt.install_grub2(self.device, directory): + log.warning("Grub2 successfully installed") + else: + log.error("Installing Grub2 failed") + + elif "syslinux" in self.boot_manager: + boot_opt = Syslinux() + if self.chroot_option: + directory,self.real_root=chroot(directory) + boot_folder="/boot" + if self.make_config_file: + + boot_opt.build_sislinux_menu(boot_folder,dev_boot_tuples) + if boot_opt.install_syslinux(self.device,directory): + log.warning("Syslinux successfully installed") + else: + log.error("Installing Syslinux failed") + + if self.chroot_option: + try: + undo_chroot(self.real_root) + except: + log.error("chroot exit failed") + + umount_all_devices() + umount_all_devices_system() diff --git a/src/carbono/boot_manager/disk_utils.py b/src/carbono/boot_manager/disk_utils.py new file mode 100644 index 0000000..c8ec345 --- /dev/null +++ b/src/carbono/boot_manager/disk_utils.py @@ -0,0 +1,237 @@ +#!/usr/bin/python +# coding: utf-8 + +# Author: MStech +# Bruno Fernandes Casella + +import os +import parted +from carbono.device import Device +from carbono.disk import Disk +from carbono.utils import * +from carbono.boot_manager.utils_misc import * +from carbono.log import log + +mounted_devices = [] +MOUNT_OPTIONS = "" + +def list_devices(device): + devices_linux = [] + devices_windows = [] + #device = device[:8] + device="/dev/{0}".format(device) + try: + p = parted.Device(device) + disk = parted.Disk(p) + for i in disk.partitions: + if "ext" in "{0}".format(i.fileSystem): + devices_linux.append("{0}".format(i.path)) + elif ("ntfs" in "{0}".format(i.fileSystem)) or ("fat" in "{0}".format(i.fileSystem)): + devices_windows.append("{0}".format(i.path)) + + devices_linux.sort() + bubble_sort(devices_linux) + + return devices_linux, devices_windows + except: + return None,None + +def get_filesystem_type(device): + + dev = device[:8] + p = parted.Device(dev) + t = parted.Disk(p) + pt = parted.Disk.getPrimaryPartitions(t) + ptl = parted.Disk.getLogicalPartitions(t) + for i in pt: + #scan the primary partitions + if device in i.path: + if "ext" in "{0}".format(i.fileSystem): + fileS = "ext" + elif ("ntfs" in "{0}".format(i.fileSystem)): + fileS = "win" + elif ("fat" in "{0}".format(i.fileSystem)): + fileS = "fat" + elif "swap" in "{0}".format(i.fileSystem): + fileS = "swap" + else: + fileS = "other" + return fileS + for i in ptl: + #scan the logical partitions + if device in i.path: + if "ext" in "{0}".format(i.fileSystem): + fileS = "ext" + elif ("ntfs" in "{0}".format(i.fileSystem)): + fileS = "win" + elif ("fat" in "{0}".format(i.fileSystem)): + fileS = "fat" + elif "swap" in "{0}".format(i.fileSystem): + fileS = "swap" + else: + fileS = "other" + return fileS + #print "\nsda{0} --> {1} --> {2}\n".format(i.path,i.type,fileS) + + +def mount_device(device,folder): + ''' + Mount devices (as /dev, /sys) + Usefull to mount chroot needed devices + ''' + if folder is None: + log.error("No folder as received as mounting point") + if device is None: + log.error("No device as received to mount") + output,erro,ret = run_simple_command_echo("mount -o bind /{0} {1}{0}".format(device, folder),True) + if ret is not 0: + log.error("error output={0} command output={1}".format(erro,output)) + raise ErrorMountingFilesystem + mounted_devices.insert(0,"{0}{1}".format(folder,device)) + +def mount_partition(device, folder = None): + ''' + Monta a particao recebida.(Ex.: /dev/sda1) + Caso 'folder' nao tenha um destino valido, + a particao eh montada em uma pasta temporaria + ''' + mount_device = None + if folder is None: + tmpd = make_temp_dir() + else: + tmpd = folder + + if device is None: + log.error("No device given") + return False + + output,erro,ret = run_simple_command_echo("mount {0} {1} {2}".format(MOUNT_OPTIONS, + device, tmpd),True) + + if "already" in erro: + log.error("{0} is arealdy exclusively mounted.".format(mount_device)) + log.info("Trying to umount device above, and re-run") + umount_real(mount_device) + output,erro,ret = run_simple_command_echo("mount {0} {1} {2}".format(MOUNT_OPTIONS, + mount_device, tmpd),True) + + if ret is not 0: + log.error("{0}".format(erro)) + + #raise ErrorMountingFilesystem + if folder is not None: + mounted_devices.insert(0, folder) + else: + mounted_devices.append(tmpd) + return tmpd + +def umount_partition(directory): + ''' + Desmonta a ultima particao montada + ou a pasta/particao recebida + ''' + if directory is None: + log.error("directory to umount not received") + return None + run_simple_command_echo("sync") + output,erro,ret = run_simple_command_echo("umount {0}".format(directory),True) + + if ret is 0: + #pops the mounted folder from the list + for i in mounted_devices: + if directory in i: + mounted_devices.pop(mounted_devices.index(i)) + else: + log.error("error output={0} command output={1}".format(erro,output)) + + return ret + + +def get_boot_flag(device): + ''' return if the device given has the boot flag or not''' + dev = device[:8] + p = parted.Device(dev) + t = parted.Disk(p) + cont=0 + for i in t.partitions: + if i.getFlag(parted.PARTITION_BOOT): + cont+=1 + if device in i.path: + aux = i.getFlag(parted.PARTITION_BOOT) + if cont >1: + return False + else: + return aux + +def unset_boot_flag(device): + ''' unset the boot flag in the given device''' + dev = device[:8] + p = parted.Device(dev) + t = parted.Disk(p) + + for i in t.partitions: + if device in i.path: + aux = i.unsetFlag(parted.PARTITION_BOOT) + break + t.commit() + return aux + +def unset_all_boot_flags(device): + ''' unset all the boot flags''' + dev = device[:8] + p = parted.Device(dev) + t = parted.Disk(p) + for i in t.partitions: + i.unsetFlag(parted.PARTITION_BOOT) + t.commit() + + +def set_boot_flag(device): + ''' set the boot flag in the given device''' + dev = device[:8] + p = parted.Device(dev) + t = parted.Disk(p) + for i in t.partitions: + if device in i.path: + aux = i.setFlag(parted.PARTITION_BOOT) + break + t.commit() + return aux + + +def umount_all_devices(): + for i in mounted_devices: + ret = umount_partition(i) + if ret is not 0: + log.warning("umount device {0} failed".format(i)) + #raise ErrorUmountingFilesystem + +def umount_real(device): + while True: + output,err,ret = run_simple_command_echo("umount {0}".format(device)) + if ("not" in err) or ("busy" in err): + break + +def umount_all_devices_system(): + mount_cmd,err,ret = run_simple_command_echo("mount") + for j in ["a","b","c","d"]: + for i in range(1,20): + if "/dev/sd{0}{1}".format(j,i) in mount_cmd: + umount_real("/dev/sd{0}{1}".format(j,i)) + +def chroot(root): + real_root = os.open("/", os.O_RDONLY) + mount_device("dev",root) + mount_device("sys",root) + mount_device("proc",root) + os.chdir(root) + log.info("chroot {0}".format(root)) + os.chroot(root) + return "/",real_root + +def undo_chroot(real_root): + log.info("Exiting chroot") + os.fchdir(real_root) + os.chroot(".") + os.close(real_root) + os.chdir("/") diff --git a/src/carbono/boot_manager/grub2.py b/src/carbono/boot_manager/grub2.py new file mode 100644 index 0000000..c465403 --- /dev/null +++ b/src/carbono/boot_manager/grub2.py @@ -0,0 +1,39 @@ +#!/usr/bin/python +# coding: utf-8 + +# Author: MStech +# Bruno Fernandes Casella + +from carbono.utils import * +from carbono.boot_manager.utils_misc import * +from carbono.boot_manager.disk_utils import * +from carbono.log import log + +class Grub2: + + def build_grub2_menu(self, directory, arq=None): + + boot_folder = directory+"boot" + if arq is None: + arq = "grub.cfg" + output,erro,ret = run_simple_command_echo("grub-mkconfig -o \ + {0}/grub/{1}".format(boot_folder,arq),True) + if ret is not 0: + log.error("Generating Grub2 config file failed") + log.error("error output={0} command output={1}".format(erro,output)) + return False + else: + return True + + def install_grub2(self, device, directory): + + boot_folder = directory+"boot" + dev = device[:8] + + output,erro,ret = run_simple_command_echo("grub-install \ + --root-directory={0} {1}".format(directory, dev),True) + if ret is not 0: + log.error("error output={0} command output={1}".format(erro,output)) + return False + else: + return True diff --git a/src/carbono/boot_manager/grub_legacy.py b/src/carbono/boot_manager/grub_legacy.py new file mode 100644 index 0000000..5441b72 --- /dev/null +++ b/src/carbono/boot_manager/grub_legacy.py @@ -0,0 +1,169 @@ +#!/usr/bin/python +# coding: utf-8 + +# Author: MStech +# Bruno Fernandes Casella + +from carbono.utils import * +from carbono.boot_manager.utils_misc import * +from carbono.boot_manager.disk_utils import * +from carbono.log import log + +class GrubLegacy: + + def build_grub_legacy_menu(self,boot_folder, dev_boot_tuples=None): + + ''' + search for linux kernels in all devices. search windows boot files. + built the menu.lst in the target device, also copy grub-legacy files there. + ''' + cont = 0 + menu_linux = [] + menu_windows = [] + FILE = "{0}/grub/menu.lst".format(boot_folder) + try: + f = open(FILE,"w") + except: + os.makedirs("{0}/grub/".format(boot_folder)) + ret,err,out = run_simple_command_echo("touch {0}/grub/menu.lst".format(boot_folder)) + f = open(FILE,"w") + menu_linux.append("#AUTOMAGIC GENERATED\n") + menu_linux.append("default saved\n") + menu_linux.append("timeout 10\n") + menu_linux.append("password --md5 $1$7QG0J0$l2j8MS763EKQ3u.sDdh8Z0\n") + + dev = os.listdir("/dev") + + for i in dev: + if ("sd" in i) and (len(i)==3): + devices_linux,devices_windows = list_devices(i) + if devices_linux is not None: + for lin in devices_linux: + directory = mount_partition(lin) + boot_directory = directory+"/boot" + try: + filenames = os.listdir("{0}".format(boot_directory)) + except: + filenames=None + try: + filenames2 = os.listdir("{0}".format(directory)) + except: + filenames2 = None + var_temp = None + vmlinuz = None + if filenames is not None: + filenames.sort() + boot_me = "/boot" + for aux in filenames: + if "init" in aux: + initrd = aux + if len(aux) < 12: + var_temp="" + else: + var_temp = aux[11:] # take just the kernel version + elif ((var_temp is not None) and ("vmlinuz" in aux) and (var_temp in aux)) or ("vmlinuz"==aux): + vmlinuz = aux + cont+=1 + if (filenames2 is not None) and (vmlinuz is None): + filenames2.sort() + boot_me = "" + for aux in filenames2: + if "init" in aux: + initrd = aux + if len(aux) < 12: + var_temp="" + else: + var_temp = aux[11:] # take just the kernel version + elif ((var_temp is not None) and ("vmlinuz" in aux) and (var_temp in aux)) or ("vmlinuz"==aux): + vmlinuz = aux#+"MAOEEE" + cont+=1 + + if (vmlinuz is not None): + line = distro_name(directory) + if line is None: + line = cont + menu_linux.append("\ntitle Linux {0} in {1}\n".format(line,lin)) + menu_linux.append("root (hd0,{0})\n".format(int(lin[8:])-1)) + temp = False + + if (dev_boot_tuples is not None): + for i in dev_boot_tuples: + if lin in i[0]: + temp = True + menu_linux.append("kernel {0}/{1} root={2} ro quiet splash\ + \n".format(boot_me,vmlinuz,i[1])) + dev_boot_tuples.pop(dev_boot_tuples.index(i)) + if not dev_boot_tuples: + dev_boot_tuples = None + if not temp: + #if temp_dev is None: + menu_linux.append("kernel {0}/{1} root={2} ro quiet splash\ + \n".format(boot_me,vmlinuz,lin)) + #else: + # menu_linux.append("kernel {0}/{1} root = {2} ro quiet splash\ + # \n".format(boot_me,vmlinuz,temp_dev)) + # temp_dev = None + menu_linux.append("initrd {0}/{1}\n".format(boot_me,initrd)) + menu_linux.append("quiet\n") + menu_linux.append("savedefault\n") + else: + temp_dev = lin + + umount_partition(directory) + cont = 0 + for win in devices_windows: + cont+= 1 + filenames = None + directory = mount_partition(win) + try: + filenames = os.listdir("{0}".format(directory)) + except: + log.info("Folder {0} didn't exist or it'a empty".format(directory)) + + if filenames is not None: + bootWin = False + for i in filenames: + if "boot" in i.lower(): + bootWin = True + if bootWin: + windev = int(win[8:]) + + menu_windows.append("\ntitle Windows {0}\n".format(cont)) + ''' + the two line below should exist if windows is on sdb + ''' + if "1" not in win: + #if windows is not in the firs filesystem + menu_windows.append("map (hd0) (hd{0})\n".format(windev)) + menu_windows.append("map (hd{0}) (hd0)\n".format(windev)) + menu_windows.append("rootnoverify (hd0,{0})\n".format(windev-1)) + menu_windows.append("makeactive\n") + menu_windows.append("chainloader +1\n") + + for i in menu_linux: + f.write(i) + for i in menu_windows: + f.write(i) + f.close() + + def install_grub_legacy(self, device, directory): + ''' + install grub-legacy in sdX, giving de mounted device as the root device + ''' + boot_folder = directory+"boot" + dev = device[:8] + try: + output,erro,ret = run_simple_command_echo("grub-install \ + --root-directory={0} {1}".format(directory, dev),True) + except: + run_simple_command_echo("grep -v rootfs /proc/mounts > /etc/mtab") + output,erro,ret = run_simple_command_echo("grub-install \ + --root-directory={0} {1}".format(directory, dev),True) + if ret is not 0: + log.error("Grub installation failed. [grub-install \ + --root-directory={0} {1}]".format(directory, dev)) + log.error("error output={0} command output={1}".format(erro,output)) + return False + else: + return True + diff --git a/src/carbono/boot_manager/syslinux.py b/src/carbono/boot_manager/syslinux.py new file mode 100644 index 0000000..8d049d7 --- /dev/null +++ b/src/carbono/boot_manager/syslinux.py @@ -0,0 +1,210 @@ +#!/usr/bin/python +# coding: utf-8 + +# Author: MStech +# Bruno Fernandes Casella + +from shutil import copy2 as copyFile + +from carbono.utils import * +from carbono.boot_manager.disk_utils import * +from carbono.boot_manager.utils_misc import * +from carbono.log import log + +class Syslinux: + + def build_sislinux_menu(self,boot_folder,dev_boot_tuples = None): + + KERNEL_OPTIONS="ro quiet"#if trying to boot in a netbook, add boot=hdd + cont = 0 + contador_win=0 + try: + os.listdir("{0}/syslinux/".format(boot_folder)) + + except: + os.makedirs("{0}/syslinux/".format(boot_folder)) + FILE = "{0}/syslinux/syslinux.cfg".format(boot_folder) + try: + f = open(FILE,"w") + except: + ret,err,out = run_simple_command_echo("touch {0}/syslinux/syslinux.cfg".format(boot_folder)) + f = open(FILE,"w") + + menu_linux = [] + menu_windows = [] + menu_linux.append("UI menu.c32\n") + menu_linux.append("PROMPT 1\n") + menu_linux.append("TIMEOUT 100\n") + contador_label=0 + + dev = os.listdir("/dev") + for i in dev: + if ("sd" in i) and (len(i)==3): + + devices_linux,devices_windows = list_devices(i) + if devices_linux is not None: + for lin in devices_linux: + + directory = mount_partition(lin) + boot_directory = directory+"/boot" + try: + filenames = os.listdir("{0}".format(boot_directory)) + except: + filenames=None + try: + filenames2 = os.listdir("{0}".format(directory)) + except: + filenames2 = None + var_temp = None + vmlinuz = None + if filenames is not None: + filenames.sort() + boot_me = "/boot/" + for aux in filenames: + if "init" in aux: + initrd = aux + if len(aux) < 12: + var_temp="" + else: + var_temp = aux[11:] # take just the kernel version + elif (((var_temp is not None) and ("vmlinuz" in aux) and (var_temp in aux)) + or ("vmlinuz"==aux)): + vmlinuz = aux + cont+=1 + if (filenames2 is not None) and (vmlinuz is None): + filenames2.sort() + boot_me = "/" + for aux in filenames2: + if "init" in aux: + initrd = aux + if len(aux) < 12: + var_temp="" + else: + var_temp = aux[11:] # take just the kernel version + elif (((var_temp is not None) and ("vmlinuz" in aux) and (var_temp in aux)) + or ("vmlinuz"==aux)): + vmlinuz = aux#+"MAOEEE" + cont+=1 + + if (vmlinuz is not None): + line = distro_name(directory) + if line is None: + line = cont + temp = True + if (dev_boot_tuples is not None): + for i in dev_boot_tuples: + if ((lin in i[0]) or (lin in i[1])): + temp = False + if (lin not in i[0]): + contador_label+=1 + menu_linux.append("\nLABEL {0}\n".format(contador_label)) + menu_linux.append("MENU LABEL Linux {0} in {1}\n".format(line,lin)) + menu_linux.append("KERNEL {0}{1}\nAPPEND root={2} {3}\ + \n".format(boot_me,vmlinuz,i[1],KERNEL_OPTIONS)) + menu_linux.append("INITRD {0}{1}\n".format(boot_me,initrd)) + break + else: + dev_boot_tuples.pop(dev_boot_tuples.index(i)) + break + if not dev_boot_tuples: + dev_boot_tuples = None + if temp: + #if temp_dev is None: + contador_label+=1 + menu_linux.append("\nLABEL {0}\n".format(contador_label)) + menu_linux.append("MENU LABEL Linux {0} in {1}\n".format(line,lin)) + menu_linux.append("KERNEL {0}{1}\nAPPEND root={2} {3}\ + \n".format(boot_me,vmlinuz,lin,KERNEL_OPTIONS)) + menu_linux.append("INITRD {0}{1}\n".format(boot_me,initrd)) + #else: + # menu_linux.append("kernel {0}/{1} root = {2} ro quiet splash\ + # \n".format(boot_me,vmlinuz,temp_dev)) + # temp_dev = None + + + else: + temp_dev = lin + + umount_partition(directory) + cont = 0 + #fim for + if devices_windows is not None: + for win in devices_windows: + filenames = None + + directory = mount_partition(win) + try: + filenames = os.listdir("{0}".format(directory)) + except: + log.warning("Folder {0} didn't exist or it'a empty".format(directory)) + if filenames is not None: + bootWin = False + for i in filenames: + if "windows" in i.lower(): + bootWin = True + if bootWin: + windev = int(win[8:]) + contador_label+=1 + menu_windows.append("\nLABEL {0}\n".format(contador_label)) + menu_windows.append("MENU LABEL Windows\n") + menu_windows.append("COM32 chain.c32\n") + menu_windows.append("APPEND hd{0} {1}\n".format(contador_win,windev)) + contador_win+=1 + + menu_windows.append("\nLABEL Reboot\n") + menu_windows.append("MENU LABEL Reboot\n") + menu_windows.append("COM32 reboot.c32\n") + menu_windows.append("\nLABEL Shutdown\n") + menu_windows.append("MENU LABEL Shutdown\n") + menu_windows.append("COMBOOT poweroff.com\n") + for i in menu_linux: + f.write(i) + for i in menu_windows: + f.write(i) + f.close() + + + def install_syslinux(self, device, directory): + + boot_folder = directory+"boot" + dev = device[:8] + try: + log.info("cp /usr/lib/syslinux/menu.c32 {0}/syslinux".format(boot_folder)) + copyFile("/usr/lib/syslinux/menu.c32", boot_folder+"/syslinux") + log.info("cp /usr/lib/syslinux/chain.c32 {0}/syslinux".format(boot_folder)) + copyFile("/usr/lib/syslinux/chain.c32", boot_folder+"/syslinux") + log.info("cp /usr/lib/syslinux/reboot.c32 {0}/syslinux".format(boot_folder)) + copyFile("/usr/lib/syslinux/reboot.c32", boot_folder+"/syslinux") + log.info("cp /usr/lib/syslinux/poweroff.com {0}/syslinux".format(boot_folder)) + copyFile("/usr/lib/syslinux/poweroff.com", boot_folder+"/syslinux") + + except: + log.error("Error copying files") + return False + try: + aux = get_filesystem_type(device) + if "fat" in aux: + run_simple_command_echo("syslinux -d {0}/syslinux {1}".format(boot_folder,device),True) + elif "ext" in aux: + log.info("extlinux --install {0}/syslinux".format(boot_folder)) + run_simple_command_echo("extlinux --install {0}/syslinux".format(boot_folder),True) + + else: + log.error("Filesystem not accepted.") + return False + + except: + log.error("installing syslinux in {0} failed".format(device)) + return False + try: + run_simple_command_echo("dd if=/usr/lib/syslinux/mbr.bin of={0} \ + bs=440 conv=notrunc count=1".format(dev),True) + except: + log.error("dd failed") + return False + + if not get_boot_flag(device): + unset_all_boot_flags(device) + if set_boot_flag(device): + log.info("{0} has been set as bootable device".format(device)) + return True diff --git a/src/carbono/boot_manager/utils_misc.py b/src/carbono/boot_manager/utils_misc.py new file mode 100644 index 0000000..fd44c3e --- /dev/null +++ b/src/carbono/boot_manager/utils_misc.py @@ -0,0 +1,83 @@ +#!/usr/bin/python +# coding: utf-8 + +# Author: MStech +# Bruno Fernandes Casella + +import os +import subprocess +import shlex +import re +from carbono.log import log + +def bubble_sort(list2): + #swap_test = False + for i in range(0, len(list2) - 1): + swap_test = False + for j in range(0, len(list2) - i - 1): + if len(list2[j]) > len(list2[j + 1]): + list2[j], list2[j + 1] = list2[j + 1], list2[j] # swap + swap_test = True + if swap_test == False: + break + +def distro_name(directory): + #Return the name of the linux distro, or "None" if it didn't find it + try: + expr = re.compile(r"\\+.+",re.DOTALL) + files = os.listdir("{0}etc/".format(directory)) + tmp_filei = None + tmp_filer = None + for i in files: + if "issue" in i: + tmp_filei = "{0}/etc/issue".format(directory) + elif "-release" in i: + tmp_filer = "{0}/etc/{1}".format(directory,i) + if tmp_filei is not None: + FILE = tmp_filei + fl = open(FILE,"r") + line = fl.readline() + line = expr.sub('',line) + elif tmp_filer is not None: + FILE = tmp_filer + fl = open(FILE,"r") + for l in fl: + if "DISTRIB_DESCRIPTION" in l: + line = l[20:] + line = expr.sub('',line) + line = re.sub(r'"+','',line) + line = re.sub(r'\n+','',line) + return line + except: + return None + + +def grub_version(): + try: + version = os.popen("grub-install --version").read() + except: + log.error("Grub is not installed") + return False + if "0.97" in version: + return "grub-legacy" + elif "1." in version: + return "grub2" + elif "GNU" in version: + return "grub-legacy" + + +def run_simple_command_echo(cmd, echo=False): + ''' + run a given command + returns the output, errors (if any) and returncode + ''' + if echo: + log.info("{0}".format(cmd)) + p = subprocess.Popen(cmd, shell=True, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE) + p.wait() + output, err = p.communicate() + ret = p.returncode + return output,err,ret diff --git a/carbono/buffer_manager/__init__.py b/src/carbono/buffer_manager/__init__.py similarity index 72% rename from carbono/buffer_manager/__init__.py rename to src/carbono/buffer_manager/__init__.py index 8b27676..0f42d73 100644 --- a/carbono/buffer_manager/__init__.py +++ b/src/carbono/buffer_manager/__init__.py @@ -23,12 +23,16 @@ class BufferManagerFactory: - def __init__(self, read_callback, job_callback=None): + def __init__(self, read_callback,notify_status, job_callback=None): if job_callback: - if available_processors() <= 2 and is_hyperthreading(): - self._manager = SimpleManager(read_callback, job_callback) - else: - self._manager = WorkManager(read_callback, job_callback) + #if available_processors() <= 2:# and is_hyperthreading(): + self._manager = SimpleManager(read_callback, + job_callback, + notify_status) + #else: + # self._manager = WorkManager(read_callback, + # job_callback, + # notify_status) else: self._manager = DummyManager(read_callback) diff --git a/carbono/buffer_manager/dummy_manager.py b/src/carbono/buffer_manager/dummy_manager.py similarity index 90% rename from carbono/buffer_manager/dummy_manager.py rename to src/carbono/buffer_manager/dummy_manager.py index 6fdf2ed..b132e49 100644 --- a/carbono/buffer_manager/dummy_manager.py +++ b/src/carbono/buffer_manager/dummy_manager.py @@ -20,6 +20,7 @@ from carbono.utils import * from carbono.config import * +from carbono.log import log class DummyManager(Thread): @@ -40,12 +41,7 @@ def put(self, data): def run(self): self.active = True while self.active: - try: - data = self.read_block() - except ErrorReadingFromDevice, e: - self.stop() - raise e - + data = self.read_block() if not data: self.stop() break diff --git a/carbono/buffer_manager/reorder_buffer.py b/src/carbono/buffer_manager/reorder_buffer.py similarity index 100% rename from carbono/buffer_manager/reorder_buffer.py rename to src/carbono/buffer_manager/reorder_buffer.py diff --git a/carbono/buffer_manager/simple_manager.py b/src/carbono/buffer_manager/simple_manager.py similarity index 74% rename from carbono/buffer_manager/simple_manager.py rename to src/carbono/buffer_manager/simple_manager.py index b126bbe..2c450e2 100644 --- a/carbono/buffer_manager/simple_manager.py +++ b/src/carbono/buffer_manager/simple_manager.py @@ -19,10 +19,14 @@ class SimpleManager(DummyManager): - def __init__(self, read_callback, job_callback): + def __init__(self, read_callback, job_callback, notify_status): DummyManager.__init__(self, read_callback) self.job = job_callback + self.notify_status = notify_status def put(self, data): - worked_data = self.job(data) - self.output_buffer.put(worked_data) + try: + worked_data = self.job(data) + self.output_buffer.put(worked_data) + except Exception as e: + self.notify_status("read_buffer_error",{"read_buffer_error":str(e)}) diff --git a/carbono/buffer_manager/work_manager.py b/src/carbono/buffer_manager/work_manager.py similarity index 93% rename from carbono/buffer_manager/work_manager.py rename to src/carbono/buffer_manager/work_manager.py index 2242187..6bbe96a 100644 --- a/carbono/buffer_manager/work_manager.py +++ b/src/carbono/buffer_manager/work_manager.py @@ -28,11 +28,12 @@ from carbono.exception import * class Worker(Process): - def __init__(self, buffer, reorder_buffer, job): + def __init__(self, buffer, reorder_buffer, job, status_callback): Process.__init__(self) self.buffer = buffer self.reorder_buffer = reorder_buffer self.job = job + self.status_callback = status_callback self.event = Event() def run(self): @@ -47,7 +48,12 @@ def run(self): if data == EOF: self.stop() break - worked_data = self.job(data) + + try: + worked_data = self.job(data) + except Exception as e: + self.notify_status("read_buffer_error",{"read_buffer_error":str(e)}) + raise ErrorReadingToDevice(e) while self.event.is_set(): try: diff --git a/src/carbono/caspart/__init__.py b/src/carbono/caspart/__init__.py new file mode 100644 index 0000000..247ecfe --- /dev/null +++ b/src/carbono/caspart/__init__.py @@ -0,0 +1,6 @@ +#!/usr/bin/python +# coding: utf-8 + +# Author: MStech +# Bruno Fernandes Casella +from caspart import CasPart diff --git a/src/carbono/caspart/caspart.py b/src/carbono/caspart/caspart.py new file mode 100644 index 0000000..026437f --- /dev/null +++ b/src/carbono/caspart/caspart.py @@ -0,0 +1,233 @@ +#!/usr/bin/python +# coding: utf-8 + +# Author: MStech +# Bruno Fernandes Casella +import re +import parted + +from _ped import partition_flag_get_by_name + +from carbono.device import Device +from carbono.disk import Disk +from carbono.log import log +from carbono.caspart.utils_misc import * + + +class CasPart: + + def __init__(self, particoes = None): + ''' + recebe um dicionario de particao e tamanho em MB + eg.: {'/dev/sda1': 20480, '/dev/sda2': 1024} + ''' + if particoes is not None: + self.particoes = particoes + else: + log.error("Nao foi informada nenhuma particao a ser criada.") + + def _wait_devices(self, disk): + """Wait OS create all device nodes""" + for p in disk.partitions: + while not os.path.exists(p.path): + time.sleep(1) + + def verify_4k(self, hd = 'sda'): + ''' + Verifica o tipo de tamanho fisico dos blocos da HD + Se for 4096bytes, retorna True + Caso contrario (512 bytes) retorna False + ''' + f = open("/sys/block/{0}/queue/physical_block_size".format(hd)) + physical_block_size = f.readline() + if "4096" in physical_block_size: + return True + return False + + + def fdisk_parser(self, hd='sda'): + ''' + Pega informacoes da hd a partir do comando fdisk -l + ''' + + info = {'sector_size_physical' : 0, 'sector_size_logical' : 0, + 'cylinders_number' : 0, 'sectors_per_track' : 0, + 'total_sectors' : 0, 'disk_size' : 0, 'heads' : 0} + + out,err,ret = run_simple_command_echo("fdisk -l /dev/{0}".format(hd)) + if ret is not 0: + print "Erro ao pegar informacoes do hd" + + for linha in out.splitlines(): + if "Disk /dev/{0}".format(hd) in linha: + + #split the numbers of the line into a list + #them get the last occurence, + #which is the size of the disk in bytes + info['disk_size'] = re.findall('\d+',linha)[-1] + elif "cylinders" in linha: + + #[heads, sectors/track, cylinders, total sectors] + lst_aux = re.split('[,]',linha) + for str_aux in lst_aux: + if "heads" in str_aux: + info['heads'] = re.findall('\d+', str_aux)[-1] + elif "sectors/track" in str_aux: + info['sectors_per_track'] = re.findall('\d+', str_aux)[-1] + elif "cylinders" in str_aux: + info['cylinders_number'] = re.findall('\d+', str_aux)[-1] + elif "total" in str_aux: + info['total_sectors'] = re.findall('\d+', str_aux)[-1] + + elif "Sector size" in linha: + #[logical, physical] + info['sector_size_logical'] = re.findall('\d+',linha)[0] + info['sector_size_physical'] = re.findall('\d+',linha)[-1] + + return info + + + def verify_gpt(self, hd = 'sda'): + ''' + Verifica se a tabela de particao é GPT + ''' + out,err,ret = run_simple_command_echo("fdisk -l /dev/{0}".format(hd)) + if ret is not 0: + print "Erro ao verificar tipo da tabela de particao. {0}".format(err) + if "gpt" in out.lower(): + return True + return False + + def get_hd_info(self, hd = 'sda'): + + hd_info_dic = {'sector_size_physical' : 0, 'sector_size_logical' : 0, + 'cylinders_number' : 0, 'sectors_per_track' : 0, + 'total_sectors' : 0, 'disk_size' : 0, + 'heads' : 0, 'ptable_type' : None} + + parser_info = self.fdisk_parser(hd) + + for aux in parser_info: + hd_info_dic[aux] = parser_info[aux] + + #hd_info_dic['sector_size_physical'] = 4096 if self.verify_4k(hd) else 512 + hd_info_dic['ptable_type'] = 'gpt' if self.verify_gpt(hd) else 'mbr' + + return hd_info_dic + + def calculates_startofpart(self, end, size, disk='sda'): + ''' + Given the end of the last partition and the size in GB of the current + one, calculates where this one should start, returning a sector that + don't give an cilinder conflict - where the end of last partition and + the start of the new one is in the same cilinder - and also does not + get an wrong sector - where it could place a start/end of a part in the + middle of a 4096b sector. + ''' + hd_info_dict = self.get_hd_info(disk) + cylinder_end = end/(int(hd_info_dict['heads']) * int(hd_info_dict['sectors_per_track'])) + cylinder_start = cylinder_end + 1 + sector_start = cylinder_start * \ + int(hd_info_dict['heads']) * \ + int(hd_info_dict['sectors_per_track']) + if self.verify_4k(disk): + sector_start *= 512 + if (sector_start % 4096): + #sector_start += (sector_start % 4096)*4096 - (sector_start % 4096) + sector_start += ((sector_start % 4096)/4096 +1) * 4096 - (sector_start % 4096) + sector_start /= 512 + return sector_start + + + + def calculates_endofpart(self, start, size_in_gb, disk='sda'): + ''' + Given the size of the partition, gives where it should end + It doesn't get a conflictant sector, just as the function above + ''' + size = int(size_in_gb) * 1024 * 1024 *1024 #bytes + if not self.verify_4k(disk): + end = int(start)*512 + size + if (end % 512): #if end is not divisable by 512b, its wrong (?) + #then, goes to the end of the next sector + end += (end % 512)*512 - (end % 512) + return end / 512 + else: #4096b + end = int(start)*512 + size + if (end % 4096): #if end is not divisable by 4k, its wrong + #then, goes to the end of that sector + end += (end % 4096)*4096 - (end % 4096) + return end / 512 + + def partionate_disk(self, part_list = [['sda1','ntfs','30',''], + ['sda2','ext3','20','boot']]): + ''' + particiona o disco + ''' + for part in part_list: + #get the hd using the 3 first letters of the 1st elem. of the dict. + disk = "{0}".format(part[0][:3]) + break + disk_path = "/dev/{0}".format(disk) + device = Device(disk_path) + ''' + print device.sectorSize + print device.physicalSectorSize + ''' + disk_obj = Disk(device) + start = end = 0 + layout = list() #objs of parted.partition will be added in the list + disk_obj.unsetFlag(parted.DISK_CYLINDER_ALIGNMENT) + for part in part_list: + #got through all elements of the received dict + + if part[0][3:] == '1': + #if is the 1st partition, then it should start at sector 2048 + start = 2048 + end = self.calculates_endofpart(start, part[2], disk) + else: + #starting from 2nd partition, we will make the calculus to where + # it will start/end using the last partition ending + start = self.calculates_startofpart(end, part[2], disk) + + end = self.calculates_endofpart(start, part[2], disk) + #length = int(part[2]) * 1024 * 1024 *1024 / device.sectorSize + length = end - start + #end = start + length - 1 + print "AQUI {0} {1} {2}".format(start,end,length) + + geometry = parted.geometry.Geometry(device = disk_obj.device, + start = start, + #length = length) + end = end) + part_fs = None + fs = part[1] + print geometry + if fs is not None: + part_fs = parted.filesystem.FileSystem(type = fs, + geometry = geometry) + print part_fs + partition = parted.partition.Partition(disk=disk_obj, + type=0, + geometry=geometry, + fs=part_fs) + #TODO swap e etc ? + #partition.setFlag(partition_flag_get_by_name(p.flags)) + print partition + print disk_obj.partitionAlignment + layout.append(partition) + + disk_obj.deleteAllPartitions() + + #last_partition = len(layout) + print layout + constraint = parted.Constraint(device=disk_obj.device) + for cont, partition in enumerate(layout, 1): + disk_obj.addPartition(partition, constraint) + #could use 'cont' to know if is the last part, + #and expand it to end of the hard drive + + #commit to device, OS and wait until OS create all devices nodes + disk_obj.commitToDevice() + disk_obj.commitToOS() + self._wait_devices(disk_obj) diff --git a/src/carbono/caspart/utils_misc.py b/src/carbono/caspart/utils_misc.py new file mode 100644 index 0000000..fd44c3e --- /dev/null +++ b/src/carbono/caspart/utils_misc.py @@ -0,0 +1,83 @@ +#!/usr/bin/python +# coding: utf-8 + +# Author: MStech +# Bruno Fernandes Casella + +import os +import subprocess +import shlex +import re +from carbono.log import log + +def bubble_sort(list2): + #swap_test = False + for i in range(0, len(list2) - 1): + swap_test = False + for j in range(0, len(list2) - i - 1): + if len(list2[j]) > len(list2[j + 1]): + list2[j], list2[j + 1] = list2[j + 1], list2[j] # swap + swap_test = True + if swap_test == False: + break + +def distro_name(directory): + #Return the name of the linux distro, or "None" if it didn't find it + try: + expr = re.compile(r"\\+.+",re.DOTALL) + files = os.listdir("{0}etc/".format(directory)) + tmp_filei = None + tmp_filer = None + for i in files: + if "issue" in i: + tmp_filei = "{0}/etc/issue".format(directory) + elif "-release" in i: + tmp_filer = "{0}/etc/{1}".format(directory,i) + if tmp_filei is not None: + FILE = tmp_filei + fl = open(FILE,"r") + line = fl.readline() + line = expr.sub('',line) + elif tmp_filer is not None: + FILE = tmp_filer + fl = open(FILE,"r") + for l in fl: + if "DISTRIB_DESCRIPTION" in l: + line = l[20:] + line = expr.sub('',line) + line = re.sub(r'"+','',line) + line = re.sub(r'\n+','',line) + return line + except: + return None + + +def grub_version(): + try: + version = os.popen("grub-install --version").read() + except: + log.error("Grub is not installed") + return False + if "0.97" in version: + return "grub-legacy" + elif "1." in version: + return "grub2" + elif "GNU" in version: + return "grub-legacy" + + +def run_simple_command_echo(cmd, echo=False): + ''' + run a given command + returns the output, errors (if any) and returncode + ''' + if echo: + log.info("{0}".format(cmd)) + p = subprocess.Popen(cmd, shell=True, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE) + p.wait() + output, err = p.communicate() + ret = p.returncode + return output,err,ret diff --git a/carbono/compressor.py b/src/carbono/compressor.py similarity index 100% rename from carbono/compressor.py rename to src/carbono/compressor.py diff --git a/carbono/config.py b/src/carbono/config.py old mode 100644 new mode 100755 similarity index 84% rename from carbono/config.py rename to src/carbono/config.py index 8cfb794..dc71359 --- a/carbono/config.py +++ b/src/carbono/config.py @@ -15,14 +15,20 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see . + +import os + FILE_PATTERN = "{name}-{partition}-{volume}.data" BLOCK_SIZE = 1048576 # 1MB -BASE_SYSTEM_SIZE = 0 #TODO (in bytes) -LOG_FILE = "/var/tmp/carbono.log" +BASE_SYSTEM_SIZE = 1024 * 1024 * 250 #Em bytes. TODO: pegar tamanho real +LOG_FILE = "/var/log/mstech/carbono.log" EOF = -1 VERSION = (0, 1, 0, "alpha", 0) def get_version(): + if ("BUILD_ID" in os.environ): + return os.environ["BUILD_ID"] + version = "%s.%s" % (VERSION[0], VERSION[1]) if VERSION[2]: version = "%s.%s" % (version, VERSION[2]) diff --git a/carbono/device.py b/src/carbono/device.py similarity index 100% rename from carbono/device.py rename to src/carbono/device.py diff --git a/carbono/disk.py b/src/carbono/disk.py similarity index 100% rename from carbono/disk.py rename to src/carbono/disk.py diff --git a/carbono/disk_layout_manager.py b/src/carbono/disk_layout_manager.py old mode 100644 new mode 100755 similarity index 97% rename from carbono/disk_layout_manager.py rename to src/carbono/disk_layout_manager.py index f4cb18b..eb4559f --- a/carbono/disk_layout_manager.py +++ b/src/carbono/disk_layout_manager.py @@ -68,6 +68,10 @@ def _wait_devices(self, disk): def restore_from_file(self, disk, expand=False): """ """ + + #Desativa o alinhamento automatico + disk.unsetFlag(parted.DISK_CYLINDER_ALIGNMENT) + with open(self.file_path, 'r') as f: layout = list() partitions = cPickle.load(f) diff --git a/carbono/exception.py b/src/carbono/exception.py similarity index 89% rename from carbono/exception.py rename to src/carbono/exception.py index be7653f..ed48836 100644 --- a/carbono/exception.py +++ b/src/carbono/exception.py @@ -66,3 +66,14 @@ class ExpandingPartitionError(Exception): class DeviceIsMounted(Exception): pass +class InvalidFolder(Exception): + pass + +class ErrorOpenToWrite(Exception): + pass + +class ErrorCloseToWrite(Exception): + pass + +class ErrorFileNotFound(Exception): + pass diff --git a/carbono/filesystem/__init__.py b/src/carbono/filesystem/__init__.py similarity index 96% rename from carbono/filesystem/__init__.py rename to src/carbono/filesystem/__init__.py index 3c2952b..4f5dc58 100644 --- a/carbono/filesystem/__init__.py +++ b/src/carbono/filesystem/__init__.py @@ -53,7 +53,10 @@ def _get_filesystem_instance(self): def open_to_read(self): self._fs.open_to_read() - + + def get_error_ext(self): + return self._fs.fderr + def open_to_write(self, uuid=None): if uuid is not None: self._fs.open_to_write(uuid) @@ -111,6 +114,9 @@ def fill_with_zeros(self): def resize(self): return self._fs.resize() + + def format_filesystem(self): + return self._fs.format_filesystem() def stop(self): self._fs.stop() diff --git a/carbono/filesystem/btrfs.py b/src/carbono/filesystem/btrfs.py similarity index 100% rename from carbono/filesystem/btrfs.py rename to src/carbono/filesystem/btrfs.py diff --git a/carbono/filesystem/ext.py b/src/carbono/filesystem/ext.py similarity index 76% rename from carbono/filesystem/ext.py rename to src/carbono/filesystem/ext.py index 053417b..3b6b718 100644 --- a/carbono/filesystem/ext.py +++ b/src/carbono/filesystem/ext.py @@ -52,21 +52,25 @@ def get_used_size(self): def open_to_read(self): """ """ - cmd = "{0} -q -c -s {1} -o -".format(which("partclone.extfs"), self.path) + #cmd = "{0} -q -c -s {1} -o -".format(which("partclone.extfs"), self.path) + cmd = "{0} -c -s {1} -o -".format(which("partclone.extfs"), self.path) try: self.process = RunCmd(cmd) self.process.run() self._fd = self.process.stdout + self.fderr = self.process.stderr except: raise ErrorOpenToRead("Cannot open {0} to read".format(self.path)) def open_to_write(self, uuid=None): """ """ - cmd = "{0} -q -r -o {1} -s - ".format(which("partclone.extfs"), self.path) + #cmd = "{0} -q -r -o {1} -s - ".format(which("partclone.extfs"), self.path) + cmd = "{0} -r -o {1} -s - ".format(which("partclone.extfs"), self.path) try: self.process = RunCmd(cmd) self.process.run() self._fd = self.process.stdin + self.fderr = self.process.stderr except: raise ErrorOpenToWrite("Cannot open {0} to write".format(self.path)) @@ -79,7 +83,7 @@ def uuid(self): if not len(output): return uuid - + try: uuid = re.search('(?<=UUID=")\w+-\w+-\w+-\w+-\w+', output).group(0) except AttributeError: @@ -89,6 +93,7 @@ def uuid(self): def close(self): """ """ + os.system("pkill -9 partclone.extfs") if self._fd is None or \ self._fd.closed: return @@ -98,15 +103,34 @@ def close(self): def check(self): ret = run_simple_command("{0} -f -y -v {1}".format(which("e2fsck"), self.path)) + proc = subprocess.Popen([which("resize2fs"), "-f", self.path], + stdout=subprocess.PIPE) + output = proc.stdout.read() + output = output.strip() if ret in (0, 1, 2, 256): return True return False + def format_filesystem(self): + + proc = subprocess.Popen([which("mkfs.ext3"), self.path], + stdout=subprocess.PIPE) + output = proc.stdout.read() + output = output.strip() + if output == 0: + return True + return False + + def resize(self): + if self.check(): - ret = run_simple_command("{0} {1}".format(which("resize2fs"), - self.path)) - if ret == 0: + proc = subprocess.Popen([which("resize2fs"), self.path], + stdout=subprocess.PIPE) + proc.wait() + output = proc.stdout.read() + output = output.strip() + if proc.returncode == 0: return True return False diff --git a/carbono/filesystem/generic.py b/src/carbono/filesystem/generic.py similarity index 84% rename from carbono/filesystem/generic.py rename to src/carbono/filesystem/generic.py index 06cd47b..7a529b3 100644 --- a/carbono/filesystem/generic.py +++ b/src/carbono/filesystem/generic.py @@ -28,6 +28,7 @@ def __init__(self, path, type, geometry): self.type = type self.geometry = geometry self._fd = None + self.fderr = None self.process = None def get_size(self): @@ -141,8 +142,25 @@ def resize(self): return True def read_label(self): + proc = subprocess.Popen([which("ntfslabel"), "--force", self.path], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output = proc.stdout.read() + err = proc.stdout.read() + if "mount" in err: + return None + if output: + output = output.strip() + return output return None def write_label(self, label): + try: + ret = run_simple_command('{0} --force {1} "{2}"'.\ + format(which("ntfslabel"), self.path, label)) + if ret == 0: + return True + return False + except Exception as e: + pass return True diff --git a/carbono/filesystem/linux_swap.py b/src/carbono/filesystem/linux_swap.py similarity index 100% rename from carbono/filesystem/linux_swap.py rename to src/carbono/filesystem/linux_swap.py diff --git a/carbono/filesystem/ntfs.py b/src/carbono/filesystem/ntfs.py old mode 100644 new mode 100755 similarity index 84% rename from carbono/filesystem/ntfs.py rename to src/carbono/filesystem/ntfs.py index df46616..e3b1115 --- a/carbono/filesystem/ntfs.py +++ b/src/carbono/filesystem/ntfs.py @@ -58,12 +58,12 @@ def get_size(self): size = long(l.split()[3]) except ValueError: raise ErrorGettingSize - + if size is None: raise ErrorGettingSize return size - + def get_used_size(self): """ """ proc = subprocess.Popen("{0} -i {1} -f".format(which("ntfsresize"), self.path), @@ -77,7 +77,7 @@ def get_used_size(self): size = long(l.split()[4]) except ValueError: raise ErrorGettingUsedSize - + if size is None: raise ErrorGettingUsedSize @@ -90,10 +90,27 @@ def check(self): self.process.run() return not self.process.wait() + def format_filesystem(self): + proc = subprocess.Popen([which("mkfs.ntfs"), "--fast {0}".format(self.path)], + stdout=subprocess.PIPE) + output = proc.stdout.read() + output = output.strip() + if output == 0: + return True + return False + def resize(self): + + returncode = -1 if self.check(): - ret = run_simple_command("ntfsresize -f {0}".format(self.path)) - if ret == 0: + proc = subprocess.Popen([which("ntfsresize"), "--force", self.path], + stdout=subprocess.PIPE) + proc.wait() + output = proc.stdout.read() + output = output.strip() + returncode = proc.returncode + + if returncode == 0: return True return False diff --git a/carbono/image_creator.py b/src/carbono/image_creator.py old mode 100644 new mode 100755 similarity index 62% rename from carbono/image_creator.py rename to src/carbono/image_creator.py index 0cb8a0c..dfefd4b --- a/carbono/image_creator.py +++ b/src/carbono/image_creator.py @@ -17,7 +17,8 @@ import math import os - +import time +import shutil from carbono.device import Device from carbono.disk import Disk from carbono.mbr import Mbr @@ -39,10 +40,6 @@ def __init__(self, source_device, output_folder, raw=False, split_size=0, create_iso=False, fill_with_zeros=False): - assert check_if_root(), "You need to run this application as root" - assert os.path.isdir(output_folder), "{0} folder is invalid".\ - format(output_folder) - self.image_name = image_name self.device_path = source_device self.target_path = adjust_path(output_folder) @@ -59,16 +56,54 @@ def __init__(self, source_device, output_folder, self.current_percent = -1 self.active = False self.canceled = False + self.partclone_stderr = None + self.partclone_sucess = False + self.data_is_eof = False + + if not check_if_root(): + log.info("You need to run this application as root") + self.notify_status("not_root", \ + {"not_root":"You dont't have permission"}) + + if not os.path.isdir(output_folder): + try: + os.mkdir(output_folder) + except Exception as e: + log.info("The folder is invalid. {0}".format(e)) + self.notify_status("invalid_folder",\ + {"invalid_folder":output_folder}) + raise InvalidFolder("Invalid folder {0}".format(output_folder)) def notify_percent(self): + #refresh the interface percentage percent = (self.processed_blocks/float(self.total_blocks)) * 100 if percent > self.current_percent: self.current_percent = percent self.notify_status("progress", {"percent": percent}) + #verify stderr from partclone + if self.partclone_stderr != None: + partclone_status = self.partclone_stderr.readline() + if partclone_status.startswith("Partclone successfully cloned the device"): + self.partclone_sucess = True + else: + if self.data_is_eof: + part_list = partclone_status.split() + if part_list[0] == "current": + if len(part_list) >= 13: + try: + status = partclone_status.split()[13].split(",")[0] + status2 = status.split("%")[0] + self.notify_status("waiting_partclone",{"partclone_percent":float(status2)}) + except Exception as e: + log.info(e) + raise ErrorCreatingImage("Create image wasn't made with success") + def create_image(self): """ """ if is_mounted(self.device_path): + log.error("The partition {0} is mounted, please umount first, and try again".format(self.device_path)) + self.notify_status("mounted_partition_error",{"mounted_partition_error":self.device_path}) raise DeviceIsMounted("Please umount first") self.active = True @@ -76,11 +111,17 @@ def create_image(self): disk = Disk(device) if device.is_disk(): - mbr = Mbr(self.target_path) - mbr.save_to_file(self.device_path) - dlm = DiskLayoutManager(self.target_path) - dlm.save_to_file(disk) - + try: + mbr = Mbr(self.target_path) + mbr.save_to_file(self.device_path) + dlm = DiskLayoutManager(self.target_path) + dlm.save_to_file(disk) + except Exception as e: + log.info(e) + self.notify_status("write_error", \ + {"write_error":self.target_path}) + raise ErrorWritingToDevice("Write error in {0}".format(self.target_path)) + partition_list = disk.get_valid_partitions(self.raw) if not partition_list: raise ErrorCreatingImage("Partition(s) hasn't a " +\ @@ -110,14 +151,16 @@ def create_image(self): total_bytes = 0 for part in partition_list: total_bytes += part.filesystem.get_used_size() - - self.total_blocks = long(math.ceil(total_bytes/float(BLOCK_SIZE))) + self.total_blocks = long(math.ceil(total_bytes/float(BLOCK_SIZE))) information = Information(self.target_path) information.set_image_is_disk(device.is_disk()) information.set_image_name(self.image_name) information.set_image_compressor_level(self.compressor_level) - + if device.is_disk(): + disk_info = DiskInfo() + disk_dict = disk_info.formated_disk(self.device_path) + information.set_disk_size(disk_dict["size"]) # TODO: Abstract this whole part, when creating isos, # splitting in files, etc... @@ -127,17 +170,23 @@ def create_image(self): remaining_size -= BASE_SYSTEM_SIZE slices = dict() # Used when creating iso iso_volume = 1 # Used when creating iso + for part in partition_list: if not self.active: break log.info("Creating image of {0}".format(part.get_path())) + self.notify_status("image_creator", \ + {"image_creator ": part.get_path()}) number = part.get_number() uuid = part.filesystem.uuid() label = part.filesystem.read_label() type = part.filesystem.type - part.filesystem.open_to_read() + #check if partclone is running + if type in ("ext2","ext3","ext4"): + self.partclone_stderr = part.filesystem.get_error_ext() + compact_callback = None if self.compressor_level: compressor = Compressor(self.compressor_level) @@ -145,10 +194,11 @@ def create_image(self): self.buffer_manager = BufferManagerFactory( part.filesystem.read_block, + self.notify_status, compact_callback) self.buffer_manager.start() - buffer = self.buffer_manager.output_buffer + buffer = self.buffer_manager.output_buffer volumes = 1 while self.active: total_written = 0 # Used to help splitting the file @@ -156,18 +206,40 @@ def create_image(self): partition=number, volume=volumes) file_path = self.target_path + pattern - fd = open(file_path, "wb") - + try: + fd = open(file_path, "wb") + except Exception as e: + log.info(e) + self.notify_status("open_file", \ + {"open_file":file_path}) + raise ImageNotFound("The file wasn't found {0}". \ + format(file_path)) next_partition = False while self.active: try: data = buffer.get() except IOError, e: + #self.notify_status("read_buffer_error", \ + # {"read_buffer_error":str(e)}) if e.errno == errno.EINTR: + self.notify_status("read_buffer_error", \ + {"read_buffer_error":str(e)}) + data = "" self.cancel() + raise ErrorReadingFromDevice(e) break + if data == EOF: + if (self.partclone_stderr != None): + self.data_is_eof = True + while self.partclone_sucess == False: + pass + + self.partclone_stderr = None + self.partclone_sucess = False + self.data_is_eof = False + next_partition = True if self.create_iso: remaining_size -= total_written @@ -175,8 +247,15 @@ def create_image(self): slices[iso_volume] = list() slices[iso_volume].append(file_path) break + try: + fd.write(data) + except Exception as e: + log.info("{0}".format(e)) + self.notify_status("disk_full") + self.cancel() + raise ErrorWritingToDevice("Error in write file {0}".\ + format(file_path)) - fd.write(data) self.processed_blocks += 1 if self.split_size: @@ -192,8 +271,13 @@ def create_image(self): slices[iso_volume].append(file_path) iso_volume += 1 break + try: + fd.close() + except Exception as e: + log.info(e) + self.notify_status("write_error",{"write_error":e}) + raise ErrorCloseToWrite("Close Error {0}".format(e)) - fd.close() if next_partition: break self.buffer_manager.join() @@ -212,12 +296,20 @@ def create_image(self): uuid = swap.filesystem.uuid() type = swap.filesystem.type information.add_partition(number, type, 0, 0, uuid) + try: + information.save() + except Exception as e: + log.info(e) + self.notify_status("write_error",{"write_error":e}) + raise ErrorWritingToDevice("Write Error {0}".format(e)) - information.save() self.stop() if self.create_iso: log.info("Starting create ISO operation") + self.notify_status("create_iso", \ + {"create_iso":self.create_iso}) + iso_creator = IsoCreator(self.target_path, slices, self.image_name, self.notify_status, @@ -225,18 +317,19 @@ def create_image(self): iso_creator.run() if self.canceled: - self.notify_status("canceled", {"operation": + log.info("Creation canceled") + self.notify_status("canceled", {"operation": "Create image"}) else: self.notify_status("finish") - log.info("Creation finished") + log.info("Creation finished") def stop(self): if self.active: if hasattr(self, "buffer_manager"): self.buffer_manager.stop() self.active = False - self.timer.stop() + self.timer.stop() log.info("Create image stopped") def cancel(self): diff --git a/carbono/image_reader/__init__.py b/src/carbono/image_reader/__init__.py similarity index 100% rename from carbono/image_reader/__init__.py rename to src/carbono/image_reader/__init__.py diff --git a/carbono/image_reader/compressed.py b/src/carbono/image_reader/compressed.py similarity index 100% rename from carbono/image_reader/compressed.py rename to src/carbono/image_reader/compressed.py diff --git a/carbono/image_reader/generic.py b/src/carbono/image_reader/generic.py similarity index 79% rename from carbono/image_reader/generic.py rename to src/carbono/image_reader/generic.py index 4fc23e1..989f92c 100644 --- a/carbono/image_reader/generic.py +++ b/src/carbono/image_reader/generic.py @@ -36,7 +36,7 @@ def _check_fd(self): def open(self): if self._check_fd(): file_pattern = self.pattern.format(volume=self.current_volume) - + image_path_aux = None # Loop untill find the the next slice or # cancel the operation while True: @@ -44,15 +44,19 @@ def open(self): if os.path.exists(file_path): self._fd = open(file_path, 'rb') else: - self.image_path = self.notify_callback("file_not_found", - {"path": self.image_path, "file": file_pattern}) - - if self.image_path: - self.image_path = adjust_path(self.image_path) - continue + image_path_aux = self.notify_callback("file_not_found_cd", + {"path": self.image_path, "file": file_pattern, "current_volume": self.current_volume}) + if image_path_aux: + self.image_path = image_path_aux + if self.image_path: + self.image_path = adjust_path(self.image_path) + continue + else: + raise Exception("canceled") + break else: - self.notify_callback("canceled", - {"operation": "Restoring image"}) + continue + break def close(self): diff --git a/carbono/image_restorer.py b/src/carbono/image_restorer.py old mode 100644 new mode 100755 similarity index 51% rename from carbono/image_restorer.py rename to src/carbono/image_restorer.py index 0749f31..8bc32ab --- a/carbono/image_restorer.py +++ b/src/carbono/image_restorer.py @@ -17,6 +17,8 @@ import math import _ped +import os + from parted import PARTITION_NORMAL from carbono.device import Device @@ -32,12 +34,19 @@ from carbono.config import * from carbono.log import log +from partition_expander import PartitionExpander + +from _ped import disk_new_fresh class ImageRestorer: def __init__(self, image_folder, target_device, status_callback, partitions=None, - expand=False): + expand=2): + # expand -> 0 Expande a ultima particao + # -> 1 Formata a ultima particao + # -> 2 Nenhuma operacao (ele aplica a imagem e nao faz mais nada) + # -> 3 Ele nao aplica a ultima particao assert check_if_root(), "You need to run this application as root" @@ -54,6 +63,12 @@ def __init__(self, image_folder, target_device, self.active = False self.canceled = False + if not os.path.isdir(self.image_path): + log.info("The folder is invalid") + self.notify_status("invalid_folder", \ + {"invalid_folder":self.image_path}) + raise InvalidFolder("Invalid folder {0}".format(output_folder)) + def notify_percent(self): # Total blocks can be 0 when restoring only a swap partition # for example @@ -66,7 +81,11 @@ def notify_percent(self): def restore_image(self): """ """ + invalid_partitions = False + if is_mounted(self.target_device): + log.error("The partition {0} is mounted, please umount first, and try again".format(self.target_device)) + self.notify_status("mounted_partition_error",{"mounted_partition_error":self.target_device}) raise DeviceIsMounted("Please umount first") self.active = True @@ -90,6 +109,8 @@ def restore_image(self): if device.is_disk() != \ information.get_image_is_disk(): log.error("Invalid target device %s" % device.path) + self.notify_status("write_error", \ + {"write_error":device_path}) raise ErrorRestoringImage("Invalid target device") try: @@ -103,16 +124,72 @@ def restore_image(self): raise ErrorRestoringImage("Unrecognized disk label") if information.get_image_is_disk(): + if ("msdos" not in disk.getPedDisk().type.name): + #se a tabela nao for msdos, recria ela como msdos para nao haver problemas + d = disk_new_fresh(device.getPedDevice(), _ped.disk_type_get("msdos")) + d.commit_to_dev() + disk = Disk(device) + + #Get total disk target size + disk_size = get_disk_size(self.target_device) + if (total_bytes > disk_size): + log.info("Total size of image is {0}".format(total_bytes)) + log.info("Total size of {0} is {1}".format(self.target_device,disk_size)) + log.error("The size of {0} is {1}, is not enough to apply the selected image".format(self.target_device, disk_size)) + disk_space_info = [] + disk_space_info.append(total_bytes) + disk_space_info.append(disk_size) + self.notify_status("no_enough_space", {"disk_minimum_size":disk_space_info}) + raise ErrorRestoringImage("No enough space on disk") + log.info("Restoring MBR and Disk Layout") mbr = Mbr(self.image_path) - mbr.restore_from_file(self.target_device) + try: + mbr.restore_from_file(self.target_device) + except Exception as e: + log.error("Error to restore the Mbr file") + image_path = self.image_path.split("/")[3] + "/mbr.bin" + self.notify_status("file_not_found",{"file_not_found":image_path}) + raise ErrorFileNotFound("File not Found {0}".format(image_path)) + dlm = DiskLayoutManager(self.image_path) - dlm.restore_from_file(disk, self.expand) + #try: + if self.expand != 2: + dlm.restore_from_file(disk, True) + else: + dlm.restore_from_file(disk, False) + #except Exception as e: + # log.error("Error to restore the disk.dl file") + # image_path = self.image_path.split("/")[3] + "/disk.dl" + # self.notify_status("file_not_found",{"file_not_found":image_path}) + # raise ErrorFileNotFound("File not found {0}".format(image_path)) + + else: + parent_path = get_parent_path(self.target_device) + parent_device = Device(parent_path) + parent_disk = Disk(parent_device) + partition = parent_disk.get_partition_by_path( + self.target_device, + part.type) + part_size = partition.getSize('b') + if (total_bytes > part_size): + part_space_info = [] + part_space_info.append(total_bytes) + part_space_info.append(part_size) + log.error("The partition selected is smaller than the image") + self.notify_status("no_enough_space_part", {"disk_minimum_size":part_space_info}) + raise ErrorRestoringImage("No enought space on partition") self.timer.start() + + total_partitions = len(partitions) for part in partitions: + total_partitions -= 1 + if not self.active: break - + + if (self.expand == 3) and (total_partitions == 0): break + if information.get_image_is_disk(): partition = disk.get_partition_by_number(part.number, part.type) @@ -124,10 +201,15 @@ def restore_image(self): self.target_device, part.type) + if partition is None: + invalid_partitions = True + continue + log.info("Restoring partition {0}".format(partition.get_path())) + self.notify_status("restore_partition",\ + {"restoring_partition":partition.get_path()}) - if partition is None: - raise ErrorRestoringImage("No valid partitions found") + invalid_partitions = False if hasattr(part, "uuid"): partition.filesystem.open_to_write(part.uuid) @@ -146,10 +228,12 @@ def restore_image(self): volumes = 1 if hasattr(part, "volumes"): volumes = part.volumes - - image_reader = ImageReaderFactory(self.image_path, pattern, - volumes, compressor_level, - self.notify_status) + try: + image_reader = ImageReaderFactory(self.image_path, pattern, + volumes, compressor_level, + self.notify_status) + except Exception as e: + log.info(e) extract_callback = None if compressor_level: @@ -157,6 +241,7 @@ def restore_image(self): extract_callback = compressor.extract self.buffer_manager = BufferManagerFactory(image_reader.read_block, + self.notify_status, extract_callback) # open the file after instantiating BufferManager, cause of a @@ -171,7 +256,10 @@ def restore_image(self): data = buffer.get() except IOError, e: if e.errno == errno.EINTR: + self.notify_status("read_buffer_error",{"read_buffer_error":str(e)}) + data = "" self.cancel() + raise ErrorReadingFromDevice(e) break if data == EOF: @@ -180,6 +268,7 @@ def restore_image(self): try: partition.filesystem.write_block(data) except ErrorWritingToDevice, e: + self.notify_status("write_partition_error") if not self.canceled: self.stop() raise e @@ -188,35 +277,63 @@ def restore_image(self): self.buffer_manager.join() partition.filesystem.close() + if invalid_partitions: + self.notify_status("no_valid_partitions", \ + {"no_valid_partitions":partitions}) + raise ErrorRestoringImage("No valid partitions found") self.timer.stop() - if self.expand: + if self.expand != 2: if information.get_image_is_disk(): - self.expand_last_partition() + self.expand_last_partition(self.expand) if self.canceled: - self.notify_status("canceled", {"operation": + self.notify_status("canceled", {"operation": "Restore image"}) else: self._finish() - log.info("Restoration finished") + log.info("Restoration finished") + log.info("Iniciando gtk grubinstall") + cmd = "{0}".format(which("grubinstall")) + try: + os.system("{0} &".format(cmd)) + except: + log.error("Erro ao iniciar grubinstall. {0}".format(e)) - def expand_last_partition(self): + def expand_last_partition(self,opt_expand): # After all data is copied to the disk # we instance class again to reload + sync() device = Device(self.target_device) disk = Disk(device) partition = disk.get_last_partition() if partition is not None: if partition.type == PARTITION_NORMAL: - log.info("Expanding {0} filesystem".\ - format(partition.get_path())) - self.notify_status("expand", {"device": - partition.get_path()}) - partition.filesystem.resize() - + expander = PartitionExpander(device.path) + log.info("Checking and try expand {0}".format(partition.get_path())) + new_size = expander.try_expand() + log.info("The new_size of the disk will be {0}".format(new_size)) + if new_size!= -1: + + if opt_expand == 0: + log.info("Expanding {0} filesystem".format(partition.get_path())) + self.notify_status("expand", {"device": + partition.get_path()}) + returncode = partition.filesystem.resize() + if returncode == True: + log.info("Resize in {0} was made with sucess".format(partition.get_path())) + else: + log.info("Resize in {0} failed - Versao sem mensagem!".format(partition.get_path())) + #self.notify_status("expand_last_partition_error", {"last_partition":partition.get_path()}) + #self.canceled = True + else: + if opt_expand == 1: + log.info("Formating {0} filesystem".format(partition.get_path())) + self.notify_status("format", {"device": + partition.get_path()}) + partition.filesystem.format_filesystem() def stop(self): # When restoring only a swap partition, buffer_manager # isnt necessary diff --git a/carbono/information.py b/src/carbono/information.py similarity index 92% rename from carbono/information.py rename to src/carbono/information.py index 403a63e..5ef0f51 100644 --- a/carbono/information.py +++ b/src/carbono/information.py @@ -21,6 +21,7 @@ from carbono.utils import * from carbono.exception import * + class Information: def __init__(self, target_path): @@ -56,6 +57,10 @@ def set_image_is_disk(self, is_disk): """ """ self._doc.update({"is_disk": is_disk}) + def set_disk_size(self, size): + """ """ + self._doc.update({"disk_size": size}) + def add_partition(self, number, type, volumes, size, uuid=None, label=None): """ """ @@ -79,7 +84,13 @@ def add_partition(self, number, type, volumes, size, def get_image_name(self): """ """ return self._doc["name"] - + + def get_disk_size(self): + """ """ + if hasattr(self, _doc["disk_size"]): + return self._doc["disk_size"] + return None + def get_image_compressor_level(self): """ """ return self._doc["compressor_level"] @@ -90,7 +101,7 @@ def get_image_is_disk(self): def get_partitions(self): """ """ - if hasattr(self,"_partitions"): + if hasattr(self, "_partitions"): partitions = self._partitions else: partitions = self._doc["partitions"] @@ -114,4 +125,3 @@ def load(self): except IOError, e: if e.errno == errno.ENOENT: raise ImageNotFound("Image not found") - diff --git a/carbono/iso_creator.py b/src/carbono/iso_creator.py similarity index 74% rename from carbono/iso_creator.py rename to src/carbono/iso_creator.py index c5d6ca4..2bcec37 100644 --- a/carbono/iso_creator.py +++ b/src/carbono/iso_creator.py @@ -21,8 +21,12 @@ from carbono.utils import * from carbono.exception import * from carbono.config import * +from carbono.utils import DiskPartition + +CARBONO_FILES = ("filesystem.squashfs", + "vmlinuz", + "initrd.lz") -CARBONO_FILES = ("initram.gz", "vmlinuz", "isolinux.cfg") class IsoCreator: def __init__(self, target_path, slices, @@ -38,6 +42,20 @@ def __init__(self, target_path, slices, self.process = None self.mount_point = None + def __generate_isolinux_file(self): + isolinux_tmp_file = "/tmp/isolinux.cfg" + tmp_file = open(isolinux_tmp_file,"w") + tmp_file.write("prompt 0\n"+ + "timeout 1\n"+ + " default menu.c32\n"+ + "label upimage\n"+ + " kernel /casper/vmlinuz boot=casper toram\n"+ + " append initrd=/casper/initrd.lz file=/cdrom/preseed/ubuntu.seed boot=casper") + tmp_file.close() + + return isolinux_tmp_file + + def notify_percent(self): if self.process is not None: if self.process.stderr is not None: @@ -56,15 +74,14 @@ def notify_percent(self): return None def mount_device(self, device): - tmpd = make_temp_dir() - ret = run_simple_command("mount {0} {1}".\ - format(device, tmpd)) - if ret is not 0: - raise ErrorMountingFilesystem - return tmpd + ''' ''' + disk_part = DiskPartition(device) + folder = disk_part.mount_partition(ro=True) + + return folder def find_carbono_files(self, path): - dev_files = os.listdir(path) + dev_files = os.listdir(os.path.join(path,"casper")) ret = True if filter(lambda x: not x in dev_files, CARBONO_FILES): @@ -96,7 +113,6 @@ def run(self): run_simple_command("umount {0}".\ format(self.mount_point)) error = True - if error: device = self.notify_callback("base_files_not_found", {"device": device}) @@ -109,28 +125,40 @@ def run(self): break - # Add carbono files - map(lambda x: self.slices[volume].\ - append(self.mount_point + x), CARBONO_FILES) + add = os.path.join(self.mount_point,"casper/") + add="/casper="+add + + self.slices[volume].append(add) + self.slices[volume].append(os.path.join(self.mount_point,"menu.c32")) + + # Add isolinux.cfg file + isolinux_file = self.__generate_isolinux_file() + self.slices[volume].append(isolinux_file) # Bootloader self.slices[volume].append("/usr/lib/syslinux/isolinux.bin") - + self.slices[volume].append("{0}image.info".format( + self.target_path)) if self.is_disk: # Add the rest of files needed to # restore the image map(lambda x: self.slices[volume].\ append(self.target_path + x), - ("mbr.bin", "disk.dl","image.info")) - + ("mbr.bin", "disk.dl")) + if os.path.exists("{0}UpImageAuto.json".format( + self.target_path)): + + self.slices[volume].append("{0}UpImageAuto.json".format( + self.target_path)) + if first_volume: extra_params = "-joliet-long -b " + \ "isolinux.bin -c " + \ "boot.cat -no-emul-boot " + \ - "-boot-load-size 4 -boot-info-table" + "-boot-load-size 4 -boot-info-table -graft-points" slist = ' '.join(self.slices[volume]) - cmd = "{0} -R -J -o {1}{2}{3}.iso {4} {5}".format( + cmd = "{0} -R -J {4} {5} > {1}{2}{3}.iso".format( which("mkisofs"), self.target_path, self.name, diff --git a/carbono/log.py b/src/carbono/log.py similarity index 100% rename from carbono/log.py rename to src/carbono/log.py diff --git a/carbono/mbr.py b/src/carbono/mbr.py similarity index 100% rename from carbono/mbr.py rename to src/carbono/mbr.py diff --git a/carbono/partition.py b/src/carbono/partition.py old mode 100644 new mode 100755 similarity index 100% rename from carbono/partition.py rename to src/carbono/partition.py diff --git a/src/carbono/partition_expander.py b/src/carbono/partition_expander.py new file mode 100644 index 0000000..70c417f --- /dev/null +++ b/src/carbono/partition_expander.py @@ -0,0 +1,121 @@ +#!/usr/bin/python +# coding: utf-8 + +# Author: MStech +# Lucas Alvares Gomes + +import os +import subprocess +import logging +import parted +import time +from carbono.log import log + +class PartitionExpander: + + def __init__(self, path): + self.path = path + + def try_expand(self): + """ """ + device = parted.Device(self.path) + disk = parted.Disk(device) + + try: + last_partition = disk.partitions[-1] + except IndexError: + log.error("The disk has no partitions") + return -1 + + # A partição deve ser primária e conter NTFS como sistema + # de arquivos + + if last_partition.type != parted.PARTITION_NORMAL: + log.error("The partition must be primary") + return -1 + + if last_partition.fileSystem is None: + log.error("The partition hasn't filesystem") + return -1 + + #if last_partition.fileSystem is not None: + # if last_partition.fileSystem.type != "ntfs": + # log.error("We only support ntfs filesystem for now") + # return -1 + + # Recria a última partição do disco utilizando + # todo o espaço que desalocado + start = last_partition.geometry.start + fs = last_partition.fileSystem + ped = last_partition.getPedPartition() + + disk.removePartition(last_partition) + + new_geometry = None + if (len(disk.getFreeSpaceRegions()) == 1): + new_geometry = disk.getFreeSpaceRegions()[0] + new_geometry.start = start + else: + new_geometry = None + for region in disk.getFreeSpaceRegions(): + if region.start == start: + new_geometry = region + if (start > region.start and start < region.end): + new_geometry = region + new_geometry.start = start + constraint = parted.Constraint(exactGeom=new_geometry) + new_partition = parted.Partition(disk = disk, + type = parted.PARTITION_NORMAL, + fs = fs, + geometry = new_geometry, + PedPartition = ped) + + disk.addPartition(partition=new_partition, constraint=constraint) + try: + disk.commit() + except: + try: + disk.commitToDevice() + disk.commitToOS() + except Exception as e: + log.error("PartitionExpander : {0}".format(e)) + + + # Após criar a tabela de partição temos que fazer + # com o kernel releia essa tabela. Será preciso + # fazer isso para dar continuidade ao processo + attempt = 0 + while True: + p = subprocess.Popen("sfdisk -R %s" % device.path, + shell = True, + stderr = subprocess.PIPE, + ) + + if not len(p.stderr.readlines()): + break + + if attempt >= 5: + return -1 + + attempt += 1 + time.sleep(2) + + # Apos o kernel re-ler a nova tabela de partição + # temos que esperar o dispositivo ficar pronto + attempt = 0 + while True: + + if os.path.exists(new_partition.path): + break + + if attempt >= 5: + return -1 + + attempt += 1 + time.sleep(2) + + # Agora da um resize no sistema de arquivos + # Pegar o tamanho total do dispositivo da partição a ser redimensionada + size = new_partition.geometry.length * device.sectorSize + + return size diff --git a/carbono/ui/__init__.py b/src/carbono/ui/__init__.py similarity index 100% rename from carbono/ui/__init__.py rename to src/carbono/ui/__init__.py diff --git a/carbono/ui/cli.py b/src/carbono/ui/cli.py similarity index 100% rename from carbono/ui/cli.py rename to src/carbono/ui/cli.py diff --git a/src/carbono/utils.py b/src/carbono/utils.py new file mode 100644 index 0000000..663a877 --- /dev/null +++ b/src/carbono/utils.py @@ -0,0 +1,723 @@ +#!/usr/bin/python +# coding: utf-8 +# Copyright (C) 2010 Lucas Alvares Gomes +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . + +import subprocess +import tempfile +import multiprocessing +import random +import errno +import os +import parted +import dbus + +from threading import Thread, Event +from os.path import realpath +from carbono.exception import * +from carbono.config import * + +class HalInfo(): + ''' + Get info of the disk using HAL + ''' + + def __init__(self): + self.external_devices = [] + + def get_storages_udis(self): + ''' + Get the dbus path of all disk(/dev/sda) + Returns a list + ''' + out,err,ret = run_simple_command_echo('{0} --capability "storage"'.format( + which('hal-find-by-capability')),False) + + return out.split() + + def get_volumes_udis(self): + ''' + Get the dbus path of all devices (/dev/sda1) + Returns a list + ''' + out,err,ret = run_simple_command_echo('{0} --capability "volume"'.format( + which('hal-find-by-capability')),False) + + return out.split() + + def is_storage_cdrom(self, storage_udi): + '''Verifies if given udi is a cdrom storage''' + out,err,ret = run_simple_command_echo('{0} --udi {1} \ + --key storage.cdrom.cdr'.format( + which('hal-get-property'),storage_udi), False) + if 'true' in out: + return True + return False + + + def is_storage_removable(self, storage_udi): + '''Verifies if given udi is a removable storage''' + out,err,ret = run_simple_command_echo('{0} --udi {1} \ + --key storage.removable'.format( + which('hal-get-property'),storage_udi), False) + if 'true' in out: + return True + return False + + def is_device_fat(self, storage_udi): + '''Verifies if given udi ''' + out,err,ret = run_simple_command_echo('{0} --udi {1} \ + --key volume.fstype'.format( + which('hal-get-property'),storage_udi), False) + if 'fat' in out: + return True + return False + + def get_block_device(self, udi): + ''' + Get the block of the given udi + Returns /dev/sda or /dev/sda1, for example + ''' + out,err,ret = run_simple_command_echo('{0} --udi {1} \ + --key block.device'.format( + which('hal-get-property'),udi), False + ) + return out + + def get_storage_model(self, storage_udi): + '''Returns model of given storage''' + out,err,ret = run_simple_command_echo('{0} --udi {1} \ + --key storage.model'.format( + which('hal-get-property'),storage_udi), False) + return out + + def get_external_devices(self): + ''' + get all external devices + returns a list of /dev/xxx. ie.: ['/dev/sdd','/dev/sr0'] + ''' + storages_udis = self.get_storages_udis() + volumes_udis = self.get_volumes_udis() + for s in volumes_udis: + if self.is_device_fat(s): + dev = self.get_block_device(s).split()[0] + self.external_devices.append(dev) + for s in storages_udis: + if self.is_storage_removable(s): + device = self.get_block_device(s).split()[0] + if "sr" in device: + self.external_devices.append(device) + else: + for v in volumes_udis: + vol = self.get_block_device(v).split()[0] + if ( device in vol ) and ( vol not in self.external_devices ): + self.external_devices.append(vol) + return self.external_devices + + def get_cdrom_devices(self): + ''' + get cdroms devices + returns a dict with device path and it model string + ie.: {"/dev/sr0":{'model': "dvdrw"} } + ''' + storage_udis = self.get_storages_udis() + cdrom_dict = {} + for st in storage_udis: + if self.is_storage_cdrom(st): + device = self.get_block_device(st).split()[0] + model = self.get_storage_model(st) + cdrom_dict[device] = {"model": model} + return cdrom_dict + + +class DiskInfo(): + + def __init__(self): + self.__DISK_DICT = {} + self.__PARTITION_DICT = {} + + # self.disk_info = {"/dev/sda":{"size":123123124, + # "label":model:self.__DISK_DICT, + # "partitions":[]} + + def __get_devices(self): + ''' Filter = only partitions with a valid filesystem ''' + + disk_dict = {} + + devices = parted.getAllDevices() + for device in devices: + dev_path = device.path + dev_model = device.model + dev_size = device.getSize('b') + + disk_dict[dev_path] = {"model": dev_model, + "size": dev_size, + "partitions": {}} + + try: + disk = parted.Disk(device) + except Exception as e: + print e + continue + + part_dict = {} + for p in disk.partitions: + if p.type not in (parted.PARTITION_NORMAL, + parted.PARTITION_LOGICAL): + continue + + part_path = p.path + part_size =1024*1024*int(p.getSize('b')) + part_type = "unknown" + if p.fileSystem: + part_type = p.fileSystem.type + + part_dict[part_path] = {"size": part_size, + "type": part_type} + + disk_dict[dev_path]["partitions"] = part_dict + + return disk_dict + + + def __collect_information_about_devices(self): + ''' + Pega informacoes sobre os discos e particoes, + todas as telas que precisarem de informacoes sobre discos ou particoes + pegam elas dos dicionarios aqui criados + ''' + _dict = self.__get_devices() + for disk in _dict: + partitions = _dict[disk]["partitions"].keys() + for part in partitions: + self.__PARTITION_DICT[part] = {"type": _dict[disk]["partitions"][part]["type"], + "size": _dict[disk]["partitions"][part]["size"]} + self.__DISK_DICT[disk] = {"model": _dict[disk]["model"], + "size": _dict[disk]["size"], + "partitions" : [], + "detach": self.device_detachable(disk) + } + def disk_usage(self, path): + st = os.statvfs(path) + disk_usage = {} + disk_usage["free"] = st.f_bavail * st.f_frsize + disk_usage["total"] = st.f_blocks * st.f_frsize + disk_usage["used"] = (st.f_blocks - st.f_bfree) * st.f_frsize + + return disk_usage + + def device_detachable(self, device): + try: + bus = dbus.SystemBus() + ud_manager_obj = bus.get_object('org.freedesktop.UDisks', + '/org/freedesktop/UDisks') + proplist = [] + ud_manager = dbus.Interface(ud_manager_obj, 'org.freedesktop.UDisks') + for device_pc in ud_manager.EnumerateDevices(): + device_obj = bus.get_object('org.freedesktop.UDisks', device_pc) + device_props = dbus.Interface(device_obj, dbus.PROPERTIES_IFACE) + proplist.append(device_props.GetAll('org.freedesktop.UDisks.Device')) + + for device_props in proplist: + if (device_props["DeviceFile"] == device and + device_props['DriveCanDetach']): + return True + except Exception as e: + print e + return False + + def formated_disk(self, filter_disk=None): + """ """ + self.__collect_information_about_devices() + formated_partitions_list = self.__DISK_DICT + formated_partitions_dict = {} + if filter_disk is not None: + return formated_partitions_list[filter_disk] + else: + for disk in formated_partitions_list: + formated_partitions_dict[disk] = formated_partitions_list[disk] + return formated_partitions_dict + + def formated_partitions(self, filter_disk=None): + formated_partitions = [] + formated_partitions_dict = self.__DISK_DICT + self.__collect_information_about_devices() + + device_info = {"size":None,"label":None,"partitions":None} + + if self.__PARTITION_DICT.keys() == []: + return formated_partitions_dict + + for part in self.__PARTITION_DICT.keys(): + part_dict = {} + disk_part = DiskPartition(part) + tmp_folder_part = disk_part.get_mounted_folder() + was_mounted = True + if not tmp_folder_part: + was_mounted = False + tmp_folder_part = disk_part.mount_partition(ro=True) + part_dict["total_size"] = None + part_dict["used_size"] = None + part_dict["free_size"] = None + + if tmp_folder_part: + part_usage = self.disk_usage(tmp_folder_part) + part_dict["total_size"] = part_usage["total"] + part_dict["used_size"] = part_usage["used"] + part_dict["free_size"] = part_usage["free"] + + part_type = self.__PARTITION_DICT[part]['type'] + size_bytes = self.__PARTITION_DICT[part]['size'] + size_mb = int(long(size_bytes) / (1024 * 1024.0)) + part_dict["type"] = part_type + part_dict["path"] = part + part_dict["size"] = size_mb + formated_partitions.append(part_dict) + if not was_mounted: + disk_part.umount_partition() + + formated_partitions.sort(reverse=False) + disk = formated_partitions[0]['path'][:8] + formated_partitions_dict[disk]["partitions"] = [] + for aux in range(0, len(formated_partitions)): + temp_disk = formated_partitions[aux]['path'][:8] + if formated_partitions_dict[temp_disk]["partitions"] is None: + formated_partitions_dict[temp_disk]["partitions"] = [] + if temp_disk != disk: + disk = temp_disk + formated_partitions_dict[temp_disk]["partitions"].append( + formated_partitions[aux]) + if filter_disk is not None: + formated_partitions_dict = dict((key,value) + for key, value + in formated_partitions_dict.iteritems() + if key == filter_disk) + return(formated_partitions_dict) + + +class DiskPartition(): + + + def __init__(self, partition=""): + + self.__temp_folder = "" + self.__partition = partition + + def __generate_temp_folder(self, destino="/tmp/"): + + self.__temp_folder = adjust_path(tempfile.mkdtemp()) + + return self.__temp_folder + + def __set_partition(self, partition): + + self.__partition = partition + + def get_partition(self): + + return self.__partition + + def umount_partition(self, device=None): + disk_mounted = self.get_mounted_devices() + disk_list = [] + result_disk_umounted = {} + if self.__partition not in disk_mounted.keys(): + return "{0} is already umounted".format(self.__partition) + else: + disk_list = disk_mounted[self.__partition] + for item in disk_list: + cmd = "umount {0}".format(item) + p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE, shell=True) + ret = os.waitpid(p.pid,0)[1] + if not ret: + result_disk_umounted[item] = 0 + print "A particao {0} foi desmontada do diretorio {1}".format(self.__partition, item) + else: + cmd = "umount {0}".format(item) + p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE, shell=True) + ret = os.waitpid(p.pid,0)[1] + if not ret: + result_disk_umounted[item] = 0 + print "A particao {0} foi desmontada do diretorio {1}".format(self.__partition, item) + else: + result_disk_umounted[item] = -1 + print "A particao {0} montada em {1} nao foi desmontada".format(self.__partition,item) + return result_disk_umounted + + def umount_all_partitions(self): + disk_mounted = self.get_mounted_devices() + result_part_umounted = {} + result_disk_umounted = {} + disk_list = [] + for item in disk_mounted.keys(): + disk_list = disk_mounted[item] + result_part_umounted = {} + for part in disk_list: + cmd = "umount {0}".format(item) + p = subprocess.Popen(cmd,stdout=subprocess.PIPE,stderr=subprocess.PIPE, shell=True) + ret = os.waitpid(p.pid,0)[1] + if not ret: + result_part_umounted[part] = 0 + else: + result_part_umounted[part] = -1 + result_disk_umounted[item] = result_part_umounted + return result_disk_umounted + + def get_mounted_folder(self): + mounted_folder = self.get_mount_point(self.__partition) + + if mounted_folder: + return mounted_folder[0] + return False + + def mount_partition(self, destino = None, ro = False): + mount_options = "" + if ro: + mount_options += "-o ro" + + mounted_folder = self.get_mount_point(self.__partition) + + if mounted_folder: + return mounted_folder[0] + + mounted_folder = "" + + if destino is None: + mounted_folder = adjust_path(tempfile.mkdtemp()) + cmd = "mount {0} {1} {2}".format(mount_options, self.__partition, mounted_folder) + p = subprocess.Popen(cmd,stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + ret = os.waitpid(p.pid,0)[1] + if not ret: + return mounted_folder + else: + return False + + else: + cmd = "mount {0} {1} {2}".format(mount_options, self.__partition, destino) + p = subprocess.Popen(cmd,stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + ret = os.waitpid(p.pid,0)[1] + if not ret: + return destino + else: + return False + + def get_mount_point(self, device = None): + if device is not None: + mounted_dest = self.get_mounted_devices() + if device not in mounted_dest.keys(): + return False + else: + return mounted_dest[device] + else: + return False + + def get_mounted_devices(self, device = None): + disk_mounted = {} + list_dest = [] + mount_command = subprocess.check_output(['mount','-l']).split('\n') + for lines in mount_command: + line = lines.split(' ') + if (line[0].startswith('/dev/sd') or + line[0].startswith('/dev/sr') or + line[0].startswith('/dev/cd')): + if line[0] not in disk_mounted.keys(): + list_dest = [] + list_dest.append(line[2]) + disk_mounted[line[0]] = list_dest + list_dest = [] + else: + list_dest = [] + for element in xrange(len(disk_mounted[line[0]])): + list_dest.append(disk_mounted[line[0]][element]) + list_dest.append(line[2]) + disk_mounted[line[0]] = list_dest + list_dest = [] + return disk_mounted + + +class Timer(Thread): + def __init__(self, callback, timeout=2): + Thread.__init__(self) + self.callback = callback + self.timeout = timeout + self.event = Event() + + def run(self): + while not self.event.is_set(): + self.callback() + self.event.wait(self.timeout) + + def stop(self): + self.event.set() + + +class RunCmd: + def __init__(self, cmd): + self.cmd = cmd + self.stdout = None + self.stdin = None + self.stderr = None + + def run(self): + self.process = subprocess.Popen(self.cmd, shell=True, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE) + self.stdout = self.process.stdout + self.stdin = self.process.stdin + self.stderr = self.process.stderr + + def wait(self): + if hasattr(self, "process"): + self.process.wait() + return self.process.returncode + + def stop(self): + if hasattr(self, "process"): + try: + self.process.kill() + except OSError, e: + if e.errno == errno.ESRCH: + pass + + +def run_simple_command(cmd): + """ """ + p = subprocess.Popen(cmd, shell=True, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE) + p.wait() + return p.returncode + +def run_simple_command_echo(cmd, echo=False): + ''' + run a given command + returns the output, errors (if any) and returncode + ''' + if echo: + print "{0}".format(cmd) + p = subprocess.Popen(cmd, shell=True, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + stderr=subprocess.PIPE) + p.wait() + output, err = p.communicate() + ret = p.returncode + return output,err,ret + +def get_disk_size(path): + devices = parted.getAllDevices() + for device in devices: + if (device.path == path): + return device.getSize('b') + return False + +def random_string(length=5): + return ''.join([random.choice(tempfile._RandomNameSequence.characters) \ + for i in range(length)]) + +def adjust_path(path): + """ """ + path = realpath(path) + if not path[-1] == '/': + path += '/' + return path + +def make_temp_dir(): + """ """ + return adjust_path(tempfile.mkdtemp()) + +def get_parent_path(path): + num = -1 + while True: + try: + int(path[num]) + num -= 1 + except ValueError: + return path[:num+1] + +def singleton(cls): + instance_list = list() + def getinstance(): + if not len(instance_list): + instance_list.append(cls()) + return instance_list[0] + return getinstance + +def available_processors(): + return multiprocessing.cpu_count() + +def is_hyperthreading(): + with open("/proc/cpuinfo", "r") as f: + for line in f.readlines(): + if line.startswith("flags"): + if "ht" in line.split(): + return True + break + return False + +def available_memory(percent=100): + free = 0 + with open("/proc/meminfo", 'r') as f: + for line in f: + if line.startswith("MemFree:"): + free = int(line.split()[1]) * 1024 + break + + if percent < 100: + free = (free * percent) / 100 + + return free + +def get_devices(): + disk_dic = {} + devices = parted.getAllDevices() + for device in devices: + dev_path = device.path + try: + disk = parted.Disk(device) + except: + continue + part_dict = {} + for p in disk.partitions: + part_path = p.path + part_type = "unkown" + if p.fileSystem: + part_type = p.fileSystem.type + if part_type == "fat32" or part_type == "fat16": + part_dict[part_path] = {"type":part_type} + disk_dic[dev_path] = {"partitions": part_dict} + return disk_dic + +CARBONO_FILES2 = ("initrd.lz","vmlinuz","filesystem.squashfs") + +def mount_pen(device): + tmpd = make_temp_dir() + ret = run_simple_command("mount {0} {1}".format(device, tmpd)) + if ret is not 0: + raise ErrorMountingFilesystem + return tmpd + +def find_carbono(path): + try: + dev_files = os.listdir(os.path.join(path, "casper")) + except Exception as e: + print e + return False + ret = True + if dev_files is None: + ret = False + if filter(lambda x:not x in dev_files, CARBONO_FILES2): + ret = False + return ret + + +def mount_point(device): + mounts = {} + for line in subprocess.check_output(['mount', '-l']).split('\n'): + parts = line.split(' ') + if len(parts) > 2: + mounts[parts[0]] = parts[2] + try: + if mounts[device]: + return mounts[device] + else: + raise ErrorWithoutConnectedDevice("Sem Pen-Drive conectado") + except: + raise ErrorIdentifyDevice("Erro na identificação do Pendrive") + +def get_upimage_device(): + devices = get_devices() + for dev in devices: + device = devices[dev]["partitions"].keys() + if is_mounted(device[0]): + mount_path = mount_point(device[0]) + else: + mount_path = mount_pen(device[0]) + ret = find_carbono(mount_path) + if ret: + run_simple_command("umount {0}".format(mount_path)) + return device[0], mount_path + else: + if ret == 0: + run_simple_command("umount {0}".format(mount_path)) + return -1,-1 + +def get_cdrom_device(): + device = None + path = None + try: + device,path = get_upimage_device() + except: + pass + if (device and path) == -1: + with open("/proc/sys/dev/cdrom/info", 'r') as f: + for line in f: + if line.startswith("drive name:"): + try: + device = "/dev/" + line.split()[2] + except IndexError: + continue + return device + +def which(program): + def is_exe(fpath): + return os.path.exists(fpath) and os.access(fpath, os.X_OK) + + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + + raise CommandNotFound("{0}: command not found".\ + format(program)) + +def sync(): + run_simple_command("sync") + +def is_mounted(device): + with open("/etc/mtab", 'r') as f: + for line in f: + if line.find(device) > -1: + return True + return False + +def check_if_root(): + if os.getuid() == 0: + return True + return False + +def verify_4k(hd = "sda"): + ''' + Retorna o tamanho fisico do setor + ''' + try: + f = open("/sys/block/{0}/queue/physical_block_size".format(hd)) + block = f.readline() + if "4096" in block: + return(4096) + #se nao for 4K, considera-se 512 + return(512) + except Exception as e: + #nao tem HD (uma vm sem hd, por exemplo) + return(512) + diff --git a/scripts/carbono b/src/scripts/carbono similarity index 100% rename from scripts/carbono rename to src/scripts/carbono diff --git a/src/setup.py b/src/setup.py new file mode 100755 index 0000000..6749c42 --- /dev/null +++ b/src/setup.py @@ -0,0 +1,26 @@ +#!/usr/bin/python +import os +from setuptools import setup + +def read(fname): + return open(os.path.join(os.path.dirname(__file__), fname)).read() + +VERSION_ID = "2.2.2" +BUILD_ID = "{0}-SNAPSHOT".format(VERSION_ID) +if 'BUILD_NUMBER' in os.environ: + BUILD_ID = "{0}.{1}".format(VERSION_ID, os.environ['BUILD_NUMBER']) + +setup( + name = "carbono", + version = BUILD_ID, + author = "Bruno Casella", + author_email = "bruno.casella@gmail.com", + description = ("A hard disk imaging and recovery application"), + license = "GPL", + keywords = "network_manager dbus", + url = "http://umago.info/carbono", + packages = ["carbono","carbono.buffer_manager", + "carbono.filesystem", "carbono.ui", + "carbono.image_reader"], + scripts = ["scripts/carbono"], +) diff --git a/test/gui/carbono.glade b/src/test/gui/carbono.glade similarity index 100% rename from test/gui/carbono.glade rename to src/test/gui/carbono.glade diff --git a/test/gui/gui.py b/src/test/gui/gui.py similarity index 100% rename from test/gui/gui.py rename to src/test/gui/gui.py diff --git a/test/gui/main_bg.png b/src/test/gui/main_bg.png similarity index 100% rename from test/gui/main_bg.png rename to src/test/gui/main_bg.png diff --git a/test/gui/refs b/src/test/gui/refs similarity index 100% rename from test/gui/refs rename to src/test/gui/refs diff --git a/test/gui/select_file.glade b/src/test/gui/select_file.glade similarity index 100% rename from test/gui/select_file.glade rename to src/test/gui/select_file.glade diff --git a/test/gui/treeview_disks.py b/src/test/gui/treeview_disks.py similarity index 100% rename from test/gui/treeview_disks.py rename to src/test/gui/treeview_disks.py diff --git a/tests/.gitkeep b/tests/.gitkeep new file mode 100644 index 0000000..e69de29