def restore(bu_dir, filelist=None, to_dir='.', verbose=False, keep=False, touch=False): def restore_file(file_nm): backup = os.path.join(bu_dir, file_nm) file_nm = file_nm if to_dir is None else os.path.relpath(os.path.join(to_dir, file_nm)) _create_parents(file_nm) try: stat_data = os.stat(backup) except OSError as edata: output.perror(edata, backup) return False if stat_data.st_size == 0: try: os.unlink(file_nm) except OSError as edata: if edata.errno != errno.ENOENT: output.perror(edata, file_nm) return False if verbose: output.write('Removing %s\n' % file_nm) if not keep: os.unlink(backup) _remove_parents(backup) else: if verbose: output.write('Restoring %s\n' % file_nm) try: os.unlink(file_nm) except OSError as edata: if edata.errno != errno.ENOENT: raise if not _link_or_copy_file(backup, stat_data, file_nm): return False if not keep: os.unlink(backup) _remove_parents(backup) if touch: os.utime(file_nm, None) else: os.utime(file_nm, (stat_data.st_mtime, stat_data.st_mtime)) return True if not os.path.isdir(bu_dir): return False status = True if filelist is None or len(filelist) == 0: def onerror(exception): raise exception try: for basedir, dirnames, filenames in os.walk(bu_dir, onerror=onerror): reldir = '' if basedir == bu_dir else os.path.relpath(basedir, bu_dir) for filename in filenames: if not restore_file(os.path.join(reldir, filename)): return False except OSError as edata: output.perror(edata) return False else: for filename in filelist: if not restore_file(filename): return False return True
def remove(bu_dir, filelist=False, verbose=False): '''Remove backup files''' def remove_file(backup): try: if verbose: output.write('Removing %s\n' % backup) os.unlink(backup) except OSError as edata: output.perror(edata, backup) return False try: _remove_parents(backup) except IOError as edata: output.perror(edata, backup) return False return True if not os.path.isdir(bu_dir): return False status = True if filelist is None or len(filelist) == 0: def onerror(exception): raise exception try: for basedir, dirnames, filenames in os.walk(bu_dir): for filename in filenames: if not remove_file(os.path.join(basedir, filename)): return False except IOError as edata: output.perror(edata) return False else: for filename in filelist: if not remove_file(os.path.join(bu_dir, filename)): return False return True
def ensure_nolinks(filename): try: stat_data = os.stat(filename) except OSError as edata: output.perror(edata) return False if stat_data.st_nlink > 1: from_fd = to_fd = None try: from_fd = os.open(filename, os.O_RDONLY) # Temp file name is "path/to/.file.XXXXXX" to_fd, tmpname = tempfile.mkstemp(prefix=os.path.abspath(filename)) _copy_fd(from_fd, to_fd) os.fchmod(to_fd, stat_data.st_mode) os.rename(tmpname, filename) return True; except OSError as edata: output.perror(edata) return False finally: if from_fd is not None: os.close(from_fd); if to_fd is not None: os.close(to_fd); else: return True
def set_text(pfile, text): if args.opt_backup: try: shutil.copy2(pfile, pfile + '~') except Exception as edata: output.perror(edata) if args.opt_strip_trailing_whitespace: text = _trim_trailing_ws(text) putils.set_patch_hdr(pfile, text, omit_diffstat=args.opt_strip_diffstat)
def _link_or_copy_file(from_fn, stat_data, to_fn): try: os.link(from_fn, to_fn) except OSError as edata: if edata.errno not in [errno.EXDEV, errno.EPERM, errno.EMLINK, errno.ENOSYS]: output.perror(edata, 'Could not link file \`%s\' to \`%s\'' % (from_fn, to_fn)) return False else: return True return _copy_file(from_fn, stat_data, to_fn)
def _copy_file(from_fn, stat_data, to_fn): is_ok = True; try: from_fd = os.open(from_fn, os.O_RDONLY) except OSError as edata: output.perror(edata) return False try: # make sure we don't inherit this file's mode. os.unlink(to_fn) except OSError as edata: if edata.errno != errno.ENOENT: output.perror(edata) return False try: to_fd = _creat(to_fn, mode=stat_data.st_mode) except OSError as edata: output.perror(edata); os.close(from_fd); return False os.fchmod(to_fd, stat_data.st_mode) try: _copy_fd(from_fd, to_fd) except OSError as edata: output.perror(edata, '%s -> %s' % (from_fn, to_fn)) os.unlink(to_fn) return False finally: os.close(from_fd) os.close(to_fd) return True
def remove_file(backup): try: if verbose: output.write('Removing %s\n' % backup) os.unlink(backup) except OSError as edata: output.perror(edata, backup) return False try: _remove_parents(backup) except IOError as edata: output.perror(edata, backup) return False return True
def ensure_nolinks_in_dir(in_dir, verbose=False): def onerror(exception): raise exception if not os.path.isdir(in_dir): return False try: for basedir, dirnames, filenames in os.walk(in_dir, onerror=onerror): for filename in filenames: filename = os.path.join(basedir, filename) if verbose: output.write('Delinking %s\n' % filename) if not ensure_nolinks(filename): return False except IOError as edata: output.perror(edata) return False return True
def get_files(): '''Return a list of the files in the current directory. Omitting those that are quilt metadata files.''' files = [] def onerror(exception): raise exception try: dirname = os.getcwd() for basedir, dirnames, filenames in os.walk(dirname, onerror=onerror): reldir = '' if basedir == dirname else os.path.relpath(basedir, dirname) if reldir.split(os.sep)[0] in [patchfns.QUILT_PC, patchfns.QUILT_PATCHES]: continue for entry in filenames: files.append(os.path.join(reldir, entry)) except OSError as edata: output.perror(edata) return [] return files
def files_in_dir(dirname, recurse=True, exclude_timestamp=True): '''Return a list of the files in the given directory.''' files = [] if recurse: def onerror(exception): raise exception try: for basedir, dirnames, filenames in os.walk(dirname, onerror=onerror): reldir = '' if basedir == dirname else os.path.relpath(basedir, dirname) for entry in filenames: if not exclude_timestamp or not entry == '.timestamp': files.append(os.path.join(reldir, entry)) except OSError as edata: output.perror(edata) return [] else: for entry in os.listdir(dirname): if os.path.isdir(entry): continue if not exclude_timestamp or not entry == '.timestamp': files.append(entry) return files
def restore_file(file_nm): backup = os.path.join(bu_dir, file_nm) file_nm = file_nm if to_dir is None else os.path.relpath(os.path.join(to_dir, file_nm)) _create_parents(file_nm) try: stat_data = os.stat(backup) except OSError as edata: output.perror(edata, backup) return False if stat_data.st_size == 0: try: os.unlink(file_nm) except OSError as edata: if edata.errno != errno.ENOENT: output.perror(edata, file_nm) return False if verbose: output.write('Removing %s\n' % file_nm) if not keep: os.unlink(backup) _remove_parents(backup) else: if verbose: output.write('Restoring %s\n' % file_nm) try: os.unlink(file_nm) except OSError as edata: if edata.errno != errno.ENOENT: raise if not _link_or_copy_file(backup, stat_data, file_nm): return False if not keep: os.unlink(backup) _remove_parents(backup) if touch: os.utime(file_nm, None) else: os.utime(file_nm, (stat_data.st_mtime, stat_data.st_mtime)) return True
def backup_file(file_nm): backup = os.path.join(bu_dir, file_nm) try: stat_data = os.stat(file_nm) except OSError as edata: missing_file = edata.errno == errno.ENOENT else: missing_file = False try: os.unlink(backup) except OSError as edata: if edata.errno != errno.ENOENT: output.perror(edata) return False _create_parents(backup) if missing_file: if verbose: output.write('New file %s\n' % file_nm) try: os.close(_creat(backup, mode=0666)) except OSError as edata: output.perror(edata) return False else: if verbose: output.write('Copying %s\n' % file_nm) if stat_data.st_nlink == 1: result = _copy_file(file_nm, stat_data, backup) if result is not True: return result else: result = _link_or_copy_file(file_nm, stat_data, backup) if result is not True: return result result = ensure_nolinks(file_nm) if result is not True: return result os.utime(backup, (stat_data.st_mtime, stat_data.st_mtime,)) return True
def run_mail(args): patchfns.chdir_to_base_dir() if not shell.which('formail'): output.write("You have to install 'formail' to use 'quilt mail'") return cmd_result.ERROR if not args.opt_signature or args.opt_signature == '-': args.opt_signature = None else: try: args.opt_signature = open(args.opt_signature).read() except IOError as edata: output.perror(edata) if args.first_patch: args.first_patch = patchfns.find_first_patch() if args.first_patch == '-' else patchfns.find_patch(args.first_patch) if not args.first_patch: return cmd_result.ERROR if not args.last_patch: args.last_patch = args.first_patch else: args.last_patch = patchfns.find_last_patch() if args.last_patch == '-' else patchfns.find_patch(args.last_patch) if not args.last_patch: return cmd_result.ERROR if not args.opt_sender: hostname = socket.gethostname() args.opt_sender = '%s@%s' % (get_login_name(), hostname) if not re.match('^\S+@\S+\.\S+$', args.opt_sender): output.error('Could not determine the envelope sender address. Please use --sender.\n') return cmd_result.ERROR _dummy, args.opt_sender_address = email.utils.parseaddr(args.opt_sender) if not args.opt_charset: lc_all = os.getenv('LC_ALL', patchfns.ORIGINAL_LANG) if lc_all and lc_all.endswith('UTF-8'): args.opt_charset = 'UTF-8' else: args.opt_charset = 'ISO-8859-15' patches = patchfns.cat_series() if args.first_patch: first_index = patches.index(args.first_patch) last_index = patches.index(args.last_patch) if last_index < first_index: output.error('Patch %s not applied before patch %s\n' % (patchfns.print_patch(args.first_patch), patchfns.print_patch(args.first_patch))) return cmd_result.ERROR patches = patches[first_index:last_index + 1] total = len(patches) tmpdir = patchfns.gen_tempfile(asdir=True) atexit.register(lambda: not os.path.exists(tmpdir) or shutil.rmtree(tmpdir, ignore_errors=True)) subject_map = {} patch_msgs = [] for patch in patches: contents = fsutils.get_file_contents(patchfns.patch_file_name(patch)) mailmsg = message_from_patch(contents, args.opt_charset) if mailmsg is False: subject = None else: patch_msgs.append(mailmsg) subject = mailmsg['Replace-Subject'] if mailmsg is False or not subject: output.error('Unable to extract a subject header from %s\n' % patchfns.print_patch(patch)) return cmd_result.ERROR if subject in subject_map: subject_map[subject].append(patch) else: subject_map[subject] = [patch] if len(subject_map) != len(patches): duplicates = [] for key in sorted(subject_map): plist = subject_map[key] if len(plist) > 1: duplicates += plist output.error('Patches %s have duplicate subject headers.\n' % ', '.join([patchfns.print_patch(dup) for dup in duplicates])) return cmd_result.ERROR if args.opt_reply_to: if not os.path.exists(args.opt_reply_to): output.error('File %s does not exist\n' % args.opt_reply_to) return cmd_result.ERROR args.opt_reply_to = email.message_from_string(open(args.opt_reply_to).read()) if not args.opt_subject: repto_subject = args.opt_reply_to['Subject'] args.opt_subject = 'Re: %s' % re.sub('^([ \t]*[rR][eE]:[ \t]*)', '', repto_subject) intro = 'Message-Id: <%s>\n' % msgid(args) intro += 'User-Agent: pyquilt\n' last_ts = time.localtime() intro += time.strftime('Date: %a, %d %b %Y %H:%M:%S %z\n', last_ts) intro += 'From: %s\n' % (args.opt_from if args.opt_from else args.opt_sender) intro += 'To: %s\n' % (', '.join(args.opt_to) if args.opt_to else '') intro += 'Cc: %s\n' % (', '.join(args.opt_cc) if args.opt_cc else '') intro += 'Bcc: %s\n' % (', '.join(args.opt_bcc) if args.opt_bcc else '') if args.opt_reply_to: intro += in_reply_to_header(args.opt_reply_to) intro += references_header(args.opt_reply_to) intro += 'Subject-Prefix: [%s @num@/@total@]\n' % args.opt_prefix intro += 'Subject: %s\n\n' % (args.opt_subject if args.opt_subject else '') intro += ('%s\n\n' % args.opt_message) if args.opt_message else '' intro += ('-- \n%s\n' % args.opt_signature) if args.opt_signature else '' intro_message = email.message_from_string(intro) intro_message.set_charset(args.opt_charset) if not args.opt_message: introfile = patchfns.gen_tempfile() open(introfile, 'w').write(intro_message.as_string()) result = shell.run_cmd('%s %s' % (os.getenv('EDITOR'), introfile)) output.write(result.stdout) output.error(result.stderr) intro_message = email.message_from_string(open(introfile).read(), charset=args.opt_charset) os.remove(introfile) if result.eflags != 0: return cmd_result.ERROR subject = join_lines(intro_message['Subject']) if not subject: if not args.opt_message: savefile = patchfns.gen_tempfile() open(savefile, 'w').write(intro_message.as_string()) output.error('Introduction has no subject header (saved as %s)\n' % savefile) else: output.error('Introduction has no subject header\n') return cmd_result.ERROR if args.opt_mbox: fsutils.touch(args.opt_mbox) subject_prefix = email.utils.quote(join_lines(intro_message['Subject-Prefix'])) subject_prefix += ' ' if subject_prefix else '' subject_prefix = re.sub('@total@', str(total), subject_prefix) del intro_message['Subject-Prefix'] pnum_fmt = '{0:0%s}' % len(str(total)) pfx = re.sub('@num@', pnum_fmt.format(0), subject_prefix) intro_message.replace_header('Subject', pfx + intro_message['Subject']) remove_empty_headers(intro_message) for key in ['To', 'Cc', 'Bcc']: # Consolidate up the various recipient fields values = intro_message.get_all(key) if values: del intro_message[key] intro_message[key] = ', '.join(values) process_mail(intro_message, args) pnum = 0 for msg in patch_msgs: msg.add_header('Content-Disposition', 'inline', filename=patches[pnum]) for key in intro_message.keys(): if key.lower() not in ['message-id', 'references', 'in-reply-to', 'subject']: for value in intro_message.get_all(key): msg[key] = value msg['References'] = get_reference_to(intro_message) msg.set_charset(args.opt_charset) for aclass in ['To', 'Cc', 'Bcc']: rclass = 'Recipient-' + aclass if msg.has_key(rclass): msg[aclass] = ',\n '.join(msg.get_all(rclass)) del msg[rclass] pnum += 1 ppfx = re.sub('@num@', pnum_fmt.format(pnum), subject_prefix) msg['Message-Id'] = msgid(args) msg['Subject'] = '%s%s' % (ppfx, msg['Replace-Subject']) if ppfx else msg['Replace-Subject'] del msg['Replace-Subject'] remove_empty_headers(msg) process_mail(msg, args) return cmd_result.OK