Support duo4k.
[vuplus_dvbapp] / lib / python / Components / Harddisk.py
1 from os import system, listdir, statvfs, popen, makedirs, stat, major, minor, path, access
2
3 from Tools.Directories import SCOPE_HDD, resolveFilename, fileExists
4
5 from Tools.CList import CList
6 from SystemInfo import SystemInfo
7 import time
8 from Components.Console import Console
9 import os
10 import glob
11
12 def MajorMinor(path):
13         rdev = stat(path).st_rdev
14         return (major(rdev),minor(rdev))
15
16 def readFile(filename):
17         file = open(filename)
18         data = file.read().strip()
19         file.close()
20         return data
21
22 def getProcMounts():
23         try:
24                 mounts = open("/proc/mounts", 'r')
25         except IOError, ex:
26                 print "[Harddisk] Failed to open /proc/mounts", ex
27                 return []
28         result = [line.strip().split(' ') for line in mounts]
29         for item in result:
30                 # Spaces are encoded as \040 in mounts
31                 item[1] = item[1].replace('\\040', ' ')
32         return result
33
34 def CheckSfdiskVer():
35         cmd = 'sfdisk --version'
36         lines = popen(cmd).readlines()
37         for l in lines:
38                 if l.find("sfdisk from util-linux") != -1:
39                         ver = l.split()[-1].strip()
40                         break
41         try:
42                 vs = ver.split('.')
43                 if len(vs) > 2:
44                         ver = '.'.join(vs[:2])
45
46                 ver = float(ver)
47         except:
48                 print "[CheckSfdiskVer] check parted version Failed!"
49                 return 0
50         return ver
51
52 def enableUdevEvent(enable = True):
53         if enable:
54                 option = '--start-exec-queue'
55         else:
56                 option = '--stop-exec-queue'
57         cmd = "udevadm control %s" % option
58         print "CMD : ", cmd
59         system(cmd)
60
61 def findMountPoint(path):
62         'Example: findMountPoint("/media/hdd/some/file") returns "/media/hdd"'
63         path = os.path.abspath(path)
64         while not os.path.ismount(path):
65                 path = os.path.dirname(path)
66         return path
67
68 def getDeviceFile(dev_path):
69         for parts in getProcMounts():
70                 if os.path.realpath(parts[1]).startswith(dev_path):
71                         return parts[0]
72
73 def getMountPath(mountPath):
74         mountPath = os.path.realpath(mountPath)
75         while not os.path.ismount(mountPath):
76                 mountPath = os.path.dirname(mountPath)
77
78         return mountPath
79
80 def getDeviceInterface(mountPath):
81         mountPath = getMountPath(mountPath)
82
83         if mountPath == '/':
84                 return None
85
86         from Components.Harddisk import getDeviceFile
87         dev = getDeviceFile(mountPath)
88
89         if dev and dev.startswith("/dev/sd"):
90                 dev = os.path.basename(dev)
91                 phyPath = os.path.realpath('/sys/block/' + dev[:3])
92
93                 # check usb
94                 for x in glob.glob("/sys/bus/usb/devices/usb*"):
95                         if phyPath.find(os.path.realpath(x)) != -1:
96                                 from Tools.HardwareInfo import HardwareInfo
97                                 if (HardwareInfo().get_vu_device_name() == "zero4k") and (phyPath.find("f0b00500.ehci_v2/usb2/2-1") != -1):
98                                         return "ata"
99                                 else:
100                                         return "usb"
101
102                 # check ata
103                 if phyPath.find("/ata") != -1:
104                         return "ata"
105
106         return None
107
108 def isUsbStorage(dirname):
109         return getDeviceInterface(dirname) in (None, "usb")
110
111 DEVTYPE_UDEV = 0
112 DEVTYPE_DEVFS = 1
113
114 class Harddisk:
115         def __init__(self, device):
116                 self.device = device
117
118                 if access("/dev/.udev", 0):
119                         self.type = DEVTYPE_UDEV
120                 elif access("/dev/.devfsd", 0):
121                         self.type = DEVTYPE_DEVFS
122                 else:
123                         print "Unable to determine structure of /dev"
124
125                 self.max_idle_time = 0
126                 self.idle_running = False
127                 self.timer = None
128
129                 self.dev_path = ''
130                 self.disk_path = ''
131                 self.mount_path = None
132                 self.mount_device = None
133                 self.phys_path = path.realpath(self.sysfsPath('device'))
134
135                 if self.type == DEVTYPE_UDEV:
136                         self.dev_path = '/dev/' + self.device
137                         self.disk_path = self.dev_path
138
139                 elif self.type == DEVTYPE_DEVFS:
140                         tmp = readFile(self.sysfsPath('dev')).split(':')
141                         s_major = int(tmp[0])
142                         s_minor = int(tmp[1])
143                         for disc in listdir("/dev/discs"):
144                                 dev_path = path.realpath('/dev/discs/' + disc)
145                                 disk_path = dev_path + '/disc'
146                                 try:
147                                         rdev = stat(disk_path).st_rdev
148                                 except OSError:
149                                         continue
150                                 if s_major == major(rdev) and s_minor == minor(rdev):
151                                         self.dev_path = dev_path
152                                         self.disk_path = disk_path
153                                         break
154
155                 print "new Harddisk", self.device, '->', self.dev_path, '->', self.disk_path
156                 self.startIdle()
157
158         def __lt__(self, ob):
159                 return self.device < ob.device
160
161         def partitionPath(self, n):
162                 if self.type == DEVTYPE_UDEV:
163                         return self.dev_path + n
164                 elif self.type == DEVTYPE_DEVFS:
165                         return self.dev_path + '/part' + n
166
167         def sysfsPath(self, filename):
168                 return path.realpath('/sys/block/' + self.device + '/' + filename)
169
170         def stop(self):
171                 if self.timer:
172                         self.timer.stop()
173                         self.timer.callback.remove(self.runIdle)
174
175         def bus(self):
176                 # CF (7025 specific)
177                 if self.type == DEVTYPE_UDEV:
178                         ide_cf = False  # FIXME
179                 elif self.type == DEVTYPE_DEVFS:
180                         ide_cf = self.device[:2] == "hd" and "host0" not in self.dev_path
181
182                 internal = "pci" in self.phys_path
183
184                 if ide_cf:
185                         ret = "External (CF)"
186                 elif internal:
187                         ret = "Internal"
188                 else:
189                         ret = "External"
190                 return ret
191
192         def diskSize(self):
193                 try:
194                         line = readFile(self.sysfsPath('size'))
195                 except:
196                         harddiskmanager.removeHotplugPartition(self.device)
197                         print "error remove",self.device
198                         return -1
199                 try:
200                         cap = int(line)
201                 except:
202                         return 0;
203                 return cap / 1000 * 512 / 1000
204
205         def capacity(self):
206                 cap = self.diskSize()
207                 if cap == 0:
208                         return ""
209                 return "%d.%03d GB" % (cap/1000, cap%1000)
210
211         def model(self):
212                 try:
213                         if self.device[:2] == "hd":
214                                 return readFile('/proc/ide/' + self.device + '/model')
215                         elif self.device[:2] == "sd":
216                                 vendor = readFile(self.sysfsPath('device/vendor'))
217                                 model = readFile(self.sysfsPath('device/model'))
218                                 return vendor + '(' + model + ')'
219                         else:
220                                 assert False, "no hdX or sdX"
221                 except:
222                         harddiskmanager.removeHotplugPartition(self.device)
223                         print "error remove",self.device
224                         return -1
225
226         def free(self):
227                 try:
228                         mounts = open("/proc/mounts")
229                 except IOError:
230                         return -1
231
232                 lines = mounts.readlines()
233                 mounts.close()
234
235                 for line in lines:
236                         parts = line.strip().split(" ")
237                         real_path = path.realpath(parts[0])
238                         if not real_path[-1].isdigit():
239                                 continue
240                         try:
241                                 if MajorMinor(real_path) == MajorMinor(self.partitionPath(real_path[-1])):
242                                         stat = statvfs(parts[1])
243                                         return stat.f_bfree/1000 * stat.f_bsize/1000
244                         except OSError:
245                                 pass
246                 return -1
247
248         def numPartitions(self):
249                 numPart = -1
250                 if self.type == DEVTYPE_UDEV:
251                         try:
252                                 devdir = listdir('/dev')
253                         except OSError:
254                                 return -1
255                         for filename in devdir:
256                                 if filename.startswith(self.device):
257                                         numPart += 1
258
259                 elif self.type == DEVTYPE_DEVFS:
260                         try:
261                                 idedir = listdir(self.dev_path)
262                         except OSError:
263                                 return -1
264                         for filename in idedir:
265                                 if filename.startswith("disc"):
266                                         numPart += 1
267                                 if filename.startswith("part"):
268                                         numPart += 1
269                 return numPart
270
271         def mountDevice(self):
272                 for parts in getProcMounts():
273                         if path.realpath(parts[0]).startswith(self.dev_path):
274                                 self.mount_device = parts[0]
275                                 self.mount_path = parts[1]
276                                 return parts[1]
277
278         def findMount(self):
279                 if self.mount_path is None:
280                         return self.mountDevice()
281                 return self.mount_path
282
283         def unmount(self):
284                 try:
285                         mounts = open("/proc/mounts")
286                 except IOError:
287                         return -1
288
289                 lines = mounts.readlines()
290                 mounts.close()
291
292                 cmd = "umount"
293
294                 for line in lines:                                                                          
295                         parts = line.strip().split(" ")                                                     
296                         real_path = path.realpath(parts[0])                                                 
297                         if not real_path[-1].isdigit():                                                     
298                                 continue                                                                    
299                         try:                                                                                
300                                 if MajorMinor(real_path) == MajorMinor(self.partitionPath(real_path[-1])):
301                                         cmd = ' ' . join([cmd, parts[1]])
302                                         break
303                         except OSError:
304                                 pass
305
306                 res = system(cmd)
307                 print "CMD : ", cmd
308                 return (res >> 8)
309
310         def checkPartionPath(self, path):
311                 import time, os
312                 for i in range(1,10):
313                         if os.path.exists(path):
314                                 return True
315                         time.sleep(1)
316                 return False
317
318         def updatePartition(self):
319                 sfdiskVer = CheckSfdiskVer()
320                 if sfdiskVer < 2.26: # sfdisk -R option is deprecated at sfdiskVer >= 2.26
321                         cmd = 'sfdisk -R %s; sleep 5' % (self.disk_path)
322                 elif path.exists('/usr/sbin/partprobe'):
323                         cmd = 'partprobe %s; sleep 5' % (self.disk_path)
324                 elif path.exists('/usr/sbin/partx'):
325                         cmd = 'partx -u %s' % (self.disk_path)
326                 else:
327                         return -1
328
329                 print "CMD : ", cmd
330                 res = system(cmd)
331
332                 return (res >> 8)
333
334         def createPartition(self):
335                 def CheckPartedVer():
336                         cmd = 'parted --version'
337                         lines = popen(cmd).readlines()
338                         for l in lines:
339                                 if l.find("parted (GNU parted)") != -1:
340                                         ver = l.split()[3].strip()
341                                         break
342                         try:
343                                 ver = float(ver)
344                         except:
345                                 print "[CheckPartedVer] check parted version Failed!"
346                                 return 0
347                         return ver
348
349                 disk_size = self.diskSize()
350
351                 if disk_size > 2.2 * 1000 * 1000: # if 2.2 TB
352                         setAlign = ""
353                         partedVer = CheckPartedVer()
354                         if partedVer >= 2.1: # align option is supported in version 2.1 or later
355                                 setAlign = "--align optimal"
356                         cmd = 'parted %s %s --script mklabel gpt mkpart disk ext2 0%% 100%%' % ( setAlign, self.disk_path )
357
358                 else:
359                         sfdiskVer = CheckSfdiskVer()
360                         if sfdiskVer <= 2.21:
361                                 cmd = 'printf "8,\n;0,0\n;0,0\n;0,0\ny\n" | sfdisk -f -uS ' + self.disk_path
362                         else:
363                                 cmd = 'printf "8,\nquit\nY\n" | sfdisk -f -uS ' + self.disk_path
364
365                 print "CMD : ", cmd
366                 res = system(cmd)
367
368                 if not self.checkPartionPath(self.partitionPath("1")):
369                         print "no exist : ", self.partitionPath("1")
370                         return 1
371                 return (res >> 8)
372
373         def mkfs(self):
374                 cmd = "mkfs.ext3 "
375                 if self.diskSize() > 4 * 1024:
376                         cmd += "-T largefile "
377                 cmd += "-m0 -O dir_index " + self.partitionPath("1")
378                 print "CMD : ", cmd
379                 res = system(cmd)
380                 return (res >> 8)
381
382         def mount(self):
383                 try:
384                         fstab = open("/etc/fstab")
385                 except IOError:
386                         return -1
387
388                 lines = fstab.readlines()
389                 fstab.close()
390
391                 res = -1
392                 mount_point = None
393                 for line in lines:
394                         parts = line.strip().split(" ")
395                         real_path = path.realpath(parts[0])
396                         if not real_path[-1].isdigit():
397                                 continue
398                         try:
399                                 if MajorMinor(real_path) == MajorMinor(self.partitionPath(real_path[-1])):
400                                         mount_point = parts[0]
401                                         break
402                         except OSError:
403                                 pass
404
405                 if mount_point is None:
406                         return 0
407
408                 cmd = "mount -t ext3 " + mount_point
409                 print "CMD : ", cmd
410                 res = system(cmd)
411
412                 if (res >> 8) != 0:
413                         return -3
414
415                 if self.createMovieFolder() != 0:
416                         return -4
417
418                 return 0
419
420         def createMovieFolder(self):
421                 try:
422                         if not fileExists("/hdd", 0):
423                                 print "not found /hdd"
424                                 system("ln -s /media/hdd /hdd")
425         
426                         makedirs(resolveFilename(SCOPE_HDD))
427                 except OSError:
428                         return -1
429                 return 0
430
431         def fsck(self):
432                 # We autocorrect any failures
433                 # TODO: we could check if the fs is actually ext3
434                 cmd = "fsck.ext3 -f -p " + self.partitionPath("1")
435                 res = system(cmd)
436                 return (res >> 8)
437
438         def killPartition(self, n):
439                 part = self.partitionPath(n)
440
441                 if access(part, 0):
442                         cmd = 'dd bs=512 count=3 if=/dev/zero of=' + part
443                         print "CMD : ", cmd
444                         res = system(cmd)
445                 else:
446                         res = 0
447
448                 return (res >> 8)
449
450         errorList = [ _("Everything is fine"), _("Creating partition failed"), _("Mkfs failed"), _("Mount failed"), _("Create movie folder failed"), _("Fsck failed"), _("Please Reboot"), _("Filesystem contains uncorrectable errors"), _("Unmount failed"), _("partx failed")]
451
452         def initialize(self):
453                 enableUdevEvent(False)
454                 self.unmount()
455
456                 # Udev tries to mount the partition immediately if there is an
457                 # old filesystem on it when fdisk reloads the partition table.
458                 # To prevent that, we overwrite the first 3 sectors of the
459                 # partition, if the partition existed before. That's enough for
460                 # ext3 at least.
461                 self.killPartition("1")
462
463                 if self.updatePartition() != 0:
464                         res = -9
465
466                 elif self.createPartition() != 0:
467                         res = -1
468
469                 elif self.updatePartition() != 0:
470                         res = -9
471
472                 elif self.mkfs() != 0:
473                         res = -2
474
475                 else:
476                         res = self.mount()
477
478                 enableUdevEvent(True)
479                 return res
480
481         def check(self):
482                 self.unmount()
483
484                 res = self.fsck()
485                 if res & 2 == 2:
486                         return -6
487
488                 if res & 4 == 4:
489                         return -7
490
491                 if res != 0 and res != 1:
492                         # A sum containing 1 will also include a failure
493                         return -5
494
495                 if self.mount() != 0:
496                         return -3
497
498                 return 0
499
500         def getDeviceDir(self):
501                 return self.dev_path
502
503         def getDeviceName(self):
504                 return self.disk_path
505
506         # the HDD idle poll daemon.
507         # as some harddrives have a buggy standby timer, we are doing this by hand here.
508         # first, we disable the hardware timer. then, we check every now and then if
509         # any access has been made to the disc. If there has been no access over a specifed time,
510         # we set the hdd into standby.
511         def readStats(self):
512                 try:
513                         l = open("/sys/block/%s/stat" % self.device).read()
514                 except IOError:
515                         return -1,-1
516                 (nr_read, _, _, _, nr_write) = l.split()[:5]
517                 return int(nr_read), int(nr_write)
518
519         def startIdle(self):
520                 self.last_access = time.time()
521                 self.last_stat = 0
522                 self.is_sleeping = False
523                 from enigma import eTimer
524
525                 # disable HDD standby timer
526                 Console().ePopen(("hdparm", "hdparm", "-S0", self.disk_path))
527                 self.timer = eTimer()
528                 self.timer.callback.append(self.runIdle)
529                 self.idle_running = True
530                 self.setIdleTime(self.max_idle_time) # kick the idle polling loop
531
532         def runIdle(self):
533                 if not self.max_idle_time:
534                         return
535                 t = time.time()
536
537                 idle_time = t - self.last_access
538
539                 stats = self.readStats()
540
541                 if stats == -1:
542                         self.setIdleTime(0)
543                         return
544                 print "nr_read", stats[0], "nr_write", stats[1]
545                 l = sum(stats)
546                 print "sum", l, "prev_sum", self.last_stat
547
548                 if l != self.last_stat and l >= 0: # access
549                         print "hdd was accessed since previous check!"
550                         self.last_stat = l
551                         self.last_access = t
552                         idle_time = 0
553                         self.is_sleeping = False
554                 else:
555                         print "hdd IDLE!"
556
557                 print "[IDLE]", idle_time, self.max_idle_time, self.is_sleeping
558                 if idle_time >= self.max_idle_time and not self.is_sleeping:
559                         self.setSleep()
560                         self.is_sleeping = True
561
562         def setSleep(self):
563                 Console().ePopen(("hdparm", "hdparm", "-y", self.disk_path))
564
565         def setIdleTime(self, idle):
566                 self.max_idle_time = idle
567                 if self.idle_running:
568                         if not idle:
569                                 self.timer.stop()
570                         else:
571                                 self.timer.start(idle * 100, False)  # poll 10 times per period.
572
573         def isSleeping(self):
574                 return self.is_sleeping
575
576 class Partition:
577         def __init__(self, mountpoint, device = None, description = "", force_mounted = False):
578                 self.mountpoint = mountpoint
579                 self.description = description
580                 self.force_mounted = force_mounted
581                 self.is_hotplug = force_mounted # so far; this might change.
582                 self.device = device
583
584         def stat(self):
585                 return statvfs(self.mountpoint)
586
587         def free(self):
588                 try:
589                         s = self.stat()
590                         return s.f_bavail * s.f_bsize
591                 except OSError:
592                         return None
593
594         def total(self):
595                 try:
596                         s = self.stat()
597                         return s.f_blocks * s.f_bsize
598                 except OSError:
599                         return None
600
601         def mounted(self):
602                 # THANK YOU PYTHON FOR STRIPPING AWAY f_fsid.
603                 # TODO: can os.path.ismount be used?
604                 if self.force_mounted:
605                         return True
606
607                 try:
608                         mounts = open("/proc/mounts")
609                 except IOError:
610                         return False
611
612                 lines = mounts.readlines()
613                 mounts.close()
614
615                 for line in lines:
616                         if line.split(' ')[1] == self.mountpoint:
617                                 return True
618                 return False
619
620 DEVICEDB_SR = \
621         {"dm8000":
622                 {
623                         "/devices/pci0000:01/0000:01:00.0/host0/target0:0:0/0:0:0:0": _("DVD Drive"),
624                         "/devices/pci0000:01/0000:01:00.0/host1/target1:0:0/1:0:0:0": _("DVD Drive"),
625                         "/devices/platform/brcm-ehci-1.1/usb2/2-1/2-1:1.0/host3/target3:0:0/3:0:0:0": _("DVD Drive"),
626                 },
627         "dm800":
628         {
629         },
630         "dm7025":
631         {
632         }
633         }
634
635 DEVICEDB = \
636         {"dm8000":
637                 {
638                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.1/1-1.1:1.0": _("Front USB Slot"),
639                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.2/1-1.2:1.0": _("Back, upper USB Slot"),
640                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.3/1-1.3:1.0": _("Back, lower USB Slot"),
641                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.1/1-1.1:1.0": _("Front USB Slot"),
642                         "/devices/platform/brcm-ehci-1.1/usb2/2-1/2-1:1.0/": _("Internal USB Slot"),
643                         "/devices/platform/brcm-ohci-1.1/usb4/4-1/4-1:1.0/": _("Internal USB Slot"),
644                 },
645         "dm800":
646         {
647                 "/devices/platform/brcm-ehci.0/usb1/1-2/1-2:1.0": "Upper USB Slot",
648                 "/devices/platform/brcm-ehci.0/usb1/1-1/1-1:1.0": "Lower USB Slot",
649         },
650         "dm7025":
651         {
652                 "/devices/pci0000:00/0000:00:14.1/ide1/1.0": "CF Card Slot", #hdc
653                 "/devices/pci0000:00/0000:00:14.1/ide0/0.0": "Internal Harddisk"
654         }
655         }
656
657 class HarddiskManager:
658         def __init__(self):
659                 self.hdd = [ ]
660                 self.cd = ""
661                 self.partitions = [ ]
662                 self.devices_scanned_on_init = [ ]
663
664                 self.on_partition_list_change = CList()
665
666                 self.enumerateBlockDevices()
667
668                 # currently, this is just an enumeration of what's possible,
669                 # this probably has to be changed to support automount stuff.
670                 # still, if stuff is mounted into the correct mountpoints by
671                 # external tools, everything is fine (until somebody inserts
672                 # a second usb stick.)
673                 p = [
674                                         ("/media/hdd", _("Harddisk")),
675                                         ("/media/card", _("Card")),
676                                         ("/media/cf", _("Compact Flash")),
677                                         ("/media/mmc1", _("MMC Card")),
678                                         ("/media/net", _("Network Mount")),
679                                         ("/media/ram", _("Ram Disk")),
680                                         ("/media/usb", _("USB Stick")),
681                                         ("/", _("Internal Flash"))
682                                 ]
683
684                 self.partitions.extend([ Partition(mountpoint = x[0], description = x[1]) for x in p ])
685
686         def getBlockDevInfo(self, blockdev):
687                 devpath = "/sys/block/" + blockdev
688                 error = False
689                 removable = False
690                 blacklisted = False
691                 is_cdrom = False
692                 partitions = []
693                 try:
694                         removable = bool(int(readFile(devpath + "/removable")))
695                         dev = int(readFile(devpath + "/dev").split(':')[0])
696                         if dev in (7, 31, 179): # loop, mtdblock, mmcblock
697                                 blacklisted = True
698                         if blockdev[0:2] == 'sr':
699                                 is_cdrom = True
700                         if blockdev[0:2] == 'hd':
701                                 try:
702                                         media = readFile("/proc/ide/%s/media" % blockdev)
703                                         if "cdrom" in media:
704                                                 is_cdrom = True
705                                 except IOError:
706                                         error = True
707                         # check for partitions
708                         if not is_cdrom:
709                                 for partition in listdir(devpath):
710                                         if partition[0:len(blockdev)] != blockdev:
711                                                 continue
712                                         partitions.append(partition)
713                         else:
714                                 self.cd = blockdev
715                 except IOError:
716                         error = True
717                 # check for medium
718                 medium_found = True
719                 try:
720                         open("/dev/" + blockdev).close()
721                 except IOError, err:
722                         if err.errno == 159: # no medium present
723                                 medium_found = False
724
725                 return error, blacklisted, removable, is_cdrom, partitions, medium_found
726
727         def enumerateBlockDevices(self):
728                 print "enumerating block devices..."
729                 for blockdev in listdir("/sys/block"):
730                         error, blacklisted, removable, is_cdrom, partitions, medium_found = self.addHotplugPartition(blockdev)
731                         if not error and not blacklisted:
732                                 if medium_found:
733                                         for part in partitions:
734                                                 self.addHotplugPartition(part)
735                                 self.devices_scanned_on_init.append((blockdev, removable, is_cdrom, medium_found))
736
737         def getAutofsMountpoint(self, device):
738                 return "/autofs/%s/" % (device)
739
740
741         def is_hard_mounted(self, device):
742                 mounts = file('/proc/mounts').read().split('\n')
743                 for x in mounts:
744                         if x.find('/autofs') == -1 and x.find(device) != -1:
745                                 return True
746                 return False
747
748         def getMountpoint(self, device):
749                 dev = "/dev/%s" % device
750                 for item in getProcMounts():
751                         if item[0] == dev and item[1].find('/autofs') == -1:
752                                 return item[1]
753                 return None
754
755         def addHotplugPartition(self, device, physdev = None):
756                 if not physdev:
757                         dev, part = self.splitDeviceName(device)
758                         try:
759                                 physdev = path.realpath('/sys/block/' + dev + '/device')[4:]
760                         except OSError:
761                                 physdev = dev
762                                 print "couldn't determine blockdev physdev for device", device
763
764                 error, blacklisted, removable, is_cdrom, partitions, medium_found = self.getBlockDevInfo(device)
765                 print "found block device '%s':" % device,
766
767                 if blacklisted:
768                         print "blacklisted"
769                 else:
770                         if error:
771                                 print "error querying properties"
772                         elif not medium_found:
773                                 print "no medium"
774                         else:
775                                 print "ok, removable=%s, cdrom=%s, partitions=%s" % (removable, is_cdrom, partitions)
776
777                         l = len(device)
778                         if l:
779                                 # see if this is a harddrive
780                                 if not device[l-1].isdigit() and not removable and not is_cdrom:
781                                         self.hdd.append(Harddisk(device))
782                                         self.hdd.sort()
783                                         SystemInfo["Harddisk"] = len(self.hdd) > 0
784
785                                 if not removable or medium_found:
786                                         # device is the device name, without /dev
787                                         # physdev is the physical device path, which we (might) use to determine the userfriendly name
788                                         description = self.getUserfriendlyDeviceName(device, physdev)
789                                         p = Partition(mountpoint = self.getAutofsMountpoint(device), description = description, force_mounted = True, device = device)
790                                         self.partitions.append(p)
791                                         self.on_partition_list_change("add", p)
792
793                 return error, blacklisted, removable, is_cdrom, partitions, medium_found
794
795         def removeHotplugPartition(self, device):
796                 for x in self.partitions[:]:
797                         if x.device == device:
798                                 self.partitions.remove(x)
799                                 if x.mountpoint:
800                                         self.on_partition_list_change("remove", x)
801                 l = len(device)
802                 if l and not device[l-1].isdigit():
803                         for hdd in self.hdd:
804                                 if hdd.device == device:
805                                         hdd.stop()
806                                         self.hdd.remove(hdd)
807                                         break
808                         SystemInfo["Harddisk"] = len(self.hdd) > 0
809
810         def HDDCount(self):
811                 return len(self.hdd)
812
813         def HDDList(self):
814                 list = [ ]
815                 for hd in self.hdd:
816                         if hd.model() == -1:
817                                 continue
818                         hdd = hd.model() + " - " + hd.bus()
819                         cap = hd.capacity()
820                         if cap != "":
821                                 hdd += " (" + cap + ")"
822                         list.append((hdd, hd))
823                 return list
824
825         def getCD(self):
826                 return self.cd
827
828         def getMountedPartitions(self, onlyhotplug = False):
829                 parts = [x for x in self.partitions if (x.is_hotplug or not onlyhotplug) and x.mounted() and x.mountpoint]
830                 devs = set([x.device for x in parts])
831                 for devname in devs.copy():
832                         if not devname:
833                                 continue
834                         dev, part = self.splitDeviceName(devname)
835                         if part and dev in devs: # if this is a partition and we still have the wholedisk, remove wholedisk
836                                 devs.remove(dev)
837
838                 # remove duplicate device
839                 no_force_mounted_list = [x.mountpoint for x in self.partitions if not x.force_mounted]
840                 for x in parts:
841                         if x.force_mounted:
842                                 mp = self.getMountpoint(x.device)
843                                 if mp and mp in no_force_mounted_list:
844                                         devs.remove(x.device)
845
846                 # return all devices which are not removed due to being a wholedisk when a partition exists
847                 return [x for x in parts if not x.device or x.device in devs]
848
849         def getLabelFromDevName(self, dev):
850                 data = None
851                 try:
852                         data = os.popen("e2label %s" % dev).read().strip()
853                         if not data:
854                                 data = None
855                 except:
856                         data = None
857
858                 return data
859
860         def splitDeviceName(self, devname):
861                 # this works for: sdaX, hdaX, sr0 (which is in fact dev="sr0", part=""). It doesn't work for other names like mtdblock3, but they are blacklisted anyway.
862                 dev = devname[:3]
863                 part = devname[3:]
864                 for p in part:
865                         if not p.isdigit():
866                                 return devname, 0
867                 return dev, part and int(part) or 0
868
869         def getUserfriendlyDeviceName(self, dev, phys):
870                 label = self.getLabelFromDevName("/dev/" + dev)
871                 dev, part = self.splitDeviceName(dev)
872                 description = "External Storage %s" % dev
873                 have_model_descr = False
874
875                 if label:
876                         description = label
877                         have_model_descr = True
878                 else:
879                         try:
880                                 description = readFile("/sys" + phys + "/model")
881                                 have_model_descr = True
882                         except IOError, s:
883                                 print "couldn't read model: ", s
884                 from Tools.HardwareInfo import HardwareInfo
885                 if dev.find('sr') == 0 and dev[2].isdigit():
886                         devicedb = DEVICEDB_SR
887                 else:
888                         devicedb = DEVICEDB
889                 for physdevprefix, pdescription in devicedb.get(HardwareInfo().device_name,{}).items():
890                         if phys.startswith(physdevprefix):
891                                 if have_model_descr:
892                                         description = pdescription + ' - ' + description
893                                 else:
894                                         description = pdescription
895                 # not wholedisk and not partition 1
896                 if part and part != 1:
897                         description += " (Partition %d)" % part
898                 return description
899
900         def addMountedPartition(self, device, desc):
901                 already_mounted = False
902                 for x in self.partitions[:]:
903                         if x.mountpoint == device:
904                                 already_mounted = True
905                 if not already_mounted:
906                         self.partitions.append(Partition(mountpoint = device, description = desc))
907
908         def removeMountedPartition(self, mountpoint):
909                 for x in self.partitions[:]:
910                         if x.mountpoint == mountpoint:
911                                 self.partitions.remove(x)
912                                 self.on_partition_list_change("remove", x)
913
914 harddiskmanager = HarddiskManager()