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