6 if sys.hexversion < 0x02040000:
7 print >> sys.stderr, "Python 2.4 or newer is required."
28 Diff two folders and create delta using SS_BSDIFF
29 Will maintain same format of script that will be generated when we use diffutil
31 1. Create a list of files in each Base folders,
32 2. These files will fall into one these below categories:
33 1) Only in OLD - Should be deleted
34 2) Only in NEW - Should be added or renamed accordingly
35 3) File exists in both directories but contents are different - Create Diff.
36 4) File name is same but TYPE can change (File to Folder, Folder to Link etc.)
37 5) Duplicates in the list of Deletes and News
38 6) Close matching diffs even though name changes across directories. (for matching extension)
39 7) Clearing empty directories after Moves or diffs under Rename.
40 8) Supporting Verbatim - Any entry under Verbatim_list.txt will be treated as NEW files instead of patch.
43 1. Given two folders, from list of REMOVED and NEW files find if there
44 is version change and create diff between them
47 Want to extend the same script for entire DIFF generation and replace TOTAlib.sh file
48 Catching errors at all stages. SHOULD exit & return error in case of failure
55 global NEW_FILES_FOLDER
56 global NEW_FILES_ZIP_NAME
59 global SYMLINK_DOC_NAME
65 global SUPPORT_CONTAINERS
70 global COMMON_BIN_PATH
76 COMMON_BIN_PATH = "../../common/bin/"
77 DIFF_UTIL = "/usr/local/bin/ss_bsdiff"
78 DIFFPATCH_UTIL = "/usr/local/bin/ss_bspatch"
80 #ZIPUTIL = "7z a system.7z "
81 NEW_FILES_PATH = "system"
82 NEW_FILES_FOLDER = "system"
83 NEW_FILES_ZIP_NAME = "system.7z"
85 ATTR_DOC_EXT = "_attr.txt"
86 SYMLINK_DOC_NAME = "_sym.txt"
89 DIFF_SUFFIX = ".delta"
92 DELTA_IMG = "DELTA_IMG"
96 VERBATIM_LIST = "Verbatim_List.txt"
101 SUPPORT_RENAME = "TRUE" #Use appropriate name
102 SUPPORT_CONTAINERS = "FALSE"
103 SUPPORT_DZIMAGE = "TRUE"
104 SUPPORT_VERBATIM = "TRUE"
109 logging.basicConfig(filename=LOGFILE, level=logging.DEBUG)
111 global GenerateDiffAttr
114 if len(sys.argv) < 5:
115 sys.exit('Usage: CreatePatch.py UPDATE_TYPE PARTNAME OLDBASE NEWBASE OUTFOLDER')
116 UPDATE_TYPE = sys.argv[1]
117 PART_NAME = sys.argv[2] # lets make this also optional
119 BASE_OLD = sys.argv[3]
120 BASE_NEW = sys.argv[4]
121 OUT_DIR = sys.argv[5]
124 UPDATE_CFG_PATH = EMPTY
125 GenerateDiffAttr = "FALSE"
126 if UPDATE_TYPE == DELTA_FS:
127 #instead of arguments check it in outdirectory ?
128 if len(sys.argv) == 9:
129 ATTR_OLD = sys.argv[6]
130 ATTR_NEW = sys.argv[7]
131 UPDATE_CFG_PATH = '../'+sys.argv[8]
132 GenerateDiffAttr = "TRUE"
134 elif UPDATE_TYPE == DELTA_IMG or UPDATE_TYPE == FULL_IMG:
135 if len(sys.argv) == 7:
136 #Use path in better way
137 UPDATE_CFG_PATH = '../'+sys.argv[6]
140 global DIFFPATCH_UTIL
141 if not (os.path.isfile(DIFF_UTIL) and os.access(DIFF_UTIL, os.X_OK)):
142 DIFF_UTIL = COMMON_BIN_PATH+DIFF_UTIL
143 DIFFPATCH_UTIL = COMMON_BIN_PATH+DIFFPATCH_UTIL
144 if not (os.path.isfile(DIFF_UTIL) and os.access(DIFF_UTIL, os.X_OK)):
145 print >> sys.stderr, "Diff Util Does NOT exist -- ABORT"
146 logging.info ('Diff Util Does NOT exist -- ABORT')
149 start = datetime.datetime.now().time()
150 logging.info('*************** ENTERED PYTHON SCRIPT *****************')
151 logging.info('Arguments Passed: [UpdateType - %s][Part Name - %s] [BaseOld - %s] [BaseNew - %s] \n [OUTPUTDir - %s] [BASE ATTR - %s] [TARGET ATTR - %s]'% (UPDATE_TYPE, PART_NAME, BASE_OLD, BASE_NEW, OUT_DIR, ATTR_OLD, ATTR_NEW))
153 ensure_dir_exists(OUT_DIR)
154 if GenerateDiffAttr == "TRUE":
155 if not (os.path.isfile(ATTR_OLD) and os.path.isfile(ATTR_NEW)):
156 print >> sys.stderr, "Attributes missing -- ABORT"
160 # Should check if APT is supported on other linux flavours
162 if cache['p7zip'].is_installed and cache['attr'].is_installed and cache['tar'].is_installed:
163 logging.info ('Basic utils installed')
165 print >> sys.stderr, "Basic utils missing -- ABORT"
168 if UPDATE_TYPE == FULL_IMG:
169 SS_mk_full_img(BASE_OLD, BASE_NEW, OUT_DIR, PART_NAME, UPDATE_CFG_PATH)
170 elif UPDATE_TYPE == DELTA_IMG:
171 SS_mk_delta_img(BASE_OLD, BASE_NEW, OUT_DIR, PART_NAME, UPDATE_CFG_PATH)
172 elif UPDATE_TYPE == DELTA_FS:
173 AttributeFile = ATTR_NEW
174 ATTR_FILE = OUT_DIR+'/'+PART_NAME+ATTR_DOC_EXT
175 Diff_AttrFiles(ATTR_OLD, ATTR_NEW, ATTR_FILE)
176 Old_files, Old_dirs = Get_Files(BASE_OLD)
177 New_files, New_dirs = Get_Files(BASE_NEW)
178 SS_Generate_Delta(PART_NAME, BASE_OLD, Old_files, Old_dirs, BASE_NEW, New_files, New_dirs, OUT_DIR, ATTR_FILE)
180 if not UPDATE_CFG_PATH == EMPTY:
181 SS_update_cfg(PART_NAME, UPDATE_CFG_PATH)
184 elif UPDATE_TYPE == EXTRA:
185 print('UPDATE_TYPE ---- EXTRA')
187 print('UPDATE_TYPE ---- UNKNOWN FORMAT')
189 if GenerateDiffAttr == "TRUE":
190 if os.path.exists(ATTR_OLD) and os.path.exists(ATTR_NEW):
193 end = datetime.datetime.now().time()
195 logging.info('Max Memory requried to upgrade [%s] is [%d] for File[%s]' % (PART_NAME, MEM_REQ, MEM_FILE))
196 logging.info('*************** DONE WITH PYTHON SCRIPT ***************')
197 logging.info('Time start [%s] - Time end [%s]' % (start, end))
198 print('Done with [%s][%d]---- Time start [%s] - Time end [%s]' % (PART_NAME, MEM_REQ, start, end))
201 logging.error('Usage: {} <Update_Type> <Part_Name> <OLD_Base> <NEW_Base> <OUT_DIR>'.format(os.path.basename(sys.argv[0])))
205 def SS_update_cfg(DELTA_BIN, UPDATE_CFG_PATH):
206 f = open(UPDATE_CFG_PATH, 'r')
207 lines = f.readlines()
209 f = open(UPDATE_CFG_PATH, 'w')
211 ConfigItems = line.split()
212 if ConfigItems[0] == DELTA_BIN:
213 DELTA = ConfigItems[1]
214 logging.info ('Updating %s config' % DELTA_BIN)
215 line = line.rstrip('\n')
217 line = line.replace(line, line+'\t'+str(Value)+'\n')
223 def SS_mk_delta_img(BASE_OLD, BASE_NEW, OUT_DIR, DELTA_BIN, UPDATE_CFG_PATH):
226 ZIMAGE_SCRIPT = COMMON_BIN_PATH+'./dzImagescript.sh'
227 ZIMAGE_OLD = BASE_OLD+'_unpacked'
228 ZIMAGE_NEW = BASE_NEW+'_unpacked'
229 DZIMAGE_HEADER = 'UnpackdzImage'
232 oldsize_d= os.path.getsize(BASE_OLD)
233 newsize_d= os.path.getsize(BASE_NEW)
234 SHA_BIN_DEST= hash_file(BASE_NEW)
235 SHA_BIN_BASE=hash_file(BASE_OLD)
237 #incase UPDATE CFG is empty
239 SS_UpdateSize(BASE_OLD, BASE_NEW)
240 #Should throw error if PART NAME NOT found??
241 if not UPDATE_CFG_PATH == EMPTY:
242 f = open(UPDATE_CFG_PATH, 'r')
243 lines = f.readlines()
245 f = open(UPDATE_CFG_PATH, 'w')
247 ConfigItems = line.split()
248 if ConfigItems[0] == DELTA_BIN:
249 logging.info ('Updating %s config' % DELTA_BIN)
250 DELTA = ConfigItems[1]
251 line = line.rstrip('\n')
252 line = line.replace(line, line+'\t'+str(oldsize_d)+'\t\t'+str(newsize_d)+'\t\t'+str(SHA_BIN_BASE)+'\t\t'+str(SHA_BIN_DEST)+'\n')
258 #Any validation checks required?
259 if (DELTA_BIN == "zImage" or DELTA_BIN == "dzImage" or DELTA_BIN == "KERNEL" or DELTA_BIN == "BOOT") and SUPPORT_DZIMAGE == "TRUE":
261 #Unpack Old and New Images for creating delta
262 subprocess.call([ZIMAGE_SCRIPT, '-u', BASE_OLD])
263 subprocess.call([ZIMAGE_SCRIPT, '-u', BASE_NEW])
266 Old_files, Old_dirs = Get_Files(ZIMAGE_OLD)
267 New_files, New_dirs = Get_Files(ZIMAGE_NEW)
269 patchLoc = '%s/%s_temp' % (OUT_DIR, DELTA_BIN)
270 ensure_dir_exists(patchLoc)
272 for elt in New_files:
274 src_file = ZIMAGE_OLD+'/'+elt
275 dst_file = ZIMAGE_NEW+'/'+elt
276 if not filecmp.cmp(src_file, dst_file):
277 patch = '%s/%s' % (patchLoc,elt)
278 DeltaFiles.append(patch)
279 subprocess.call([DIFF_UTIL,src_file,dst_file,patch])
280 logging.info('Make dz Image %s <--> %s ==> %s %s' % (src_file, dst_file , DELTA_BIN, patch))
282 #Append all delta files to make image.delta
284 #HEADER FORMAT MAGICNAME:FILECOUNT:[FILENAME:FILESIZE:][FILECONTENT/S]
285 HeaderStr = DZIMAGE_HEADER+DZIMAGE_SEP+'%d' % len(DeltaFiles)
286 HeaderStr = HeaderStr+DZIMAGE_SEP
288 with open(OUT_DIR+'/'+DELTA, 'w') as DeltaFile:
289 for fname in DeltaFiles:
290 DeltaSize = os.path.getsize(fname)
291 HeaderStr = HeaderStr+path_leaf(fname)+DZIMAGE_SEP+'%d' % DeltaSize
292 HeaderStr = HeaderStr+DZIMAGE_SEP
293 #Using 128 bytes as max Header.
294 logging.info('zImage Header - %s' % HeaderStr.ljust(128,'0'))
295 DeltaFile.write(HeaderStr.ljust(128,'0'))
296 for fname in DeltaFiles:
297 with open(fname) as infile:
298 DeltaFile.write(infile.read())
302 shutil.rmtree(patchLoc)
303 shutil.rmtree(ZIMAGE_OLD)
304 shutil.rmtree(ZIMAGE_NEW)
305 #Do we need to incorprate Max memory required for backup??
308 patchLoc = '%s/%s' % (OUT_DIR, DELTA)
309 logging.info('Make Delta Image %s <--> %s ==> %s %s' % (BASE_OLD, BASE_NEW , DELTA_BIN, patchLoc))
310 subprocess.call([DIFF_UTIL,BASE_OLD,BASE_NEW,patchLoc])
314 def SS_mk_full_img(BASE_OLD, BASE_NEW, OUT_DIR, DELTA_BIN ,UPDATE_CFG_PATH):
315 logging.info('Make Full Image %s <--> %s ==> %s' % (BASE_OLD, BASE_NEW ,DELTA_BIN))
316 oldsize_d= os.path.getsize(BASE_OLD)
317 newsize_d= os.path.getsize(BASE_NEW)
318 SHA_BIN_DEST= hash_file(BASE_NEW)
319 SHA_BIN_BASE=hash_file(BASE_OLD)
320 #echo -e "\t${oldsize_d}\t\t${newsize_d}\t\t${SHA_BIN_BASE}\t\t${SHA_BIN_DEST}" >> ${DATA_DIR}/update_new.cfg
321 SS_UpdateSize(BASE_OLD, BASE_NEW)
323 if not UPDATE_CFG_PATH == EMPTY:
324 f = open(UPDATE_CFG_PATH, 'r')
325 lines = f.readlines()
327 f = open(UPDATE_CFG_PATH, 'w')
329 ConfigItems = line.split()
330 if ConfigItems[0] == DELTA_BIN:
331 logging.info ('Updating %s config' % DELTA_BIN)
332 DELTA = ConfigItems[1]
333 line = line.rstrip('\n')
334 line = line.replace(line, line+'\t'+str(oldsize_d)+'\t\t'+str(newsize_d)+'\t\t'+str(SHA_BIN_BASE)+'\t\t'+str(SHA_BIN_DEST)+'\n')
340 def zipdir(path, zip):
341 for root, dirs, files in os.walk(path):
343 zip.write(os.path.join(root, file))
345 def ensure_dir_exists(path):
346 if not os.path.exists(path):
353 head, tail = ntpath.split(path) #This is for windows?? Recheck
357 head, tail = ntpath.split(path)
360 def difflines(list1, list2):
361 c = set(list1).union(set(list2))
362 d = set(list1).intersection(set(list2))
365 #Creating Diff between OLD and NEW attribute files v12
366 def Diff_AttrFiles(ATTR_OLD, ATTR_NEW, ATTR_FILE):
367 if GenerateDiffAttr == "FALSE":
369 with open(ATTR_OLD, 'r') as f_old:
370 lines1 = set(f_old.read().splitlines())
372 with open(ATTR_NEW, 'r') as f_new:
373 lines2 = set(f_new.read().splitlines())
375 lines = difflines(lines2, lines1)
376 with open(ATTR_FILE, 'w+') as file_out:
378 if line not in lines1:
379 logging.info('Diff_AttrFiles - %s' % line)
380 file_out.write(line+'\n')
388 def Update_Attr(RequestedPath, Type, File_Attibutes, Sym_Attibutes):
389 #Full File Path should MATCH
390 if GenerateDiffAttr == "FALSE":
392 FilePath = '"/'+RequestedPath+'"'
393 #print ('FilePath - %s'% (FilePath))
394 with open(AttributeFile) as f:
397 if Type == SYMLINK_TYPE:
398 Sym_Attibutes.append(line)
400 File_Attibutes.append(line)
403 '''This function returns the SHA-1 hash of the file passed into it'''
404 def hash_file(filename):
409 # open file for reading in binary mode
410 with open(filename,'rb') as file:
411 # loop till the end of the file
414 # read only 1024 bytes at a time
415 chunk = file.read(1024*1024)
418 # return the hex representation of digest
421 def find_dupes_dir(BASE_OLD, BASE_NEW):
424 print('Finding Duplicates in - %s %s' % (BASE_OLD, BASE_NEW))
425 logging.info('Finding Duplicates in - %s %s' % (BASE_OLD, BASE_NEW))
426 for rootbase, subdirsB, fileListB in os.walk(BASE_OLD):
427 #print('Scanning %s...' % rootbase)
428 for filename in fileListB:
429 path = os.path.join(rootbase, filename)
430 if os.path.islink(path):
433 file_hash = hash_file(path)
434 dups[file_hash] = path
436 for roottarget, subdirsT, fileListT in os.walk(BASE_NEW):
437 #print('Scanning %s...' % roottarget)
438 for filename in fileListT:
439 # Get the path to the file
440 path = os.path.join(roottarget, filename)
441 if os.path.islink(path):
444 file_hash = hash_file(path)
445 # Add or append the file path
446 if file_hash in dups:
447 BaseStr = dups.get(file_hash)
448 Baseloc = path.find('/')
449 TarLoc = BaseStr.find('/')
450 if not path[Baseloc:] == BaseStr[TarLoc:]:
451 logging.info('Dupes - %s ==> %s' % (path[Baseloc:], BaseStr[TarLoc:]))
452 fdupes[path] = BaseStr
453 logging.info('Total Duplicate files %d' % (len(fdupes)))
457 def find_dupes_list(BASE_OLD, BASE_NEW, fileListB, fileListT):
460 print('Finding Duplicates in - %s %s' % (BASE_OLD, BASE_NEW))
462 for filename in fileListB:
463 Src_File = BASE_OLD+'/'+filename
464 if os.path.islink(Src_File) or os.path.isdir(Src_File):
467 file_hash = hash_file(Src_File)
468 dups[file_hash] = Src_File
471 for filename in fileListT:
472 Dest_File = BASE_NEW+'/'+filename
473 if os.path.islink(Dest_File) or os.path.isdir(Dest_File):
476 file_hash = hash_file(Dest_File)
477 if file_hash in dups:
478 BaseStr = dups.get(file_hash)
479 Baseloc = BaseStr.find('/')
480 if not BaseStr[Baseloc:] == filename:
481 #print('Dupes - %s ==> %s' % (BaseStr[Baseloc:], filename))
482 fdupes[BaseStr] = filename
484 logging.info('Total Duplicate files %d' % (len(fdupes)))
487 def SS_UpdateSize(src_file, dst_file):
490 oldsize_d= os.path.getsize(src_file)
491 newsize_d= os.path.getsize(dst_file)
492 if oldsize_d >= newsize_d:
502 def SS_Generate_Delta(PART_NAME, BASE_OLD, Old_files, Old_dirs, BASE_NEW, New_files, New_dirs, OUT_DIR, ATTR_FILE):
503 print('Going from %d files to %d files' % (len(Old_files), len(New_files)))
504 logging.info('Going from %d files to %d files' % (len(Old_files), len(New_files)))
506 # First let's fill up these categories
526 for elt in New_files:
527 if elt not in Old_files:
528 files_new.append(elt)
529 logging.info('New files %s' % elt)
531 # Generate Delete List
532 for elt in Old_files:
533 if elt not in New_files:
534 # Cant we just append it here only if this is NOT a directory???? so that we have list of removed files ONLY. including directories
535 files_removed.append(elt)
536 logging.info('Old files %s' % elt)
540 #print('List of Old Dirs %s' % elt)
541 # Delete END logic goes in hand with UPG, After Diffs and moves, DEL END should be done.
542 if elt not in New_dirs:
543 Dir_removed.append(elt)
544 logging.info('Old Dirs %s' % elt+'/')
547 if elt not in Old_dirs:
548 Dir_Added.append(elt)
549 #++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
551 # What files have changed contents but not name/path?
552 for elt in New_files:
554 #Both are symbolic linkes and they differ
555 src_file = BASE_OLD+'/'+elt
556 dst_file = BASE_NEW+'/'+elt
557 #print('Files Changed - %s -%s' % (src_file,dst_file))
558 if os.path.islink(src_file) and os.path.islink(dst_file):
559 if not os.readlink(src_file) == os.readlink(dst_file):
560 files_changed.append(elt)
561 #print('%d Sym link files changed' % len(files_changed))
562 logging.info('Sym links Changed - %s' % elt)
564 files_unchanged.append(elt)
565 #Both are Normal files and they differ. (Is file returns true in case of symlink also, so additional check to find either of the file is symlink)
566 elif (not (os.path.islink(src_file) or os.path.islink(dst_file))) and os.path.isfile(src_file) and os.path.isfile(dst_file):
567 if not filecmp.cmp(src_file, dst_file):
568 files_changed.append(elt)
569 #print('%d Normal files changed' % len(files_changed))
570 #print('Files Changed - %s' % elt)
572 files_unchanged.append(elt)
573 #File types differ between BASE and TARGET
575 logging.info('Files are of diff types but same names Src- %s Des- %s' % (src_file, dst_file))
576 #Both file types have changed and they differ
577 #Case 1: First Delete the OLD entry file type (Be it anything)
578 #Processing and updating partition txt file will be done under REMOVED case and NEW files case accordingly, we just make an entry here
579 files_removed.append(elt)
580 files_new.append(elt)
583 # HANDLING VERBATIM - Remove from changed list and delete the entries on device first
584 #This script is called partition wise, So, how do u want to handle it? (specialy for delete case?)
586 print("Check for any verbatim under - %s" % VERBATIM_LIST)
587 if SUPPORT_VERBATIM == "TRUE" and os.path.exists(VERBATIM_LIST):
588 with open(VERBATIM_LIST, 'r') as F_News:
589 lines = set(F_News.read().splitlines())
591 if line in files_changed:
592 files_changed.remove(line)
593 files_removed.append(line)
594 if line in files_new:
595 files_new.remove(line)
597 #Currently if Version or number is the first character of the file, then we are NOT making any diffs.
598 if SUPPORT_RENAME == "TRUE":
599 for elt in files_removed:
600 if os.path.isfile(BASE_OLD+'/'+elt):
601 FileName = path_leaf(elt)
602 entries = re.split('[0-9]' , FileName)
603 #Gives the STRING part of NAME. if name starts with version then later part wil b string
604 #print('Entires under removed list after split - %s %s - %s' % (FileName, entries[0], elt))
605 #If version is starting at the begining of the string?? shd we hav additional check for such cases??
606 if len(entries[0]) > 0:
607 files_Del_List.update({entries[0]: elt})
609 for elt in files_new:
610 if os.path.isfile(BASE_NEW+'/'+elt):
611 FileName = path_leaf(elt)
612 entries = re.split('[0-9]' , FileName)
613 #print('Entires under NEWfiles list after split - %s %s - %s' % (FileName, entries[0], elt))
614 if len(entries[0]) > 0:
615 files_New_List.update({entries[0]: elt})
617 for key, value in files_Del_List.iteritems():
618 #print('Key value pair -%s -%s' % (key, value))
619 if key in files_New_List:
620 # this file is the same name in both!
621 src_file = BASE_OLD+'/'+value
622 dst_file = BASE_NEW+'/'+files_New_List[key]
623 olddirpath = path_head(files_New_List[key])
624 newdirpath = path_head(value)
625 if os.path.islink(src_file) or os.path.islink(dst_file):
626 logging.debug('Cannot diff as one of them is Symlink')
627 elif os.path.isdir(src_file) or os.path.isdir(dst_file):
628 logging.debug('Cannot diff as one of them is dir')
630 #Pick the best diff of same type and diff names
631 files_renamed.append([files_New_List[key], value])
632 files_removed.remove(value)
633 files_new.remove(files_New_List[key])
637 Partition.txt contains Protocol for UPI
638 Types Supported: DIFFS, MOVES, NEWS, DELETES, SYMDIFFS, SYMNEWS.
647 SymLinkDoc = OUT_DIR+'/'+PART_NAME+SYMLINK_DOC_NAME
648 Partition_Doc = open(OUT_DIR+'/'+PART_NAME+'.txt','w')
649 Partition_Doc_SymLinks = open(SymLinkDoc,'w')
651 print("writing diff'ed changed files...")
652 for elt in files_changed:
653 dst_file = BASE_NEW+'/'+elt
654 src_file = BASE_OLD+'/'+elt
655 #Both files are symbolic links and they differ
656 if os.path.islink(dst_file) and os.path.islink(src_file):
657 #Both are symlinks and they differ
658 logging.debug(' File Changed is Link %s ' % dst_file)
659 patch = os.readlink(dst_file)
660 Sym_Diff_Cnt = Sym_Diff_Cnt + 1
661 Partition_Doc_SymLinks.write('SYM:DIFF:%s:%s:%s\n' % (elt, elt, patch))
662 Update_Attr(elt, "SYM", File_Attibutes, Sym_Attibutes)
663 #Both are NORMAL files and they differ
664 elif (not (os.path.islink(src_file) or os.path.islink(dst_file))) and os.path.isfile(dst_file) and os.path.isfile(src_file):
665 #Both are files and they differ
666 Diff_Cnt = Diff_Cnt + 1
667 patchName = (DIFF_PREFIX+'%d_%s_'+PART_NAME+DIFF_SUFFIX) % (Diff_Cnt, path_leaf(elt))
668 patchLoc = '%s/%s' % (OUT_DIR, patchName)
669 logging.debug(' File Differ %s %s' % (src_file, dst_file))
670 SS_UpdateSize(src_file, dst_file)
673 ret = subprocess.call([DIFF_UTIL,src_file,dst_file,patchLoc])
675 logging.debug('Failed to create diff %d %s %s\n' % (ret, src_file, dst_file))
676 files_new.append(elt)
677 Diff_Cnt = Diff_Cnt - 1
679 Partition_Doc.write('DIFF:REG:%s:%s:%s:%s:%s\n' % (elt, elt, hash_file(src_file), hash_file(dst_file), patchName))
681 Update_Attr(elt, "FILE", File_Attibutes, Sym_Attibutes)
682 #Both differ but they are of diff types
684 #Processing and updating partition txt file will be done under REMOVED case and NEW files case accordingly, we just make an entry here
685 files_removed.append(elt)
686 files_new.append(elt)
688 fdupes = find_dupes_list(BASE_OLD, BASE_NEW, files_removed, files_new)
689 for oldpath, newpath in fdupes.iteritems():
690 logging.info('Dupes %s -> %s' % (oldpath, newpath))
692 for elt in files_removed:
693 src_file = BASE_OLD+'/'+elt
694 #If parent directory is deleted.. & del end not possible. (==> Moves should be done before deletes in ENGINE)
695 if src_file in fdupes.keys():
696 dst_file = BASE_NEW+'/'+ fdupes[src_file]
697 logging.debug(' File Moved %s ==> %s' % (src_file, dst_file))
698 Move_Cnt = Move_Cnt + 1
699 Partition_Doc.write('MOVE:REG:%s:%s:%s\n' % (elt, fdupes[src_file], hash_file(src_file)))
700 files_removed.remove(elt)
701 files_new.remove(fdupes[src_file])
703 #Should be placed after removing duplicates, else they will be filtered here.
704 # loop shd b for all NEW files, rather than for all delete files (Current understanding)
705 # First Step: Sort & Filter out unwanted files
706 # Minimum condition used is,
707 # 1. File name should match 70%
708 # 2. Extensions should be same
709 # 3. File name length shd b greater than 3 char
710 # 4. As we are using sorting on file names, once file name does not match and R_Flag is set to true, we nee not check remaining files. So, will execute break.
711 # 5. Should consider editdistance for RENAME LOGIC ==> TBD
713 Base_DelList = files_removed[:]
714 Base_NewList = files_new[:]
715 DelList = sorted(Base_DelList, key=path_leaf)
716 NewList = sorted(Base_NewList, key=path_leaf)
717 logging.debug('Rename Logic before filter: Delcount -%d NewCount -%d' % (len(DelList), len(NewList)))
721 #Remove unwanted items which we cant make diff with for rename logic
723 if os.path.islink(BASE_OLD+'/'+file):
725 elif os.path.isdir(BASE_OLD+'/'+file):
729 #logging.debug('Sorted del list - %s' % (file))
734 if os.path.islink(BASE_NEW+'/'+file):
736 elif os.path.isdir(BASE_NEW+'/'+file):
738 elif len(path_leaf(file)) <= 3:
739 logging.debug('Ignored for best picks -%s ' % (BASE_NEW+'/'+file))
746 logging.debug('Rename Logic After filter: Delcount -%d NewCount -%d' % (len(DelList), len(NewList)))
748 for new_file in NewList:
750 DirPathNew = path_head(new_file)
751 FileNameNew = path_leaf(new_file)
753 winning_patch_sz = os.path.getsize(BASE_NEW+'/'+new_file)
754 New_fs = winning_patch_sz
757 for del_file in DelList:
758 FileNameOld = path_leaf(del_file)
759 if (FileNameOld.startswith(FileNameNew[:len(FileNameNew)*7/10]) and (os.path.splitext(FileNameNew)[1] == os.path.splitext(del_file)[1])):
760 #winning_patch_sz = 0.9 * os.path.getsize(BASE_NEW+'/'+new_file)
761 #Percentage difference between two file sizes is within 30%, then we consider for diff generation
762 Del_fs = os.path.getsize(BASE_OLD+'/'+del_file)
763 v1 = abs(New_fs-Del_fs)
764 v2 = (New_fs+Del_fs)/2
765 if( v2<=0 or ((v1/v2) * 100) > 30 ):
766 logging.debug('Ignore diff generation New_fs - %d Del_Fs - %d' % (New_fs, Del_fs))
768 logging.debug('I can compute diff between %s %s Del_Fs - %d New_Fs - %d' % (del_file, new_file, Del_fs, New_fs))
770 DiffSize = measure_two_filediffs(BASE_OLD+'/'+del_file, BASE_NEW+'/'+new_file)
771 if (DiffSize < 0.8 * winning_patch_sz):
772 winning_patch_sz = DiffSize
773 winning_file = del_file
774 elif (not FileNameOld.startswith(FileNameNew[:len(FileNameNew)*7/10]) and R_Flag == 'TRUE'):
775 logging.debug('Becuase nex set of files will not have matching name - break @@ %s %s' % (del_file, new_file))
777 if len(winning_file) > 0:
778 logging.debug('Best Pick -%s ==> %s [%d]' % (winning_file, new_file, DiffSize))
779 files_renamed.append([new_file, winning_file])
780 DelList.remove(winning_file)
781 files_removed.remove(winning_file)
782 files_new.remove(new_file)
784 #********************** Files should NOT be deleted for any such renames ***********************
786 if SUPPORT_RENAME == "TRUE":
787 for elt in files_renamed:
788 src_file = BASE_OLD+'/'+elt[1]
789 dst_file = BASE_NEW+'/'+elt[0]
790 Diff_Cnt = Diff_Cnt + 1
791 patchName = (DIFF_PREFIX+'%d_%s_'+PART_NAME+DIFF_SUFFIX) % (Diff_Cnt, path_leaf(elt[1]))
792 #patchName = (DIFF_PREFIX+'_%s'+DIFF_SUFFIX) % (path_leaf(elt[0]))
793 patchLoc = '%s/%s' % (OUT_DIR, patchName)
794 logging.debug(' File Renamed %s ==> %s' % (src_file, dst_file))
795 # Should be careful of renaming files??
796 # Should we consider measure_two_filediffs ?? so that patch size is NOT greater than actual file?
797 # What if folder path has numerics??
799 if os.path.isdir(src_file) or os.path.isdir(dst_file):
800 #This case never occurs??
801 Partition_Doc.write('"%s" and "%s" renamed 0 0\n' % (elt[0], elt[1]))
802 Update_Attr(elt[0], "FILE", File_Attibutes, Sym_Attibutes)
803 else: #Make sure these files are PROPER and they shd NOT be symlinks
804 if filecmp.cmp(src_file, dst_file):
805 Move_Cnt = Move_Cnt + 1
806 Diff_Cnt = Diff_Cnt - 1
807 Partition_Doc.write('MOVE:REG:%s:%s:%s\n' % (elt[1], elt[0], hash_file(src_file)))
810 ret = subprocess.call([DIFF_UTIL,src_file,dst_file,patchLoc])
812 logging.debug('Failed to create diff %d %s %s\n' % (ret, src_file, dst_file))
813 files_new.append(elt)
814 Diff_Cnt = Diff_Cnt - 1
816 Partition_Doc.write('DIFF:REG:%s:%s:%s:%s:%s\n' % (elt[1], elt[0], hash_file(src_file), hash_file(dst_file), patchName))
818 SS_UpdateSize(src_file, dst_file)
819 Update_Attr(elt[0], "FILE", File_Attibutes, Sym_Attibutes)
822 #HANDLING VERBATIM - We Process NEWs and DELETEs for Verbatim list ONLY after processing duplicates & rename functionality.
823 #So that, the rename functionality will NOT create PATCH instead of verbatims.
825 if SUPPORT_VERBATIM == "TRUE" and os.path.exists(VERBATIM_LIST):
826 with open(VERBATIM_LIST, 'r') as F_News:
827 lines = set(F_News.read().splitlines())
829 if not line in files_new:
830 if os.path.exists(BASE_NEW+'/'+line):
831 files_new.append(line)
832 Verbatim_Cnt = Verbatim_Cnt+1
833 logging.debug("Added to list of verbatims -%s" % BASE_NEW+'/'+line)
837 for elt in files_removed:
838 #if files are part of patches after renaming, we shd remove them as part of removed.
839 src_file = BASE_OLD+'/'+elt
840 if os.path.islink(src_file):
841 Partition_Doc.write('DEL:SYM:%s\n' % (elt))
842 elif os.path.isdir(src_file):
843 #If we change to DIR TYPE, then the same token should be modified on UA also and SHA should be accordingly passed.
844 Partition_Doc.write('DEL:REG:%s:NA\n' % (elt))
846 Partition_Doc.write('DEL:REG:%s:%s\n' % (elt, hash_file(src_file)))
847 logging.debug(' File Deleted %s' % src_file)
848 Del_Cnt = Del_Cnt + 1
850 Dir_removed.sort(reverse=True)
851 for elt in Dir_removed:
852 #if Dir is empty, add it to the removed list.
853 src_file = BASE_OLD+'/'+elt
854 #Irrespective of weather files are MOVED or DIFF'ed, we can delete the folders. This action can be performed at the end.
855 #It covers symlinks also, as NEW symlinks cannot point to NON existant folders of TARGET (NEW binary)
856 if os.path.isdir(src_file):
857 Partition_Doc.write('DEL:END:%s\n' % (elt))
858 Del_Cnt = Del_Cnt + 1
859 logging.debug(' Dir Deleted- %s' % src_file)
862 for elt in files_new:
863 dst_file = BASE_NEW+'/'+elt
864 newfiles_dest_path = 'system/'
865 ensure_dir_exists(newfiles_dest_path)
866 if os.path.islink(dst_file):
867 patch = os.readlink(dst_file)
868 logging.debug(' File New Links %s' % elt)
869 Partition_Doc_SymLinks.write('SYM:NEW:%s:%s\n' % (elt, patch))
870 #What if this is only a new sym link and folder already exists??? Should recheck
871 destpath = newfiles_dest_path + elt
872 if not os.path.exists(path_head(destpath)):
873 os.makedirs(path_head(destpath))
874 logging.info('New SymLink - Adding missing Dir')
875 #Update_Attr(elt, "SYM", File_Attibutes, Sym_Attibutes)
876 Sym_New_Cnt = Sym_New_Cnt + 1
877 elif os.path.isdir(dst_file): # We create just empty directory here
878 destpath = newfiles_dest_path + elt
879 if not os.path.exists(destpath):
880 os.makedirs(destpath)
881 logging.debug(' File New Dir %s' % destpath)
882 New_Cnt = New_Cnt + 1
884 New_Cnt = New_Cnt + 1
885 #newfiles_dest_path = OUT_DIR + '/system/'
886 destpath = newfiles_dest_path + elt
887 destdir = os.path.dirname(destpath)
888 logging.debug('New files - %s ==> %s' % (dst_file, destdir))
890 if not os.path.isdir(destdir):
894 logging.critical('Error in NEW files DIR entry -%s' % destdir)
898 if not stat.S_ISFIFO(os.stat(dst_file).st_mode):
899 shutil.copy2(dst_file, destpath)
900 logging.debug('New files copied from- %s to- %s' % (dst_file, destpath))
902 logging.critical('Error in NEW files entry -%s -%s' % (dst_file, destpath))
905 for elt in Dir_Added:
906 newfiles_dest_path = 'system/'
907 ensure_dir_exists(newfiles_dest_path)
908 destpath = newfiles_dest_path + elt
909 if not os.path.exists(destpath):
910 os.makedirs(destpath)
911 logging.debug(' DirList New Dir %s' % destpath)
912 New_Cnt = New_Cnt + 1
914 #Base directory should be system
915 print 'Compressing New files'
916 if (New_Cnt > 0 or Sym_New_Cnt > 0):
917 WorkingDir = os.getcwd()
918 os.chdir(os.getcwd()+"/"+NEW_FILES_PATH)
919 logging.info('Curr Working Dir - %s' % os.getcwd())
920 os.system(ZIPUTIL+NEW_FILES_PATH+" >> " + LOGFILE)
921 shutil.move(NEW_FILES_ZIP_NAME, WorkingDir+"/"+OUT_DIR)
922 #New file size?? cos, we extract system.7z from delta.tar and then proceed with decompression
923 SS_UpdateSize(WorkingDir+"/"+OUT_DIR+"/"+NEW_FILES_ZIP_NAME, WorkingDir+"/"+OUT_DIR+"/"+NEW_FILES_ZIP_NAME)
925 shutil.rmtree(NEW_FILES_PATH)
926 # use 7z a system.7z ./*
928 #logging.info('%d Dir to be removed' % len(Dir_removed))
929 logging.info('%d files unchanged' % len(files_unchanged))
930 logging.info('%d files files_renamed' % len(files_renamed))
931 logging.info('%d files NEW' % len(files_new))
932 logging.info('%d File attr' % len(File_Attibutes))
933 logging.info('%d Sym attr' % len(Sym_Attibutes))
934 logging.info('PaTcHCoUnT:Diffs-%d Moves-%d News-%d Delets-%d SymDiffs-%d SymNews-%d Verbatim -%d\n' % (Diff_Cnt, Move_Cnt, New_Cnt, Del_Cnt, Sym_Diff_Cnt, Sym_New_Cnt, Verbatim_Cnt))
935 print('PaTcHCoUnT:Diffs-%d Moves-%d News-%d Delets-%d SymDiffs-%d SymNews-%d Verbatim -%d\n' % (Diff_Cnt, Move_Cnt, New_Cnt, Del_Cnt, Sym_Diff_Cnt, Sym_New_Cnt, Verbatim_Cnt))
937 #There could be duplicates, TODO, can check before adding..
938 ATTR_FILE_D = open(ATTR_FILE,'a+')
939 for elt in File_Attibutes:
940 ATTR_FILE_D.write(elt)
941 for elt in Sym_Attibutes:
942 ATTR_FILE_D.write(elt)
946 Partition_Doc_SymLinks.close()
947 Partition_Read_SymLinks = open(SymLinkDoc,'r+')
948 Partition_Doc.write(Partition_Read_SymLinks.read())
949 Partition_Doc.write('PaTcHCoUnT:%d %d %d %d %d %d\n' % (Diff_Cnt, Move_Cnt, New_Cnt, Del_Cnt, Sym_Diff_Cnt, Sym_New_Cnt))
950 Partition_Doc_SymLinks.close()
951 Partition_Doc.close()
952 os.remove(SymLinkDoc)
954 if Diff_Cnt + Move_Cnt + New_Cnt+ Del_Cnt + Sym_Diff_Cnt + Sym_New_Cnt + Verbatim_Cnt + os.path.getsize(ATTR_FILE) == 0:
955 print('No Delta Generated for %s - %s' % (PART_NAME,OUT_DIR))
956 logging.info('No Delta Generated for %s' % PART_NAME)
957 shutil.rmtree(OUT_DIR)
960 def Apply_Container_Delta(a_apk, b_apk, new_apk, a_folder, g_output_dir):
962 #CONTROL NAMES, AND PRINTS AND ERROR CASES... SHOULD NOT PROCEED.
963 print 'ApplyContainerDelta - ', b_apk, a_folder, g_output_dir
964 shutil.copy2(g_output_dir+'/'+b_apk, g_output_dir+'/temp')
965 temp_apk = '../'+g_output_dir+'/'+b_apk
966 Patch = 'Patch_'+b_apk
967 ensure_dir_exists(Patch)
968 shutil.copy2(g_output_dir+'/'+b_apk, Patch+'/'+b_apk)
970 #Size issue on Device side?? shd check this
971 subprocess.call(['unzip','-q', Patch+'/'+b_apk, '-d', Patch])
972 with open(g_output_dir+'/PATCH.txt', 'r') as f_new:
973 lines = set(f_new.read().splitlines())
975 #print('Action ==> %s' % line)
976 #Action, Path, Patch = line.split('|')
977 Items = line.split('|')
980 ActualPath = a_folder+'/'+Path
981 PatchPath = Patch+'/'+Path
982 SrcPath = g_output_dir+'/'+path_leaf(Path)
983 #print('Action ==> %s Path ==> %s ' % (Action, Path))
985 patchName = g_output_dir+'/'+Items[2]
986 #print('Apply Patch: ActualPath %s SrcPath %s PatchLoc %s ' % (PatchPath, ActualPath, patchName))
987 subprocess.call([DIFFPATCH_UTIL,ActualPath,ActualPath,patchName])
988 WorkingDir = os.getcwd()
989 os.chdir(WorkingDir+"/"+"temp_a")
990 subprocess.call(['cp', '--parents', Path, '../'+Patch])
993 WorkingDir = os.getcwd()
994 os.chdir(WorkingDir+"/"+"temp_a")
995 subprocess.call(['cp', '--parents', Path, '../'+Patch])
998 print('Apply_Container_Delta - Unknown Error')
999 #print('Touch all files and set common attributes for DIFF generation')
1000 WorkingDir = os.getcwd()
1001 os.chdir(WorkingDir+"/"+Patch)
1003 CONTAINER_DATE = '200011111111.11'
1004 CONTAINER_MODE = '0755'
1005 subprocess.call(['find', '.', '-type', 'l', '-exec', 'rm', '-rf', '{}', ';'])
1006 subprocess.call(['find', '.', '-exec', 'touch', '-t', CONTAINER_DATE, '{}', ';'])
1007 subprocess.call(['chmod', '-R', CONTAINER_MODE, '../'+Patch])
1009 print 'Update Intermediate Archive'
1010 #subprocess.call(['zip','-ryX', b_apk, '*'])
1011 subprocess.call(['zip','-ryX', b_apk] + glob.glob('*'))
1012 os.chdir(WorkingDir)
1013 #print('Apply Path completed - Now create diff for this and place in patch folder')
1015 print('Patch Applied, Create Final Diff - %s %s' % (g_output_dir+'/'+b_apk,new_apk))
1016 patchName = ('New'+'_%s'+DIFF_SUFFIX) % (b_apk)
1017 patchLoc = '%s/%s' % (g_output_dir, patchName)
1019 subprocess.call([DIFF_UTIL, Patch+'/'+b_apk ,new_apk,patchLoc])
1021 #Only on HOST... for testing
1022 if TEST_MODE == 'TRUE':
1023 UpgradedName = '%s_Upgraded' % (b_apk)
1024 subprocess.call([DIFFPATCH_UTIL,Patch+'/'+b_apk,UpgradedName,patchLoc])
1026 #This is file only with NEWS and empty diffs and same files.
1027 if TEST_MODE == 'FALSE':
1028 os.remove(g_output_dir+'/'+b_apk)
1029 os.rename(g_output_dir+'/temp', g_output_dir+'/'+b_apk)
1030 shutil.rmtree(Patch)
1032 def IsSymlink(info):
1033 return (info.external_attr >> 16) == 0120777
1035 def NewFiles(src, dest):
1037 subprocess.call(['cp','-rp', src,dest])
1039 #shutil.copytree(src, dest)
1040 #except OSError as e:
1041 # If the error was caused because the source wasn't a directory
1042 #if e.errno == errno.ENOTDIR:
1043 #shutil.copy2(src, dest)
1045 #print('Directory not copied. Error: %s' % e)
1047 def measure_two_filediffs(src, dst):
1048 patchLoc = 'temp.patch'
1049 subprocess.call([DIFF_UTIL,src,dst,patchLoc])
1050 result_size = os.path.getsize(patchLoc)
1054 def Get_Files(path):
1058 for root, directories, filenames in os.walk(path, topdown=False, followlinks=False):
1059 for directory in directories:
1060 #DirName = os.path.join(root+'/',directory)
1061 DirName = os.path.join(root,directory)
1062 if os.path.islink(DirName):
1063 logging.debug('This is symlink pointing to dir -%s' % DirName)
1064 all_files.append(os.path.relpath(DirName, path))
1065 elif not os.listdir(DirName):
1066 #print('*****Empty Directory******* -%s', DirName)
1067 #This should NOT be appended ??? Empty dir shd b considered
1068 all_dirs.append(os.path.relpath(DirName, path))
1070 all_dirs.append(os.path.relpath(DirName, path))
1071 for filename in filenames:
1072 FileName = os.path.join(root,filename)
1073 all_files.append(os.path.relpath(FileName, path))
1077 return all_files, all_dirs
1080 USAGE_DOCSTRING = """
1081 Generate Delta using BASEOLD AND BASE NEW
1082 Attributes is optional
1083 Usage: CreatePatch.py UPDATE_TYPE PARTNAME OLDBASE NEWBASE OUTFOLDER
1086 def Usage(docstring):
1087 print docstring.rstrip("\n")
1088 print COMMON_DOCSTRING
1092 if __name__ == '__main__':