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