From 8bfcded22c0dc66e0a4c971a5640fd822800fdad Mon Sep 17 00:00:00 2001 From: Alan Chan Date: Mon, 13 Feb 2017 14:34:43 +0800 Subject: [PATCH 1/2] Introduce flags & recursive protection - `protect` now has options: R - `rm-p` now can protect you from removing files inside a protected directories. --- rm_protection/protect.py | 12 ++++-- rm_protection/rm_p.py | 81 ++++++++++++++++++++++++++++++++-------- 2 files changed, 74 insertions(+), 19 deletions(-) diff --git a/rm_protection/protect.py b/rm_protection/protect.py index b540673..408fd5f 100755 --- a/rm_protection/protect.py +++ b/rm_protection/protect.py @@ -7,11 +7,17 @@ def protect(protect_args=None): c = Config() + flags = '' + option_end = False if not protect_args: protect_args = argv[1:] for arg in protect_args: - if arg in c.invalid: - print("\".\" and \"..\" may not be protected") + if arg == '--': + option_end = True + elif (arg.startswith("-") and not option_end): + flags = flags + arg[arg.rfind('-') + 1:] + elif arg in c.invalid: + print('protect: "." and ".." may not be protected') else: path = abspath(expv(expu(arg))) evalpath = dirname(path) + "/." + basename(path) + c.suffix @@ -20,7 +26,7 @@ def protect(protect_args=None): with open(evalpath, "w") as f: question = input("Question for " + path + ": ") answer = input("Answer: ") - f.write(question + "\n" + answer) + f.write(question + "\n" + answer + "\n" + flags.upper()) if __name__ == "__main__": diff --git a/rm_protection/rm_p.py b/rm_protection/rm_p.py index 7801c2d..a5968e4 100755 --- a/rm_protection/rm_p.py +++ b/rm_protection/rm_p.py @@ -6,31 +6,76 @@ from rm_protection.config import Config -def ask(evalpath, path=""): +c = Config() + + +def ask(evalpath, parent=False): with open(evalpath, "r") as f: - if path: - print(path + ": " + f.readline().rstrip("\n")) - else: - print("A file is protected by " + evalpath) - print(f.readline().rstrip("\n")) - if input("Answer: ") == f.readline().rstrip("\n"): + question = f.readline().rstrip("\n") + answer = f.readline().rstrip("\n") + try: + flags = f.readline().rstrip("\n") + except: + flags = '' + if parent and 'R' not in flags: + print(original_path(evalpath) + ' is protected but flag "R" is missing.') return True - elif path: - print("Wrong answer! " + path + " will not be removed") - print("The answer is stored in " + evalpath) - return False else: - print("Wrong answer! File/directory protected by " + evalpath + " will not be removed") + print(original_path(evalpath) + ": " + question) + if input("Answer: ") == answer: + return True + else: + print("Wrong answer! " + original_path(evalpath) + " will not be removed") + print("The answer is stored in " + evalpath) return False +def original_path(evalpath): + global c + basepath = dirname(evalpath) + filename = basename(evalpath)[1:-len(c.suffix)] + if basepath == '/': + return basepath + filename + else: + return basepath + '/' + filename + def ask_in(q, a): return bool(input(q) in a) +def gen_evalpaths(path): + paths = {} + path = dirname(path) + while path != '/': + evalpath = gen_eval(path) + paths[path] = evalpath + path = dirname(path) + return paths + + +def gen_eval(path): + global c + basedir = dirname(path) + if basedir == '/': + basedir = '' + return basedir + "/." + basename(path) + c.suffix + + +def parent_clear(file_evalpaths): + for filepath in file_evalpaths: + parent_eval = file_evalpaths[filepath] + if exists(parent_eval): + print('The parent directory ' + filepath + ' is protected') + result = ask(parent_eval, parent=True) + if not result: + print(filepath + ' will not be removed') + return False + return True + + def rm(rm_args=None): + global c args = '' - c = Config() paths = [] evalpaths = [] option_end = False @@ -43,7 +88,8 @@ def rm(rm_args=None): pass else: path = abspath(expv(expu(arg))) - evalpath = dirname(path) + "/." + basename(path) + c.suffix + file_evalpaths = gen_evalpaths(path) + evalpath = gen_eval(path) if c.suffix in arg: print(path + " is a protection file") if ask_in(q="Do you want to remove it? (y/n) ", a="Yesyes"): @@ -52,15 +98,18 @@ def rm(rm_args=None): print(path + " will not be removed") continue if exists(evalpath): - if ask(evalpath, path): + if ask(evalpath): paths.append(path) evalpaths.append(evalpath) else: continue - elif isdir(path): + if not parent_clear(file_evalpaths): + continue + if isdir(path): find_exec = "find " + path + " -name " + "\".*" + c.suffix + "\"" + " -print" out, err = Popen(find_exec, shell=True, stdout=PIPE, stderr=PIPE, universal_newlines=True).communicate() for pfile in iter(out.splitlines()): + print("A protected file or directory is found inside " + path) if not ask(pfile): print("Terminated due to potentially dangerous action") exit(1) From f856ec5a7ab19de8a122512cee9742b437dd42e9 Mon Sep 17 00:00:00 2001 From: Alan Chan Date: Tue, 21 Feb 2017 15:01:44 +0800 Subject: [PATCH 2/2] v0.1.2 Introducing recursive protection - introduces recursive protection - updates READMEs and setup.py --- README.md | 2 +- README.rst | 2 +- rm_protection/config.py | 2 ++ rm_protection/protect.py | 14 ++++++-- rm_protection/rm_p.py | 72 +++++++++++++++++++++++++--------------- setup.py | 2 +- 6 files changed, 61 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 689f252..9ca847b 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The disaster could have been avoided. `pip install rm-protection` and optionally, `alias rm="rm-p"` for your daily user and **root** (so that it works for `sudo`). -2. Protect your files using `protect`. +2. Protect your files using `protect`. If you want to protect everything inside, `protect -R`. 3. Happy rm-ing! diff --git a/README.rst b/README.rst index 45be84e..c462ca9 100644 --- a/README.rst +++ b/README.rst @@ -19,7 +19,7 @@ Quick Start ``pip install rm-protection`` 1. Install from PyPi and make an alias for ``rm-p``. -2. Protect your files using ``protect``. +2. Protect your files using ``protect``. If you want to protect everything inside, ``protect -R``. 3. Happy rm-ing! How does it work? diff --git a/rm_protection/config.py b/rm_protection/config.py index 01c8a17..458440b 100644 --- a/rm_protection/config.py +++ b/rm_protection/config.py @@ -2,3 +2,5 @@ class Config(): def __init__(self): self.suffix = ".rm-protection" self.invalid = ['.', '..', './', '../'] + self.protect_prefix = 'protect: ' + self.rm_prefix = 'rm-p: ' \ No newline at end of file diff --git a/rm_protection/protect.py b/rm_protection/protect.py index 408fd5f..dceba9a 100755 --- a/rm_protection/protect.py +++ b/rm_protection/protect.py @@ -5,8 +5,16 @@ from rm_protection.config import Config +c = Config() + + +def pprint(msg): + global c + print(c.protect_prefix + msg) + + def protect(protect_args=None): - c = Config() + global c flags = '' option_end = False if not protect_args: @@ -17,12 +25,12 @@ def protect(protect_args=None): elif (arg.startswith("-") and not option_end): flags = flags + arg[arg.rfind('-') + 1:] elif arg in c.invalid: - print('protect: "." and ".." may not be protected') + pprint('"." and ".." may not be protected') else: path = abspath(expv(expu(arg))) evalpath = dirname(path) + "/." + basename(path) + c.suffix if not exists(path): - print("Warning: " + path + " does not exist") + pprint("Warning: " + path + " does not exist") with open(evalpath, "w") as f: question = input("Question for " + path + ": ") answer = input("Answer: ") diff --git a/rm_protection/rm_p.py b/rm_protection/rm_p.py index a5968e4..6a72066 100755 --- a/rm_protection/rm_p.py +++ b/rm_protection/rm_p.py @@ -7,27 +7,44 @@ c = Config() +evaledpaths = [] + + +def pprint(msg): + global c + print(c.rm_prefix + msg) def ask(evalpath, parent=False): - with open(evalpath, "r") as f: - question = f.readline().rstrip("\n") - answer = f.readline().rstrip("\n") - try: - flags = f.readline().rstrip("\n") - except: - flags = '' - if parent and 'R' not in flags: - print(original_path(evalpath) + ' is protected but flag "R" is missing.') - return True - else: - print(original_path(evalpath) + ": " + question) - if input("Answer: ") == answer: + global evaledpaths + if evalpath in evaledpaths: + return True + else: + with open(evalpath, "r") as f: + question = f.readline().rstrip("\n") + answer = f.readline().rstrip("\n") + try: + flags = f.readline().rstrip("\n") + except: + flags = '' + if parent and 'R' not in flags: + pprint(original_path(evalpath) + ' is protected but flag "R" is missing') + evaledpaths.append(evalpath) return True else: - print("Wrong answer! " + original_path(evalpath) + " will not be removed") - print("The answer is stored in " + evalpath) - return False + if parent: + pprint('The parent directory ' + original_path(evalpath) + ' is protected') + pprint(original_path(evalpath) + ": " + question) + if input("Answer: ") == answer: + evaledpaths.append(evalpath) + return True + else: + if parent: + return False + else: + pprint("Wrong answer! " + original_path(evalpath) + " will not be removed") + pprint("The answer is stored in " + evalpath) + return False def original_path(evalpath): @@ -39,6 +56,7 @@ def original_path(evalpath): else: return basepath + '/' + filename + def ask_in(q, a): return bool(input(q) in a) @@ -61,20 +79,19 @@ def gen_eval(path): return basedir + "/." + basename(path) + c.suffix -def parent_clear(file_evalpaths): +def parent_clear(file_evalpaths, path): for filepath in file_evalpaths: parent_eval = file_evalpaths[filepath] if exists(parent_eval): - print('The parent directory ' + filepath + ' is protected') - result = ask(parent_eval, parent=True) - if not result: - print(filepath + ' will not be removed') + if not ask(parent_eval, parent=True): + pprint(path + ' will not be removed') return False return True def rm(rm_args=None): global c + global evaledpaths args = '' paths = [] evalpaths = [] @@ -91,11 +108,11 @@ def rm(rm_args=None): file_evalpaths = gen_evalpaths(path) evalpath = gen_eval(path) if c.suffix in arg: - print(path + " is a protection file") + pprint(path + " is a protection file") if ask_in(q="Do you want to remove it? (y/n) ", a="Yesyes"): args += arg + ' ' else: - print(path + " will not be removed") + pprint(path + " will not be removed") continue if exists(evalpath): if ask(evalpath): @@ -103,15 +120,15 @@ def rm(rm_args=None): evalpaths.append(evalpath) else: continue - if not parent_clear(file_evalpaths): + if not parent_clear(file_evalpaths, path): continue if isdir(path): find_exec = "find " + path + " -name " + "\".*" + c.suffix + "\"" + " -print" out, err = Popen(find_exec, shell=True, stdout=PIPE, stderr=PIPE, universal_newlines=True).communicate() for pfile in iter(out.splitlines()): - print("A protected file or directory is found inside " + path) + pprint("A protected file or directory is found inside " + path) if not ask(pfile): - print("Terminated due to potentially dangerous action") + pprint("Terminated due to potentially dangerous action") exit(1) args += arg + ' ' Popen("rm " + args, shell=True).wait() @@ -120,7 +137,8 @@ def rm(rm_args=None): if exists(evalpath) and not exists(path): remove_protection_files += evalpath + ' ' if remove_protection_files: - Popen("rm " + remove_protection_files, shell=True) + Popen("rm " + remove_protection_files, shell=True).wait() + evaledpaths = [] if __name__ == "__main__": diff --git a/setup.py b/setup.py index fe881a8..4161a75 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ setup( name="rm_protection", - version="0.1.1.1", + version="0.1.2", license='MIT', description="A safe alternative for \"rm\" with minimum difference.", author='Alan Chen',