Skip to content

Commit

Permalink
Add -I flag
Browse files Browse the repository at this point in the history
  • Loading branch information
hudson-newey committed Nov 2, 2024
1 parent 4f6ab06 commit 56b41fc
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 78 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ A wrapper for the "rm" command with soft-deletes, config-based deletion, debug i
## "GNU Like" command line arguments

- `-i` Interactivly prompt before each deletion request
- `-I` Prompt if deleting more three files
- `--help` Display help information (without deletion)
- `--version` Display version information (without deletion)

Expand All @@ -28,10 +29,9 @@ A wrapper for the "rm" command with soft-deletes, config-based deletion, debug i
- `-v`, `--verbose` Emit additional verbose information
- `--version` Show version information
- `--help` Show help information
- `-I` Prompt if deleting more than three files
- `--interactive[=WHEN]` Interactive with a custom threshold
- `--one-file-system` Do not allow cross-file-system deletes
- `-f`, `--force` (partially implemented)
- `-f`, `--force` Bypass protections

## Features

Expand Down Expand Up @@ -111,10 +111,10 @@ hard:
soft:
- "*.bak"
# do not allow deleting these files/directories
# without using the `-f` or `--force` flags
# this does not make the file un-deletable
# through other tools, but it does protect
# against accidental deletion through 2rm
# without using the `--bypass-protected` flag this
# does not make the file protected at the system level
# through other tools, but it does protect against
# accidental deletion through 2rm
protected:
- ".ssh/"
```
1 change: 1 addition & 0 deletions src/cli/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ const NOTIFICATION_CLA = "--notify"

// gnu rm CLI arguments
const INTERACTIVE_CLA = "-i"
const INTERACTIVE_GROUP_CLA = "-I"
const HELP_CLA = "--help"
const VERSION_CLA = "--version"
21 changes: 17 additions & 4 deletions src/cli/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,28 @@ Mark FILE(s) for deletion.
"GNU Like" OPTION(s):
-i Prompt before every deletion request
--force Bypass 2rm protections
-I Prompt once before deleting more than three files
-f, --force Bypass protections
2rm OPTION(s) Flags:
--overwrite Overwrite the disk location location with zeros
--hard Do not soft-delete FILE(s)
--soft Soft delete a file and a store backup (default /tmp/2rm)
--silent Do not print out additional information priduced by 2rm. This is useful for scripting situations
--dry-run Perform a dry run and show all the files that would be deleted
--bypass-protected Using this flag will allow you to delete a file protected by the 2rm config
--silent Do not print out additional information priduced by 2rm.
This is useful for scripting situations
--dry-run Perform a dry run and show all the files that would be
deleted if run without the dry-run flag
--bypass-protected Using this flag will allow you to delete a file
protected by the 2rm config
--notify Send a system notification once deletion is complete
By default, 2rm will soft-delete a file and store a backup (default /tmp/2rm)
Expand Down
127 changes: 59 additions & 68 deletions src/patches/rm.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,17 @@ import (
)

const TRASH_DIR_PERMISSIONS = 0755
const INTERACTIVE_THRESHOLD = 3

func RmPatch(arguments []string, config models.Config) {
forceHardDelete := util.InArray(arguments, cli.HARD_DELETE_CLA)
forceSoftDelete := util.InArray(arguments, cli.SOFT_DELETE_CLA)
silent := util.InArray(arguments, cli.SILENT_CLA)
dryRun := util.InArray(arguments, cli.DRY_RUN_CLA)
bypassProtected := util.InArray(arguments, cli.BYPASS_PROTECTED_CLA)
overwrite := util.InArray(arguments, cli.OVERWRITE_CLA)
shouldNotify := util.InArray(arguments, cli.NOTIFICATION_CLA)

requestingHelp := util.InArray(arguments, cli.HELP_CLA)
requestingVersion := util.InArray(arguments, cli.VERSION_CLA)

actionedArgs := removeUnNeededArguments(
removeDangerousArguments(arguments),
)
actionedArgs := removeDangerousArguments(arguments)

if requestingHelp {
cli.PrintHelp()
Expand All @@ -54,36 +49,7 @@ func RmPatch(arguments []string, config models.Config) {
return
}

for _, path := range filePaths {
absolutePath := relativeToAbsolute(path)
isTmp := isTmpPath(absolutePath)

isProtected := config.IsProtected(absolutePath)
if isProtected && bypassProtected {
fmt.Println("Cannot delete protected file:", absolutePath)
fmt.Println("Use the --bypass-protected flag to force deletion")
continue
}

isConfigHardDelete := config.ShouldHardDelete(absolutePath)
isConfigSoftDelete := config.ShouldSoftDelete(absolutePath)

// overwriting a file is not exclusive to hard/soft deletes
// meaning that you can overwrite the contents of a file with zeros and
// also soft delete it
// I have made this decision because I think soft-deleting an
// overwritten file has auditing/logging use cases
// e.g. Who deleted this file? When was it deleted?
// if we hard deleted the file, we would lose this information
isConfigOverwrite := config.ShouldOverwrite(absolutePath)
if overwrite || isConfigOverwrite {
overwriteFile(absolutePath)
}

shouldHardDelete := isTmp || forceHardDelete || isConfigHardDelete && !isConfigSoftDelete && !forceSoftDelete

deletePath(absolutePath, shouldHardDelete, config, arguments)
}
deletePaths(filePaths, config, arguments)

if shouldNotify {
fileNames := strings.Join(filePaths, ", ")
Expand All @@ -94,28 +60,6 @@ func RmPatch(arguments []string, config models.Config) {
}
}

func removeUnNeededArguments(arguments []string) []string {
returnedArguments := []string{}
unNeededArguments := []string{
"-r",
cli.HARD_DELETE_CLA,
cli.SOFT_DELETE_CLA,
cli.SILENT_CLA,
cli.DRY_RUN_CLA,
cli.BYPASS_PROTECTED_CLA,
cli.OVERWRITE_CLA,
cli.NOTIFICATION_CLA,
}

for _, arg := range arguments {
if !util.InArray(unNeededArguments, arg) {
returnedArguments = append(returnedArguments, arg)
}
}

return returnedArguments
}

func removeDangerousArguments(arguments []string) []string {
// I have excluded the root slash as a forbidden argument just incase
// you make a typo like rm ./myDirectory /
Expand Down Expand Up @@ -165,20 +109,61 @@ func backupFileName(path string) string {
return result + ".bak"
}

func deletePath(path string, hard bool, config models.Config, arguments []string) {
isInteractive := util.InArray(arguments, cli.INTERACTIVE_CLA)
func deletePaths(paths []string, config models.Config, arguments []string) {
forceHardDelete := util.InArray(arguments, cli.HARD_DELETE_CLA)
forceSoftDelete := util.InArray(arguments, cli.SOFT_DELETE_CLA)
bypassProtected := util.InArray(arguments, cli.BYPASS_PROTECTED_CLA)
overwrite := util.InArray(arguments, cli.OVERWRITE_CLA)

if isInteractive {
fmt.Println("Are you sure you want to delete", path, "? (y/n)")
var response string
fmt.Scanln(&response)
hasInteraciveCla := util.InArray(arguments, cli.INTERACTIVE_CLA)
hasGroupInteractiveCla := util.InArray(arguments, cli.INTERACTIVE_GROUP_CLA)
isInteractiveGroup := hasGroupInteractiveCla && len(paths) >= INTERACTIVE_THRESHOLD
isInteractive := hasInteraciveCla || isInteractiveGroup

for _, path := range paths {
if isInteractive {
fmt.Println("Are you sure you want to delete", path, "? (y/n)")
var response string
fmt.Scanln(&response)

if response != "y" && response != "yes" {
fmt.Println("Skipping file", path)
continue
}
}

if response != "y" && response != "yes" {
fmt.Println("Exiting without removing file(s).")
return
absolutePath := relativeToAbsolute(path)
isTmp := isTmpPath(absolutePath)

isProtected := config.IsProtected(absolutePath)
if isProtected && bypassProtected {
fmt.Println("Cannot delete protected file:", absolutePath)
fmt.Println("Use the --bypass-protected flag to force deletion")
continue
}

isConfigHardDelete := config.ShouldHardDelete(absolutePath)
isConfigSoftDelete := config.ShouldSoftDelete(absolutePath)

// overwriting a file is not exclusive to hard/soft deletes
// meaning that you can overwrite the contents of a file with zeros and
// also soft delete it
// I have made this decision because I think soft-deleting an
// overwritten file has auditing/logging use cases
// e.g. Who deleted this file? When was it deleted?
// if we hard deleted the file, we would lose this information
isConfigOverwrite := config.ShouldOverwrite(absolutePath)
if overwrite || isConfigOverwrite {
overwriteFile(absolutePath)
}

shouldHardDelete := isTmp || forceHardDelete || isConfigHardDelete && !isConfigSoftDelete && !forceSoftDelete

deletePath(absolutePath, shouldHardDelete, config, arguments)
}
}

func deletePath(path string, hard bool, config models.Config, arguments []string) {
if hard {
hardDelete(path)
} else {
Expand Down Expand Up @@ -225,6 +210,12 @@ func softDelete(filePath string, tempDir string) {
}

err = os.Remove(absoluteSrcPath)
if err != nil {
fmt.Println("Error deleting file:", err)

// pause the program so the user can see the error message
fmt.Scanln()
}
}

func hardDelete(filePath string) {
Expand Down

0 comments on commit 56b41fc

Please sign in to comment.