enigma2 20131118 (master) -> 20140214 (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                                 cfg_uuid = config.storage.get(uuid, None)
1104                                 if cfg_uuid is not None:
1105                                         self.storageDeviceChanged(uuid)
1106                         else:   # found old partition entry
1107                                 if config.storage.get(x.uuid, None) is not None:
1108                                         del config.storage[x.uuid] #delete old uuid reference entries
1109                                         config.storage.save()
1110                                 x.mountpoint = device_mountpoint
1111                                 x.force_mounted = True
1112                                 x.updatePartitionInfo()
1113
1114                 for callback in self.delayed_device_Notifier:
1115                         try:
1116                                 callback(device, "add_delayed" )
1117                         except AttributeError:
1118                                 self.delayed_device_Notifier.remove(callback)
1119
1120         def HDDCount(self):
1121                 return len(self.hdd)
1122
1123         def HDDList(self):
1124                 list = [ ]
1125                 for hd in self.hdd:
1126                         hdd = hd.model() + " - " + hd.bus()
1127                         cap = hd.capacity()
1128                         if cap != "":
1129                                 hdd += " (" + cap + ")"
1130                         list.append((hdd, hd))
1131                 return list
1132
1133         def HDDEnabledCount(self):
1134                 cnt = 0
1135                 for uuid, cfg in config.storage.items():
1136                         #print "uuid", uuid, "cfg", cfg
1137                         if cfg["enabled"].value:
1138                                 cnt += 1
1139                 return cnt
1140
1141         def getHDD(self, part):
1142                 for hdd in self.hdd:
1143                         if hdd.device == part[:3]:
1144                                 return hdd
1145                 return None
1146
1147         def getCD(self):
1148                 return self.cd
1149
1150         def getFdiskInfo(self, devname):
1151                 size = sizeg = fstype = sys = sectors = None
1152                 try:
1153                         _, output = Util.run(['fdisk', '-l', '/dev/%s' % devname])
1154                         for line in output:
1155                                 if line.startswith("Found valid GPT"):
1156                                         sys = "GPT"
1157                                 if line.startswith("Disk"):
1158                                         sizeobj = search(r', ((?:[a-zA-Z0-9])*) bytes', line)
1159                                         if sizeobj:
1160                                                 size = sizeobj.group(1)
1161                                         sizegobj = search(r': ((?:[0-9.0-9])*) GB', line)
1162                                         if sizegobj:
1163                                                 sizeg = sizegobj.group(1)
1164                                         sectorsobj = search(r': ((?:[0-9.0-9])*) sectors', line)
1165                                         if sectorsobj:
1166                                                 sectors = sectorsobj.group(1)
1167                                 if not line.startswith('/'):
1168                                         continue
1169                                 if line.startswith("/dev/" + devname):
1170                                         a,b,c,d, fstype, sys = line.split(None,5)
1171                 except:
1172                         print "error getting fdisk device info"
1173                 #print "getFdiskInfo:",devname, fstype, sys, size, sizeg, sectors
1174                 return fstype, sys, size, sizeg, sectors
1175
1176         def __getBlkidAttributes(self, options):
1177                 res = dict()
1178                 try:
1179                         rc, output = Util.run(['blkid', '-o', 'export' ] + options)
1180                         if rc == 0:
1181                                 for line in output:
1182                                         key, value = line.split('=', 1)
1183                                         res[key] = value
1184                 except:
1185                         pass
1186                 return res
1187
1188         def __getBlkidAttribute(self, options, name):
1189                 attrs = self.__getBlkidAttributes(options)
1190                 if name in attrs:
1191                         return attrs[name]
1192                 return None
1193
1194         def __getBlkidAttributeByDevice(self, device, name):
1195                 return self.__getBlkidAttribute([device], name)
1196
1197         def __getBlkidAttributeByUuid(self, uuid, name):
1198                 return self.__getBlkidAttribute(['-t', 'UUID=%s' % uuid, '-l'], name)
1199
1200         def getBlkidPartitionType(self, device):
1201                 return self.__getBlkidAttributeByDevice(device, 'TYPE')
1202
1203         def isMount(self, mountdir):
1204                 return path.ismount(path.realpath(mountdir))
1205
1206         def _inside_mountpoint(self, filename):
1207                 #print "is mount? '%s'" % filename
1208                 if filename == "":
1209                         return False
1210                 if filename == "/":
1211                         return False
1212                 if path.ismount(filename):
1213                         return True
1214                 return self._inside_mountpoint("/".join(filename.split("/")[:-1]))
1215
1216         def inside_mountpoint(self,filename):
1217                 return self._inside_mountpoint(path.realpath(filename))
1218
1219         def isUUIDpathFsTabMount(self, uuid, mountpath):
1220                 uuidpartitionPath = "/dev/disk/by-uuid/" + uuid
1221                 if self.is_hard_mounted(uuidpartitionPath) and self.is_fstab_mountpoint(uuidpartitionPath, mountpath):
1222                         if self.get_fstab_mountstate(uuidpartitionPath, mountpath) == 'auto':
1223                                 return True
1224                 return False
1225
1226         def isPartitionpathFsTabMount(self, uuid, mountpath):
1227                 dev = self.getDeviceNamebyUUID(uuid)
1228                 if dev is not None:
1229                         partitionPath = "/dev/" + str(dev)
1230                         if self.is_hard_mounted(partitionPath) and self.is_fstab_mountpoint(partitionPath, mountpath):
1231                                 if self.get_fstab_mountstate(partitionPath, mountpath) == 'auto':
1232                                         return True
1233                 return False
1234
1235         def getPartitionVars(self, hd, partitionNum = False):
1236                 #print "getPartitionVars for hdd:'%s' and partitionNum:'%s'" % (hd.device, partitionNum)
1237                 hdd = hd
1238                 numPartitions = hdd.numPartitions()
1239                 uuid = partitionPath = uuidPath = deviceName = None
1240                 if partitionNum is False:
1241                         if numPartitions == 0:
1242                                 deviceName = hdd.device
1243                                 uuid = self.getPartitionUUID(deviceName)
1244                                 partitionPath = hdd.dev_path
1245                         if numPartitions == 1:
1246                                 deviceName = hdd.device + str(numPartitions)
1247                                 uuid = self.getPartitionUUID(deviceName)
1248                                 partitionPath = hdd.partitionPath(str(numPartitions))
1249                         else: #just in case, we should never get here
1250                                 deviceName = hdd.device
1251                                 partitionPath = hdd.dev_path
1252                 else:
1253                         deviceName = hdd.device + str(partitionNum)
1254                         uuid = self.getPartitionUUID(deviceName)
1255                         partitionPath = hdd.partitionPath(str(partitionNum))
1256                 if uuid is not None:
1257                         uuidPath = "/dev/disk/by-uuid/" + uuid
1258                 return deviceName, uuid, numPartitions, partitionNum, uuidPath, partitionPath
1259
1260         def cleanupMountpath(self, devname):
1261                 return devname.strip().replace(' ','').replace('-','').replace('_','').replace('.','')
1262
1263         def suggestDeviceMountpath(self,uuid):
1264                 p = self.getPartitionbyUUID(uuid)
1265                 if p is not None:
1266                         hdd = self.getHDD(p.device)
1267                         if hdd is not None:
1268                                 val = self.cleanupMountpath(str(hdd.model(model_only = True)))
1269                                 cnt = 0
1270                                 for dev in self.hdd:
1271                                         tmpval = self.cleanupMountpath(str(dev.model(model_only = True)))
1272                                         if tmpval == val:
1273                                                 cnt +=1
1274                                 if cnt <=1:
1275                                         cnt = 0
1276                                         for uid in config.storage.keys():
1277                                                 if uid == uuid:
1278                                                         cnt += 1
1279                                                         continue
1280                                                 data = config.storage[uid]["device_description"].value.split(None,1)
1281                                                 tmpval = self.cleanupMountpath(data[0])
1282                                                 if tmpval == val or tmpval.endswith(val):
1283                                                         cnt += 1
1284                                 if cnt >= 2:
1285                                         val += "HDD" + str(cnt)
1286                                 partNum = p.device[3:]
1287                                 if hdd.numPartitions() == 2 and partNum == "1":
1288                                         part2Type = self.getBlkidPartitionType(hdd.partitionPath("2"))
1289                                         if part2Type is not None and part2Type != "swap":
1290                                                 val += "Part" + str(partNum)
1291                                 else:
1292                                         if str(partNum).isdigit():
1293                                                 val += "Part" + str(partNum)
1294                                 print "suggestDeviceMountpath for uuid: '%s' -> '%s'" %(uuid,val)
1295                                 return "/media/" + val
1296                 else:
1297                         mountpath = ""
1298                         uuid_cfg = config.storage.get(uuid, None)
1299                         if uuid_cfg is not None:
1300                                 if uuid_cfg["mountpoint"].value != "" and uuid_cfg["mountpoint"].value != "/media/hdd":
1301                                         mountpath = uuid_cfg["mountpoint"].value
1302                                 else:
1303                                         if uuid_cfg["device_description"].value != "":
1304                                                 tmp = uuid_cfg["device_description"].value.split(None,1)
1305                                                 mountpath = "/media/" + self.cleanupMountpath(tmp[0])
1306                         if mountpath != "":
1307                                 cnt = 0
1308                                 for uid in config.storage.keys():
1309                                         if config.storage[uid]["mountpoint"].value != "" and config.storage[uid]["mountpoint"].value != "/media/hdd":
1310                                                 tmp = config.storage[uid]["mountpoint"].value
1311                                         else:
1312                                                 data = config.storage[uid]["device_description"].value.split(None,1)
1313                                                 mountpath = "/media/" + self.cleanupMountpath(data[0])
1314                                         if tmp == mountpath:
1315                                                 cnt += 1
1316                                 if cnt >= 2:
1317                                         mountpath += "HDD" + str(cnt)
1318                                 return mountpath
1319                 return ""
1320
1321         def changeStorageDevice(self, uuid = None, action = None , mountData = None ):
1322                 # mountData should be [oldenable,oldmountpath, newenable,newmountpath]
1323                 print "[changeStorageDevice] uuid:'%s' - action:'%s' - mountData:'%s'" %(uuid, action, mountData)
1324                 oldcurrentDefaultStorageUUID = currentDefaultStorageUUID = config.storage_options.default_device.value
1325                 print "[changeStorageDevice]: currentDefaultStorageUUID:",currentDefaultStorageUUID
1326                 successfully = False
1327                 def_mp = "/media/hdd"
1328                 cur_default_newmp = new_default_newmp = old_cur_default_mp = old_new_default_mp = ""
1329                 cur_default = new_default = None
1330                 cur_default_dev = new_default_dev = None
1331                 cur_default_cfg = new_default_cfg = None
1332                 old_cur_default_enabled = old_new_default_enabled = False
1333
1334                 if action == "mount_default":
1335                         if currentDefaultStorageUUID != "<undefined>" and currentDefaultStorageUUID != uuid:
1336                                 cur_default = self.getDefaultStorageDevicebyUUID(currentDefaultStorageUUID)
1337                         new_default = self.getPartitionbyUUID(uuid)
1338                         if cur_default is not None:
1339                                 cur_default_cfg = config.storage.get(currentDefaultStorageUUID, None)
1340                         new_default_cfg = config.storage.get(uuid, None)
1341                         if new_default is not None:
1342                                 new_default_dev = new_default.device
1343                                 if currentDefaultStorageUUID != "<undefined>" and currentDefaultStorageUUID != uuid:
1344                                         cur_default_newmp = self.suggestDeviceMountpath(currentDefaultStorageUUID)
1345                                 if cur_default is not None:
1346                                         cur_default_dev = cur_default.device
1347                                 if new_default_cfg is not None:
1348                                         old_new_default_enabled = new_default_cfg["enabled"].value
1349                                         old_new_default_mp = new_default_cfg["mountpoint"].value
1350                                         #[oldmountpath, oldenable, newmountpath, newenable]
1351                                         if mountData is not None and isinstance(mountData, (list, tuple)):
1352                                                 old_new_default_enabled = mountData[1]
1353                                                 old_new_default_mp = mountData[0]
1354                                         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:
1355                                                 old_cur_default_enabled = cur_default_cfg["enabled"].value
1356                                                 old_cur_default_mp = cur_default_cfg["mountpoint"].value
1357                                                 self.unmountPartitionbyMountpoint(def_mp)
1358                                         if not path.exists(def_mp) or (path.exists(def_mp) and not self.isMount(def_mp)) or not self.isPartitionpathFsTabMount(uuid, def_mp):
1359                                                 if cur_default_cfg is not None:
1360                                                         cur_default_cfg["mountpoint"].value = cur_default_newmp
1361                                                 if cur_default_dev is not None:
1362                                                         self.setupConfigEntries(initial_call = False, dev = cur_default_dev)
1363                                                 if cur_default_dev is None or (path.exists(cur_default_newmp) and self.isMount(cur_default_newmp)):
1364                                                         if new_default_cfg["enabled"].value and path.exists(new_default_cfg["mountpoint"].value) and self.isMount(new_default_cfg["mountpoint"].value):
1365                                                                 self.unmountPartitionbyMountpoint(new_default_cfg["mountpoint"].value, new_default_dev )
1366                                                         if not new_default_cfg["enabled"].value or not self.isMount(new_default_cfg["mountpoint"].value):
1367                                                                 new_default_cfg["mountpoint"].value = def_mp
1368                                                                 new_default_cfg["enabled"].value = True
1369                                                                 config.storage_options.default_device.value = uuid  #temporary assign the default storage uuid
1370                                                                 self.storageDeviceChanged(uuid)
1371                                                                 config.storage_options.default_device.value = currentDefaultStorageUUID  #reassign the original default storage uuid
1372                                                                 new_default = self.getPartitionbyMountpoint(def_mp)
1373                                                                 if cur_default_cfg is None and cur_default_newmp is not "": #currentdefault was offline
1374                                                                         cur_default_cfg = config.storage.get(currentDefaultStorageUUID, None)
1375                                                                 if cur_default_cfg is not None:
1376                                                                         old_cur_default_enabled = cur_default_cfg["enabled"].value
1377                                                                         old_cur_default_mp = cur_default_cfg["mountpoint"].value
1378                                                                         cur_default_cfg["mountpoint"].value = cur_default_newmp
1379                                                                 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:
1380                                                                         # reverify if the movie folder was created correctly
1381                                                                         if not path.exists(resolveFilename(SCOPE_HDD)):
1382                                                                                 print "default movie folder still missing...try again to create it."
1383                                                                                 try:
1384                                                                                         makedirs(resolveFilename(SCOPE_HDD))
1385                                                                                 except OSError:
1386                                                                                         pass
1387                                                                         if path.exists(resolveFilename(SCOPE_HDD)):
1388                                                                                 successfully = True
1389                                                                                 config.storage_options.default_device.value = uuid
1390                 if action == "mount_only":
1391                         new_default = self.getPartitionbyUUID(uuid)
1392                         new_default_cfg = config.storage.get(uuid, None)
1393                         if new_default is not None:
1394                                 new_default_dev = new_default.device
1395                                 new_default_newmp = self.suggestDeviceMountpath(uuid)
1396                                 if new_default_cfg is not None:
1397                                         old_new_default_enabled = new_default_cfg["enabled"].value
1398                                         old_new_default_mp = new_default_cfg["mountpoint"].value
1399                                         #[oldmountpath, oldenable, newmountpath, newenable]
1400                                         if mountData is not None and isinstance(mountData, (list, tuple)):
1401                                                 old_new_default_enabled = mountData[1]
1402                                                 old_new_default_mp = mountData[0]
1403                                                 new_default_newmp = mountData[2]
1404                                         if old_new_default_enabled and path.exists(def_mp) and self.isMount(def_mp) and old_new_default_mp == def_mp:
1405                                                 if uuid == currentDefaultStorageUUID:
1406                                                         self.unmountPartitionbyMountpoint(def_mp) #current partition is default, unmount!
1407                                         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):
1408                                                 self.unmountPartitionbyMountpoint(old_new_default_mp, new_default_dev) #current partition is already mounted atm. unmount!
1409                                         if not new_default_cfg["enabled"].value or old_new_default_mp == "" or not self.isMount(old_new_default_mp):
1410                                                 new_default_cfg["enabled"].value = True
1411                                                 new_default_cfg["mountpoint"].value = new_default_newmp
1412                                                 if path.exists(new_default_newmp) and self.isMount(new_default_newmp):
1413                                                         tmppath = self.get_mountdevice(new_default_newmp)
1414                                                         if tmppath is not None and tmppath == "/dev/disk/by-uuid/" + uuid:
1415                                                                 self.unmountPartitionbyMountpoint(new_default_newmp)
1416                                                 x = None
1417                                                 if new_default_dev is not None:
1418                                                         x = self.getPartitionbyDevice(new_default_dev)
1419                                                 if x is None:
1420                                                         self.setupConfigEntries(initial_call = False, dev = new_default_dev)
1421                                                 else:
1422                                                         self.storageDeviceChanged(uuid)
1423                                                 new_default = self.getPartitionbyUUID(uuid)
1424                                                 if new_default is not None and path.exists(new_default_newmp) and self.isMount(new_default_newmp):
1425                                                         successfully = True
1426                                                         if uuid == currentDefaultStorageUUID:
1427                                                                 config.storage_options.default_device.value = "<undefined>"
1428                 if action in ("unmount", "eject"):
1429                         new_default = self.getPartitionbyUUID(uuid)
1430                         new_default_cfg = config.storage.get(uuid, None)
1431                         if new_default is not None:
1432                                 new_default_dev = new_default.device
1433                                 if new_default_cfg is not None and new_default_cfg["mountpoint"].value == new_default.mountpoint:
1434                                         old_new_default_mp = new_default_cfg["mountpoint"].value
1435                                         old_new_default_enabled = new_default_cfg["enabled"].value
1436                                         #[oldmountpath, oldenable, newmountpath, newenable]
1437                                         if action == "unmount":
1438                                                 if mountData is not None and isinstance(mountData, (list, tuple)):
1439                                                         old_new_default_enabled = mountData[1]
1440                                                         old_new_default_mp = mountData[0]
1441                                 if new_default_cfg is not None and path.exists(old_new_default_mp) and self.isMount(old_new_default_mp):
1442                                         if uuid == currentDefaultStorageUUID:
1443                                                 self.unmountPartitionbyMountpoint(old_new_default_mp)
1444                                         else:
1445                                                 self.unmountPartitionbyMountpoint(old_new_default_mp, new_default_dev)
1446                                 if path.exists(old_new_default_mp) and not self.isMount(old_new_default_mp):
1447                                         if action == "unmount":
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                                                 if action == "unmount":
1454                                                         if uuid == currentDefaultStorageUUID:
1455                                                                 config.storage_options.default_device.value = "<undefined>"
1456                 if not successfully:
1457                         print "[changeStorageDevice]: << 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                                 if currentDefaultStorageUUID != "<undefined>":
1462                                         self.storageDeviceChanged(currentDefaultStorageUUID)
1463                         if new_default_cfg is not None:
1464                                 new_default_cfg["mountpoint"].value = old_new_default_mp
1465                                 new_default_cfg["enabled"].value = old_new_default_enabled
1466                                 self.storageDeviceChanged(uuid)
1467                 else:
1468                         print "[changeStorageDevice]: successfully, verifying fstab entries"
1469                         cur_defaultPart = new_defaultPart = None
1470                         if action == "mount_default":
1471                                 if (cur_default_dev is not None and new_default_dev is not None):
1472                                         cur_defaultPart = self.getPartitionbyDevice(cur_default_dev)
1473                                         new_defaultPart = self.getPartitionbyDevice(new_default_dev)
1474                                         if cur_defaultPart is not None:
1475                                                 devpath = "/dev/disk/by-uuid/" + cur_defaultPart.uuid
1476                                                 if self.is_fstab_mountpoint(devpath, "/media/hdd"):
1477                                                         self.modifyFstabEntry(devpath, "/media/hdd", mode = "remove")
1478                                                 if not self.is_fstab_mountpoint(devpath, cur_default_newmp):
1479                                                         self.modifyFstabEntry(devpath, cur_default_newmp, mode = "add_activated")
1480                                         if new_defaultPart is not None:
1481                                                 if old_new_default_mp != "":
1482                                                         devpath = "/dev/disk/by-uuid/" + new_defaultPart.uuid
1483                                                         if self.is_fstab_mountpoint(devpath, old_new_default_mp):
1484                                                                 self.modifyFstabEntry(devpath, old_new_default_mp, mode = "remove")
1485                                 if (cur_default_dev is None and new_default_dev is not None):
1486                                         new_defaultPart = self.getPartitionbyDevice(new_default_dev)
1487                                         if (new_defaultPart is not None and cur_default is None):
1488                                                 devpath = "/dev/disk/by-uuid/" + oldcurrentDefaultStorageUUID
1489                                                 if self.is_fstab_mountpoint(devpath, old_cur_default_mp):
1490                                                         self.modifyFstabEntry(devpath, old_cur_default_mp, mode = "remove")
1491                                                 if old_new_default_mp != "":
1492                                                         devpath = "/dev/disk/by-uuid/" + currentDefaultStorageUUID
1493                                                         if self.is_fstab_mountpoint(devpath, old_new_default_mp):
1494                                                                 self.modifyFstabEntry(devpath, old_new_default_mp, mode = "remove")
1495                                                         devpath = "/dev/disk/by-uuid/" + config.storage_options.default_device.value
1496                                                         if self.is_fstab_mountpoint(devpath, old_new_default_mp):
1497                                                                 self.modifyFstabEntry(devpath, old_new_default_mp, mode = "remove")
1498                                                 if cur_default_newmp != "":
1499                                                         devpath = "/dev/disk/by-uuid/" + oldcurrentDefaultStorageUUID
1500                                                         if not self.is_fstab_mountpoint(devpath, cur_default_newmp):
1501                                                                 self.modifyFstabEntry(devpath, cur_default_newmp, mode = "add_activated")
1502                         if action == "mount_only":
1503                                 if (cur_default_dev is None and new_default_dev is not None):
1504                                         if (cur_default is None and new_default is not None):
1505                                                 if old_new_default_mp != "":
1506                                                         devpath = "/dev/disk/by-uuid/" + uuid
1507                                                         if self.is_fstab_mountpoint(devpath, old_new_default_mp):
1508                                                                 self.modifyFstabEntry(devpath, old_new_default_mp, mode = "remove")
1509                                                         if self.isMount(old_new_default_mp):
1510                                                                 self.unmountPartitionbyMountpoint(old_new_default_mp)
1511                 config.storage_options.save()
1512                 config.storage.save()
1513                 configfile.save()
1514                 print "changeStorageDevice default is now:",config.storage_options.default_device.value
1515                 return successfully
1516
1517         def isConfiguredStorageDevice(self,uuid):
1518                 cfg_uuid = config.storage.get(uuid, None)
1519                 if cfg_uuid is not None and cfg_uuid["enabled"].value:
1520                         #print "isConfiguredStorageDevice:",uuid
1521                         return True
1522                 return False
1523
1524         def isDefaultStorageDeviceActivebyUUID(self, uuid):
1525                 p = self.getDefaultStorageDevicebyUUID(uuid)
1526                 if p is not None and p.uuid == uuid:
1527                         #print "isDefaultStorageDeviceActivebyUUID--for UUID:->",uuid,p.description, p.device, p.mountpoint, p.uuid
1528                         return True
1529                 return False
1530
1531         def getDefaultStorageDevicebyUUID(self, uuid):
1532                 for p in self.getConfiguredStorageDevices():
1533                         if p.uuid == uuid:
1534                                 #print "getDefaultStorageDevicebyUUID--p:",uuid, p.description, p.device, p.mountpoint, p.uuid
1535                                 return p
1536                 return None
1537
1538         def getConfiguredStorageDevices(self):
1539                 parts = [x for x in self.partitions if (x.uuid is not None and x.mounted() and self.isConfiguredStorageDevice(x.uuid))]
1540                 return [x for x in parts]
1541
1542         def getMountedPartitions(self, onlyhotplug = False):
1543                 parts = [x for x in self.partitions if (x.is_hotplug or not onlyhotplug) and x.mounted()]
1544                 devs = set([x.device for x in parts])
1545                 for devname in devs.copy():
1546                         if not devname:
1547                                 continue
1548                         dev, part = self.splitDeviceName(devname)
1549                         if part and dev in devs: # if this is a partition and we still have the wholedisk, remove wholedisk
1550                                 devs.remove(dev)
1551
1552                 # return all devices which are not removed due to being a wholedisk when a partition exists
1553                 return [x for x in parts if not x.device or x.device in devs]
1554
1555         def splitDeviceName(self, devname):
1556                 # 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.
1557                 dev = devname[:3]
1558                 part = devname[3:]
1559                 for p in part:
1560                         if not p.isdigit():
1561                                 return devname, 0
1562                 return dev, part and int(part) or 0
1563
1564         def getUserfriendlyDeviceName(self, dev, phys):
1565                 #print "getUserfriendlyDeviceName",dev, phys
1566                 dev, part = self.splitDeviceName(dev)
1567                 description = "External Storage %s" % dev
1568                 have_model_descr = False
1569                 try:
1570                         description = Util.readFile("/sys" + phys + "/model")
1571                         have_model_descr = True
1572                 except IOError, s:
1573                         print "couldn't read model: ", s
1574                 from Tools.HardwareInfo import HardwareInfo
1575                 if dev.find('sr') == 0 and dev[2].isdigit():
1576                         devicedb = DEVICEDB_SR
1577                 else:
1578                         devicedb = DEVICEDB
1579                 for physdevprefix, pdescription in devicedb.get(HardwareInfo().device_name,{}).items():
1580                         if phys.startswith(physdevprefix):
1581                                 if have_model_descr:
1582                                         description = pdescription + ' - ' + description
1583                                 else:
1584                                         description = pdescription
1585                 # not wholedisk and not partition 1
1586                 if part and part != 1:
1587                         description += " (Partition %d)" % part
1588                 return description
1589
1590         def addMountedPartition(self, device, desc):
1591                 already_mounted = False
1592                 for x in self.partitions[:]:
1593                         if x.mountpoint == device:
1594                                 already_mounted = True
1595                 if not already_mounted:
1596                         self.partitions.append(Partition(mountpoint = device, description = desc))
1597
1598         def removeMountedPartition(self, mountpoint):
1599                 for x in self.partitions[:]:
1600                         if x.mountpoint == mountpoint:
1601                                 self.partitions.remove(x)
1602                                 self.on_partition_list_change("remove", x)
1603
1604         def removeMountedPartitionbyDevice(self, device):
1605                 p = self.getPartitionbyDevice(device)
1606                 if p is not None:
1607                         #print "[removeMountedPartitionbyDevice] '%s', '%s', '%s', '%s', '%s'" % (p.mountpoint,p.description,p.device,p.force_mounted,p.uuid)
1608                         self.partitions.remove(p)
1609                         self.on_partition_list_change("remove", p)
1610
1611         def trigger_udev(self):
1612                 # We have to trigger udev to rescan sysfs
1613                 Console().ePopen(("udevadm", "udevadm", "trigger"))
1614
1615         def getPartitionbyUUID(self, uuid):
1616                 for x in self.partitions[:]:
1617                         if x.uuid == uuid:
1618                                 #print "[getPartitionbyUUID] '%s', '%s', '%s', '%s', '%s'" % (x.mountpoint,x.description,x.device,x.force_mounted,x.uuid)
1619                                 return x
1620                 return None
1621
1622         def getPartitionbyDevice(self, dev):
1623                 for x in self.partitions[:]:
1624                         if x.device == dev:
1625                                 #print "[getPartitionbyDevice] '%s', '%s', '%s', '%s', '%s'" % (x.mountpoint,x.description,x.device,x.force_mounted,x.uuid)
1626                                 return x
1627                 return None
1628
1629         def getPartitionbyMountpoint(self, mountpoint):
1630                 for x in self.partitions[:]:
1631                         if x.mountpoint == mountpoint:
1632                                 #print "[getPartitionbyMountpoint] '%s', '%s', '%s', '%s', '%s'" % (x.mountpoint,x.description,x.device,x.force_mounted,x.uuid)
1633                                 return x
1634                 return None
1635
1636         def getDeviceNamebyUUID(self, uuid):
1637                 # try blkid first
1638                 devname = self.__getBlkidAttributeByUuid(uuid, 'DEVNAME')
1639                 if devname:
1640                         return path.basename(devname)
1641                 # fallback to udev symlinks
1642                 if path.exists("/dev/disk/by-uuid/" + uuid):
1643                         return path.basename(path.realpath("/dev/disk/by-uuid/" + uuid))
1644                 return None
1645
1646         def getPartitionUUID(self, part):
1647                 absPart = '/dev/%s' % part
1648                 # try blkid first
1649                 uuid = self.__getBlkidAttributeByDevice(absPart, 'UUID')
1650                 if uuid:
1651                         return uuid
1652                 # fallback to udev symlinks
1653                 if path.exists("/dev/disk/by-uuid"):
1654                         for uuid in listdir("/dev/disk/by-uuid/"):
1655                                 if path.realpath("/dev/disk/by-uuid/%s" % uuid) == absPart:
1656                                         #print "[getPartitionUUID] '%s' - '%s'" % (uuid, path.basename(path.realpath("/dev/disk/by-uuid/" + uuid)) )
1657                                         return uuid
1658                 return None
1659
1660         def getDeviceDescription(self, dev):
1661                 physdev = path.realpath('/sys/block/' + dev[:3] + '/device')[4:]
1662                 description = self.getUserfriendlyDeviceName(dev[:3], physdev)
1663                 #print "[getDeviceDescription] -> device:'%s' - desc: '%s' phy:'%s'" % (dev, description, physdev)
1664                 return description
1665
1666         def reloadExports(self):
1667                 if path.exists("/etc/exports"):
1668                         Console().ePopen(("exportfs -r"))
1669
1670         def unmountPartitionbyMountpoint(self, mountpoint, device = None):
1671                 if (path.exists(mountpoint) and path.ismount(mountpoint)) or (not path.exists(mountpoint) and self.get_mountdevice(mountpoint) is not None):
1672                         #call the mount/unmount event notifier to inform about an unmount
1673                         for callback in self.onUnMount_Notifier:
1674                                 try:
1675                                         callback(self.EVENT_UNMOUNT, mountpoint)
1676                                 except AttributeError:
1677                                         self.onUnMount_Notifier.remove(callback)
1678                         cmd = "umount" + " " + mountpoint
1679                         print "[unmountPartitionbyMountpoint] %s:" % (cmd)
1680                         system(cmd)
1681                 if path.exists(mountpoint) and not path.ismount(mountpoint):
1682                         part = self.getPartitionbyMountpoint(mountpoint)
1683                         if part is not None:
1684                                 if part.uuid is not None and part.uuid == config.storage_options.default_device.value: #unmounting Default Mountpoint /media/hdd
1685                                         #call the notifier also here if we unmounted the default partition
1686                                         for callback in self.delayed_device_Notifier:
1687                                                 try:
1688                                                         callback(part.device, "remove_default" )
1689                                                 except AttributeError:
1690                                                         self.delayed_device_Notifier.remove(callback)
1691                                         part.device = None
1692                                         part.updatePartitionInfo()
1693                         if device is not None and not path.ismount(mountpoint):
1694                                 self.removeMountedPartitionbyDevice(device)
1695                         self.reloadExports()
1696
1697         def unmountPartitionbyUUID(self, uuid):
1698                 mountpoint = ""
1699                 cfg = config.storage.get(uuid, None)
1700                 if cfg is not None:
1701                         mountpoint = config.storage[uuid]['mountpoint'].value
1702                 if mountpoint != "":
1703                         if path.exists(mountpoint) and path.ismount(mountpoint):
1704                                 #call the mount/unmount event notifier to inform about an unmount
1705                                 for callback in self.onUnMount_Notifier:
1706                                         try:
1707                                                 callback(self.EVENT_UNMOUNT, mountpoint)
1708                                         except AttributeError:
1709                                                 self.onUnMount_Notifier.remove(callback)
1710                                 cmd = "umount" + " " + mountpoint
1711                                 print "[unmountPartitionbyUUID] %s:" % (mountpoint)
1712                                 system(cmd)
1713                                 self.reloadExports()
1714
1715         def mountPartitionbyUUID(self, uuid):
1716                 if path.exists("/dev/disk/by-uuid/" + uuid):
1717                         cfg_uuid = config.storage.get(uuid, None)
1718                         partitionPath = "/dev/disk/by-uuid/" + uuid
1719                         mountpoint = cfg_uuid['mountpoint'].value
1720                         dev = self.getDeviceNamebyUUID(uuid)
1721                         #print "[mountPartitionbyUUID] for UUID:'%s' - '%s'" % (uuid,mountpoint)
1722
1723                         Util.forceAutofsUmount(dev)
1724
1725                         #verify if mountpoint is still mounted from elsewhere (e.g fstab)
1726                         if path.exists(mountpoint) and path.ismount(mountpoint):
1727                                 tmppath = self.get_mountdevice(mountpoint)
1728                                 if tmppath is not None and tmppath.startswith("/dev/disk/by-uuid/") and tmppath != partitionPath: #probably different device mounted on our mountpoint
1729                                         tmpuuid = tmppath.rsplit("/",1)[1]
1730                                         if not self.isUUIDpathFsTabMount(tmpuuid, mountpoint) and not self.isPartitionpathFsTabMount(tmpuuid, mountpoint):
1731                                                 self.unmountPartitionbyMountpoint(mountpoint)
1732
1733                         #verify if our device is still mounted to somewhere else
1734                         tmpmount = self.get_mountpoint(partitionPath)
1735                         if tmpmount is not None and tmpmount != mountpoint and path.exists(tmpmount) and path.ismount(tmpmount):
1736                                 if not self.isUUIDpathFsTabMount(uuid, tmpmount) and not self.isPartitionpathFsTabMount(uuid, tmpmount):
1737                                                 self.unmountPartitionbyMountpoint(tmpmount)
1738
1739                         if cfg_uuid['enabled'].value:
1740                                 if mountpoint != "":
1741                                         if not path.exists(mountpoint):
1742                                                 try:
1743                                                         makedirs(mountpoint)
1744                                                 except OSError:
1745                                                         print "[mountPartitionbyUUID] could not create mountdir:",mountpoint
1746
1747                                         if path.exists(mountpoint) and not path.ismount(mountpoint) and not path.islink(mountpoint):
1748                                                 cmd = "mount /dev/disk/by-uuid/" + uuid + " " + mountpoint
1749                                                 system(cmd)
1750                                                 print "[mountPartitionbyUUID]:",cmd
1751                                                 #call the mount/unmount event notifier to inform about an mount
1752                                                 for callback in self.onUnMount_Notifier:
1753                                                         try:
1754                                                                 callback(self.EVENT_MOUNT, mountpoint)
1755                                                         except AttributeError:
1756                                                                 self.onUnMount_Notifier.remove(callback)
1757
1758                                         if path.ismount(mountpoint):
1759                                                 dev = self.getDeviceNamebyUUID(uuid)
1760                                                 if dev is not None:
1761                                                         # verify if the current storage device is our default storage and create the movie folder if it is missing
1762                                                         if uuid == config.storage_options.default_device.value and not path.exists(resolveFilename(SCOPE_HDD)):
1763                                                                 print "default movie folder is missing...trying to create it."
1764                                                                 try:
1765                                                                         makedirs(resolveFilename(SCOPE_HDD))
1766                                                                 except OSError:
1767                                                                         pass
1768                                                         p = self.getPartitionbyMountpoint(mountpoint)
1769                                                         if p is not None:
1770                                                                 x = self.getPartitionbyDevice(dev)
1771                                                                 if x is not None and x.mountpoint.startswith('/autofs'):
1772                                                                         self.removeMountedPartitionbyDevice(dev) #remove now obsolete entry
1773                                                                 p.mountpoint = mountpoint
1774                                                                 p.uuid = uuid
1775                                                                 p.device = dev
1776                                                                 p.force_mounted = False
1777                                                                 p.updatePartitionInfo()
1778                                                         else:
1779                                                                 p = self.getPartitionbyDevice(dev)
1780                                                                 if p is not None:
1781                                                                         p.mountpoint = mountpoint
1782                                                                         p.uuid = uuid
1783                                                                         p.device = dev
1784                                                                         p.force_mounted = False
1785                                                                         p.updatePartitionInfo()
1786                                         else:
1787                                                 print "[mountPartitionbyUUID] could not mount mountdir:",mountpoint
1788                 else:
1789                         print "[mountPartitionbyUUID] failed for UUID:'%s'" % (uuid)
1790
1791         def storageDeviceChanged(self, uuid):
1792                 if config.storage[uuid]["enabled"].value:
1793                         #print "[storageDeviceChanged] for enabled UUID:'%s'" % (uuid)
1794                         self.mountPartitionbyUUID(uuid)
1795                 else:
1796                         #print "[storageDeviceChanged] for disabled UUID:'%s'" % (uuid)
1797                         self.unmountPartitionbyUUID(uuid)
1798
1799         def setupConfigEntries(self, initial_call = False, dev = None):
1800                 if initial_call and not dev:
1801                         for uuid in config.storage.stored_values:
1802                                 print "[setupConfigEntries] initial_call for stored uuid:",uuid,config.storage.stored_values[uuid]
1803                                 config.storage[uuid] = ConfigSubDict()
1804                                 config.storage[uuid]["enabled"] = ConfigYesNo(default = False)
1805                                 config.storage[uuid]["mountpoint"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1806                                 config.storage[uuid]["device_description"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1807                                 config.storage[uuid]["device_info"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1808                                 config.storage[uuid]["isRemovable"] = ConfigBoolean(default = False)
1809                                 if config.storage[uuid]['enabled'].value:
1810                                         dev = self.getDeviceNamebyUUID(uuid)
1811                                         if uuid == config.storage_options.default_device.value and config.storage[uuid]["mountpoint"].value != "/media/hdd":
1812                                                 print "[setupConfigEntries] initial_call discovered a default storage device misconfiguration, reapplied default storage config for:",uuid
1813                                                 if path.exists("/media/hdd") and path.islink("/media/hdd") and path.realpath("/media/hdd") == config.storage[uuid]["mountpoint"].value:
1814                                                         unlink("/media/hdd")
1815                                                 if dev is not None:
1816                                                         self.unmountPartitionbyMountpoint(config.storage[uuid]["mountpoint"].value, dev)
1817                                                 config.storage[uuid]["mountpoint"].value = "/media/hdd"
1818                                         if dev is not None:
1819                                                 p = self.getPartitionbyDevice(dev) or self.getPartitionbyMountpoint(config.storage[uuid]["mountpoint"].value)
1820                                                 if p is None: # manually add partition entry
1821                                                         description = self.getDeviceDescription(dev)
1822                                                         device_mountpoint = self.getAutofsMountpoint(dev)
1823                                                         if config.storage[uuid]['mountpoint'].value != "":
1824                                                                 device_mountpoint = config.storage[uuid]['mountpoint'].value
1825                                                         p = Partition(mountpoint = device_mountpoint, description = description, force_mounted = False, device = dev)
1826                                                         p.uuid = uuid
1827                                                         p.updatePartitionInfo()
1828                                                         self.partitions.append(p)
1829                                                         self.on_partition_list_change("add", p)
1830                                         if path.exists("/dev/disk/by-uuid/" + uuid):
1831                                                 self.storageDeviceChanged(uuid)
1832                                 else:
1833                                         del config.storage[uuid]
1834                                         config.storage.save()
1835                 if dev is not None:
1836                         uuid = self.getPartitionUUID(dev)
1837                         if uuid is not None:
1838                                 if config.storage.get(uuid, None) is None: #new unconfigured device added
1839                                         print "[setupConfigEntries] new device add for '%s' with uuid:'%s'" % (dev, uuid)
1840                                         hdd = self.getHDD(dev)
1841                                         if hdd is not None:
1842                                                 hdd_description = hdd.model()
1843                                                 cap = hdd.capacity()
1844                                                 if cap != "":
1845                                                         hdd_description += " (" + cap + ")"
1846                                                 device_info =  hdd.bus_description()
1847                                         else:
1848                                                 device_info = dev
1849                                                 hdd_description = self.getDeviceDescription(dev)
1850                                         config.storage[uuid] = ConfigSubDict()
1851                                         config.storage[uuid]["enabled"] = ConfigYesNo(default = False)
1852                                         config.storage[uuid]["mountpoint"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1853                                         config.storage[uuid]["device_description"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1854                                         config.storage[uuid]["device_info"] = ConfigText(default = "", visible_width = 50, fixed_size = False)
1855                                         config.storage[uuid]["isRemovable"] = ConfigBoolean(default = False)
1856                                         removable = False
1857                                         if hdd is not None:
1858                                                 removable = hdd.isRemovable
1859                                         config.storage[uuid]["device_description"].setValue(hdd_description)
1860                                         config.storage[uuid]["device_info"].setValue(device_info)
1861                                         config.storage[uuid]["isRemovable"].setValue(removable)
1862                                         config.storage[uuid].save()
1863                                         self.verifyInstalledStorageDevices()
1864                                         p = self.getPartitionbyDevice(dev)
1865                                         if p is None: # manually add partition entry (e.g. on long spinup times)
1866                                                 description = self.getDeviceDescription(dev)
1867                                                 device_mountpoint = self.getAutofsMountpoint(dev)
1868                                                 p = Partition(mountpoint = device_mountpoint, description = description, force_mounted = True, device = dev)
1869                                                 p.uuid = uuid
1870                                                 p.updatePartitionInfo()
1871                                                 self.partitions.append(p)
1872                                                 self.on_partition_list_change("add", p)
1873                                         self.storageDeviceChanged(uuid)
1874                                 else:
1875                                         p = self.getPartitionbyDevice(dev)
1876                                         device_mountpoint = self.getAutofsMountpoint(dev)
1877                                         if config.storage[uuid]['enabled'].value and config.storage[uuid]['mountpoint'].value != "":
1878                                                 device_mountpoint = config.storage[uuid]['mountpoint'].value
1879                                         if p is None: # manually add partition entry (e.g. on default storage device change)
1880                                                 description = self.getDeviceDescription(dev)
1881                                                 p = Partition(mountpoint = device_mountpoint, description = description, force_mounted = True, device = dev)
1882                                                 p.uuid = uuid
1883                                                 p.updatePartitionInfo()
1884                                                 self.partitions.append(p)
1885                                                 self.on_partition_list_change("add", p)
1886                                                 print "[setupConfigEntries] new/changed device add for '%s' with uuid:'%s'" % (dev, uuid)
1887                                                 self.storageDeviceChanged(uuid)
1888                                         else:
1889                                                 tmp = self.getPartitionbyMountpoint(device_mountpoint)
1890                                                 if tmp is not None and (tmp.uuid != uuid or tmp.mountpoint != config.storage[uuid]['mountpoint'].value):
1891                                                         print "[setupConfigEntries] new/changed device add for '%s' with uuid:'%s'" % (dev, uuid)
1892                                                         self.storageDeviceChanged(uuid)
1893                         else:
1894                                 print "[setupConfigEntries] device add for '%s' without uuid !!!" % (dev)
1895
1896         def configureUuidAsDefault(self, uuid, device):
1897                 #verify if our device is manually mounted from fstab to somewhere else
1898                 isManualFstabMount = False
1899                 uuidPath = "/dev/disk/by-uuid/" + uuid
1900                 tmpmount = self.get_mountpoint(uuidPath)
1901                 if tmpmount is not None and tmpmount != "/media/hdd":
1902                         isManualFstabMount = True
1903                 if not isManualFstabMount:
1904                         if not path.islink("/media/hdd") and not self.isPartitionpathFsTabMount(uuid, "/media/hdd"):
1905                                 print "configureUuidAsDefault: using found %s as default storage device" % device
1906                                 config.storage_options.default_device.value = uuid
1907                                 config.storage_options.save()
1908                                 cfg_uuid = config.storage.get(uuid, None)
1909                                 if cfg_uuid is not None and not cfg_uuid["enabled"].value:
1910                                         cfg_uuid["enabled"].value = True
1911                                         cfg_uuid["mountpoint"].value = "/media/hdd"
1912                                         config.storage.save()
1913                                         self.modifyFstabEntry("/dev/disk/by-uuid/" + uuid, "/media/hdd", mode = "add_activated")
1914                                         self.storageDeviceChanged(uuid)
1915                                 configfile.save()
1916
1917         def isInitializedByEnigma2(self, hdd):
1918                 isInitializedByEnigma2 = False
1919                 uuid = device = None
1920                 if hdd and hdd.numPartitions() <= 2:
1921                         numPart = hdd.numPartitions()
1922                         device = hdd.device
1923                         if numPart == 1 or numPart == 2:
1924                                 device = hdd.device + "1"
1925                         p = self.getPartitionbyDevice(device)
1926                         if p is not None and p.uuid and p.isInitialized: #only one by e2 initialized partition
1927                                 isInitializedByEnigma2 = True
1928                                 uuid = p.uuid
1929                         if numPart == 2:
1930                                 part2Type = self.getBlkidPartitionType(hdd.partitionPath("2"))
1931                                 if part2Type != "swap":
1932                                         isInitializedByEnigma2 = False
1933                 return isInitializedByEnigma2,device,uuid
1934
1935         def verifyInstalledStorageDevices(self):
1936                 print "verifyInstalledStorageDevices"
1937                 if self.HDDCount() == 1 and not self.HDDEnabledCount(): #only one installed and unconfigured device
1938                         hdd = self.hdd[0]
1939                         isInitializedByEnigma2,device,uuid = self.isInitializedByEnigma2(hdd)
1940                         if config.storage_options.default_device.value == "<undefined>" or config.storage_options.default_device.value == uuid:
1941                                 if isInitializedByEnigma2:
1942                                         self.configureUuidAsDefault(uuid, device)
1943
1944
1945 harddiskmanager = HarddiskManager()
1946 harddiskmanager.enumerateBlockDevices()
1947 harddiskmanager.verifyInstalledStorageDevices() #take sure enumerateBlockdev is finished so we don't miss any at startup installed storage devices