| 
					
				 | 
			
			
				@@ -4,6 +4,7 @@ import logging 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import zlib 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import argparse 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import sys 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import stat 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 from datetime import datetime 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 import msgpack 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -98,11 +99,17 @@ class Archive(object): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         for item in self.items: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             assert item['path'][0] not in ('/', '\\', ':') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             path = os.path.join(dest, item['path']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            logging.info(path) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             if item['type'] == 'DIRECTORY': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                logging.info(path) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if not os.path.exists(path): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     os.makedirs(path) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if item['type'] == 'FILE': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif item['type'] == 'SYMLINK': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                logging.info('%s => %s', path, item['source']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if not os.path.exists(os.path.dirname(path)): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    os.makedirs(os.path.dirname(path)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                os.symlink(item['source'], path) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif item['type'] == 'FILE': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                logging.info(path) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if not os.path.exists(os.path.dirname(path)): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     os.makedirs(os.path.dirname(path)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 with open(path, 'wb') as fd: 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -144,34 +151,48 @@ class Archive(object): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         del cache.archives[self.name] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         cache.save() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def walk(self, path): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        st = os.lstat(path) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if stat.S_ISDIR(st.st_mode): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for f in os.listdir(path): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                for x in self.walk(os.path.join(path, f)): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    yield x 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            yield path, st 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     def create(self, name, paths, cache): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if name in cache.archives: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             raise NameError('Archive already exists') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         for path in paths: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            for root, dirs, files in os.walk(unicode(path)): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                for d in dirs: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    p = os.path.join(root, d) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    self.items.append(self.process_dir(p, cache)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                for f in files: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    p = os.path.join(root, f) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    entry = self.process_file(p, cache) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if entry: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        self.items.append(entry) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for path, st in self.walk(unicode(path)): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if stat.S_ISDIR(st.st_mode): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    self.process_dir(path, st) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                elif stat.S_ISLNK(st.st_mode): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    self.process_link(path, st) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                elif stat.S_ISREG(st.st_mode): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    self.process_file(path, st) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    logging.error('Unknown file type: %s', path) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         self.save(name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         cache.archives[name] = self.id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         cache.save() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def process_dir(self, path, cache): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def process_dir(self, path, st): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         path = path.lstrip('/\\:') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         logging.info(path) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return {'type': 'DIRECTORY', 'path': path} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.items.append({'type': 'DIRECTORY', 'path': path}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def process_link(self, path, st): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        source = os.readlink(path) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        path = path.lstrip('/\\:') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        logging.info('%s => %s', path, source) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.items.append({'type': 'SYMLINK', 'path': path, 'source': source}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    def process_file(self, path, cache): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def process_file(self, path, st): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             fd = open(path, 'rb') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         except IOError, e: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             logging.error(e) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         with fd: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             path = path.lstrip('/\\:') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             logging.info(path) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -179,8 +200,8 @@ class Archive(object): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             size = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             for chunk in chunkify(fd, CHUNK_SIZE, 30): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 size += len(chunk) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                chunks.append(self.add_chunk(*cache.add_chunk(chunk))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return {'type': 'FILE', 'path': path, 'chunks': chunks, 'size': size} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                chunks.append(self.add_chunk(*self.cache.add_chunk(chunk))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.items.append({'type': 'FILE', 'path': path, 'chunks': chunks, 'size': size}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 class Archiver(object): 
			 |