enigma2: 20121220 master -> 20130114
[enigma2.git] / usr / lib / enigma2 / python / Components / Harddisk.py
1 from os import system, listdir, statvfs, popen, makedirs, stat, major, minor, path, access, readlink, unlink, getcwd, chdir, W_OK
2 from Tools.Directories import SCOPE_HDD, resolveFilename
3 from Tools.CList import CList
4 from SystemInfo import SystemInfo
5 import time
6 import re
7 from Components.Console import Console
8 from config import config, configfile, ConfigYesNo, ConfigText, ConfigSubDict, ConfigSubsection, ConfigBoolean
9
10 def MajorMinor(path):
11         rdev = stat(path).st_rdev
12         return (major(rdev),minor(rdev))
13
14 def readFile(filename):
15         file = open(filename)
16         data = file.read().strip()
17         file.close()
18         return data
19
20 DEVTYPE_UDEV = 0
21 DEVTYPE_DEVFS = 1
22
23 def forceAutofsUmount(dev):
24         try:
25                 mounts = open("/proc/mounts")
26         except IOError:
27                 return -1
28
29         lines = mounts.readlines()
30         mounts.close()
31
32         res = -1
33         for line in lines:
34                 parts = line.strip().split(" ")
35                 real_path = path.realpath(parts[0])
36                 if not real_path[-1].isdigit():
37                         continue
38                 try:
39                         if parts[1] == '/autofs/' + dev:
40                                 print "forced", parts[1], "umount!!"
41                                 cmd = "umount /autofs/" + dev + " || /bin/true"
42                                 res = system(cmd)
43                                 break
44                 except OSError:
45                         pass
46         return (res >> 8)
47
48 class Harddisk:
49         def __init__(self, device, removable = False):
50                 self.device = device
51                 self.isRemovable = removable
52
53                 if access("/dev/.udev", 0):
54                         self.type = DEVTYPE_UDEV
55                 elif access("/dev/.devfsd", 0):
56                         self.type = DEVTYPE_DEVFS
57                 else:
58                         print "Unable to determine structure of /dev fallback to udev"
59                         self.type = DEVTYPE_UDEV
60
61                 self.is_sleeping = False
62                 self.max_idle_time = 0
63                 self.idle_running = False
64                 self.timer = None
65
66                 self.isInitializing = False
67                 self.isVerifying = False
68
69                 self.dev_path = ''
70                 self.disk_path = ''
71                 self.phys_path = path.realpath(self.sysfsPath('device'))
72
73                 if self.type == DEVTYPE_UDEV:
74                         self.dev_path = '/dev/' + self.device
75                         self.disk_path = self.dev_path
76
77                 elif self.type == DEVTYPE_DEVFS:
78                         tmp = readFile(self.sysfsPath('dev')).split(':')
79                         s_major = int(tmp[0])
80                         s_minor = int(tmp[1])
81                         for disc in listdir("/dev/discs"):
82                                 dev_path = path.realpath('/dev/discs/' + disc)
83                                 disk_path = dev_path + '/disc'
84                                 try:
85                                         rdev = stat(disk_path).st_rdev
86                                 except OSError:
87                                         continue
88                                 if s_major == major(rdev) and s_minor == minor(rdev):
89                                         self.dev_path = dev_path
90                                         self.disk_path = disk_path
91                                         break
92
93                 print "new Harddisk", self.device, '->', self.dev_path, '->', self.disk_path
94                 if not self.isRemovable:
95                         self.startIdle()
96
97         def __lt__(self, ob):
98                 return self.device < ob.device
99
100         def partitionPath(self, n):
101                 if self.type == DEVTYPE_UDEV:
102                         return self.dev_path + n
103                 elif self.type == DEVTYPE_DEVFS:
104                         return self.dev_path + '/part' + n
105
106         def sysfsPath(self, filename):
107                 return path.realpath('/sys/block/' + self.device + '/' + filename)
108
109         def stop(self):
110                 if self.timer:
111                         self.timer.stop()
112                         self.timer.callback.remove(self.runIdle)
113
114         def bus(self):
115                 # CF (7025 specific)
116                 if self.type == DEVTYPE_UDEV:
117                         ide_cf = False  # FIXME
118                 elif self.type == DEVTYPE_DEVFS:
119                         ide_cf = self.device[:2] == "hd" and "host0" not in self.dev_path
120
121                 internal = "pci" in self.phys_path
122
123                 if ide_cf:
124                         ret = "External (CF)"
125                 elif internal:
126                         ret = "Internal"
127                 else:
128                         ret = "External"
129                 return ret
130
131         def bus_type(self):
132                 # CF (7025 specific)
133                 if self.type == DEVTYPE_UDEV:
134                         ide_cf = False  # FIXME
135                 elif self.type == DEVTYPE_DEVFS:
136                         ide_cf = self.device[:2] == "hd" and "host0" not in self.dev_path
137
138                 sata = "pci" in self.phys_path
139                 sata_desc = self.bus_description()
140
141                 if ide_cf:
142                         ret = "IDE"
143                 elif sata:
144                         if sata_desc is not None:
145                                 ret = sata_desc
146                         else:
147                                 ret = "SATA"
148                 else:
149                         ret = "USB"
150                 return ret
151
152         def bus_description(self):
153                 phys = self.phys_path[4:]
154                 from Tools.HardwareInfo import HardwareInfo
155                 if self.device.find('sr') == 0 and self.device[2].isdigit():
156                         devicedb = DEVICEDB_SR
157                 else:
158                         devicedb = DEVICEDB
159
160                 for physdevprefix, pdescription in devicedb.get(HardwareInfo().device_name,{}).items():
161                         #print "bus_description:",phys, physdevprefix, pdescription
162                         if phys.startswith(physdevprefix):
163                                 return pdescription
164                 if phys.find('pci') != -1:
165                         return "SATA"
166                 elif phys.find('usb') != -1:
167                         return "USB"
168                 return "External Storage"
169
170         def diskSize(self, sectors = False):
171                 try:
172                         line = readFile(self.sysfsPath('size'))
173                         cap = int(line)
174                 except:
175                         return 0;
176                 if sectors:
177                         return cap
178                 return cap / 1000 * 512 / 1000
179
180         def capacity(self):
181                 cap = self.diskSize()
182                 if cap == 0:
183                         return ""
184                 return "%d.%03d GB" % (cap/1000, cap%1000)
185
186         def model(self, model_only = False, vendor_only = False):
187                 if self.device[:2] == "hd":
188                         return readFile('/proc/ide/' + self.device + '/model')
189                 elif self.device[:2] == "sd":
190                         try:
191                                 vendor = readFile(self.sysfsPath('device/vendor'))
192                                 model = readFile(self.sysfsPath('device/model'))
193                         except:
194                                 vendor = ""
195                                 model = ""
196
197                         if vendor_only:
198                                 return vendor
199                         if model_only:
200                                 return model
201                         return vendor + '-' + model
202                 else:
203                         assert False, "no hdX or sdX"
204
205         def free(self):
206                 try:
207                         mounts = open("/proc/mounts")
208                 except IOError:
209                         return -1
210
211                 lines = mounts.readlines()
212                 mounts.close()
213
214                 for line in lines:
215                         parts = line.strip().split(" ")
216                         real_path = path.realpath(parts[0])
217                         if not real_path[-1].isdigit():
218                                 continue
219                         try:
220                                 if MajorMinor(real_path) == MajorMinor(self.partitionPath(real_path[-1])):
221                                         stat = statvfs(parts[1])
222                                         return stat.f_bfree/1000 * stat.f_bsize/1000
223                         except OSError:
224                                 pass
225                 return -1
226
227         def numPartitions(self):
228                 numPart = -1
229                 if self.type == DEVTYPE_UDEV:
230                         try:
231                                 devdir = listdir('/dev')
232                         except OSError:
233                                 return -1
234                         for filename in devdir:
235                                 if filename.startswith(self.device):
236                                         numPart += 1
237
238                 elif self.type == DEVTYPE_DEVFS:
239                         try:
240                                 idedir = listdir(self.dev_path)
241                         except OSError:
242                                 return -1
243                         for filename in idedir:
244                                 if filename.startswith("disc"):
245                                         numPart += 1
246                                 if filename.startswith("part"):
247                                         numPart += 1
248                 return numPart
249
250         def unmount(self, numpart = None):
251                 try:
252                         mounts = open("/proc/mounts")
253                 except IOError:
254                         return -1
255
256                 lines = mounts.readlines()
257                 mounts.close()
258
259                 cmd = "umount"
260                 for line in lines:
261                         parts = line.strip().split(" ")
262                         real_path = path.realpath(parts[0])
263                         if not real_path[-1].isdigit():
264                                 if numpart is not None and numpart == 0:
265                                         if real_path.startswith("/dev/sd"):
266                                                 uuid = harddiskmanager.getPartitionUUID(self.device)
267                                                 if uuid is not None:
268                                                         try:
269                                                                 if MajorMinor(real_path) == MajorMinor(self.dev_path):
270                                                                         cmd = ' ' . join([cmd, parts[1]])
271                                                                         break
272                                                         except OSError:
273                                                                 pass
274                         try:
275                                 if MajorMinor(real_path) == MajorMinor(self.partitionPath(real_path[-1])):
276                                         cmd = ' ' . join([cmd, parts[1]])
277                                         break
278                         except OSError:
279                                 pass
280                 res = system(cmd)
281                 if cmd == "umount": # nothing found to unmount
282                         res = 0
283                 return (res >> 8)
284
285         def createPartition(self):
286                 cmd = 'printf "8,\n;0,0\n;0,0\n;0,0\ny\n" | sfdisk -f -uS -q ' + self.disk_path
287                 if harddiskmanager.KernelVersion >= "3.2":
288                         maxSectorsMBR = int(4294967295) #2TB
289                         swapPartSize = int(2097152) #1GB
290                         fstype, sys, size, sizeg, sectors = harddiskmanager.getFdiskInfo(self.device[:3])
291                         if sectors is None:
292                                 sectors = self.diskSize(sectors = True)
293                         cmd = 'parted --script --align=min ' + self.disk_path + ' -- mklabel msdos mkpart primary ext3 40s 100%'
294                         if sectors and not self.isRemovable:
295                                 part1end = int(sectors-swapPartSize) #leaving 1GB for swap
296                                 cmd = 'parted --script --align=min ' + self.disk_path + ' -- mklabel msdos mkpart primary ext3 40s ' + str(part1end) + 's'
297                                 cmd+=  ' mkpart primary linux-swap ' + str(int(part1end+1)) + 's -1s'
298                                 if sectors > maxSectorsMBR:
299                                         cmd = 'parted --script --align=opt -- ' + self.disk_path + ' mklabel gpt mkpart primary ext3 2048s ' + str(part1end) + 's'
300                                         cmd+=  ' mkpart primary linux-swap ' + str(int(part1end+1)) + 's -1'
301
302                 res = system(cmd)
303                 print  "[createPartition]:",res,cmd
304                 return (res >> 8)
305
306         def mkfs(self, fstype = "ext3", partitionNum = "1"):
307                 cmd = "mkfs." + fstype + " "
308                 if self.diskSize() > 4 * 1024:
309                         cmd += "-T largefile "
310                 cmd += "-m0 -O dir_index " + self.partitionPath(partitionNum)
311                 res = system(cmd)
312                 print "[mkfs]:",res,cmd
313                 return (res >> 8)
314
315         def mkswap(self, partitionNum = "2"):
316                 cmd = "mkswap " + self.partitionPath(partitionNum)
317                 res = system(cmd)
318                 print "[mkswap]:",cmd,res
319                 return (res >> 8)
320
321         def activateswap(self, partitionNum = "2"):
322                 partitionType = harddiskmanager.getBlkidPartitionType(self.partitionPath(partitionNum))
323                 if partitionType is not None and partitionType == "swap":
324                         cmd = "swapon " + self.partitionPath(partitionNum)
325                         system(cmd)
326
327         def deactivateswap(self, partitionNum = "2"):
328                 partitionType = harddiskmanager.getBlkidPartitionType(self.partitionPath(partitionNum))
329                 if partitionType is not None and partitionType == "swap":
330                         cmd = "swapoff " + self.partitionPath(partitionNum)
331                         print "[deactivate swap]:",cmd
332                         system(cmd)
333
334         def mount(self):
335                 try:
336                         fstab = open("/etc/fstab")
337                 except IOError:
338                         return -1
339
340                 lines = fstab.readlines()
341                 fstab.close()
342
343                 res = -1
344                 for line in lines:
345                         parts = line.strip().split(" ")
346                         real_path = path.realpath(parts[0])
347                         if not real_path[-1].isdigit():
348                                 continue
349                         try:
350                                 if MajorMinor(real_path) == MajorMinor(self.partitionPath(real_path[-1])):
351
352                                         forceAutofsUmount(self.device+real_path[-1])
353                                         # we must umount autofs first because autofs mounts with "sync" option
354                                         # and the real mount than also mounts with this option
355                                         # this is realy bad for the performance!
356
357                                         cmd = "mount -t auto " + parts[0]
358                                         res = system(cmd)
359                                         break
360                         except OSError:
361                                 pass
362
363                 return (res >> 8)
364
365         def createMovieFolder(self, isFstabMounted = False):
366                 if isFstabMounted:
367                         try:
368                                 makedirs(resolveFilename(SCOPE_HDD))
369                         except OSError:
370                                 return -1
371                 else:
372                         try:
373                                 makedirs("/autofs/" + self.device[:3] + "1/movie")
374                         except OSError:
375                                 return -1
376                 return 0
377
378         def fsck(self, numpart):
379                 # We autocorrect any failures and check if the fs is actually one we can check (currently ext2/ext3)
380                 partitionPath = self.partitionPath("1")
381
382                 # Lets activate the swap partition if exists
383                 self.activateswap()
384
385                 if numpart is not None:
386                         if numpart == 0:
387                                 partitionPath = self.dev_path
388                         if numpart >= 1:
389                                 partitionPath = self.partitionPath(str(numpart))
390
391                 partitionType = harddiskmanager.getBlkidPartitionType(partitionPath)
392
393                 res = -1
394                 if access(partitionPath, 0):
395                         if partitionType is not None and partitionType in ("ext2", "ext3"):
396                                 cmd = "fsck." + partitionType + " -f -p -C 0 " + partitionPath
397                                 res = system(cmd)
398
399                 # Lets deactivate the swap partition
400                 self.deactivateswap()
401
402                 return (res >> 8)
403
404         def killPartition(self):
405                 part = self.disk_path
406                 cmd = 'dd bs=4k count=3 if=/dev/zero of=' + part
407                 if harddiskmanager.KernelVersion >= "3.2":
408                         cmd = 'parted --script --align=min -- ' + part + ' mklabel msdos'
409                 if access(part, 0):
410                         res = system(cmd)
411                 else:
412                         res = 0
413                 print "[killPartition]",res,cmd
414                 return (res >> 8)
415
416         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")]
417
418         def initialize(self, isFstabMounted = False, numpart = None):
419                 if self.unmount(numpart) != 0:
420                         return -8
421                 # Udev tries to mount the partition immediately if there is an
422                 # old filesystem on it when fdisk reloads the partition table.
423                 # To prevent that, we overwrite the first sectors of the
424                 # partitions, if the partition existed before. This should work
425                 # for ext2/ext3 and also for GPT/EFI partitions.
426                 self.killPartition()
427
428                 if self.createPartition() != 0:
429                         return -1
430
431                 if self.mkfs() != 0:
432                         return -2
433
434                 # init the swap partition
435                 if harddiskmanager.KernelVersion >= "3.2":
436                         if not self.isRemovable:
437                                 if self.mkswap() != 0:
438                                         return -2
439                         # Call partprobe to inform the system about the partition table change.
440                         Console().ePopen(("partprobe", "partprobe", "-s"))
441
442                 if isFstabMounted:
443                         if self.mount() != 0:
444                                 return -3
445
446                 if self.createMovieFolder(isFstabMounted) != 0:
447                         return -4
448
449                 return 0
450
451         def check(self, isFstabMounted = False, numpart = None):
452
453                 if self.unmount(numpart) != 0:
454                         return -8
455
456                 res = self.fsck(numpart)
457                 if res & 2 == 2:
458                         return -6
459
460                 if res & 4 == 4:
461                         return -7
462
463                 if res != 0 and res != 1:
464                         # A sum containing 1 will also include a failure
465                         return -5
466
467                 if isFstabMounted:
468                         if self.mount() != 0:
469                                 return -3
470
471                 return 0
472
473         def getDeviceDir(self):
474                 return self.dev_path
475
476         def getDeviceName(self):
477                 return self.disk_path
478
479         # the HDD idle poll daemon.
480         # as some harddrives have a buggy standby timer, we are doing this by hand here.
481         # first, we disable the hardware timer. then, we check every now and then if
482         # any access has been made to the disc. If there has been no access over a specifed time,
483         # we set the hdd into standby.
484         def readStats(self):
485                 try:
486                         l = open("/sys/block/%s/stat" % self.device).read()
487                 except IOError:
488                         return -1,-1
489                 (nr_read, _, _, _, nr_write) = l.split()[:5]
490                 return int(nr_read), int(nr_write)
491
492         def startIdle(self):
493                 self.last_access = time.time()
494                 self.last_stat = 0
495                 from enigma import eTimer
496
497                 # disable HDD standby timer
498                 if self.bus() == "External":
499                         Console().ePopen(("sdparm", "sdparm", "--set=SCT=0", self.disk_path))
500                 else:
501                         Console().ePopen(("hdparm", "hdparm", "-S0", self.disk_path))
502                 self.timer = eTimer()
503                 self.timer.callback.append(self.runIdle)
504                 self.idle_running = True
505                 self.setIdleTime(self.max_idle_time) # kick the idle polling loop
506
507         def runIdle(self):
508                 if not self.max_idle_time:
509                         return
510                 t = time.time()
511
512                 idle_time = t - self.last_access
513
514                 stats = self.readStats()
515                 print "nr_read", stats[0], "nr_write", stats[1]
516                 l = sum(stats)
517                 print "sum", l, "prev_sum", self.last_stat
518
519                 if l != self.last_stat and l >= 0: # access
520                         print "hdd was accessed since previous check!"
521                         self.last_stat = l
522                         self.last_access = t
523                         idle_time = 0
524                         self.is_sleeping = False
525                 else:
526                         print "hdd IDLE!"
527
528                 print "[IDLE]", idle_time, self.max_idle_time, self.is_sleeping
529                 if idle_time >= self.max_idle_time and not self.is_sleeping:
530                         self.setSleep()
531
532         def setSleep(self):
533                 if self.bus() == "External":
534                         Console().ePopen(("sdparm", "sdparm", "--command=stop", self.disk_path))
535                 else:
536                         Console().ePopen(("hdparm", "hdparm", "-y", self.disk_path))
537                 self.is_sleeping = True
538
539         def setIdleTime(self, idle):
540                 self.max_idle_time = idle
541                 if self.idle_running:
542                         if not idle:
543                                 self.timer.stop()
544                         else:
545                                 self.timer.start(idle * 100, False)  # poll 10 times per period.
546
547         def isSleeping(self):
548                 return self.is_sleeping
549
550         def isIdle(self):
551                 return self.idle_running
552
553 class Partition:
554         def __init__(self, mountpoint, device = None, description = "", force_mounted = False):
555                 self.mountpoint = mountpoint
556                 self.description = description
557                 self.force_mounted = force_mounted
558                 self.is_hotplug = force_mounted # so far; this might change.
559                 self.device = device
560                 self.disc_path = None
561                 self.uuid = None
562                 self.isMountable = False
563                 self.isReadable = False
564                 self.isWriteable = False
565                 self.isInitialized = False
566                 self.fsType = None
567                 if self.device is not None:
568                         self.updatePartitionInfo()
569
570         def updatePartitionInfo(self, dstpath = "", dev = None ):
571                 curdir = getcwd()
572                 testpath = ""
573                 if dstpath != "" and dev is not None:
574                         self.device = dev
575                         testpath = dstpath + self.device
576
577                 if self.device is not None:
578                         if testpath == "":
579                                 testpath = "/autofs/" + self.device
580                         self.uuid = harddiskmanager.getPartitionUUID(self.device)
581                         try:
582                                 chdir(testpath)
583                                 self.isMountable = True
584                         except OSError:
585                                 pass
586                         if self.isMountable:
587                                 try:
588                                         listdir(testpath)
589                                         self.isReadable = True
590                                 except OSError:
591                                         pass
592                         if self.uuid is not None:
593                                 if self.fsType is None:
594                                         self.fsType = harddiskmanager.getBlkidPartitionType("/dev/" + self.device)
595                         if self.isReadable:
596                                 mountpoint, fsType, mountopt = harddiskmanager.getMountInfo("/dev/" + self.device)
597                                 if self.fsType is None and fsType is not None:
598                                         self.fsType = fsType
599                                 if mountopt is not None and mountopt == 'rw':
600                                         self.isWriteable = True
601                                         if not access(testpath, W_OK):
602                                                 self.isWriteable = False
603                         if self.isWriteable:
604                                 if access(testpath + "/movie", W_OK):
605                                         self.isInitialized = True
606                 else:
607                         self.uuid = None
608                         self.isMountable = False
609                         self.isReadable = False
610                         self.isWriteable = False
611                         self.isInitialized = False
612                         self.fsType = None
613                 chdir(curdir)
614
615         def stat(self):
616                 return statvfs(self.mountpoint)
617
618         def free(self):
619                 try:
620                         s = self.stat()
621                         return s.f_bavail * s.f_bsize
622                 except OSError:
623                         return None
624
625         def total(self):
626                 try:
627                         s = self.stat()
628                         return s.f_blocks * s.f_bsize
629                 except OSError:
630                         return None
631
632         def mounted(self):
633                 # THANK YOU PYTHON FOR STRIPPING AWAY f_fsid.
634                 # TODO: can os.path.ismount be used?
635                 if self.force_mounted:
636                         return True
637                 try:
638                         mounts = open("/proc/mounts")
639                 except IOError:
640                         return False
641
642                 lines = mounts.readlines()
643                 mounts.close()
644
645                 for line in lines:
646                         if line.split(' ')[1] == self.mountpoint:
647                                 return True
648                 return False
649
650
651 DEVICEDB_SR = \
652         {"dm8000":
653                 {
654                         "/devices/pci0000:01/0000:01:00.0/host0/target0:0:0/0:0:0:0": _("DVD Drive"),
655                         "/devices/pci0000:01/0000:01:00.0/host1/target1:0:0/1:0:0:0": _("DVD Drive"),
656                         "/devices/platform/brcm-ehci-1.1/usb2/2-1/2-1:1.0/host3/target3:0:0/3:0:0:0": _("DVD Drive"),
657                 },
658         "dm800":
659         {
660         },
661         "dm7025":
662         {
663         }
664         }
665
666 DEVICEDB = \
667         {"dm8000":
668                 {
669                         "/devices/pci0000:01/0000:01:00.0/host1/target1:0:0/1:0:0:0": _("SATA"),
670                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.1/1-1.1:1.0": _("Front USB"),
671                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.1/1-1.1.": _("Front USB"),
672                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.2/1-1.2:1.0": _("Back, upper USB"),
673                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.2/1-1.2.": _("Back, upper USB"),
674                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.3/1-1.3:1.0": _("Back, lower USB"),
675                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.3/1-1.3.": _("Back, lower USB"),
676                         "/devices/platform/brcm-ehci-1.1/usb2/2-1/2-1:1.0/": _("Internal USB"),
677                         "/devices/platform/brcm-ohci-1.1/usb4/4-1/4-1:1.0/": _("Internal USB"),
678                         "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.4/1-1.4.": _("Internal USB"),
679                 },
680         "dm7020hd":
681         {
682                 "/devices/pci0000:01/0000:01:00.0/host0/target0:0:0/0:0:0:0": _("SATA"),
683                 "/devices/pci0000:01/0000:01:00.0/host1/target1:0:0/1:0:0:0": _("eSATA"),
684                 "/devices/platform/brcm-ehci-1.1/usb2/2-1/2-1:1.0": _("Front USB"),
685                 "/devices/platform/brcm-ehci-1.1/usb2/2-1/2-1.": _("Front USB"),
686                 "/devices/platform/brcm-ehci.0/usb1/1-2/1-2:1.0": _("Back, upper USB"),
687                 "/devices/platform/brcm-ehci.0/usb1/1-2/1-2.": _("Back, upper USB"),
688                 "/devices/platform/brcm-ehci.0/usb1/1-1/1-1:1.0": _("Back, lower USB"),
689                 "/devices/platform/brcm-ehci.0/usb1/1-1/1-1.": _("Back, lower USB"),
690         },
691         "dm800":
692         {
693                 "/devices/pci0000:01/0000:01:00.0/host0/target0:0:0/0:0:0:0": _("SATA"),
694                 "/devices/platform/brcm-ehci.0/usb1/1-2/1-2:1.0": _("Upper USB"),
695                 "/devices/platform/brcm-ehci.0/usb1/1-1/1-1:1.0": _("Lower USB"),
696         },
697         "dm800se":
698         {
699                 "/devices/pci0000:01/0000:01:00.0/host0/target0:0:0/0:0:0:0": _("SATA"),
700                 "/devices/pci0000:01/0000:01:00.0/host1/target1:0:0/1:0:0:0": _("eSATA"),
701                 "/devices/platform/brcm-ehci.0/usb1/1-2/1-2:1.0": _("Upper USB"),
702                 "/devices/platform/brcm-ehci.0/usb1/1-1/1-1:1.0": _("Lower USB"),
703         },
704         "dm500hd":
705         {
706                 "/devices/pci0000:01/0000:01:00.0/host1/target1:0:0/1:0:0:0": _("eSATA"),
707                 "/devices/pci0000:01/0000:01:00.0/host0/target0:0:0/0:0:0:0": _("eSATA"),
708         },
709         "dm800sev2":
710         {
711                 "/devices/pci0000:01/0000:01:00.0/host0/target0:0:0/0:0:0:0": _("SATA"),
712                 "/devices/pci0000:01/0000:01:00.0/host1/target1:0:0/1:0:0:0": _("eSATA"),
713                 "/devices/platform/brcm-ehci.0/usb1/1-2/1-2:1.0": _("Upper USB"),
714                 "/devices/platform/brcm-ehci.0/usb1/1-1/1-1:1.0": _("Lower USB"),
715         },
716         "dm500hdv2":
717         {
718                 "/devices/pci0000:01/0000:01:00.0/host1/target1:0:0/1:0:0:0": _("eSATA"),
719                 "/devices/pci0000:01/0000:01:00.0/host0/target0:0:0/0:0:0:0": _("eSATA"),
720         },
721         "dm7025":
722         {
723                 "/devices/pci0000:00/0000:00:14.1/ide1/1.0": "Compact Flash", #hdc
724                 "/devices/pci0000:00/0000:00:14.1/ide0/0.0": "Internal Harddisk"
725         }
726         }
727
728
729 class HarddiskManager:
730         EVENT_MOUNT = "mount"
731         EVENT_UNMOUNT = "unmount"
732
733         def __init__(self):
734
735                 config.storage_options = ConfigSubsection()
736                 config.storage_options.default_device = ConfigText(default = "<undefined>")
737                 config.storage = ConfigSubDict()
738                 self.hdd = [ ]
739                 self.cd = ""
740                 self.partitions = [ ]
741                 self.devices_scanned_on_init = [ ]
742                 self.delayed_device_Notifier = [ ]
743                 self.onUnMount_Notifier = [ ]
744
745                 self.on_partition_list_change = CList()
746                 self.KernelVersion = self.getKernelVersion()
747
748                 # currently, this is just an enumeration of what's possible,
749                 # this probably has to be changed to support automount stuff.
750                 # still, if stuff is mounted into the correct mountpoints by
751                 # external tools, everything is fine (until somebody inserts
752                 # a second usb stick.)
753                 p = [
754                                         ("/media/hdd", _("Hard disk")),
755                                         ("/media/card", _("Card")),
756                                         ("/media/cf", _("Compact Flash")),
757                                         ("/media/mmc1", _("SD/MMC")),
758                                         ("/media/net", _("Network Mount")),
759                                         ("/media/ram", _("Ram Disk")),
760                                         ("/media/usb", _("USB Stick")),
761                                         ("/", _("Internal Flash"))
762                                 ]
763                 self.partitions.extend([ Partition(mountpoint = x[0], description = x[1]) for x in p ])
764
765         def getKernelVersion(self):
766                 version = "3.2"
767                 try:
768                         cmd = "uname -sr"
769                         for line in popen(cmd).read().split('\n'):
770                                 if line.find("Linux 3.2") == 0:
771                                         version = "3.2"
772                                 if line.find("Linux 2.6.18") == 0:
773                                         version = "2.6.18"
774                                 if line.find("Linux 2.6.12") == 0:
775                                         version = "2.6.12"
776                 except:
777                         print "error getting kernel version"
778                 return version
779
780         def getBlockDevInfo(self, blockdev):
781                 devpath = "/sys/block/" + blockdev
782                 error = False
783                 removable = False
784                 blacklisted = False
785                 is_cdrom = False
786                 partitions = []
787                 try:
788                         removable = bool(int(readFile(devpath + "/removable")))
789                         dev = int(readFile(devpath + "/dev").split(':')[0])
790                         if dev in (1, 7, 31): # ram, loop, mtdblock
791                                 blacklisted = True
792                         if blockdev[0:2] == 'sr':
793                                 is_cdrom = True
794                         if blockdev[0:2] == 'hd':
795                                 try:
796                                         media = readFile("/proc/ide/%s/media" % blockdev)
797                                         if "cdrom" in media:
798                                                 is_cdrom = True
799                                 except IOError:
800                                         error = True
801                         # check for partitions
802                         if not is_cdrom:
803                                 for partition in listdir(devpath):
804                                         if partition[0:len(blockdev)] != blockdev:
805                                                 continue
806                                         partitions.append(partition)
807                         else:
808                                 self.cd = blockdev
809                 except IOError:
810                         error = True
811                 # check for medium
812                 medium_found = True
813                 try:
814                         open("/dev/" + blockdev).close()
815                 except IOError, err:
816                         if err.errno == 159: # no medium present
817                                 medium_found = False
818                 return error, blacklisted, removable, is_cdrom, partitions, medium_found
819
820         def enumerateBlockDevices(self):
821                 self.setupConfigEntries(initial_call = True)
822                 print "enumerating block devices..."
823                 for blockdev in listdir("/sys/block"):
824                         error, blacklisted, removable, is_cdrom, partitions, medium_found = self.addHotplugPartition(blockdev)
825                         if not error and not blacklisted:
826                                 if medium_found:
827                                         for part in partitions:
828                                                 self.addHotplugPartition(part)
829                                 self.devices_scanned_on_init.append((blockdev, removable, is_cdrom, medium_found))
830                                 print "[enumerateBlockDevices] devices_scanned_on_init:",self.devices_scanned_on_init
831
832         def getAutofsMountpoint(self, device):
833                 return "/autofs/%s/" % (device)
834
835         def is_hard_mounted(self, device):
836                 mounts = file('/proc/mounts').read().split('\n')
837                 for x in mounts:
838                         if x.find('/autofs') == -1 and x.find(device) != -1:
839                                 #print "is_hard_mounted:",device
840                                 return True
841                 return False
842
843         def get_mountdevice(self, mountpoint):
844                 mounts = file('/proc/mounts').read().split('\n')
845                 for x in mounts:
846                         if not x.startswith('/'):
847                                 continue
848                         device, mp = x.split()[0:2]
849                         if mp == mountpoint:
850                                 #print "get_mountdevice for '%s' -> %s " % (device, mp)
851                                 return device
852                 return None
853
854         def get_mountpoint(self, mountpath):
855                 mounts = file('/proc/mounts').read().split('\n')
856                 for x in mounts:
857                         if not x.startswith('/'):
858                                 continue
859                         path, mp = x.split()[0:2]
860                         if path == mountpath:
861                                 #print "get_mountpoint for '%s' -> %s " % (path, mp)
862                                 return mp
863                 return None
864
865         def is_uuidpath_mounted(self, uuidpath, mountpoint):
866                 mounts = file('/proc/mounts').read().split('\n')
867                 for x in mounts:
868                         if not x.startswith('/'):
869                                 continue
870                         path, mp = x.split()[0:2]
871                         if (path == uuidpath and mp == mountpoint):
872                                 #print "is_uuid_mounted:'%s' for: %s " % (path, mp)
873                                 return True
874                 return False
875
876         def is_fstab_mountpoint(self, device, mountpoint):
877                 mounts = file('/etc/fstab').read().split('\n')
878                 for x in mounts:
879                         if not x.startswith('/'):
880                                 continue
881                         dev, mp = x.split()[0:2]
882                         if (dev == device and mp == mountpoint):
883                                 #print "is_fstab_mountpoint:'%s' for: %s " % (mp, dev)
884                                 return True
885                 return False
886
887         def get_fstab_mountstate(self, device, mountpoint):
888                 mounts = file('/etc/fstab').read().split('\n')
889                 for x in mounts:
890                         if not x.startswith('/'):
891                                 continue
892                         dev, mp, ms = x.split()[0:3]
893                         if (dev == device and mp == mountpoint):
894                                 #print "got_fstab_mountstate:'%s' for: %s - %s" % (ms, dev, mp)
895                                 return ms
896                 return False
897
898         def get_fstab_mountpoint(self, device):
899                 mounts = file('/etc/fstab').read().split('\n')
900                 for x in mounts:
901                         if not x.startswith('/'):
902                                 continue
903                         dev, mp = x.split()[0:2]
904                         if device == dev:
905                                 #print "got_fstab_mountpoint:'%s' for: %s" % (mp, dev)
906                                 return mp
907                 return None
908
909         def modifyFstabEntry(self, partitionPath, mountpoint, mode = "add_deactivated"):
910                 try:
911                         alreadyAdded = self.is_fstab_mountpoint(partitionPath, mountpoint)
912                         oldLine = None
913                         mounts = file('/etc/fstab').read().split('\n')
914                         fp = file("/etc/fstab", 'w')
915                         fp.write("#automatically edited by enigma2, " + str(time.strftime( "%a" + ", " + "%d " + "%b" + " %Y %H:%M:%S", time.localtime(time.time() ))) + "\n")
916                         for x in mounts:
917                                 if (x.startswith(partitionPath) and mountpoint in x):
918                                         oldLine = x
919                                         continue
920                                 if len(x):
921                                         if x.startswith('#automatically'):
922                                                 continue
923                                         fp.write(x + "\n")
924                         if not alreadyAdded:
925                                 if mode == "add_deactivated":
926                                         print "modifyFstabEntry - add_deactivated:", partitionPath, mountpoint
927                                         fp.write(partitionPath + "\t" + mountpoint + "\tnoauto\tdefaults\t0 0\n")
928                                 elif mode == "add_activated":
929                                         print "modifyFstabEntry - add_activated:", partitionPath, mountpoint
930                                         fp.write(partitionPath + "\t" + mountpoint + "\tauto\tdefaults\t0 0\n")
931                         else:
932                                 if mode == "add_deactivated":
933                                         if oldLine is not None:
934                                                 if "noauto" in oldLine:
935                                                         fp.write(oldLine + "\n")
936                                                 else:
937                                                         fp.write(oldLine.replace("auto","noauto") + "\n")
938                                 elif mode == "add_activated":
939                                         if oldLine is not None:
940                                                 if "noauto" in oldLine:
941                                                         fp.write(oldLine.replace("noauto","auto") + "\n")
942                                                 else:
943                                                         fp.write(oldLine + "\n")
944                                 elif mode == "remove":
945                                         if oldLine is not None:
946                                                 pass
947                         fp.close()
948                 except:
949                         print "error adding fstab entry for: %s" % (partitionPath)
950
951         def addHotplugPartition(self, device, physdev = None):
952                 if not physdev:
953                         dev, part = self.splitDeviceName(device)
954                         try:
955                                 physdev = path.realpath('/sys/block/' + dev + '/device')[4:]
956                         except OSError:
957                                 physdev = dev
958                                 print "couldn't determine blockdev physdev for device", device
959
960                 error, blacklisted, removable, is_cdrom, partitions, medium_found = self.getBlockDevInfo(device)
961                 print "found block device '%s':" % device,
962
963                 if blacklisted:
964                         print "blacklisted"
965                 else:
966                         if error:
967                                 print "error querying properties"
968                         elif not medium_found:
969                                 print "no medium"
970                         else:
971                                 print "ok, removable=%s, cdrom=%s, partitions=%s" % (removable, is_cdrom, partitions)
972
973                         l = len(device)
974                         if l:
975                                 # see if this is a harddrive or removable drive (usb stick/cf/sd)
976                                 if not device[l-1].isdigit() and not is_cdrom:
977                                         if self.getHDD(device) is None and medium_found:
978                                                 if removable:
979                                                         self.hdd.append(Harddisk(device, True))
980                                                 else:
981                                                         self.hdd.append(Harddisk(device, False))
982                                         self.hdd.sort()
983                                         SystemInfo["Harddisk"] = len(self.hdd) > 0
984                                 if (not removable or medium_found):
985                                         self.addDevicePartition(device, physdev)
986
987                 return error, blacklisted, removable, is_cdrom, partitions, medium_found
988
989         def removeHotplugPartition(self, device):
990                 mountpoint = self.getAutofsMountpoint(device)
991                 uuid = self.getPartitionUUID(device)
992                 print "[removeHotplugPartition] for device:'%s' uuid:'%s' and mountpoint:'%s'" % (device, uuid, mountpoint)
993
994                 # we must umount autofs first because autofs mounts with "sync" option
995                 # and the real mount than also mounts with this option
996                 # this is realy bad for the performance!
997                 forceAutofsUmount(device)
998
999                 p = self.getPartitionbyDevice(device)
1000                 if p is None:
1001                         p = self.getPartitionbyMountpoint(mountpoint)
1002                 if p is not None:
1003                         if uuid is None and p.uuid is not None:
1004                                 print "[removeHotplugPartition] we got uuid:'%s' but have:'%s'" % (uuid,p.uuid)
1005                                 uuid = p.uuid
1006                                 self.unmountPartitionbyMountpoint(p.mountpoint)
1007                         if uuid is not None and config.storage.get(uuid, None) is not None:
1008                                 self.unmountPartitionbyUUID(uuid)
1009                                 if not config.storage[uuid]['enabled'].value:
1010                                         del config.storage[uuid]
1011                                         config.storage.save()
1012                                         config.save()
1013                                         print "[removeHotplugPartition] - remove uuid %s from temporary drive add" % (uuid)
1014                         if p.mountpoint != "/media/hdd":
1015                                 self.partitions.remove(p)
1016                                 self.on_partition_list_change("remove", p)
1017
1018                 l = len(device)
1019                 if l and not device[l-1].isdigit():
1020                         for hdd in self.hdd:
1021                                 if hdd.device == device:
1022                                         hdd.stop()
1023                                         self.hdd.remove(hdd)
1024                                         break
1025                         SystemInfo["Harddisk"] = len(self.hdd) > 0
1026
1027                         #call the notifier only after we have fully removed the disconnected drive
1028                         for callback in self.delayed_device_Notifier:
1029                                 try:
1030                                         callback(device, "remove_delayed" )
1031                                 except AttributeError:
1032                                         self.delayed_device_Notifier.remove(callback)
1033
1034         def addDevicePartition(self, device, physdev):
1035                 # device is the device name, without /dev
1036                 # physdev is the physical device path, which we (might) use to determine the userfriendly name
1037                 description = self.getUserfriendlyDeviceName(device, physdev)
1038                 device_mountpoint = self.getAutofsMountpoint(device)
1039                 uuid = self.getPartitionUUID(device)
1040                 print "[addDevicePartition] device:'%s' with UUID:'%s'" % (device, uuid)
1041                 if config.storage.get(uuid, None) is not None:
1042                         if config.storage[uuid]['enabled'].value and config.storage[uuid]['mountpoint'].value != "":
1043                                 device_mountpoint = config.storage[uuid]['mountpoint'].value
1044
1045                 if uuid is not None:
1046                         if config.storage.get(uuid, None) is None:
1047                                 tmp = self.getPartitionbyDevice(device)
1048                                 if tmp is not None:
1049                                         if uuid != tmp.uuid and tmp.uuid == config.storage_options.default_device.value and tmp.mountpoint == "/media/hdd": #default hdd re/initialize
1050                                                 tmp.device = None
1051                                                 tmp.updatePartitionInfo()
1052                                 self.setupConfigEntries(initial_call = False, dev = device)
1053                         else:
1054                                 tmp = self.getPartitionbyMountpoint(device_mountpoint)
1055                                 if tmp is not None and (tmp.uuid != uuid or tmp.mountpoint != device_mountpoint):
1056                                         self.storageDeviceChanged(uuid)
1057
1058                 p = self.getPartitionbyMountpoint(device_mountpoint)
1059                 if p is not None:
1060                         if uuid is not None:
1061                                 if p.uuid is not None and p.uuid != uuid:
1062                                         if config.storage.get(p.uuid, None) is not None:
1063                                                 del config.storage[p.uuid] #delete old uuid reference entries
1064                                                 config.storage.save()
1065                                                 config.save()
1066                         p.updatePartitionInfo()
1067                 else:
1068                         forced = True
1069                         if uuid is not None:
1070                                 cfg_uuid = config.storage.get(uuid, None)
1071                                 if cfg_uuid is not None:
1072                                         if cfg_uuid['enabled'].value:
1073                                                 forced = False
1074                                         else:
1075                                                 device_mountpoint = self.getAutofsMountpoint(device)
1076                         x = self.getPartitionbyDevice(device)
1077                         if x is None:
1078                                 p = Partition(mountpoint = device_mountpoint, description = description, force_mounted = forced, device = device)
1079                                 self.partitions.append(p)
1080                                 self.on_partition_list_change("add", p)
1081                         else:   # found old partition entry
1082                                 if config.storage.get(x.uuid, None) is not None:
1083                                         del config.storage[x.uuid] #delete old uuid reference entries
1084                                         config.storage.save()
1085                                         config.save()
1086                                 x.mountpoint = device_mountpoint
1087                                 x.force_mounted = True
1088                                 x.updatePartitionInfo()
1089
1090                 for callback in self.delayed_device_Notifier:
1091                         try:
1092                                 callback(device, "add_delayed" )
1093                         except AttributeError:
1094                                 self.delayed_device_Notifier.remove(callback)
1095
1096         def HDDCount(self):
1097                 return len(self.hdd)
1098
1099         def HDDList(self):
1100                 list = [ ]
1101                 for hd in self.hdd:
1102                         hdd = hd.model() + " - " + hd.bus()
1103                         cap = hd.capacity()
1104                         if cap != "":
1105                                 hdd += " (" + cap + ")"
1106                         list.append((hdd, hd))
1107                 return list
1108
1109         def HDDEnabledCount(self):
1110                 cnt = 0
1111                 for uuid, cfg in config.storage.items():
1112                         #print "uuid", uuid, "cfg", cfg
1113                         if cfg["enabled"].value:
1114                                 cnt += 1
1115                 return cnt
1116
1117         def getHDD(self, part):
1118                 for hdd in self.hdd:
1119                         if hdd.device == part[:3]:
1120                                 return hdd
1121                 return None
1122
1123         def getCD(self):
1124                 return self.cd
1125
1126         def getMountInfo(self, device):
1127                 dev = mountpoint = fstype = mountopt = None
1128                 try:
1129                         mounts = file('/proc/mounts').read().split('\n')
1130                         for x in mounts:
1131                                 if not x.startswith('/'):
1132                                         continue
1133                                 if x.startswith(device):
1134                                         data = x.split(',')
1135                                         dev, mountpoint, fstype, mountopt = data[0].split(None,4)
1136                 except:
1137                         print "error getting mount info"
1138                 #print "getMountInfo:",mountpoint, fstype, mountopt
1139                 return mountpoint, fstype, mountopt
1140
1141         def getFdiskInfo(self, devname):
1142                 size = sizeg = fstype = sys = sectors = None
1143                 cmd = "fdisk -l /dev/" + devname
1144                 try:
1145                         for line in popen(cmd).read().split('\n'):
1146                                 if line.startswith("Found valid GPT"):
1147                                         sys = "GPT"
1148                                 if line.startswith("Disk"):
1149                                         sizeobj = re.search(r', ((?:[a-zA-Z0-9])*) bytes', line)
1150                                         if sizeobj:
1151                                                 size = sizeobj.group(1)
1152                                         sizegobj = re.search(r': ((?:[0-9.0-9])*) GB', line)
1153                                         if sizegobj:
1154                                                 sizeg = sizegobj.group(1)
1155                                         sectorsobj = re.search(r': ((?:[0-9.0-9])*) sectors', line)
1156                                         if sectorsobj:
1157                                                 sectors = sectorsobj.group(1)
1158                                 if not line.startswith('/'):
1159                                         continue
1160                                 if line.startswith("/dev/" + devname):
1161                                         a,b,c,d, fstype, sys = line.split(None,5)
1162                 except:
1163                         print "error getting fdisk device info"
1164                 #print "getFdiskInfo:",devname, fstype, sys, size, sizeg, sectors
1165                 return fstype, sys, size, sizeg, sectors
1166
1167         def getBlkidPartitionType(self, device):
1168                 #print "getBlkidPartitionType",device
1169                 fstype = None
1170                 cmd = "blkid " + str(device)
1171                 try:
1172                         for line in popen(cmd).read().split('\n'):
1173                                 if not line.startswith(device):
1174                                         continue
1175                                 fstobj = re.search(r' TYPE="((?:[^"\\]|\\.)*)"', line)
1176                                 if fstobj:
1177                                         fstype = fstobj.group(1)
1178                 except:
1179                         print "error getting blkid partition type"
1180
1181                 #print "getBlkidPartitionType:",device, fstype
1182                 return fstype
1183
1184         def getLinkPath(self,link):
1185                 if path.islink(link):
1186                         p = path.normpath(readlink(link))
1187                         if path.isabs(p):
1188                                 return p
1189                         return path.join(path.dirname(link), p)
1190
1191         def getRealPath(self, dstpath):
1192                 p = self.getLinkPath(dstpath)
1193                 if p:
1194                         return p
1195                 return path.realpath(dstpath)
1196
1197         def isMount(self, mountdir):
1198                 return path.ismount( self.getRealPath(mountdir) )
1199
1200         def _inside_mountpoint(self, filename):
1201                 #print "is mount? '%s'" % filename
1202                 if filename == "":
1203                         return False
1204                 if filename == "/":
1205                         return False
1206                 if path.ismount(filename):
1207                         return True
1208                 return self._inside_mountpoint("/".join(filename.split("/")[:-1]))
1209
1210         def inside_mountpoint(self,filename):
1211                 return self._inside_mountpoint(path.realpath(filename))
1212
1213         def isUUIDpathFsTabMount(self, uuid, mountpath):
1214                 uuidpartitionPath = "/dev/disk/by-uuid/" + uuid
1215                 if self.is_hard_mounted(uuidpartitionPath) and self.is_fstab_mountpoint(uuidpartitionPath, mountpath):
1216                         if self.get_fstab_mountstate(uuidpartitionPath, mountpath) == 'auto':
1217                                 return True
1218                 return False
1219
1220         def isPartitionpathFsTabMount(self, uuid, mountpath):
1221                 dev = self.getDeviceNamebyUUID(uuid)
1222                 if dev is not None:
1223                         partitionPath = "/dev/" + str(dev)
1224                         if self.is_hard_mounted(partitionPath) and self.is_fstab_mountpoint(partitionPath, mountpath):
1225                                 if self.get_fstab_mountstate(partitionPath, mountpath) == 'auto':
1226                                         return True
1227                 return False
1228
1229         def getPartitionVars(self, hd, partitionNum = False):
1230                 #print "getPartitionVars for hdd:'%s' and partitionNum:'%s'" % (hd.device, partitionNum)
1231                 hdd = hd
1232                 numPartitions = hdd.numPartitions()
1233                 uuid = partitionPath = uuidPath = deviceName = None
1234                 if partitionNum is False:
1235                         if numPartitions == 0:
1236                                 deviceName = hdd.device
1237                                 uuid = self.getPartitionUUID(deviceName)
1238                                 partitionPath = hdd.dev_path
1239                         if numPartitions == 1:
1240                                 deviceName = hdd.device + str(numPartitions)
1241                                 uuid = self.getPartitionUUID(deviceName)
1242                                 partitionPath = hdd.partitionPath(str(numPartitions))
1243                         else: #just in case, we should never get here
1244                                 deviceName = hdd.device
1245                                 partitionPath = hdd.dev_path
1246                 else:
1247                         deviceName = hdd.device + str(partitionNum)
1248                         uuid = self.getPartitionUUID(deviceName)
1249                         partitionPath = hdd.partitionPath(str(partitionNum))
1250                 if uuid is not None:
1251                         uuidPath = "/dev/disk/by-uuid/" + uuid
1252                 return deviceName, uuid, numPartitions, partitionNum, uuidPath, partitionPath
1253
1254         def cleanupMountpath(self, devname):
1255                 return devname.strip().replace(' ','').replace('-','').replace('_','').replace('.','')
1256
1257         def suggestDeviceMountpath(self,uuid):
1258                 p = self.getPartitionbyUUID(uuid)
1259                 if p is not None:
1260                         hdd = self.getHDD(p.device)
1261                         if hdd is not None:
1262                                 val = self.cleanupMountpath(str(hdd.model(model_only = True)))
1263                                 cnt = 0
1264                                 for dev in self.hdd:
1265                                         tmpval = self.cleanupMountpath(str(dev.model(model_only = True)))
1266                                         if tmpval == val:
1267                                                 cnt +=1
1268                                 if cnt <=1:
1269                                         cnt = 0
1270                                         for uid in config.storage.keys():
1271                                                 if uid == uuid:
1272                                                         cnt += 1
1273                                                         continue
1274                                                 data = config.storage[uid]["device_description"].value.split(None,1)
1275                                                 tmpval = self.cleanupMountpath(data[0])
1276                                                 if tmpval == val or tmpval.endswith(val):
1277                                                         cnt += 1
1278                                 if cnt >= 2:
1279                                         val += "HDD" + str(cnt)
1280                                 partNum = p.device[3:]
1281                                 if hdd.numPartitions() == 2 and partNum == "1":
1282                                         part2Type = self.getBlkidPartitionType(hdd.partitionPath("2"))
1283                                         if part2Type is not None and part2Type != "swap":
1284                                                 val += "Part" + str(partNum)
1285                                 else:
1286                                         if str(partNum).isdigit():
1287                                                 val += "Part" + str(partNum)
1288                                 print "suggestDeviceMountpath for uuid: '%s' -> '%s'" %(uuid,val)
1289                                 return "/media/" + val
1290                 else:
1291                         mountpath = ""
1292                         uuid_cfg = config.storage.get(uuid, None)
1293                         if uuid_cfg is not None:
1294                                 if uuid_cfg["mountpoint"].value != "" and uuid_cfg["mountpoint"].value != "/media/hdd":
1295                                         mountpath = uuid_cfg["mountpoint"].value
1296                                 else:
1297                                         if uuid_cfg["device_description"].value != "":
1298                                                 tmp = uuid_cfg["device_description"].value.split(None,1)
1299                                                 mountpath = "/media/" + self.cleanupMountpath(tmp[0])
1300                         if mountpath != "":
1301                                 cnt = 0
1302                                 for uid in config.storage.keys():
1303                                         if config.storage[uid]["mountpoint"].value != "" and config.storage[uid]["mountpoint"].value != "/media/hdd":
1304                                                 tmp = config.storage[uid]["mountpoint"].value
1305                                         else:
1306                                                 data = config.storage[uid]["device_description"].value.split(None,1)
1307                                                 mountpath = "/media/" + self.cleanupMountpath(data[0])
1308                                         if tmp == mountpath:
1309                                                 cnt += 1
1310                                 if cnt >= 2:
1311                                         mountpath += "HDD" + str(cnt)
1312                                 return mountpath
1313                 return ""
1314
1315         def changeStorageDevice(self, uuid = None, action = None , mountData = None ):
1316                 # mountData should be [oldenable,oldmountpath, newenable,newmountpath]
1317                 print "[changeStorageDevice] uuid:'%s' - action:'%s' - mountData:'%s'" %(uuid, action, mountData)
1318                 currentDefaultStorageUUID = config.storage_options.default_device.value
1319                 print "[changeStorageDevice]: currentDefaultStorageUUID:",currentDefaultStorageUUID
1320                 successfully = False
1321                 def_mp = "/media/hdd"
1322                 cur_default_newmp = new_default_newmp = old_cur_default_mp = old_new_default_mp = ""
1323                 cur_default = new_default = None
1324                 cur_default_dev = new_default_dev = None
1325                 cur_default_cfg = new_default_cfg = None
1326                 old_cur_default_enabled = old_new_default_enabled = False
1327
1328                 if action == "mount_default":
1329                         if currentDefaultStorageUUID != "<undefined>" and currentDefaultStorageUUID != uuid:
1330                                 cur_default = self.getDefaultStorageDevicebyUUID(currentDefaultStorageUUID)
1331                         new_default = self.getPartitionbyUUID(uuid)
1332                         if cur_default is not None:
1333                                 cur_default_cfg = config.storage.get(currentDefaultStorageUUID, None)
1334                         new_default_cfg = config.storage.get(uuid, None)
1335                         if new_default is not None:
1336                                 new_default_dev = new_default.device
1337                                 if currentDefaultStorageUUID != "<undefined>" and currentDefaultStorageUUID != uuid:
1338                                         cur_default_newmp = self.suggestDeviceMountpath(currentDefaultStorageUUID)
1339                                 if cur_default is not None:
1340                                         cur_default_dev = cur_default.device
1341                                 if new_default_cfg is not None:
1342                                         old_new_default_enabled = new_default_cfg["enabled"].value
1343                                         old_new_default_mp = new_default_cfg["mountpoint"].value
1344                                         #[oldmountpath, oldenable, newmountpath, newenable]
1345                                         if mountData is not None and isinstance(mountData, (list, tuple)):
1346                                                 old_new_default_enabled = mountData[1]
1347                                                 old_new_default_mp = mountData[0]
1348                                         if cur_default_cfg is not None and path.exists(def_mp) and self.isMount(def_mp) and cur_default_cfg["enabled"].value and cur_default_cfg["mountpoint"].value == def_mp:
1349                                                 old_cur_default_enabled = cur_default_cfg["enabled"].value
1350                                                 old_cur_default_mp = cur_default_cfg["mountpoint"].value
1351                                                 self.unmountPartitionbyMountpoint(def_mp)
1352                                         if not path.exists(def_mp) or (path.exists(def_mp) and not self.isMount(def_mp)) or not self.isPartitionpathFsTabMount(uuid, def_mp):
1353                                                 if cur_default_cfg is not None:
1354                                                         cur_default_cfg["mountpoint"].value = cur_default_newmp
1355                                                 if cur_default_dev is not None:
1356                                                         self.setupConfigEntries(initial_call = False, dev = cur_default_dev)
1357                                                 if cur_default_dev is None or (path.exists(cur_default_newmp) and self.isMount(cur_default_newmp)):
1358                                                         if new_default_cfg["enabled"].value and path.exists(new_default_cfg["mountpoint"].value) and self.isMount(new_default_cfg["mountpoint"].value):
1359                                                                 self.unmountPartitionbyMountpoint(new_default_cfg["mountpoint"].value, new_default_dev )
1360                                                         if not new_default_cfg["enabled"].value or not self.isMount(new_default_cfg["mountpoint"].value):
1361                                                                 new_default_cfg["mountpoint"].value = def_mp
1362                                                                 new_default_cfg["enabled"].value = True
1363                                                                 config.storage_options.default_device.value = uuid  #temporary assign the default storage uuid
1364                                                                 self.storageDeviceChanged(uuid)
1365                                                                 config.storage_options.default_device.value = currentDefaultStorageUUID  #reassign the original default storage uuid
1366                                                                 new_default = self.getPartitionbyMountpoint(def_mp)
1367                                                                 if cur_default_cfg is None and cur_default_newmp is not "": #currentdefault was offline
1368                                                                         cur_default_cfg = config.storage.get(currentDefaultStorageUUID, None)
1369                                                                 if cur_default_cfg is not None:
1370                                                                         old_cur_default_enabled = cur_default_cfg["enabled"].value
1371                                                                         old_cur_default_mp = cur_default_cfg["mountpoint"].value
1372                                                                         cur_default_cfg["mountpoint"].value = cur_default_newmp
1373                                                                 if new_default is not None and new_default_cfg["mountpoint"].value == def_mp and path.exists(def_mp) and self.isMount(def_mp) and new_default.mountpoint == def_mp:
1374                                                                         # reverify if the movie folder was created correctly
1375                                                                         if not path.exists(resolveFilename(SCOPE_HDD)):
1376                                                                                 print "default movie folder still missing...try again to create it."
1377                                                                                 try:
1378                                                                                         makedirs(resolveFilename(SCOPE_HDD))
1379                                                                                 except OSError:
1380                                                                                         pass
1381                                                                         if path.exists(resolveFilename(SCOPE_HDD)):
1382                                                                                 successfully = True
1383                                                                                 config.storage_options.default_device.value = uuid
1384                                                                                 if new_default_cfg is not None:
1385                                                                                         new_default_cfg.save()
1386                                                                                 if cur_default_cfg is not None:
1387                                                                                         cur_default_cfg.save()
1388                 if action == "mount_only":
1389                         new_default = self.getPartitionbyUUID(uuid)
1390                         new_default_cfg = config.storage.get(uuid, None)
1391                         if new_default is not None:
1392                                 new_default_dev = new_default.device
1393                                 new_default_newmp = self.suggestDeviceMountpath(uuid)
1394                                 if new_default_cfg is not None:
1395                                         old_new_default_enabled = new_default_cfg["enabled"].value
1396                                         old_new_default_mp = new_default_cfg["mountpoint"].value
1397                                         #[oldmountpath, oldenable, newmountpath, newenable]
1398                                         if mountData is not None and isinstance(mountData, (list, tuple)):
1399                                                 old_new_default_enabled = mountData[1]
1400                                                 old_new_default_mp = mountData[0]
1401                                                 new_default_newmp = mountData[2]
1402                                         if old_new_default_enabled and path.exists(def_mp) and self.isMount(def_mp) and old_new_default_mp == def_mp:
1403                                                 if uuid == currentDefaultStorageUUID:
1404                                                         self.unmountPartitionbyMountpoint(def_mp) #current partition is default, unmount!
1405                                         if old_new_default_enabled and old_new_default_mp != "" and old_new_default_mp != def_mp and path.exists(old_new_default_mp) and self.isMount(old_new_default_mp):
1406                                                 self.unmountPartitionbyMountpoint(old_new_default_mp, new_default_dev) #current partition is already mounted atm. unmount!
1407                                         if not new_default_cfg["enabled"].value or old_new_default_mp == "" or (new_default_cfg["enabled"].value and path.exists(old_new_default_mp) and not self.isMount(old_new_default_mp)):
1408                                                 new_default_cfg["enabled"].value = True
1409                                                 new_default_cfg["mountpoint"].value = new_default_newmp
1410                                                 if path.exists(new_default_newmp) and self.isMount(new_default_newmp):
1411                                                         tmppath = self.get_mountdevice(new_default_newmp)
1412                                                         if tmppath is not None and tmppath == "/dev/disk/by-uuid/" + uuid:
1413                                                                 self.unmountPartitionbyMountpoint(new_default_newmp)
1414                                                 x = None
1415                                                 if new_default_dev is not None:
1416                                                         x = self.getPartitionbyDevice(new_default_dev)
1417                                                 if x is None:
1418                                                         self.setupConfigEntries(initial_call = False, dev = new_default_dev)
1419                                                 else:
1420                                                         self.storageDeviceChanged(uuid)
1421                                                 new_default = self.getPartitionbyUUID(uuid)
1422                                                 if new_default is not None and path.exists(new_default_newmp) and self.isMount(new_default_newmp):
1423                                                         successfully = True
1424                                                         if uuid == currentDefaultStorageUUID:
1425                                                                 config.storage_options.default_device.value = "<undefined>"
1426                                                         new_default_cfg.save()
1427                 if action == "unmount":
1428                         new_default = self.getPartitionbyUUID(uuid)
1429                         new_default_cfg = config.storage.get(uuid, None)
1430                         if new_default is not None:
1431                                 new_default_dev = new_default.device
1432                                 if new_default_cfg is not None and new_default_cfg["mountpoint"].value == new_default.mountpoint:
1433                                         old_new_default_mp = new_default_cfg["mountpoint"].value
1434                                         old_new_default_enabled = new_default_cfg["enabled"].value
1435                                         #[oldmountpath, oldenable, newmountpath, newenable]
1436                                         if mountData is not None and isinstance(mountData, (list, tuple)):
1437                                                 old_new_default_enabled = mountData[1]
1438                                                 old_new_default_mp = mountData[0]
1439                                 if new_default_cfg is not None and path.exists(old_new_default_mp) and self.isMount(old_new_default_mp):
1440                                         if uuid == currentDefaultStorageUUID:
1441                                                 self.unmountPartitionbyMountpoint(old_new_default_mp)
1442                                         else:
1443                                                 self.unmountPartitionbyMountpoint(old_new_default_mp, new_default_dev)
1444                                 if path.exists(old_new_default_mp) and not self.isMount(old_new_default_mp):
1445                                         new_default_cfg["mountpoint"].value = ""
1446                                         new_default_cfg["enabled"].value = False
1447                                         self.setupConfigEntries(initial_call = False, dev = new_default_dev)
1448                                         if path.exists(old_new_default_mp) and not self.isMount(old_new_default_mp):
1449                                                 successfully = True
1450                                                 new_default_cfg.save()
1451                                                 if uuid == currentDefaultStorageUUID:
1452                                                         config.storage_options.default_device.value = "<undefined>"
1453                 if not successfully:
1454                         print "<< not successfully >>"
1455                         if cur_default_cfg is not None:
1456                                 cur_default_cfg["mountpoint"].value = old_cur_default_mp
1457                                 cur_default_cfg["enabled"].value = old_cur_default_enabled
1458                                 cur_default_cfg.save()
1459                                 if currentDefaultStorageUUID != "<undefined>":
1460                                         self.storageDeviceChanged(currentDefaultStorageUUID)
1461                         if new_default_cfg is not None:
1462                                 new_default_cfg["mountpoint"].value = old_new_default_mp
1463                                 new_default_cfg["enabled"].value = old_new_default_enabled
1464                                 new_default_cfg.save()
1465                                 self.storageDeviceChanged(uuid)
1466                 config.storage_options.default_device.save()
1467                 config.storage_options.save()
1468                 config.storage.save()
1469                 config.save()
1470                 configfile.save()
1471                 print "changeStorageDevice default is now:",config.storage_options.default_device.value
1472                 return successfully
1473
1474         def isConfiguredStorageDevice(self,uuid):
1475                 cfg_uuid = config.storage.get(uuid, None)
1476                 if cfg_uuid is not None and cfg_uuid["enabled"].value:
1477                         #print "isConfiguredStorageDevice:",uuid
1478                         return True
1479                 return False
1480
1481         def isDefaultStorageDeviceActivebyUUID(self, uuid):
1482                 p = self.getDefaultStorageDevicebyUUID(uuid)
1483                 if p is not None and p.uuid == uuid:
1484                         #print "isDefaultStorageDeviceActivebyUUID--for UUID:->",uuid,p.description, p.device, p.mountpoint, p.uuid
1485                         return True
1486                 return False
1487
1488         def getDefaultStorageDevicebyUUID(self, uuid):
1489                 for p in self.getConfiguredStorageDevices():
1490                         if p.uuid == uuid:
1491                                 #print "getDefaultStorageDevicebyUUID--p:",uuid, p.description, p.device, p.mountpoint, p.uuid
1492                                 return p
1493                 return None
1494
1495         def getConfiguredStorageDevices(self):
1496                 parts = [x for x in self.partitions if (x.uuid is not None and x.mounted() and self.isConfiguredStorageDevice(x.uuid))]
1497                 return [x for x in parts]
1498
1499         def getMountedPartitions(self, onlyhotplug = False):
1500                 parts = [x for x in self.partitions if (x.is_hotplug or not onlyhotplug) and x.mounted()]
1501                 devs = set([x.device for x in parts])
1502                 for devname in devs.copy():
1503                         if not devname:
1504                                 continue
1505                         dev, part = self.splitDeviceName(devname)
1506                         if part and dev in devs: # if this is a partition and we still have the wholedisk, remove wholedisk
1507                                 devs.remove(dev)
1508
1509                 # return all devices which are not removed due to being a wholedisk when a partition exists
1510                 return [x for x in parts if not x.device or x.device in devs]
1511
1512         def splitDeviceName(self, devname):
1513                 # 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.
1514                 dev = devname[:3]
1515                 part = devname[3:]
1516                 for p in part:
1517                         if not p.isdigit():
1518                                 return devname, 0
1519                 return dev, part and int(part) or 0
1520
1521         def getUserfriendlyDeviceName(self, dev, phys):
1522                 #print "getUserfriendlyDeviceName",dev, phys
1523                 dev, part = self.splitDeviceName(dev)
1524                 description = "External Storage %s" % dev
1525                 have_model_descr = False
1526                 try:
1527                         description = readFile("/sys" + phys + "/model")
1528                         have_model_descr = True
1529                 except IOError, s:
1530                         print "couldn't read model: ", s
1531                 from Tools.HardwareInfo import HardwareInfo
1532                 if dev.find('sr') == 0 and dev[2].isdigit():
1533                         devicedb = DEVICEDB_SR
1534                 else:
1535                         devicedb = DEVICEDB
1536                 for physdevprefix, pdescription in devicedb.get(HardwareInfo().device_name,{}).items():
1537                         if phys.startswith(physdevprefix):
1538                                 if have_model_descr:
1539                                         description = pdescription + ' - ' + description
1540                                 else:
1541                                         description = pdescription
1542                 # not wholedisk and not partition 1
1543                 if part and part != 1:
1544                         description += " (Partition %d)" % part
1545                 return description
1546
1547         def addMountedPartition(self, device, desc):
1548                 already_mounted = False
1549                 for x in self.partitions[:]:
1550                         if x.mountpoint == device:
1551                                 already_mounted = True
1552                 if not already_mounted:
1553                         self.partitions.append(Partition(mountpoint = device, description = desc))
1554
1555         def removeMountedPartition(self, mountpoint):
1556                 for x in self.partitions[:]:
1557                         if x.mountpoint == mountpoint:
1558                                 self.partitions.remove(x)
1559                                 self.on_partition_list_change("remove", x)
1560
1561         def removeMountedPartitionbyDevice(self, device):
1562                 p = self.getPartitionbyDevice(device)
1563                 if p is not None:
1564                         #print "[removeMountedPartitionbyDevice] '%s', '%s', '%s', '%s', '%s'" % (p.mountpoint,p.description,p.device,p.force_mounted,p.uuid)
1565                         self.partitions.remove(p)
1566                         self.on_partition_list_change("remove", p)
1567
1568         def trigger_udev(self):
1569                 # We have to trigger udev to rescan sysfs
1570                 Console().ePopen(("udevadm", "udevadm", "trigger"))
1571
1572         def getPartitionbyUUID(self, uuid):
1573                 for x in self.partitions[:]:
1574                         if x.uuid == uuid:
1575                                 #print "[getPartitionbyUUID] '%s', '%s', '%s', '%s', '%s'" % (x.mountpoint,x.description,x.device,x.force_mounted,x.uuid)
1576                                 return x
1577                 return None
1578
1579         def getPartitionbyDevice(self, dev):
1580                 for x in self.partitions[:]:
1581                         if x.device == dev:
1582                                 #print "[getPartitionbyDevice] '%s', '%s', '%s', '%s', '%s'" % (x.mountpoint,x.description,x.device,x.force_mounted,x.uuid)
1583                                 return x
1584                 return None
1585
1586         def getPartitionbyMountpoint(self, mountpoint):
1587                 for x in self.partitions[:]:
1588                         if x.mountpoint == mountpoint:
1589                                 #print "[getPartitionbyMountpoint] '%s', '%s', '%s', '%s', '%s'" % (x.mountpoint,x.description,x.device,x.force_mounted,x.uuid)
1590                                 return x
1591                 return None
1592
1593         def getDeviceNamebyUUID(self, uuid):
1594                 if path.exists("/dev/disk/by-uuid/" + uuid):
1595                         return path.basename(path.realpath("/dev/disk/by-uuid/" + uuid))
1596                 return None
1597
1598         def getPartitionUUID(self, part):
1599                 if not path.exists("/dev/disk/by-uuid"):
1600                         return None
1601                 for uuid in listdir("/dev/disk/by-uuid/"):
1602                         if not path.exists("/dev/disk/by-uuid/" + uuid):
1603                                 return None
1604                         if path.basename(path.realpath("/dev/disk/by-uuid/" + uuid)) == part:
1605                                 #print "[getPartitionUUID] '%s' - '%s'" % (uuid, path.basename(path.realpath("/dev/disk/by-uuid/" + uuid)) )
1606                                 return uuid
1607                 return None
1608
1609         def getDeviceDescription(self, dev):
1610                 physdev = path.realpath('/sys/block/' + dev[:3] + '/device')[4:]
1611                 description = self.getUserfriendlyDeviceName(dev[:3], physdev)
1612                 #print "[getDeviceDescription] -> device:'%s' - desc: '%s' phy:'%s'" % (dev, description, physdev)
1613                 return description
1614
1615         def reloadExports(self):
1616                 if path.exists("/etc/exports"):
1617                         Console().ePopen(("exportfs -r"))
1618
1619         def unmountPartitionbyMountpoint(self, mountpoint, device = None):
1620                 if (path.exists(mountpoint) and path.ismount(mountpoint)) or (not path.exists(mountpoint) and self.get_mountdevice(mountpoint) is not None):
1621                         #call the mount/unmount event notifier to inform about an unmount
1622                         for callback in self.onUnMount_Notifier:
1623                                 try:
1624                                         callback(self.EVENT_UNMOUNT, mountpoint)
1625                                 except AttributeError:
1626                                         self.onUnMount_Notifier.remove(callback)
1627                         cmd = "umount" + " " + mountpoint
1628                         print "[unmountPartitionbyMountpoint] %s:" % (cmd)
1629                         system(cmd)
1630                 if path.exists(mountpoint) and not path.ismount(mountpoint):
1631                         part = self.getPartitionbyMountpoint(mountpoint)
1632                         if part is not None:
1633                                 if part.uuid is not None and part.uuid == config.storage_options.default_device.value: #unmounting Default Mountpoint /media/hdd
1634                                         #call the notifier also here if we unmounted the default partition
1635                                         for callback in self.delayed_device_Notifier:
1636                                                 try:
1637                                                         callback(part.device, "remove_default" )
1638                                                 except AttributeError:
1639                                                         self.delayed_device_Notifier.remove(callback)
1640                                         part.device = None
1641                                         part.updatePartitionInfo()
1642                         if device is not None and not path.ismount(mountpoint):
1643                                 self.removeMountedPartitionbyDevice(device)
1644                         self.reloadExports()
1645
1646         def unmountPartitionbyUUID(self, uuid):
1647                 mountpoint = ""
1648                 cfg = config.storage.get(uuid, None)
1649                 if cfg is not None:
1650                         mountpoint = config.storage[uuid]['mountpoint'].value
1651                 if mountpoint != "":
1652                         if path.exists(mountpoint) and path.ismount(mountpoint):
1653                                 #call the mount/unmount event notifier to inform about an unmount
1654                                 for callback in self.onUnMount_Notifier:
1655                                         try:
1656                                                 callback(self.EVENT_UNMOUNT, mountpoint)
1657                                         except AttributeError:
1658                                                 self.onUnMount_Notifier.remove(callback)
1659                                 cmd = "umount" + " " + mountpoint
1660                                 print "[unmountPartitionbyUUID] %s:" % (mountpoint)
1661                                 system(cmd)
1662                                 self.reloadExports()
1663
1664         def mountPartitionbyUUID(self, uuid):
1665                 if path.exists("/dev/disk/by-uuid/" + uuid):
1666                         cfg_uuid = config.storage.get(uuid, None)
1667                         partitionPath = "/dev/disk/by-uuid/" + uuid
1668                         mountpoint = cfg_uuid['mountpoint'].value
1669                         dev = self.getDeviceNamebyUUID(uuid)
1670                         #print "[mountPartitionbyUUID] for UUID:'%s' - '%s'" % (uuid,mountpoint)
1671
1672                         # we must umount autofs first because autofs mounts with "sync" option
1673                         # and the real mount than also mounts with this option
1674                         # this is realy bad for the performance!
1675                         forceAutofsUmount(dev)
1676
1677                         #verify if mountpoint is still mounted from elsewhere (e.g fstab)
1678                         if path.exists(mountpoint) and path.ismount(mountpoint):
1679                                 tmppath = self.get_mountdevice(mountpoint)
1680                                 if tmppath is not None and tmppath.startswith("/dev/disk/by-uuid/") and tmppath != partitionPath: #probably different device mounted on our mountpoint
1681                                         tmpuuid = tmppath.rsplit("/",1)[1]
1682                                         if not self.isUUIDpathFsTabMount(tmpuuid, mountpoint) and not self.isPartitionpathFsTabMount(tmpuuid, mountpoint):
1683                                                 self.unmountPartitionbyMountpoint(mountpoint)
1684
1685                         #verify if our device is still mounted to somewhere else
1686                         tmpmount = self.get_mountpoint(partitionPath)
1687                         if tmpmount is not None and tmpmount != mountpoint and path.exists(tmpmount) and path.ismount(tmpmount):
1688                                 if not self.isUUIDpathFsTabMount(uuid, tmpmount) and not self.isPartitionpathFsTabMount(uuid, tmpmount):
1689                                                 self.unmountPartitionbyMountpoint(tmpmount)
1690
1691                         if cfg_uuid['enabled'].value:
1692                                 if mountpoint != "":
1693                                         if not path.exists(mountpoint):
1694                                                 try:
1695                                                         makedirs(mountpoint)
1696                                                 except OSError:
1697                                                         print "[mountPartitionbyUUID] could not create mountdir:",mountpoint
1698
1699                                         if path.exists(mountpoint) and not path.ismount(mountpoint) and not path.islink(mountpoint):
1700                                                 cmd = "mount -t auto /dev/disk/by-uuid/" + uuid + " " + mountpoint
1701                                                 system(cmd)
1702                                                 print "[mountPartitionbyUUID]:",cmd
1703                                                 #call the mount/unmount event notifier to inform about an mount
1704                                                 for callback in self.onUnMount_Notifier:
1705                                                         try:
1706                                                                 callback(self.EVENT_MOUNT, mountpoint)
1707                                                         except AttributeError:
1708                                                                 self.onUnMount_Notifier.remove(callback)
1709
1710                                         if path.ismount(mountpoint):
1711                                                 dev = self.getDeviceNamebyUUID(uuid)
1712                                                 if dev is not None:
1713                                                         # verify if the current storage device is our default storage and create the movie folder if it is missing
1714                                                         if uuid == config.storage_options.default_device.value and not path.exists(resolveFilename(SCOPE_HDD)):
1715                                                                 print "default movie folder is missing...trying to create it."
1716                                                                 try:
1717                                                                         makedirs(resolveFilename(SCOPE_HDD))
1718                                                                 except OSError:
1719                                                                         pass
1720                                                         p = self.getPartitionbyMountpoint(mountpoint)
1721                                                         if p is not None:
1722                                                                 x = self.getPartitionbyDevice(dev)
1723                                                                 if x is not None and x.mountpoint.startswith('/autofs'):
1724                                                                         self.removeMountedPartitionbyDevice(dev) #remove now obsolete entry
1725                                                                 p.mountpoint = mountpoint
1726                                                                 p.uuid = uuid
1727                                                                 p.device = dev
1728                                                                 p.force_mounted = False
1729                                                                 p.updatePartitionInfo()
1730                                                         else:
1731                                                                 p = self.getPartitionbyDevice(dev)
1732                                                                 if p is not None:
1733                                                                         p.mountpoint = mountpoint
1734                                                                         p.uuid = uuid
1735                                                                         p.device = dev
1736                                                                         p.force_mounted = False
1737                                                                         p.updatePartitionInfo()
1738                                         else:
1739                                                 print "[mountPartitionbyUUID] could not mount mountdir:",mountpoint
1740                 else:
1741                         print "[mountPartitionbyUUID] failed for UUID:'%s'" % (uuid)
1742
1743         def storageDeviceChanged(self, uuid):
1744                 if config.storage[uuid]["enabled"].value:
1745                         #print "[storageDeviceChanged] for enabled UUID:'%s'" % (uuid)
1746                         self.mountPartitionbyUUID(uuid)
1747                 else:
1748                         #print "[storageDeviceChanged] for disabled UUID:'%s'" % (uuid)
1749                         self.unmountPartitionbyUUID(uuid)
1750
1751         def setupConfigEntries(self, initial_call = False, dev = None):
1752                 if initial_call and not dev:
1753                         for uuid in config.storage.stored_values:
1754                                 print "[setupConfigEntries] initial_call for stored uuid:",uuid,config.storage.stored_values[uuid]
1755                                 config.storage[uuid] = ConfigSubDict()
1756                                 config.storage[uuid]["enabled"] = ConfigYesNo(default = False)
1757                                 config.storage[uuid]["mountpoint"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1758                                 config.storage[uuid]["device_description"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1759                                 config.storage[uuid]["device_info"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1760                                 config.storage[uuid]["isRemovable"] = ConfigBoolean(default = False)
1761                                 if config.storage[uuid]['enabled'].value:
1762                                         dev = self.getDeviceNamebyUUID(uuid)
1763                                         if uuid == config.storage_options.default_device.value and config.storage[uuid]["mountpoint"].value != "/media/hdd":
1764                                                 print "[setupConfigEntries] initial_call discovered a default storage device misconfiguration, reapplied default storage config for:",uuid
1765                                                 if path.exists("/media/hdd") and path.islink("/media/hdd") and self.getLinkPath("/media/hdd") == config.storage[uuid]["mountpoint"].value:
1766                                                         unlink("/media/hdd")
1767                                                 if dev is not None:
1768                                                         self.unmountPartitionbyMountpoint(config.storage[uuid]["mountpoint"].value, dev)
1769                                                 config.storage[uuid]["mountpoint"].value = "/media/hdd"
1770                                         if dev is not None:
1771                                                 p = self.getPartitionbyDevice(dev) or self.getPartitionbyMountpoint(config.storage[uuid]["mountpoint"].value)
1772                                                 if p is None: # manually add partition entry
1773                                                         description = self.getDeviceDescription(dev)
1774                                                         device_mountpoint = self.getAutofsMountpoint(dev)
1775                                                         if config.storage[uuid]['mountpoint'].value != "":
1776                                                                 device_mountpoint = config.storage[uuid]['mountpoint'].value
1777                                                         p = Partition(mountpoint = device_mountpoint, description = description, force_mounted = False, device = dev)
1778                                                         p.uuid = uuid
1779                                                         p.updatePartitionInfo()
1780                                                         self.partitions.append(p)
1781                                                         self.on_partition_list_change("add", p)
1782                                         if path.exists("/dev/disk/by-uuid/" + uuid):
1783                                                 self.storageDeviceChanged(uuid)
1784                                 else:
1785                                         del config.storage[uuid]
1786                                         config.storage.save()
1787                                         config.save()
1788                 if dev is not None:
1789                         uuid = self.getPartitionUUID(dev)
1790                         if uuid is not None:
1791                                 if config.storage.get(uuid, None) is None: #new unconfigured device added
1792                                         print "[setupConfigEntries] new device add for '%s' with uuid:'%s'" % (dev, uuid)
1793                                         hdd = self.getHDD(dev)
1794                                         if hdd is not None:
1795                                                 hdd_description = hdd.model()
1796                                                 cap = hdd.capacity()
1797                                                 if cap != "":
1798                                                         hdd_description += " (" + cap + ")"
1799                                                 device_info =  hdd.bus_description()
1800                                         else:
1801                                                 device_info = dev
1802                                                 hdd_description = self.getDeviceDescription(dev)
1803                                         config.storage[uuid] = ConfigSubDict()
1804                                         config.storage[uuid]["enabled"] = ConfigYesNo(default = False)
1805                                         config.storage[uuid]["mountpoint"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1806                                         config.storage[uuid]["device_description"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1807                                         config.storage[uuid]["device_info"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1808                                         config.storage[uuid]["isRemovable"] = ConfigBoolean(default = False)
1809                                         removable = False
1810                                         if hdd is not None:
1811                                                 removable = hdd.isRemovable
1812                                         config.storage[uuid]["device_description"].setValue(hdd_description)
1813                                         config.storage[uuid]["device_info"].setValue(device_info)
1814                                         config.storage[uuid]["isRemovable"].setValue(removable)
1815                                         config.storage[uuid].save()
1816                                         self.verifyInstalledStorageDevices()
1817                                         p = self.getPartitionbyDevice(dev)
1818                                         if p is None: # manually add partition entry (e.g. on long spinup times)
1819                                                 description = self.getDeviceDescription(dev)
1820                                                 device_mountpoint = self.getAutofsMountpoint(dev)
1821                                                 p = Partition(mountpoint = device_mountpoint, description = description, force_mounted = True, device = dev)
1822                                                 p.uuid = uuid
1823                                                 p.updatePartitionInfo()
1824                                                 self.partitions.append(p)
1825                                                 self.on_partition_list_change("add", p)
1826                                         self.storageDeviceChanged(uuid)
1827                                 else:
1828                                         p = self.getPartitionbyDevice(dev)
1829                                         device_mountpoint = self.getAutofsMountpoint(dev)
1830                                         if config.storage[uuid]['enabled'].value and config.storage[uuid]['mountpoint'].value != "":
1831                                                 device_mountpoint = config.storage[uuid]['mountpoint'].value
1832                                         if p is None: # manually add partition entry (e.g. on default storage device change)
1833                                                 description = self.getDeviceDescription(dev)
1834                                                 p = Partition(mountpoint = device_mountpoint, description = description, force_mounted = True, device = dev)
1835                                                 p.uuid = uuid
1836                                                 p.updatePartitionInfo()
1837                                                 self.partitions.append(p)
1838                                                 self.on_partition_list_change("add", p)
1839                                                 print "[setupConfigEntries] new/changed device add for '%s' with uuid:'%s'" % (dev, uuid)
1840                                                 self.storageDeviceChanged(uuid)
1841                                         else:
1842                                                 tmp = self.getPartitionbyMountpoint(device_mountpoint)
1843                                                 if tmp is not None and (tmp.uuid != uuid or tmp.mountpoint != config.storage[uuid]['mountpoint'].value):
1844                                                         print "[setupConfigEntries] new/changed device add for '%s' with uuid:'%s'" % (dev, uuid)
1845                                                         self.storageDeviceChanged(uuid)
1846                         else:
1847                                 print "[setupConfigEntries] device add for '%s' without uuid !!!" % (dev)
1848
1849         def configureUuidAsDefault(self, uuid, device):
1850                 #verify if our device is manually mounted from fstab to somewhere else
1851                 isManualFstabMount = False
1852                 uuidPath = "/dev/disk/by-uuid/" + uuid
1853                 tmpmount = self.get_mountpoint(uuidPath)
1854                 if tmpmount is not None and tmpmount != "/media/hdd":
1855                         isManualFstabMount = True
1856                 if not isManualFstabMount:
1857                         if not self.inside_mountpoint("/media/hdd") and not path.islink("/media/hdd") and not self.isPartitionpathFsTabMount(uuid, "/media/hdd"):
1858                                 print "configureUuidAsDefault: using found %s as default storage device" % device
1859                                 config.storage_options.default_device.value = uuid
1860                                 config.storage_options.save()
1861                                 cfg_uuid = config.storage.get(uuid, None)
1862                                 if cfg_uuid is not None and not cfg_uuid["enabled"].value:
1863                                         cfg_uuid["enabled"].value = True
1864                                         cfg_uuid["mountpoint"].value = "/media/hdd"
1865                                         config.storage[uuid].save()
1866                                         config.storage.save()
1867                                         config.save()
1868                                         self.modifyFstabEntry("/dev/disk/by-uuid/" + uuid, "/media/hdd", mode = "add_activated")
1869                                         self.storageDeviceChanged(uuid)
1870
1871         def isInitializedByEnigma2(self, hdd):
1872                 isInitializedByEnigma2 = False
1873                 uuid = device = None
1874                 if hdd and hdd.numPartitions() <= 2:
1875                         numPart = hdd.numPartitions()
1876                         device = hdd.device
1877                         if numPart == 1 or numPart == 2:
1878                                 device = hdd.device + "1"
1879                         p = self.getPartitionbyDevice(device)
1880                         if p is not None and p.uuid and p.isInitialized: #only one by e2 initialized partition
1881                                 isInitializedByEnigma2 = True
1882                                 uuid = p.uuid
1883                         if numPart == 2:
1884                                 part2Type = self.getBlkidPartitionType(hdd.partitionPath("2"))
1885                                 if part2Type != "swap":
1886                                         isInitializedByEnigma2 = False
1887                 return isInitializedByEnigma2,device,uuid
1888
1889         def verifyInstalledStorageDevices(self):
1890                 if config.storage_options.default_device.value == "<undefined>" and self.HDDCount() == 1 and not self.HDDEnabledCount(): #only one installed and unconfigured device
1891                         hdd = self.hdd[0]
1892                         isInitializedByEnigma2,device,uuid = self.isInitializedByEnigma2(hdd)
1893                         if isInitializedByEnigma2:
1894                                 self.configureUuidAsDefault(uuid, device)
1895
1896
1897 harddiskmanager = HarddiskManager()
1898 harddiskmanager.enumerateBlockDevices()
1899 harddiskmanager.verifyInstalledStorageDevices() #take sure enumerateBlockdev is finished so we don't miss any at startup installed storage devices