comparison gcp @ 7:a110d31482f7

Journalisation + some additionnal try/except
author Goffi <goffi@goffi.org>
date Mon, 27 Sep 2010 17:31:43 +0800
parents 5f53dc5beec9
children 144cb2669f21
comparison
equal deleted inserted replaced
6:5f53dc5beec9 7:a110d31482f7
70 70
71 const_DBUS_INTERFACE = "org.goffi.gcp" 71 const_DBUS_INTERFACE = "org.goffi.gcp"
72 const_DBUS_PATH = "/org/goffi/gcp" 72 const_DBUS_PATH = "/org/goffi/gcp"
73 const_BUFF_SIZE = 4096 73 const_BUFF_SIZE = 4096
74 const_PRESERVE = set(['mode','ownership','timestamps']) 74 const_PRESERVE = set(['mode','ownership','timestamps'])
75 const_JOURNAL_PATH = "~/.gcp_journal"
75 76
76 77
77 class DbusObject(dbus.service.Object): 78 class DbusObject(dbus.service.Object):
78 79
79 def __init__(self, gcp, bus, path): 80 def __init__(self, gcp, bus, path):
99 try: 100 try:
100 args = pickle.loads(str(args)) 101 args = pickle.loads(str(args))
101 except TypeError, pickle.UnpicklingError: 102 except TypeError, pickle.UnpicklingError:
102 return (False, _("INTERNAL ERROR: invalid arguments")) 103 return (False, _("INTERNAL ERROR: invalid arguments"))
103 return self._gcp.parseArguments(args, source_path) 104 return self._gcp.parseArguments(args, source_path)
105
106 class Journal():
107 def __init__(self, path=const_JOURNAL_PATH):
108 self.journal_path = os.path.expanduser(path)
109 self.journal_fd = open(self.journal_path,'w') #TODO: check and maybe save previous journals
110
111 def __del__(self):
112 self.journal_fd.flush()
113 self.journal_fd.close()
114
115 def startFile(self, source_path):
116 """Start an entry in the journal"""
117 self.journal_fd.write(source_path+"\n")
118 self.journal_fd.flush()
119 self.success=True
120 self.errors=[]
121
122 def closeFile(self):
123 """Close the entry in the journal"""
124 if not self.success:
125 status = "FAILED"
126 else:
127 status = "OK" if not self.errors else "PARTIAL"
128 self.journal_fd.write("%(status)s: %(errors)s\n" % {'status': status, 'errors': ', '.join(self.errors)})
129 self.journal_fd.flush()
130
131 def copyFailed(self):
132 """Must be called when something is wrong with the copy itself"""
133 self.success = False
134
135 def error(self, name):
136 """Something went wrong"""
137 self.errors.append(name)
138
104 139
105 class GCP(): 140 class GCP():
106 141
107 def __init__(self): 142 def __init__(self):
108 try: 143 try:
217 def __copyNextFile(self): 252 def __copyNextFile(self):
218 """Take the last file in the list, and launch the copy using glib io_watch event 253 """Take the last file in the list, and launch the copy using glib io_watch event
219 @return: True a file was added, False else""" 254 @return: True a file was added, False else"""
220 if self.copy_list: 255 if self.copy_list:
221 source_file, dest_path, options = self.copy_list.pop() 256 source_file, dest_path, options = self.copy_list.pop()
257 self.journal.startFile(source_file)
222 source_fd = open(source_file, 'rb') 258 source_fd = open(source_file, 'rb')
223 filename = os.path.basename(source_file) 259 filename = os.path.basename(source_file)
224 assert(filename) 260 assert(filename)
225 dest_file = self.__filename_fix(os.path.join(dest_path,filename),options) 261 dest_file = self.__filename_fix(os.path.join(dest_path,filename),options)
226 if os.path.exists(dest_file) and not options.force: 262 if os.path.exists(dest_file) and not options.force:
227 warning (_("File [%s] already exists, skipping it !") % dest_file) 263 warning (_("File [%s] already exists, skipping it !") % dest_file)
228 return True 264 return True
229 dest_fd = open(dest_file, 'wb') 265 try:
266 dest_fd = open(dest_file, 'wb')
267 except:
268 self.journal.copyFailed()
269 self.journal.error("can't open dest")
270 self.journal.closeFile()
271 source_fd.close()
272 return True
230 273
231 gobject.io_add_watch(source_fd,gobject.IO_IN,self._copyFile, 274 gobject.io_add_watch(source_fd,gobject.IO_IN,self._copyFile,
232 (dest_fd, options), priority=gobject.PRIORITY_HIGH) 275 (dest_fd, options), priority=gobject.PRIORITY_HIGH)
233 if not self.progress: 276 if not self.progress:
234 info(_("COPYING %(source)s ==> %(dest)s") % {"source":source_path,"dest":dest_file}) 277 info(_("COPYING %(source)s ==> %(dest)s") % {"source":source_path,"dest":dest_file})
237 #Nothing left to copy, we quit 280 #Nothing left to copy, we quit
238 if self.progress: 281 if self.progress:
239 self.__pbar_finish() 282 self.__pbar_finish()
240 self.loop.quit() 283 self.loop.quit()
241 284
285 def __copyFailed(self, reason, source_fd, dest_fd):
286 """Write the failure in the journal and close files descriptors"""
287 self.journal.copyFailed()
288 self.journal.error(reason)
289 self.journal.closeFile()
290 source_fd.close()
291 dest_fd.close()
292
293
294
242 def _copyFile(self, source_fd, condition, data): 295 def _copyFile(self, source_fd, condition, data):
243 """Actually copy the file, callback used with io_add_watch 296 """Actually copy the file, callback used with io_add_watch
244 @param source_fd: file descriptor of the file to copy 297 @param source_fd: file descriptor of the file to copy
245 @param condition: condition which launched the callback (glib.IO_IN) 298 @param condition: condition which launched the callback (glib.IO_IN)
246 @param data: tuple with (destination file descriptor, copying options)""" 299 @param data: tuple with (destination file descriptor, copying options)"""
247 dest_fd,options = data 300 dest_fd,options = data
248 buff = source_fd.read(self.buffer_size) 301
249 dest_fd.write(buff) 302 try:
303 buff = source_fd.read(self.buffer_size)
304 except:
305 self.__copyFailed("can't read source", source_fd, dest_fd)
306 return False
307
308 try:
309 dest_fd.write(buff)
310 except:
311 self.__copyFailed("can't write to dest", source_fd, dest_fd)
312 return False
313
250 self.bytes_copied += len(buff) 314 self.bytes_copied += len(buff)
251 if self.progress: 315 if self.progress:
252 self.__pbar_update() 316 self.__pbar_update()
253 317
254 if len(buff) != self.buffer_size: 318 if len(buff) != self.buffer_size:
255 source_fd.close() 319 source_fd.close()
256 dest_fd.close() 320 dest_fd.close()
257 self.__post_copy(source_fd.name, dest_fd.name, options) 321 self.__post_copy(source_fd.name, dest_fd.name, options)
322 self.journal.closeFile()
258 return False 323 return False
259 return True 324 return True
260 325
261 def __filename_fix(self, filename, options): 326 def __filename_fix(self, filename, options):
327 """Fix filenames incompatibilities/mistake according to options
328 @param filename: full path to the file
329 @param options: options as parsed on command line
330 @return: fixed filename"""
331 fixed_filename = filename
332
262 if self.getFsType(filename) == 'vfat' and options.fs_fix: 333 if self.getFsType(filename) == 'vfat' and options.fs_fix:
263 filename = filename.replace('\\','_')\ 334 fixed_filename = filename.replace('\\','_')\
264 .replace(':',';')\ 335 .replace(':',';')\
265 .replace('*','+')\ 336 .replace('*','+')\
266 .replace('?','')\ 337 .replace('?','')\
267 .replace('"','\'')\ 338 .replace('"','\'')\
268 .replace('<','[')\ 339 .replace('<','[')\
269 .replace('>',']')\ 340 .replace('>',']')\
270 .replace('|','!') 341 .replace('|','!')
271 return filename 342
343 if fixed_filename != filename:
344 self.journal.error('filename fixed')
345 return fixed_filename
272 346
273 def __post_copy(self, source_file, dest_file, options): 347 def __post_copy(self, source_file, dest_file, options):
274 """Do post copy traitement (mainly managing --preserve option)""" 348 """Do post copy traitement (mainly managing --preserve option)"""
275 st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid, st_size, st_atime, st_mtime, st_ctime = os.stat(source_file) 349 st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid, st_size, st_atime, st_mtime, st_ctime = os.stat(source_file)
276 #TODO: complete log in case of errors 350 #TODO: complete log in case of errors
281 elif preserve == 'ownership': 355 elif preserve == 'ownership':
282 os.chown(dest_file, st_uid, st_gid) 356 os.chown(dest_file, st_uid, st_gid)
283 elif preserve == 'timestamps': 357 elif preserve == 'timestamps':
284 os.utime(dest_file, (st_atime, st_mtime)) 358 os.utime(dest_file, (st_atime, st_mtime))
285 except OSError,e: 359 except OSError,e:
286 pass #TODO: complete log here 360 self.journal.error("preserve-"+preserve)
287 361
288 def __pbar_update(self): 362 def __pbar_update(self):
289 """Update progress bar position, create the bar if it doesn't exist""" 363 """Update progress bar position, create the bar if it doesn't exist"""
290 assert(self.progress) 364 assert(self.progress)
291 try: 365 try:
376 if len(args) < 2: 450 if len(args) < 2:
377 _error_msg = _("Wrong number of arguments") 451 _error_msg = _("Wrong number of arguments")
378 return (False, _error_msg) 452 return (False, _error_msg)
379 debug(_("adding args to gcp: %s"),args) 453 debug(_("adding args to gcp: %s"),args)
380 self.__checkArgs(options, source_path, args) 454 self.__checkArgs(options, source_path, args)
455 self.journal = Journal()
381 gobject.idle_add(self.__copyNextFile) 456 gobject.idle_add(self.__copyNextFile)
382 return (True,'') 457 return (True,'')
383 458
384 def go(self): 459 def go(self):
385 """Launch main loop""" 460 """Launch main loop"""