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