#!/usr/bin/env python # -*- coding: utf-8 -*- # # chkconfig: 345 03 03 # description: If the NAND doesn't have enough free space, delete datastore\ # objects until it does. This doesn't modify the datastore's\ # index. # processname: diskspacerecover # Author: Chris Ball <cjb@laptop.org> import os, os.path, sys, statvfs, subprocess, shutil, fcntl THRESHOLD = 1024 * 20 # 20MB DATASTORE_PATH = "/home/olpc/.sugar/default/datastore" DATASTORE_PATH_0_82 = "/home/olpc/.sugar/default/datastore/store" ACTIVITY_PATH = "/home/olpc/Activities" erase_datastore_index = False def main(): global erase_datastore_index # First, check to see whether we have enough free space. if find_freespace() < THRESHOLD: print "Not enough disk space." # Per Trac #5637, delete orphaned leaks in .sugar/default/data. # This is safe on any build. try: shutil.rmtree("/home/olpc/.sugar/default/data") except OSError: pass if find_freespace() >= THRESHOLD: # The above gained enough free space. return # Okay, we'll have to delete some real data. fileinfo = [] lines = [] # Activities and entries from the 0.82 datastore can be deleted # straight-up lines = os.popen("du -s %s/*" % ACTIVITY_PATH).readlines() if os.path.isdir(DATASTORE_PATH_0_82): lines += (os.popen("du -s %s/*-*" % DATASTORE_PATH_0_82).readlines()) for line in lines: size, path = line.split('\t') fileinfo.append((int(size), path.rstrip(), delete_simple)) # The sugar-0.84 datastore is a bit more difficult. if has_datastore_0_84(): lines = os.popen("du -s %s/[0-9a-f][0-9a-f]/*" % DATASTORE_PATH).readlines() for line in lines: size, path = line.split('\t') fileinfo.append((int(size), path.rstrip(), delete_ds_0_84)) fileinfo.sort() # The below string is in latin-1, because Unicode isn't present # in the environment when this script runs. string = u""" Your disk is nearly full. The system cannot operate with a full disk. To create free space, some of the entries in your Journal will now be deleted. If you wish to avoid having items deleted, power down your XO and bring it to an expert for backup and recovery. Error code: DISKFULL Press the return key to delete some Journal entries, or the 'c' key and then return key to attempt to boot anyway. Su disco está casi lleno. El sistema no puede funcionar si el disco está lleno. Para liberar espacio, se deben borrar algunas entradas de su Diario ahora. Si prefiere no tener que borrar datos, apague su XO y llévelo a un experto para que haga una copia de seguridad y recuperación de datos. Código del error: DISKFULL Pulse retorno tecla para borrar algunas entradas del Diario, o presiona 'c' y retorno para iniciar de cualquier manera. """ key = raw_input(string.encode('utf-8')) if key is 'c': return # Now, delete files/directories one at a time. while find_freespace() < THRESHOLD and len(fileinfo) > 0: size, path, delfunc = fileinfo.pop() try: print "Deleting " + path delfunc(path) except Exception, e: print "Could not delete %s: %s" % (path, e) # If we touched the datastore, delete the index. # It will be automatically regenerated by sugar. if erase_datastore_index: try: shutil.rmtree(os.path.join(DATASTORE_PATH, "index")) except: pass try: os.remove(os.path.join(DATASTORE_PATH, "index_updated")) except: pass def has_datastore_0_84(): try: if not os.path.isfile(os.path.join(DATASTORE_PATH, "version")): return False fd = open(os.path.join(DATASTORE_PATH, "version"), "r") ver = fd.read().strip() fd.close() if not ver.isdigit(): return False if int(ver) != 1: return False return True except: return False def find_freespace(): # Determine free space on /. stat = os.statvfs("/") freebytes = stat[statvfs.F_BSIZE] * stat[statvfs.F_BAVAIL] freekbytes = freebytes / 1024 return freekbytes def delete_simple(entry): # Delete a single file from the datastore, or an activity directory if os.path.isdir(entry): shutil.rmtree(entry) else: os.remove(entry) def delete_ds_0_84(path): # for the datastore in sugar-0.84, we delete both the file itself (and # surrounding metadata) and its entry from the checksums index. # this may have almost no effect because another entry is pointing at the # same file (via a hardlink), but that entry will also be in fileinfo so # we'll likely end up deleting all hardlinks to the same file. global erase_datastore_index erase_datastore_index = True entry = os.path.basename(path) cspath = os.path.join(path, "metadata", "checksum") # read checksum first fd = open(cspath, "r") checksum = fd.read().strip() fd.close() if len(checksum) == 0: raise Exception("No checksum?") # delete the file and metadata shutil.rmtree(path) # remove checksums entry csdir = os.path.join(DATASTORE_PATH, "checksums", checksum) os.remove(os.path.join(csdir, entry)) try: os.rmdir(csdir) except OSError: # ignore directory-not-empty errors; other entries may be pointing # at the same file so we'll still have a few entries remaining in # the checksum dir. pass main()