enigma2 (20120210 rel32 -> 20120214 rel32)
[enigma2.git] / usr / lib / enigma2 / python / Components / Harddisk.py
1 from os import system, listdir, statvfs, popen, makedirs, stat, major, minor, path, access, readlink, symlink, rmdir, unlink, rename
2 from Tools.Directories import SCOPE_HDD, resolveFilename
3 from Tools.BoundFunction import boundFunction
4 from Tools.CList import CList
5 from SystemInfo import SystemInfo
6 import time
7 import re, shutil
8 from Components.Console import Console
9 from config import config, configfile, ConfigYesNo, ConfigText, ConfigSubDict, ConfigSubsection, ConfigBoolean
10
11 def MajorMinor(path):
12         rdev = stat(path).st_rdev
13         return (major(rdev),minor(rdev))
14
15 def readFile(filename):
16         file = open(filename)
17         data = file.read().strip()
18         file.close()
19         return data
20
21 DEVTYPE_UDEV = 0
22 DEVTYPE_DEVFS = 1
23
24 class Harddisk:
25         def __init__(self, device, removable = False):
26                 self.device = device
27                 self.isRemovable = removable
28
29                 if access("/dev/.udev", 0):
30                         self.type = DEVTYPE_UDEV
31                 elif access("/dev/.devfsd", 0):
32                         self.type = DEVTYPE_DEVFS
33                 else:
34                         print "Unable to determine structure of /dev"
35
36                 self.is_sleeping = False
37                 self.max_idle_time = 0
38                 self.idle_running = False
39                 self.timer = None
40
41                 self.dev_path = ''
42                 self.disk_path = ''
43                 self.phys_path = path.realpath(self.sysfsPath('device'))
44
45                 if self.type == DEVTYPE_UDEV:
46                         self.dev_path = '/dev/' + self.device
47                         self.disk_path = self.dev_path
48
49                 elif self.type == DEVTYPE_DEVFS:
50                         tmp = readFile(self.sysfsPath('dev')).split(':')
51                         s_major = int(tmp[0])
52                         s_minor = int(tmp[1])
53                         for disc in listdir("/dev/discs"):
54                                 dev_path = path.realpath('/dev/discs/' + disc)
55                                 disk_path = dev_path + '/disc'
56                                 try:
57                                         rdev = stat(disk_path).st_rdev
58                                 except OSError:
59                                         continue
60                                 if s_major == major(rdev) and s_minor == minor(rdev):
61                                         self.dev_path = dev_path
62                                         self.disk_path = disk_path
63                                         break
64
65                 print "new Harddisk", self.device, '->', self.dev_path, '->', self.disk_path
66                 if not self.isRemovable:
67                         self.startIdle()
68
69         def __lt__(self, ob):
70                 return self.device < ob.device
71
72         def partitionPath(self, n):
73                 if self.type == DEVTYPE_UDEV:
74                         return self.dev_path + n
75                 elif self.type == DEVTYPE_DEVFS:
76                         return self.dev_path + '/part' + n
77
78         def sysfsPath(self, filename):
79                 return path.realpath('/sys/block/' + self.device + '/' + filename)
80
81         def stop(self):
82                 if self.timer:
83                         self.timer.stop()
84                         self.timer.callback.remove(self.runIdle)
85
86         def bus(self):
87                 # CF (7025 specific)
88                 if self.type == DEVTYPE_UDEV:
89                         ide_cf = False  # FIXME
90                 elif self.type == DEVTYPE_DEVFS:
91                         ide_cf = self.device[:2] == "hd" and "host0" not in self.dev_path
92
93                 internal = "pci" in self.phys_path
94
95                 if ide_cf:
96                         ret = "External (CF)"
97                 elif internal:
98                         ret = "Internal"
99                 else:
100                         ret = "External"
101                 return ret
102
103         def bus_type(self):
104                 # CF (7025 specific)
105                 if self.type == DEVTYPE_UDEV:
106                         ide_cf = False  # FIXME
107                 elif self.type == DEVTYPE_DEVFS:
108                         ide_cf = self.device[:2] == "hd" and "host0" not in self.dev_path
109                         
110                 sata = "pci" in self.phys_path
111                 sata_desc = self.bus_description()
112
113                 if ide_cf:
114                         ret = "IDE"
115                 elif sata:
116                         if sata_desc is not None:
117                                 ret = sata_desc
118                         else:
119                                 ret = "SATA"
120                 else:
121                         ret = "USB"
122                 return ret
123
124         def bus_description(self):
125                 phys = self.phys_path[4:]
126                 from Tools.HardwareInfo import HardwareInfo
127                 if self.device.find('sr') == 0 and self.device[2].isdigit():
128                         devicedb = DEVICEDB_SR
129                 else:
130                         devicedb = DEVICEDB
131
132                 for physdevprefix, pdescription in devicedb.get(HardwareInfo().device_name,{}).items():
133                         if phys.startswith(physdevprefix):
134                                 return pdescription
135                 if phys.find('pci') != -1:
136                         return "SATA"
137                 elif phys.find('usb') != -1:
138                         return "USB"
139                 return "External Storage"
140
141         def diskSize(self):
142                 try:
143                         line = readFile(self.sysfsPath('size'))
144                         cap = int(line)
145                 except:
146                         return 0;
147                 return cap / 1000 * 512 / 1000
148
149         def capacity(self):
150                 cap = self.diskSize()
151                 if cap == 0:
152                         return ""
153                 return "%d.%03d GB" % (cap/1000, cap%1000)
154
155         def model(self, model_only = False, vendor_only = False):
156                 if self.device[:2] == "hd":
157                         return readFile('/proc/ide/' + self.device + '/model')
158                 elif self.device[:2] == "sd":
159                         try:
160                                 vendor = readFile(self.sysfsPath('device/vendor'))
161                                 model = readFile(self.sysfsPath('device/model'))
162                         except:
163                                 vendor = ""
164                                 model = ""
165                                 
166                         if vendor_only:
167                                 return vendor
168                         if model_only:
169                                 return model
170                         return vendor + '-' + model
171                 else:
172                         assert False, "no hdX or sdX"
173
174         def free(self):
175                 try:
176                         mounts = open("/proc/mounts")
177                 except IOError:
178                         return -1
179
180                 lines = mounts.readlines()
181                 mounts.close()
182
183                 for line in lines:
184                         parts = line.strip().split(" ")
185                         real_path = path.realpath(parts[0])
186                         if not real_path[-1].isdigit():
187                                 continue
188                         try:
189                                 if MajorMinor(real_path) == MajorMinor(self.partitionPath(real_path[-1])):
190                                         stat = statvfs(parts[1])
191                                         return stat.f_bfree/1000 * stat.f_bsize/1000
192                         except OSError:
193                                 pass
194                 return -1
195
196         def numPartitions(self):
197                 numPart = -1
198                 if self.type == DEVTYPE_UDEV:
199                         try:
200                                 devdir = listdir('/dev')
201                         except OSError:
202                                 return -1
203                         for filename in devdir:
204                                 if filename.startswith(self.device):
205                                         numPart += 1
206
207                 elif self.type == DEVTYPE_DEVFS:
208                         try:
209                                 idedir = listdir(self.dev_path)
210                         except OSError:
211                                 return -1
212                         for filename in idedir:
213                                 if filename.startswith("disc"):
214                                         numPart += 1
215                                 if filename.startswith("part"):
216                                         numPart += 1
217                 return numPart
218
219         def unmount(self, numpart = None):
220                 try:
221                         mounts = open("/proc/mounts")
222                 except IOError:
223                         return -1
224
225                 lines = mounts.readlines()
226                 mounts.close()
227
228                 cmd = "umount"
229                 for line in lines:
230                         parts = line.strip().split(" ")
231                         real_path = path.realpath(parts[0])
232                         if not real_path[-1].isdigit():
233                                 if numpart is not None and numpart == 0:
234                                         if real_path.startswith("/dev/sd"):
235                                                 uuid = harddiskmanager.getPartitionUUID(self.device)
236                                                 if uuid is not None:
237                                                         try:
238                                                                 if MajorMinor(real_path) == MajorMinor(self.dev_path):
239                                                                         cmd = ' ' . join([cmd, parts[1]])
240                                                                         break
241                                                         except OSError:
242                                                                 pass
243                         try:
244                                 if MajorMinor(real_path) == MajorMinor(self.partitionPath(real_path[-1])):
245                                         cmd = ' ' . join([cmd, parts[1]])
246                                         break
247                         except OSError:
248                                 pass
249                 res = system(cmd)
250                 if cmd == "umount": # nothing found to unmount
251                         res = 0
252                 return (res >> 8)
253
254         def createPartition(self, numpart):
255                 #gpt partitiontype support currently disabled.
256                 """devicename = self.device + "1"
257                 if numpart is not None:
258                         if numpart == 0:
259                                 devicename = self.device
260                         if numpart >= 1:
261                                 devicename = self.device + (str(numpart))
262                 type, sys, size, sizeg = harddiskmanager.getFdiskInfo(devicename)"""
263
264                 cmd = 'printf "8,\n;0,0\n;0,0\n;0,0\ny\n" | sfdisk -f -uS ' + self.disk_path
265
266                 #if sys is not None and "GPT" in sys:
267                 #       cmd = 'printf "34,,'+type+'\n;0,0\n;0,0\n;0,0\ny\n" | sfdisk -f -uS ' + self.disk_path
268
269                 res = system(cmd)
270                 return (res >> 8)
271
272         def mkfs(self):
273                 cmd = "mkfs.ext3 "
274                 if self.diskSize() > 4 * 1024:
275                         cmd += "-T largefile "
276                 cmd += "-m0 -O dir_index " + self.partitionPath("1")
277                 res = system(cmd)
278                 return (res >> 8)
279
280         def mount(self):
281                 try:
282                         fstab = open("/etc/fstab")
283                 except IOError:
284                         return -1
285
286                 lines = fstab.readlines()
287                 fstab.close()
288
289                 res = -1
290                 for line in lines:
291                         parts = line.strip().split(" ")
292                         real_path = path.realpath(parts[0])
293                         if not real_path[-1].isdigit():
294                                 continue
295                         try:
296                                 if MajorMinor(real_path) == MajorMinor(self.partitionPath(real_path[-1])):
297                                         cmd = "mount -t auto " + parts[0]
298                                         res = system(cmd)
299                                         break
300                         except OSError:
301                                 pass
302                 return (res >> 8)
303
304         def createMovieFolder(self):
305                 try:
306                         makedirs(resolveFilename(SCOPE_HDD))
307                 except OSError:
308                         return -1
309                 return 0
310
311         def fsck(self, numpart):
312                 # We autocorrect any failures and check if the fs is actually one we can check (currently ext2/ext3)
313                 partitionPath = self.partitionPath("1")
314                 if numpart is not None:
315                         if numpart == 0:
316                                 partitionPath = self.dev_path
317                         if numpart >= 1:
318                                 partitionPath = self.partitionPath(str(numpart))
319
320                 partitionType = harddiskmanager.getBlkidPartitionType(partitionPath)                            
321
322                 res = -1
323                 if access(partitionPath, 0):
324                         if partitionType is not None and partitionType in ("ext2", "ext3"):
325                                 cmd = "fsck." + partitionType + " -f -p " + partitionPath
326                                 res = system(cmd)
327                 return (res >> 8)
328
329         def killPartition(self, n):
330                 part = self.partitionPath("1")
331                 if n is not None:
332                         if n == 0:
333                                 part = self.disk_path
334                         if n >= 1:
335                                 part = self.partitionPath(str(n))
336                 if access(part, 0):
337                         #cmd = 'dd bs=512 count=3 if=/dev/zero of=' + part
338                         cmd = 'dd bs=4k count=3 if=/dev/zero of=' + part
339                         res = system(cmd)
340                 else:
341                         res = 0
342                 return (res >> 8)
343
344         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")]
345
346         def initialize(self, isFstabMounted = False, numpart = None):
347                 if self.unmount(numpart) != 0:
348                         return -8
349                 # Udev tries to mount the partition immediately if there is an
350                 # old filesystem on it when fdisk reloads the partition table.
351                 # To prevent that, we overwrite the first sectors of the
352                 # partitions, if the partition existed before. This should work
353                 # for ext2/ext3 and also for GPT/EFI partitions.
354                 if numpart is not None:
355                         for p in range(numpart+1):
356                                 self.killPartition(p)
357                 else:
358                         self.killPartition(1)
359
360                 if self.createPartition(numpart) != 0:
361                         return -1
362                 
363                 if self.mkfs() != 0:
364                         return -2
365
366                 if isFstabMounted:
367                         if self.mount() != 0:
368                                 return -3
369                         if self.createMovieFolder() != 0:
370                                 return -4
371
372                 return 0
373
374         def check(self, isFstabMounted = False, numpart = None):
375                 
376                 if self.unmount(numpart) != 0:
377                         return -8
378
379                 res = self.fsck(numpart)
380                 if res & 2 == 2:
381                         return -6
382
383                 if res & 4 == 4:
384                         return -7
385
386                 if res != 0 and res != 1:
387                         # A sum containing 1 will also include a failure
388                         return -5
389                 
390                 if isFstabMounted:
391                         if self.mount() != 0:
392                                 return -3
393
394                 return 0
395
396         def getDeviceDir(self):
397                 return self.dev_path
398
399         def getDeviceName(self):
400                 return self.disk_path
401
402         # the HDD idle poll daemon.
403         # as some harddrives have a buggy standby timer, we are doing this by hand here.
404         # first, we disable the hardware timer. then, we check every now and then if
405         # any access has been made to the disc. If there has been no access over a specifed time,
406         # we set the hdd into standby.
407         def readStats(self):
408                 try:
409                         l = open("/sys/block/%s/stat" % self.device).read()
410                 except IOError:
411                         return -1,-1
412                 (nr_read, _, _, _, nr_write) = l.split()[:5]
413                 return int(nr_read), int(nr_write)
414
415         def startIdle(self):
416                 self.last_access = time.time()
417                 self.last_stat = 0
418                 from enigma import eTimer
419
420                 # disable HDD standby timer
421                 if self.bus() == "External":
422                         Console().ePopen(("sdparm", "sdparm", "--set=SCT=0", self.disk_path))
423                 else:
424                         Console().ePopen(("hdparm", "hdparm", "-S0", self.disk_path))
425
426                 self.timer = eTimer()
427                 self.timer.callback.append(self.runIdle)
428                 self.idle_running = True
429                 self.setIdleTime(self.max_idle_time) # kick the idle polling loop
430
431         def runIdle(self):
432                 if not self.max_idle_time:
433                         return
434                 t = time.time()
435
436                 idle_time = t - self.last_access
437
438                 stats = self.readStats()
439                 print "nr_read", stats[0], "nr_write", stats[1]
440                 l = sum(stats)
441                 print "sum", l, "prev_sum", self.last_stat
442
443                 if l != self.last_stat and l >= 0: # access
444                         print "hdd was accessed since previous check!"
445                         self.last_stat = l
446                         self.last_access = t
447                         idle_time = 0
448                         self.is_sleeping = False
449                 else:
450                         print "hdd IDLE!"
451
452                 print "[IDLE]", idle_time, self.max_idle_time, self.is_sleeping
453                 if idle_time >= self.max_idle_time and not self.is_sleeping:
454                         self.setSleep()
455
456         def setSleep(self):
457                 if self.bus() == "External":
458                         Console().ePopen(("sdparm", "sdparm", "--command=stop", self.disk_path))
459                 else:
460                         Console().ePopen(("hdparm", "hdparm", "-y", self.disk_path))
461                 self.is_sleeping = True
462
463         def setIdleTime(self, idle):
464                 self.max_idle_time = idle
465                 if self.idle_running:
466                         if not idle:
467                                 self.timer.stop()
468                         else:
469                                 self.timer.start(idle * 100, False)  # poll 10 times per period.
470
471         def isSleeping(self):
472                 return self.is_sleeping
473
474 class Partition:
475         def __init__(self, mountpoint, device = None, description = "", force_mounted = False):
476                 self.mountpoint = mountpoint
477                 self.description = description
478                 self.force_mounted = force_mounted
479                 self.is_hotplug = force_mounted # so far; this might change.
480                 self.device = device
481                 self.disc_path = None
482                 self.uuid = None
483
484         def stat(self):
485                 return statvfs(self.mountpoint)
486
487         def free(self):
488                 try:
489                         s = self.stat()
490                         return s.f_bavail * s.f_bsize
491                 except OSError:
492                         return None
493
494         def total(self):
495                 try:
496                         s = self.stat()
497                         return s.f_blocks * s.f_bsize
498                 except OSError:
499                         return None
500
501         def mounted(self):
502                 # THANK YOU PYTHON FOR STRIPPING AWAY f_fsid.
503                 # TODO: can os.path.ismount be used?
504                 if self.force_mounted:
505                         return True
506
507                 try:
508                         mounts = open("/proc/mounts")
509                 except IOError:
510                         return False
511
512                 lines = mounts.readlines()
513                 mounts.close()
514
515                 for line in lines:
516                         if line.split(' ')[1] == self.mountpoint:
517                                 return True
518                 return False
519
520
521 DEVICEDB_SR = \
522         {"dm8000":
523                 {
524                         "/devices/pci0000:01/0000:01:00.0/host0/target0:0:0/0:0:0:0": _("DVD Drive"),
525                         "/devices/pci0000:01/0000:01:00.0/host1/target1:0:0/1:0:0:0": _("DVD Drive"),
526                         "/devices/platform/brcm-ehci-1.1/usb2/2-1/2-1:1.0/host3/target3:0:0/3:0:0:0": _("DVD Drive"),
527                 },
528         "dm800":
529         {
530         },
531         "dm7025":
532         {
533         }
534         }
535
536 DEVICEDB = \
537         {"dm8000":
538                 {
539                         "/devices/pci0000:01/0000:01:00.0/host1/target1:0:0/1:0:0:0": _("SATA"),
540                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.1/1-1.1:1.0": _("Front USB"),
541                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.1/1-1.1.": _("Front USB"),
542                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.2/1-1.2:1.0": _("Back, upper USB"),
543                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.2/1-1.2.": _("Back, upper USB"),
544                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.3/1-1.3:1.0": _("Back, lower USB"),
545                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.3/1-1.3.": _("Back, lower USB"),
546                         "/devices/platform/brcm-ehci-1.1/usb2/2-1/2-1:1.0/": _("Internal USB"),
547                         "/devices/platform/brcm-ohci-1.1/usb4/4-1/4-1:1.0/": _("Internal USB"),
548                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.4/1-1.4.": _("Internal USB"),
549                 },
550         "dm7020hd":
551         {
552                 "/devices/pci0000:01/0000:01:00.0/host0/target0:0:0/0:0:0:0": _("SATA"),
553                 "/devices/pci0000:01/0000:01:00.0/host1/target1:0:0/1:0:0:0": _("eSATA"),
554                 "/devices/platform/brcm-ehci-1.1/usb2/2-1/2-1:1.0": _("Front USB"),
555                 "/devices/platform/brcm-ehci-1.1/usb2/2-1/2-1.": _("Front USB"),
556                 "/devices/platform/brcm-ehci.0/usb1/1-2/1-2:1.0": _("Back, upper USB"),
557                 "/devices/platform/brcm-ehci.0/usb1/1-2/1-2.": _("Back, upper USB"),
558                 "/devices/platform/brcm-ehci.0/usb1/1-1/1-1:1.0": _("Back, lower USB"),
559                 "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.": _("Back, lower USB"),
560         },
561         "dm800":
562         {
563                 "/devices/pci0000:01/0000:01:00.0/host0/target0:0:0/0:0:0:0": _("SATA"),
564                 "/devices/platform/brcm-ehci.0/usb1/1-2/1-2:1.0": _("Upper USB"),
565                 "/devices/platform/brcm-ehci.0/usb1/1-1/1-1:1.0": _("Lower USB"),
566         },
567         "dm800se":
568         {
569                 "/devices/pci0000:01/0000:01:00.0/host0/target0:0:0/0:0:0:0": _("SATA"),
570                 "/devices/pci0000:01/0000:01:00.0/host1/target1:0:0/1:0:0:0": _("eSATA"),
571                 "/devices/platform/brcm-ehci.0/usb1/1-2/1-2:1.0": _("Upper USB"),
572                 "/devices/platform/brcm-ehci.0/usb1/1-1/1-1:1.0": _("Lower USB"),
573         },
574         "dm500hd":
575         {
576                 "/devices/pci0000:01/0000:01:00.0/host1/target1:0:0/1:0:0:0": _("eSATA"),
577                 "/devices/pci0000:01/0000:01:00.0/host0/target0:0:0/0:0:0:0": _("eSATA"),
578         },
579         "dm7025":
580         {
581                 "/devices/pci0000:00/0000:00:14.1/ide1/1.0": "Compact Flash", #hdc
582                 "/devices/pci0000:00/0000:00:14.1/ide0/0.0": "Internal Harddisk"
583         }
584         }
585
586 class HarddiskManager:
587         def __init__(self):
588                 config.storage_options = ConfigSubsection()
589                 config.storage_options.default_device = ConfigText(default = "<undefined>")
590                 config.storage = ConfigSubDict()
591                 self.hdd = [ ]
592                 self.cd = ""
593                 self.partitions = [ ]
594                 self.devices_scanned_on_init = [ ]
595                 self.delayed_device_Notifier = [ ]
596
597                 self.on_partition_list_change = CList()
598                 
599                 # currently, this is just an enumeration of what's possible,
600                 # this probably has to be changed to support automount stuff.
601                 # still, if stuff is mounted into the correct mountpoints by
602                 # external tools, everything is fine (until somebody inserts
603                 # a second usb stick.)
604                 p = [
605                                         ("/media/hdd", _("Hard disk")),
606                                         ("/media/card", _("Card")),
607                                         ("/media/cf", _("Compact Flash")),
608                                         ("/media/mmc1", _("SD/MMC")),
609                                         ("/media/net", _("Network Mount")),
610                                         ("/media/ram", _("Ram Disk")),
611                                         ("/media/usb", _("USB Stick")),
612                                         ("/", _("Internal Flash"))
613                                 ]
614                 self.partitions.extend([ Partition(mountpoint = x[0], description = x[1]) for x in p ])
615
616                 self.setupConfigEntries(initial_call = True)
617
618                 self.enumerateBlockDevices()
619
620         def getBlockDevInfo(self, blockdev):
621                 devpath = "/sys/block/" + blockdev
622                 error = False
623                 removable = False
624                 blacklisted = False
625                 is_cdrom = False
626                 partitions = []
627                 try:
628                         removable = bool(int(readFile(devpath + "/removable")))
629                         dev = int(readFile(devpath + "/dev").split(':')[0])
630                         if dev in (7, 31): # loop, mtdblock
631                                 blacklisted = True
632                         if blockdev[0:2] == 'sr':
633                                 is_cdrom = True
634                         if blockdev[0:2] == 'hd':
635                                 try:
636                                         media = readFile("/proc/ide/%s/media" % blockdev)
637                                         if "cdrom" in media:
638                                                 is_cdrom = True
639                                 except IOError:
640                                         error = True
641                         # check for partitions
642                         if not is_cdrom:
643                                 for partition in listdir(devpath):
644                                         if partition[0:len(blockdev)] != blockdev:
645                                                 continue
646                                         partitions.append(partition)
647                         else:
648                                 self.cd = blockdev
649                 except IOError:
650                         error = True
651                 # check for medium
652                 medium_found = True
653                 try:
654                         open("/dev/" + blockdev).close()
655                 except IOError, err:
656                         if err.errno == 159: # no medium present
657                                 medium_found = False
658                 return error, blacklisted, removable, is_cdrom, partitions, medium_found
659
660         def enumerateBlockDevices(self):
661                 print "enumerating block devices..."
662                 for blockdev in listdir("/sys/block"):
663                         error, blacklisted, removable, is_cdrom, partitions, medium_found = self.addHotplugPartition(blockdev)
664                         if not error and not blacklisted:
665                                 if medium_found:
666                                         for part in partitions:
667                                                 self.addHotplugPartition(part)
668                                 self.devices_scanned_on_init.append((blockdev, removable, is_cdrom, medium_found))
669                                 print "[enumerateBlockDevices] devices_scanned_on_init:",self.devices_scanned_on_init
670
671         def getAutofsMountpoint(self, device):
672                 return "/autofs/%s/" % (device)
673
674         def is_hard_mounted(self, device):
675                 mounts = file('/proc/mounts').read().split('\n')
676                 for x in mounts:
677                         if x.find('/autofs') == -1 and x.find(device) != -1:
678                                 #print "is_hard_mounted:",device
679                                 return True
680                 return False
681
682         def is_uuidpath_mounted(self, uuidpath, mountpoint):
683                 mounts = file('/proc/mounts').read().split('\n')
684                 for x in mounts:
685                         if not x.startswith('/'):
686                                 continue
687                         path, mp = x.split()[0:2]
688                         if (path == uuidpath and mp == mountpoint):
689                                 #print "is_uuid_mounted:'%s' for: %s " % (path, mp)
690                                 return True
691                 return False
692
693         def is_fstab_mountpoint(self, device, mountpoint):
694                 mounts = file('/etc/fstab').read().split('\n')
695                 for x in mounts:
696                         if not x.startswith('/'):
697                                 continue
698                         dev, mp = x.split()[0:2]
699                         if (dev == device and mp == mountpoint):
700                                 #print "is_fstab_mountpoint:'%s' for: %s " % (mp, dev)
701                                 return True
702                 return False
703
704         def get_fstab_mountstate(self, device, mountpoint):
705                 mounts = file('/etc/fstab').read().split('\n')
706                 for x in mounts:
707                         if not x.startswith('/'):
708                                 continue
709                         dev, mp, ms = x.split()[0:3]
710                         if (dev == device and mp == mountpoint):
711                                 #print "got_fstab_mountstate:'%s' for: %s - %s" % (ms, dev, mp)
712                                 return ms
713                 return False
714
715         def get_fstab_mountpoint(self, device):
716                 mounts = file('/etc/fstab').read().split('\n')
717                 for x in mounts:
718                         if not x.startswith('/'):
719                                 continue
720                         dev, mp = x.split()[0:2]
721                         if device == dev:
722                                 #print "got_fstab_mountpoint:'%s' for: %s" % (mp, dev)
723                                 return mp
724                 return None
725
726         def modifyFstabEntry(self, partitionPath, mountpoint, mode = "add_deactivated"):
727                 try:
728                         alreadyAdded = self.is_fstab_mountpoint(partitionPath, mountpoint)
729                         oldLine = None
730                         mounts = file('/etc/fstab').read().split('\n')
731                         fp = file("/etc/fstab", 'w')
732                         fp.write("#automatically edited by enigma2, " + str(time.strftime( "%a" + ", " + "%d " + "%b" + " %Y %H:%M:%S", time.localtime(time.time() ))) + "\n")
733                         for x in mounts:
734                                 if (x.startswith(partitionPath) and mountpoint in x):
735                                         oldLine = x
736                                         continue
737                                 if len(x):
738                                         if x.startswith('#automatically'):
739                                                 continue
740                                         fp.write(x + "\n")
741                         if not alreadyAdded:
742                                 if mode == "add_deactivated":
743                                         #print "modifyFstabEntry - add_deactivated:", partitionPath, mountpoint
744                                         fp.write(partitionPath + "\t" + mountpoint + "\tnoauto\tdefaults\t0 0\n")
745                         else:
746                                 if mode == "add_deactivated":
747                                         if oldLine is not None:
748                                                 if "noauto" in oldLine:
749                                                         fp.write(oldLine + "\n")
750                                                 else:
751                                                         #print "modifyFstabEntry - add_deactivated - changed:", partitionPath, mountpoint
752                                                         fp.write(oldLine.replace("auto","noauto") + "\n")
753                         fp.close()
754                 except:
755                         print "error adding fstab entry for: %s" % (partitionPath)
756
757         def addHotplugPartition(self, device, physdev = None):
758                 if not physdev:
759                         dev, part = self.splitDeviceName(device)
760                         try:
761                                 physdev = path.realpath('/sys/block/' + dev + '/device')[4:]
762                         except OSError:
763                                 physdev = dev
764                                 print "couldn't determine blockdev physdev for device", device
765
766                 error, blacklisted, removable, is_cdrom, partitions, medium_found = self.getBlockDevInfo(device)
767                 print "found block device '%s':" % device,
768
769                 if blacklisted:
770                         print "blacklisted"
771                 else:
772                         if error:
773                                 print "error querying properties"
774                         elif not medium_found:
775                                 print "no medium"
776                         else:
777                                 print "ok, removable=%s, cdrom=%s, partitions=%s" % (removable, is_cdrom, partitions)
778
779                         l = len(device)
780                         if l:
781                                 # see if this is a harddrive or removable drive (usb stick/cf/sd)
782                                 if not device[l-1].isdigit() and not is_cdrom:
783                                         if self.getHDD(device) is None and medium_found:
784                                                 if removable:
785                                                         self.hdd.append(Harddisk(device, True))
786                                                 else:
787                                                         self.hdd.append(Harddisk(device, False))                                                        
788                                         self.hdd.sort()
789                                         SystemInfo["Harddisk"] = len(self.hdd) > 0
790
791                                 if (not removable or medium_found) and not self.is_hard_mounted(device):
792                                         self.addDevicePartition(device, physdev)
793
794                 return error, blacklisted, removable, is_cdrom, partitions, medium_found
795
796         def removeHotplugPartition(self, device):
797                 mountpoint = self.getAutofsMountpoint(device)
798                 uuid = self.getPartitionUUID(device)
799                 print "[removeHotplugPartition] for device:'%s'" % (device)
800                 p = self.getPartitionbyDevice(device)
801                 if p is None:
802                         p = self.getPartitionbyMountpoint(mountpoint)
803                 if p is not None:
804                         if uuid is None and p.uuid is not None:
805                                 uuid = p.uuid
806                                 harddiskmanager.unmountPartitionbyMountpoint(p.mountpoint)
807                         if uuid is not None and config.storage.get(uuid, None) is not None:
808                                 self.unmountPartitionbyUUID(uuid)
809                                 if not config.storage[uuid]['enabled'].value:
810                                         del config.storage[uuid]
811                                         print "[removeHotplugPartition] - remove uuid %s from temporary drive add" % (uuid)
812                         self.partitions.remove(p)
813                         self.on_partition_list_change("remove", p)
814
815                 if (uuid is not None and uuid == config.storage_options.default_device.value):
816                         self.verifyDefaultStorageDevice(uuid)
817                         from Tools import Notifications
818                         from Screens.MessageBox import MessageBox
819                         Notifications.AddNotification(MessageBox,_("Default storage device was removed!") + "\n" \
820                                 + _("Please verify if your default storage device is attached or set up your default storage device in menu -> setup -> system -> recording paths."), MessageBox.TYPE_INFO, timeout=20)
821
822                 l = len(device)
823                 if l and not device[l-1].isdigit():
824                         for hdd in self.hdd:
825                                 if hdd.device == device:
826                                         hdd.stop()
827                                         self.hdd.remove(hdd)
828                                         break
829                         SystemInfo["Harddisk"] = len(self.hdd) > 0
830
831                         #call the notifier only after we have fully removed the disconnected drive
832                         for callback in self.delayed_device_Notifier:
833                                 try:
834                                         callback(device, "remove_delayed" )
835                                 except AttributeError:
836                                         self.delayed_device_Notifier.remove(callback)
837
838         def addDevicePartition(self, device, physdev):
839                 # device is the device name, without /dev
840                 # physdev is the physical device path, which we (might) use to determine the userfriendly name
841                 description = self.getUserfriendlyDeviceName(device, physdev)
842                 device_mountpoint = self.getAutofsMountpoint(device)
843                 uuid = self.getPartitionUUID(device)
844                 print "[addDevicePartition] device:'%s' with UUID:'%s'" % (device, uuid)
845                 if config.storage.get(uuid, None) is not None:
846                         if config.storage[uuid]['mountpoint'].value != "":
847                                 device_mountpoint = config.storage[uuid]['mountpoint'].value
848                 if uuid is not None:
849                         if config.storage.get(uuid, None) is None:
850                                 self.setupConfigEntries(initial_call = False, dev = device)
851                         else:
852                                 self.storageDeviceChanged(uuid)
853                 p = self.getPartitionbyMountpoint(device_mountpoint)
854                 if p is not None:
855                         if uuid is not None:
856                                 if p.uuid is not None and p.uuid != uuid:
857                                         if config.storage.get(p.uuid, None) is not None:
858                                                 del config.storage[p.uuid] #delete old uuid reference entries
859                         p.mountpoint = device_mountpoint
860                         p.force_mounted = False
861                         p.device = device
862                         p.uuid = uuid
863                 else:
864                         forced = True
865                         if uuid is not None:
866                                 cfg_uuid = config.storage.get(uuid, None)
867                                 if cfg_uuid is not None:
868                                         if cfg_uuid['enabled'].value:
869                                                 forced = False
870                                         else:
871                                                 device_mountpoint = self.getAutofsMountpoint(device)
872                         x = self.getPartitionbyDevice(device)
873                         if x is None:
874                                 p = Partition(mountpoint = device_mountpoint, description = description, force_mounted = forced, device = device)
875                                 p.uuid = uuid
876                                 self.partitions.append(p)
877                                 self.on_partition_list_change("add", p)
878                         else:   # found old partition entry
879                                 if config.storage.get(x.uuid, None) is not None:
880                                         del config.storage[x.uuid] #delete old uuid reference entries
881                                 x.mountpoint = device_mountpoint
882                                 x.force_mounted = True
883                                 x.uuid = uuid
884                 if p is not None:
885                         if uuid == config.storage_options.default_device.value:
886                                 self.verifyDefaultStorageDevice()
887
888                 for callback in self.delayed_device_Notifier:
889                         try:
890                                 callback(device, "add_delayed" )
891                         except AttributeError:
892                                 self.delayed_device_Notifier.remove(callback)
893
894         def HDDCount(self):
895                 return len(self.hdd)
896
897         def HDDList(self):
898                 list = [ ]
899                 for hd in self.hdd:
900                         hdd = hd.model() + " - " + hd.bus()
901                         cap = hd.capacity()
902                         if cap != "":
903                                 hdd += " (" + cap + ")"
904                         list.append((hdd, hd))
905                 return list
906
907         def HDDEnabledCount(self):
908                 cnt = 0
909                 for uuid, cfg in config.storage.items():
910                         print "uuid", uuid, "cfg", cfg
911                         if cfg["enabled"].value:
912                                 cnt += 1
913                 return cnt
914
915         def getHDD(self, part):
916                 for hdd in self.hdd:
917                         if hdd.device == part[:3]:
918                                 return hdd
919                 return None
920
921         def getCD(self):
922                 return self.cd
923
924         def getFdiskInfo(self, devname):
925                 size = sizeg = type = sys = None
926                 cmd = "fdisk -l /dev/" + devname
927
928                 try:
929                         for line in popen(cmd).read().split('\n'):
930                                 if line.startswith("Disk"):
931                                         sizeobj = re.search(r', ((?:[a-zA-Z0-9])*) bytes', line)
932                                         if sizeobj:
933                                                 size = sizeobj.group(1)
934                                         sizegobj = re.search(r': ((?:[0-9.0-9])*) GB', line)
935                                         if sizegobj:
936                                                 sizeg = sizegobj.group(1)
937                                 if not line.startswith('/'):
938                                         continue
939                                 if line.startswith("/dev/" + devname):
940                                         a,b,c,d, type, sys = line.split(None,5)
941                 except:
942                         print "error getting fdisk device info"
943                 return type, sys, size, sizeg
944
945         def getBlkidPartitionType(self, device):
946                 type = None
947                 if path.exists("/usr/sbin/blkid"):
948                         cmd = "/usr/sbin/blkid " + str(device)
949                         try:
950                                 for line in popen(cmd).read().split('\n'):
951                                         if not line.startswith(device):
952                                                 continue
953                                         fstobj = re.search(r' TYPE="((?:[^"\\]|\\.)*)"', line)
954                                         if fstobj:
955                                                 type = fstobj.group(1)
956                         except:
957                                 print "error getting blkid partition type"
958                 return type
959
960         def getLinkPath(self,link):
961                 if path.islink(link):
962                         p = path.normpath(readlink(link))
963                         if path.isabs(p):
964                                 return p
965                         return path.join(path.dirname(link), p)
966
967         def verifyDefaultStorageDevice(self, old_default = None):
968                 uuid = config.storage_options.default_device.value
969                 part = self.getdefaultStorageDevicebyUUID(uuid)
970                 defaultpath = "/media/hdd"
971                 curr_defaultpath = self.getLinkPath(defaultpath)
972                 if part is not None:
973                         if part in self.getConfiguredStorageDevices():
974                                 mountpoint = part.mountpoint
975                                 if path.ismount(mountpoint): #default storage mountpoint available ?
976                                         if (path.exists(defaultpath) and not path.islink(defaultpath) and not path.ismount(defaultpath) ):
977                                                 try:
978                                                         rmdir(defaultpath)
979                                                         print "removed /media/hdd dir"
980                                                 except OSError:
981                                                         print "could not remove /media/hdd dir"
982                                                         try:
983                                                                 rename(defaultpath,"/media/hdd_old")
984                                                                 print "renamed /media/hdd dir to /media/hdd_old"
985                                                         except OSError:
986                                                                 print "could not rename /media/hdd dir to /media/hdd_old"
987                                         if (path.islink(defaultpath) and self.getLinkPath(defaultpath) != mountpoint ):
988                                                 try:
989                                                         unlink(defaultpath)
990                                                         print "removed old default storage link"
991                                                 except OSError:
992                                                         pass
993                                         if not path.exists(defaultpath):
994                                                 try:
995                                                         symlink(mountpoint,defaultpath)
996                                                         print "default storage link created"
997                                                 except OSError:
998                                                         pass
999                                         if self.getLinkPath(defaultpath) == mountpoint:
1000                                                 print "default storage device successfully linked to /media/hdd"
1001                                 if not path.ismount(mountpoint): #default storage mountpoint unavailable !
1002                                         if self.getLinkPath(defaultpath) == mountpoint:
1003                                                 try:
1004                                                         unlink(defaultpath)
1005                                                         print "removed unused /media/hdd"
1006                                                 except OSError:
1007                                                         print "could not remove unused /media/hdd link"
1008                                         if not path.exists(defaultpath):
1009                                                 print "default storage link removed"
1010                 else:
1011                         if old_default is None:
1012                                 if uuid == "<undefined>":
1013                                         if (not path.islink(defaultpath) and not path.ismount(defaultpath) ):
1014                                                 try:
1015                                                         rmdir(defaultpath)
1016                                                         print "removed undefined /media/hdd dir"
1017                                                 except OSError:
1018                                                         print "could not remove undefined /media/hdd dir"
1019                                                         try:
1020                                                                 rename(defaultpath,"/media/hdd_old")
1021                                                                 print "renamed undefined /media/hdd dir to /media/hdd_old"
1022                                                         except OSError:
1023                                                                 print "could not rename undefined /media/hdd dir to /media/hdd_old"
1024                                         if (path.islink(defaultpath) and not path.ismount(defaultpath)):
1025                                                 try:
1026                                                         unlink(defaultpath)
1027                                                         print "removed undefined default storage link"
1028                                                 except OSError:
1029                                                         print "could not remove undefined /media/hdd dir"
1030                                 elif uuid != "<undefined>":
1031                                         cfg_uuid = config.storage.get(uuid, None)
1032                                         if cfg_uuid is not None and not cfg_uuid["enabled"].value:
1033                                                 config.storage_options.default_device.value = "<undefined>"
1034                                                 config.storage_options.default_device.save()
1035                                                 config.storage_options.save()
1036                                                 mountpoint = self.getLinkPath(defaultpath)
1037                                                 uuidpath = "/dev/disk/by-uuid/" + uuid
1038                                                 if (self.is_fstab_mountpoint(uuidpath, mountpoint) and self.get_fstab_mountstate(uuidpath, mountpoint) == 'noauto'):
1039                                                         try:
1040                                                                 unlink(defaultpath)
1041                                                                 print "removed unconfigured storage device link"
1042                                                         except OSError:
1043                                                                 pass
1044                                         elif cfg_uuid is not None and cfg_uuid["enabled"].value:
1045                                                 print self.getLinkPath(defaultpath), cfg_uuid["mountpoint"].value
1046                                                 if cfg_uuid["mountpoint"].value == self.getLinkPath(defaultpath):
1047                                                         try:
1048                                                                 unlink(defaultpath)
1049                                                                 print "removed default storage link for offline device"
1050                                                         except OSError:
1051                                                                 pass
1052                         else:
1053                                 if uuid == old_default:
1054                                         cfg_uuid = config.storage.get(old_default, None)
1055                                         if cfg_uuid is not None and cfg_uuid["enabled"].value:
1056                                                 print self.getLinkPath(defaultpath), cfg_uuid["mountpoint"].value
1057                                                 if cfg_uuid["mountpoint"].value == self.getLinkPath(defaultpath):
1058                                                         try:
1059                                                                 unlink(defaultpath)
1060                                                                 print "removed default storage link for offline device"
1061                                                         except OSError:
1062                                                                 pass
1063
1064         def defaultStorageDeviceChanged(self, mountpoint = None):
1065                 part = self.getPartitionbyMountpoint(mountpoint)
1066                 if (part is not None and part in self.getConfiguredStorageDevices()):
1067                         config.storage_options.default_device.value = part.uuid
1068                 if (part is not None and part not in self.getConfiguredStorageDevices()):
1069                         config.storage_options.default_device.value = "<undefined>"
1070                 if (part is None and mountpoint == "<undefined>"):
1071                         config.storage_options.default_device.value = "<undefined>"
1072                 print "defaultStorageDeviceChanged:",config.storage_options.default_device.value
1073                 config.storage_options.default_device.save()
1074                 config.storage_options.save()
1075                 self.verifyDefaultStorageDevice()
1076
1077         def isConfiguredStorageDevice(self,uuid):
1078                 cfg_uuid = config.storage.get(uuid, None)
1079                 if cfg_uuid is not None and cfg_uuid["enabled"].value:
1080                         #print "isConfiguredStorageDevice:",uuid
1081                         return True
1082                 return False
1083
1084         def getdefaultStorageDevicebyUUID(self, uuid):
1085                 part = None
1086                 for p in self.getConfiguredStorageDevices():
1087                         if p.uuid == uuid:
1088                                 part = p
1089                 return part
1090
1091         def getConfiguredStorageDevices(self):
1092                 parts = [x for x in self.partitions if (x.uuid is not None and x.mounted() and self.isConfiguredStorageDevice(x.uuid))]
1093                 return [x for x in parts]
1094
1095         def getMountedPartitions(self, onlyhotplug = False):
1096                 parts = [x for x in self.partitions if (x.is_hotplug or not onlyhotplug) and x.mounted()]
1097                 devs = set([x.device for x in parts])
1098                 for devname in devs.copy():
1099                         if not devname:
1100                                 continue
1101                         dev, part = self.splitDeviceName(devname)
1102                         if part and dev in devs: # if this is a partition and we still have the wholedisk, remove wholedisk
1103                                 devs.remove(dev)
1104                 # return all devices which are not removed due to being a wholedisk when a partition exists
1105                 return [x for x in parts if not x.device or x.device in devs]
1106
1107         def splitDeviceName(self, devname):
1108                 # 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.
1109                 dev = devname[:3]
1110                 part = devname[3:]
1111                 for p in part:
1112                         if not p.isdigit():
1113                                 return devname, 0
1114                 return dev, part and int(part) or 0
1115
1116         def getUserfriendlyDeviceName(self, dev, phys):
1117                 dev, part = self.splitDeviceName(dev)
1118                 description = "External Storage %s" % dev
1119                 have_model_descr = False
1120                 try:
1121                         description = readFile("/sys" + phys + "/model")
1122                         have_model_descr = True
1123                 except IOError, s:
1124                         print "couldn't read model: ", s
1125                 from Tools.HardwareInfo import HardwareInfo
1126                 if dev.find('sr') == 0 and dev[2].isdigit():
1127                         devicedb = DEVICEDB_SR
1128                 else:
1129                         devicedb = DEVICEDB
1130                 for physdevprefix, pdescription in devicedb.get(HardwareInfo().device_name,{}).items():
1131                         if phys.startswith(physdevprefix):
1132                                 if have_model_descr:
1133                                         description = pdescription + ' - ' + description
1134                                 else:
1135                                         description = pdescription
1136                 # not wholedisk and not partition 1
1137                 if part and part != 1:
1138                         description += " (Partition %d)" % part
1139                 return description
1140
1141         def addMountedPartition(self, device, desc):
1142                 already_mounted = False
1143                 for x in self.partitions[:]:
1144                         if x.mountpoint == device:
1145                                 already_mounted = True
1146                 if not already_mounted:
1147                         self.partitions.append(Partition(mountpoint = device, description = desc))
1148
1149         def removeMountedPartition(self, mountpoint):
1150                 for x in self.partitions[:]:
1151                         if x.mountpoint == mountpoint:
1152                                 self.partitions.remove(x)
1153                                 self.on_partition_list_change("remove", x)
1154
1155         def removeMountedPartitionbyDevice(self, device):
1156                 p = self.getPartitionbyDevice(device)
1157                 if p is not None:
1158                         #print "[removeMountedPartitionbyDevice] '%s', '%s', '%s', '%s', '%s'" % (p.mountpoint,p.description,p.device,p.force_mounted,p.uuid)
1159                         self.partitions.remove(p)
1160                         self.on_partition_list_change("remove", p)
1161
1162         def trigger_udev(self):
1163                 # We have to trigger udev to rescan sysfs 
1164                 cmd = "udevadm trigger"
1165                 res = system(cmd)
1166                 return (res >> 8)
1167
1168         def getPartitionbyDevice(self, dev):
1169                 for x in self.partitions[:]:
1170                         if x.device == dev:
1171                                 #print "[getPartitionbyDevice] '%s', '%s', '%s', '%s', '%s'" % (x.mountpoint,x.description,x.device,x.force_mounted,x.uuid)
1172                                 return x
1173                 return None
1174
1175         def getPartitionbyMountpoint(self, mountpoint):
1176                 for x in self.partitions[:]:
1177                         if x.mountpoint == mountpoint:
1178                                 #print "[getPartitionbyMountpoint] '%s', '%s', '%s', '%s', '%s'" % (x.mountpoint,x.description,x.device,x.force_mounted,x.uuid)
1179                                 return x
1180                 return None
1181
1182         def getDeviceNamebyUUID(self, uuid):
1183                 if path.exists("/dev/disk/by-uuid/" + uuid):
1184                         return path.basename(path.realpath("/dev/disk/by-uuid/" + uuid))
1185                 return None
1186
1187         def getPartitionUUID(self, part):
1188                 if not path.exists("/dev/disk/by-uuid"):
1189                         return None
1190                 for uuid in listdir("/dev/disk/by-uuid/"):
1191                         if not path.exists("/dev/disk/by-uuid/" + uuid):
1192                                 return None                     
1193                         if path.basename(path.realpath("/dev/disk/by-uuid/" + uuid)) == part:
1194                                 #print "[getPartitionUUID] '%s' - '%s'" % (uuid, path.basename(path.realpath("/dev/disk/by-uuid/" + uuid)) )
1195                                 return uuid
1196                 return None
1197
1198         def reloadExports(self):
1199                 if path.exists("/etc/exports") and path.exists("/usr/sbin/exportfs"):
1200                         Console().ePopen(("exportfs -r"))
1201
1202         def unmountPartitionbyMountpoint(self, mountpoint, device = None):
1203                 if path.exists(mountpoint) and path.ismount(mountpoint):
1204                         part = self.getPartitionbyMountpoint(mountpoint)
1205                         if part is not None and part.uuid is not None:
1206                                 if part.uuid == config.storage_options.default_device.value:
1207                                         print part.uuid,config.storage_options.default_device.value
1208                                         if (path.exists("/media/hdd") and path.islink("/media/hdd") and self.getLinkPath("/media/hdd") == mountpoint):
1209                                                 try:
1210                                                         unlink("/media/hdd")
1211                                                         print "removed old default storage link"
1212                                                 except OSError:
1213                                                         pass                                    
1214                         cmd = "umount" + " " + mountpoint
1215                         print "[unmountPartitionbyMountpoint] %s:" % (mountpoint)
1216                         system(cmd)
1217                         if (device is not None and not path.ismount(mountpoint)):
1218                                 self.removeMountedPartitionbyDevice(device)
1219                         self.reloadExports()
1220
1221         def unmountPartitionbyUUID(self, uuid):
1222                 mountpoint = config.storage[uuid]['mountpoint'].value
1223                 if mountpoint != "":
1224                         if path.exists(mountpoint) and path.ismount(mountpoint):
1225                                 partitionPath = "/dev/disk/by-uuid/" + uuid
1226                                 mountpoint = config.storage[uuid]['mountpoint'].value
1227                                 if (self.is_hard_mounted(partitionPath) and self.is_fstab_mountpoint(partitionPath, mountpoint) and self.get_fstab_mountstate(partitionPath, mountpoint) == 'auto'):
1228                                         print "[unmountPartitionbyUUID] disabling external mounted config entry for %s:" % (mountpoint)
1229                                         config.storage[uuid]["enabled"].value = False
1230                                         config.storage.save()
1231                                 else:
1232                                         part = self.getPartitionbyMountpoint(mountpoint)
1233                                         if part is not None and part.uuid is not None:
1234                                                 if part.uuid == config.storage_options.default_device.value:
1235                                                         print part.uuid,config.storage_options.default_device.value
1236                                                         if (path.exists("/media/hdd") and path.islink("/media/hdd") and self.getLinkPath("/media/hdd") == mountpoint):
1237                                                                 try:
1238                                                                         unlink("/media/hdd")
1239                                                                         print "removed old default storage link"
1240                                                                 except OSError:
1241                                                                         pass                                    
1242                                         cmd = "umount" + " " + mountpoint
1243                                         print "[unmountPartitionbyUUID] %s:" % (mountpoint)
1244                                         system(cmd)
1245                                 self.reloadExports()
1246
1247         def mountPartitionbyUUID(self, uuid):
1248                 if path.exists("/dev/disk/by-uuid/" + uuid):
1249                         print "[mountPartitionbyUUID] for UUID:'%s'" % (uuid)
1250                         cfg_uuid = config.storage.get(uuid, None)
1251                         partitionPath = "/dev/disk/by-uuid/" + uuid
1252                         mountpoint = cfg_uuid['mountpoint'].value
1253                         dev = self.getDeviceNamebyUUID(uuid)
1254                         devicepath = "/dev/" + str(dev)
1255
1256                         if (self.is_hard_mounted(partitionPath) or self.is_hard_mounted(devicepath)):
1257                                 #print "[mountPartitionbyUUID] - found possible Fstab mounted device:",partitionPath, devicepath, dev
1258                                 if (self.is_fstab_mountpoint(partitionPath, mountpoint) and self.get_fstab_mountstate(partitionPath, mountpoint) == 'auto'):
1259                                         cfg_uuid["enabled"].value = False
1260                                         config.storage.save()
1261                                 if (self.is_fstab_mountpoint(devicepath, mountpoint) and self.get_fstab_mountstate(devicepath, mountpoint) == 'auto'):
1262                                         cfg_uuid["enabled"].value = False
1263                                         config.storage.save()   
1264
1265                         if cfg_uuid['enabled'].value:
1266                                 if mountpoint != "":
1267                                         if not path.exists(mountpoint):
1268                                                 try:
1269                                                         makedirs(mountpoint)
1270                                                 except OSError:
1271                                                         print "[mountPartitionbyUUID] could not create mountdir:",mountpoint
1272
1273                                         if path.exists(mountpoint) and not path.ismount(mountpoint):
1274                                                 cmd = "mount -t auto /dev/disk/by-uuid/" + uuid + " " + mountpoint
1275                                                 system(cmd)
1276                                                 print "[mountPartitionbyUUID]:",cmd
1277
1278                                         hdd = self.getHDD(dev)
1279                                         if hdd is not None:
1280                                                 partitionType = self.getBlkidPartitionType(devicepath)
1281                                                 if partitionType is not None and partitionType in ( "ext2", "ext3" ):
1282                                                         moviedir = mountpoint + "/movie"
1283                                                         if not path.exists(moviedir):
1284                                                                 try:
1285                                                                         makedirs(moviedir)
1286                                                                 except OSError:
1287                                                                         print "[mountPartitionbyUUID] could not create moviedir:",moviedir
1288
1289                                         if path.ismount(mountpoint):
1290                                                 dev = self.getDeviceNamebyUUID(uuid)
1291                                                 if dev is not None:
1292                                                         p = self.getPartitionbyMountpoint(mountpoint)
1293                                                         if p is not None:
1294                                                                 x = self.getPartitionbyDevice(dev)
1295                                                                 if x is not None and x.mountpoint.startswith('/autofs'):
1296                                                                         self.removeMountedPartitionbyDevice(dev)
1297                                                                 p.mountpoint = mountpoint
1298                                                                 p.uuid = uuid
1299                                                                 p.device = dev
1300                                                                 p.force_mounted = False
1301                                                         else:
1302                                                                 p = self.getPartitionbyDevice(dev)
1303                                                                 if p is not None:
1304                                                                         p.mountpoint = mountpoint
1305                                                                         p.uuid = uuid
1306                                                                         p.device = dev
1307                                                                         p.force_mounted = False
1308                                         else:
1309                                                 print "[mountPartitionbyUUID] could not mount mountdir:",mountpoint
1310                 else:
1311                         print "[mountPartitionbyUUID] failed for UUID:'%s'" % (uuid)
1312
1313         def storageDeviceChanged(self, uuid):
1314                 if config.storage[uuid]["enabled"].value:
1315                         self.mountPartitionbyUUID(uuid)
1316                 else:
1317                         self.unmountPartitionbyUUID(uuid)
1318
1319         def setupConfigEntries(self, initial_call = False, dev = None):
1320                 if initial_call and not dev:
1321                         for uuid in config.storage.stored_values:
1322                                 print "[setupConfigEntries] initial_call for stored uuid:",uuid,config.storage.stored_values[uuid]
1323                                 config.storage[uuid] = ConfigSubDict()
1324                                 config.storage[uuid]["enabled"] = ConfigYesNo(default = False)
1325                                 config.storage[uuid]["mountpoint"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1326                                 config.storage[uuid]["device_description"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1327                                 config.storage[uuid]["device_info"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1328                                 config.storage[uuid]["isRemovable"] = ConfigBoolean(default = False)
1329                                 dev = self.getDeviceNamebyUUID(uuid)
1330                                 if dev is not None:
1331                                         p = self.getPartitionbyDevice(dev) or self.getPartitionbyMountpoint(config.storage[uuid]["mountpoint"].value)
1332                                         if p is None: # manually add partition entry
1333                                                 physdev = path.realpath('/sys/block/' + dev[:3] + '/device')[4:]
1334                                                 description = self.getUserfriendlyDeviceName(dev[:3], physdev)
1335                                                 p = Partition(mountpoint = config.storage[uuid]["mountpoint"].value, description = description, force_mounted = False, device = dev)
1336                                                 p.uuid = uuid
1337                                                 self.partitions.append(p)
1338                                                 self.on_partition_list_change("add", p) 
1339                                 if path.exists("/dev/disk/by-uuid/" + uuid):
1340                                         self.storageDeviceChanged(uuid)
1341                 if dev is not None:
1342                         uuid = self.getPartitionUUID(dev)
1343                         if uuid is not None:
1344                                 if config.storage.get(uuid, None) is None: #new unconfigured device added
1345                                         print "[setupConfigEntries] new device add for '%s' with uuid:'%s'" % (dev, uuid)
1346                                         hdd = self.getHDD(dev)
1347                                         if hdd is not None:
1348                                                 hdd_description = hdd.model()
1349                                                 cap = hdd.capacity()
1350                                                 if cap != "":
1351                                                         hdd_description += " (" + cap + ")"
1352                                                 device_info =  hdd.bus_description()
1353                                         else:
1354                                                 device_info = dev
1355                                                 hdd_description = "External Storage"
1356                                         config.storage[uuid] = ConfigSubDict()
1357                                         config.storage[uuid]["enabled"] = ConfigYesNo(default = False)
1358                                         config.storage[uuid]["mountpoint"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1359                                         config.storage[uuid]["device_description"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1360                                         config.storage[uuid]["device_info"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1361                                         config.storage[uuid]["isRemovable"] = ConfigBoolean(default = False)
1362                                         config.storage[uuid]["device_description"].setValue(hdd_description)
1363                                         config.storage[uuid]["device_info"].setValue(device_info)
1364                                         removable = False
1365                                         if hdd is not None:
1366                                                 removable = hdd.isRemovable
1367                                         config.storage[uuid]["isRemovable"].setValue(removable)                                 
1368                                         p = self.getPartitionbyDevice(dev)
1369                                         if p is None: # manually add partition entry (e.g. on long spinup times)
1370                                                 physdev = path.realpath('/sys/block/' + dev[:3] + '/device')[4:]
1371                                                 description = self.getUserfriendlyDeviceName(dev[:3], physdev)
1372                                                 p = Partition(mountpoint = self.getAutofsMountpoint(dev), description = description, force_mounted = True, device = dev)
1373                                                 p.uuid = uuid
1374                                                 self.partitions.append(p)
1375                                                 self.on_partition_list_change("add", p) 
1376                                         self.storageDeviceChanged(uuid)         
1377                                 else:
1378                                         print "[setupConfigEntries] new device add for '%s' with uuid:'%s'" % (dev, uuid)
1379                                         self.storageDeviceChanged(uuid)
1380                         else:
1381                                 "[setupConfigEntries] device add for '%s' without uuid !!!" % (dev)
1382
1383 harddiskmanager = HarddiskManager()