Skip to content

Commit 118925e

Browse files
authored
Merge pull request #8 from mrf345/testing
Allow multiple input paths, and tweak settings for performance (2x faster)
2 parents abcf677 + 68a05be commit 118925e

14 files changed

+108
-70
lines changed

README.md

+16-15
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@ echo "password123456" | safelock-cli encrypt path_to_encrypt encrypted_file_path
5858

5959
func main() {
6060
lock := safelock.New()
61-
inputPath := "/home/testing/important"
61+
inputPaths := []string{"/home/testing/important"}
6262
outputPath := "/home/testing/encrypted.sla"
6363
password := "testing123456"
6464

65-
// Encrypts `inputPath` with the default settings
66-
if err := lock.Encrypt(nil, inputPath, outputPath, password); err != nil {
65+
// Encrypts `inputPaths` with the default settings
66+
if err := lock.Encrypt(nil, inputPaths, outputPath, password); err != nil {
6767
panic(err)
6868
}
6969

@@ -77,31 +77,32 @@ echo "password123456" | safelock-cli encrypt path_to_encrypt encrypted_file_path
7777

7878
### Performance
7979

80-
With the default settings it should be about **twice** as fast as `gpgtar`
80+
With the default settings it should be about **three times** faster than `gpgtar`
8181

8282
```shell
8383
> du -hs testing/
8484
1.2G testing/
8585

86-
> time gpgtar --encrypt --output testing.gpg -r user testing/
87-
real 0m42.710s
88-
user 0m41.148s
89-
sys 0m7.943s
86+
> time gpgtar -e -o testing.gpg -c --yes --batch --gpg-args "--passphrase testing123456" testing/
87+
real 0m40.141s
88+
user 0m33.933s
89+
sys 0m6.930s
90+
9091

9192
> time echo "testing123456" | safelock-cli encrypt testing/ testing.sla --quiet
92-
real 0m20.697s
93-
user 0m25.355s
94-
sys 0m9.647s
93+
real 0m8.403s
94+
user 0m10.790s
95+
sys 0m4.832s
9596
```
9697

9798
> [!TIP]
98-
> You can get even faster performance using the `--sha256` flag (less secure)
99+
> You can get slightly better performance using the `--sha256` flag (less secure)
99100
100101
```shell
101102
> time echo "testing123456" | safelock-cli encrypt testing/ testing.sla --quiet --sha256
102-
real 0m16.043s
103-
user 0m17.550s
104-
sys 0m8.707s
103+
real 0m8.188s
104+
user 0m10.441s
105+
sys 0m4.709s
105106
```
106107

107108
And no major file size difference

cmd/decrypt.go

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ var decryptCmd = &cobra.Command{
4040
}
4141

4242
sl.Quiet = beQuiet
43+
sl.Registry.TempDir = tempDir
4344
inputPath, outputPath := args[0], args[1]
4445

4546
if err = sl.Decrypt(context.TODO(), inputPath, outputPath, pwd); err != nil {

cmd/encrypt.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ var encryptCmd = &cobra.Command{
4141
}
4242

4343
sl.Quiet = beQuiet
44-
inputPath, outputPath := args[0], args[1]
44+
sl.Registry.TempDir = tempDir
45+
inputPath, outputPath := []string{args[0]}, args[1]
4546

4647
if err = sl.Encrypt(context.TODO(), inputPath, outputPath, pwd); err != nil {
4748
utils.PrintErrsAndExit(err.Error())

cmd/root.go

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
var useSha256 bool
1212
var beQuiet bool
13+
var tempDir string
1314

1415
var rootCmd = &cobra.Command{
1516
Use: "safelock-cli",
@@ -30,4 +31,5 @@ func Execute() {
3031
func init() {
3132
rootCmd.PersistentFlags().BoolVar(&useSha256, "sha256", false, "use SHA256 (faster) instead of SHA512")
3233
rootCmd.PersistentFlags().BoolVar(&beQuiet, "quiet", false, "disable output logs")
34+
rootCmd.PersistentFlags().StringVar(&tempDir, "temp-dir", os.TempDir(), "directory for temporary files")
3335
}

safelock/decrypt.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ func (sl *Safelock) Decrypt(ctx context.Context, inputPath, outputPath, password
6767
}
6868

6969
sl.updateStatus(fmt.Sprintf("Decrypted into %s", outputPath), 100.0)
70-
sl.StatusObs.Trigger(EventStatusEnd)
7170
close(signals)
7271
close(errs)
7372
}()
@@ -108,7 +107,7 @@ func (sl *Safelock) decryptArchiveFileInChunks(inputPath, password string) (outp
108107
return
109108
}
110109

111-
if outputFile, err = sl.Registry.NewFile("", "d_output_temp"); err != nil {
110+
if outputFile, err = sl.Registry.NewFile("d_output_temp"); err != nil {
112111
err = fmt.Errorf("failed to create temporary file > %w", err)
113112
return
114113
}
@@ -187,7 +186,12 @@ func (sl *Safelock) extractArchiveFile(ctx context.Context, outputPath string, a
187186
return fmt.Errorf("cannot read archive file > %w", err)
188187
}
189188

190-
go sl.updateArchiveFileStatus(statusCtx, archive.Name(), outputPath, "Extracting", 70.0)
189+
go sl.updateArchiveFileStatus(
190+
statusCtx,
191+
[]string{archive.Name()},
192+
outputPath,
193+
"Extracting", 70.0,
194+
)
191195

192196
if err = sl.Archival.Extract(ctx, reader, nil, fileHandler); err != nil {
193197
return fmt.Errorf("cannot extract archive file > %w", err)

safelock/decrypt_test.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,13 @@ func TestDecryptFileWithTimeout(t *testing.T) {
5454
inputFile, _ := os.CreateTemp("", "input_file")
5555
outputDirPath, _ := os.MkdirTemp("", "output_dir")
5656
outputPath := filepath.Join(outputDirPath, "output_file.sla")
57+
inputPaths := []string{inputFile.Name()}
5758

5859
cancel()
5960
_, _ = inputFile.WriteString(content)
6061
_, _ = inputFile.Seek(0, io.SeekStart)
6162

62-
encErr := sl.Encrypt(context.TODO(), inputFile.Name(), outputPath, password)
63+
encErr := sl.Encrypt(context.TODO(), inputPaths, outputPath, password)
6364
decErr := sl.Decrypt(ctx, inputFile.Name(), outputPath, password)
6465
_, isExpectedErr := decErr.(*myErrs.ErrContextExpired)
6566

@@ -80,11 +81,12 @@ func TestDecryptFileWithWrongPassword(t *testing.T) {
8081
inputFile, _ := os.CreateTemp("", "input_file")
8182
outputDirPath, _ := os.MkdirTemp("", "output_dir")
8283
outputPath := filepath.Join(outputDirPath, "output_file.sla")
84+
inputPaths := []string{inputFile.Name()}
8385

8486
_, _ = inputFile.WriteString(content)
8587
_, _ = inputFile.Seek(0, io.SeekStart)
8688

87-
encErr := sl.Encrypt(context.TODO(), inputFile.Name(), outputPath, password)
89+
encErr := sl.Encrypt(context.TODO(), inputPaths, outputPath, password)
8890
decErr := sl.Decrypt(context.TODO(), outputPath, outputDirPath, "wrong")
8991
_, isExpectedErr := errors.Unwrap(decErr).(*myErrs.ErrFailedToAuthenticate)
9092

safelock/encrypt.go

+32-20
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ import (
2020
"golang.org/x/crypto/pbkdf2"
2121
)
2222

23-
// encrypts `inputPath` which can be either a file or directory and output into the `outputPath`
24-
// which must be a nonexisting file filepath.
23+
// encrypts `inputPaths` which can be either a slice of file or directory paths and outputs into the `outputPath`
24+
// which must be a nonexisting file path.
2525
//
2626
// NOTE: `ctx` context is optional you can pass `nil` and the method will handle it
27-
func (sl *Safelock) Encrypt(ctx context.Context, inputPath, outputPath, password string) (err error) {
27+
func (sl *Safelock) Encrypt(ctx context.Context, inputPaths []string, outputPath, password string) (err error) {
2828
errs := make(chan error)
2929
signals := sl.getExitSignalsChannel()
3030

@@ -45,7 +45,7 @@ func (sl *Safelock) Encrypt(ctx context.Context, inputPath, outputPath, password
4545
var outputFile *os.File
4646

4747
sl.updateStatus("Validating input and output", 0.0)
48-
if err = validateEncryptionPaths(inputPath, outputPath); err != nil {
48+
if err = validateEncryptionPaths(inputPaths, outputPath); err != nil {
4949
errs <- fmt.Errorf("invalid encryption input/output paths > %w", err)
5050
return
5151
}
@@ -56,7 +56,7 @@ func (sl *Safelock) Encrypt(ctx context.Context, inputPath, outputPath, password
5656
}
5757

5858
sl.updateStatus("Creating compressed archive file", 1.0)
59-
if archiveFile, err = sl.createArchiveFile(ctx, inputPath); err != nil {
59+
if archiveFile, err = sl.createArchiveFile(ctx, inputPaths); err != nil {
6060
errs <- fmt.Errorf("failed to create archive file > %w", err)
6161
return
6262
}
@@ -76,7 +76,6 @@ func (sl *Safelock) Encrypt(ctx context.Context, inputPath, outputPath, password
7676

7777
unRegister()
7878
sl.updateStatus(fmt.Sprintf("Encrypted %s", outputPath), 100.0)
79-
sl.StatusObs.Trigger(EventStatusEnd)
8079
close(signals)
8180
close(errs)
8281
}()
@@ -103,15 +102,17 @@ func (sl *Safelock) getExitSignalsChannel() chan os.Signal {
103102
return signals
104103
}
105104

106-
func validateEncryptionPaths(inputPath, outputPath string) (err error) {
107-
inputIsFile, inputErrFile := utils.IsValidFile(inputPath)
108-
inputIsDir, inputErrDir := utils.IsValidDir(inputPath)
105+
func validateEncryptionPaths(inputPath []string, outputPath string) (err error) {
106+
for _, inputPath := range inputPath {
107+
inputIsFile, inputErrFile := utils.IsValidFile(inputPath)
108+
inputIsDir, inputErrDir := utils.IsValidDir(inputPath)
109109

110-
if !inputIsFile && !inputIsDir {
111-
if inputErrFile != nil {
112-
return inputErrFile
113-
} else {
114-
return inputErrDir
110+
if !inputIsFile && !inputIsDir {
111+
if inputErrFile != nil {
112+
return inputErrFile
113+
} else {
114+
return inputErrDir
115+
}
115116
}
116117
}
117118

@@ -122,18 +123,23 @@ func validateEncryptionPaths(inputPath, outputPath string) (err error) {
122123
return
123124
}
124125

125-
func (sl *Safelock) createArchiveFile(ctx context.Context, inputPath string) (file *utils.RegFile, err error) {
126+
func (sl *Safelock) createArchiveFile(ctx context.Context, inputPaths []string) (file *utils.RegFile, err error) {
126127
var files []archiver.File
128+
var filesMap = make(map[string]string)
129+
130+
for _, input := range inputPaths {
131+
filesMap[input] = ""
132+
}
127133

128134
statusCtx, cancelStatus := context.WithCancel(ctx)
129135
defer cancelStatus()
130136

131-
if files, err = archiver.FilesFromDisk(nil, map[string]string{inputPath: ""}); err != nil {
137+
if files, err = archiver.FilesFromDisk(nil, filesMap); err != nil {
132138
err = fmt.Errorf("failed to list archive files > %w", err)
133139
return
134140
}
135141

136-
if file, err = sl.Registry.NewFile("", "e_output_temp"); err != nil {
142+
if file, err = sl.Registry.NewFile("e_output_temp"); err != nil {
137143
err = fmt.Errorf("failed to create temporary file > %w", err)
138144
return
139145
}
@@ -143,7 +149,7 @@ func (sl *Safelock) createArchiveFile(ctx context.Context, inputPath string) (fi
143149
Archival: sl.Archival,
144150
}
145151

146-
go sl.updateArchiveFileStatus(statusCtx, inputPath, file.Name(), "Creating", 1.0)
152+
go sl.updateArchiveFileStatus(statusCtx, inputPaths, file.Name(), "Creating", 1.0)
147153

148154
if err = format.Archive(ctx, file, files); err != nil {
149155
err = fmt.Errorf("failed to create archive file > %w", err)
@@ -155,13 +161,19 @@ func (sl *Safelock) createArchiveFile(ctx context.Context, inputPath string) (fi
155161
return
156162
}
157163

158-
func (sl *Safelock) updateArchiveFileStatus(ctx context.Context, inputPath, archivePath, act string, start float64) {
164+
func (sl *Safelock) updateArchiveFileStatus(
165+
ctx context.Context,
166+
inputPaths []string,
167+
archivePath,
168+
act string,
169+
start float64,
170+
) {
159171
for {
160172
select {
161173
case <-ctx.Done():
162174
return
163175
default:
164-
if p, err := utils.GetPathsPercent(inputPath, archivePath, start, 30.0); err != nil {
176+
if p, err := utils.GetPathsPercent(inputPaths, archivePath, start, 30.0); err != nil {
165177
return
166178
} else {
167179
sl.updateStatus(fmt.Sprintf("%s compressed archive file", act), p)

safelock/encrypt_test.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414

1515
func TestEncryptWithInvalidInputPath(t *testing.T) {
1616
assert := assert.New(t)
17-
inputPath := "wrong_input.txt"
17+
inputPath := []string{"wrong_input.txt"}
1818
password := "testing123456"
1919
sl := GetQuietSafelock()
2020
outputFile, _ := os.CreateTemp("", "output_file")
@@ -37,13 +37,14 @@ func TestEncryptFile(t *testing.T) {
3737
outputPath := filepath.Join(outputDir, "output_file.sla")
3838
content := "Hello World!"
3939
decryptedPath := filepath.Join(outputDir, filepath.Base(inputFile.Name()))
40+
inputPaths := []string{inputFile.Name()}
4041

4142
defer os.Remove(inputFile.Name())
4243
defer os.RemoveAll(outputDir)
4344
_, _ = inputFile.WriteString(content)
4445
inputFile.Close()
4546

46-
inErr := encSl.Encrypt(context.TODO(), inputFile.Name(), outputPath, password)
47+
inErr := encSl.Encrypt(context.TODO(), inputPaths, outputPath, password)
4748
outErr := decSl.Decrypt(context.TODO(), outputPath, outputDir, password)
4849
reader, _ := os.Open(decryptedPath)
4950
decrypted, _ := io.ReadAll(reader)
@@ -64,13 +65,14 @@ func TestEncryptFileWithSha256AndGzip(t *testing.T) {
6465
outputDir, _ := os.MkdirTemp("", "output_dir")
6566
outputPath := filepath.Join(outputDir, "output_file.sla")
6667
decryptedPath := filepath.Join(outputDir, filepath.Base(inputFile.Name()))
68+
inputPaths := []string{inputFile.Name()}
6769

6870
defer os.Remove(inputFile.Name())
6971
defer os.RemoveAll(outputDir)
7072
_, _ = inputFile.WriteString(content)
7173
_, _ = inputFile.Seek(0, io.SeekStart)
7274

73-
inErr := encSl.Encrypt(context.TODO(), inputFile.Name(), outputPath, password)
75+
inErr := encSl.Encrypt(context.TODO(), inputPaths, outputPath, password)
7476
outErr := decSl.Decrypt(context.TODO(), outputPath, outputDir, password)
7577
reader, _ := os.Open(decryptedPath)
7678
decrypted, _ := io.ReadAll(reader)
@@ -89,12 +91,13 @@ func TestEncryptFileWithTimeout(t *testing.T) {
8991
inputFile, _ := os.CreateTemp("", "input_file")
9092
outputDir, _ := os.MkdirTemp("", "output_dir")
9193
outputPath := filepath.Join(outputDir, "output_file.sla")
94+
inputPaths := []string{inputFile.Name()}
9295

9396
cancel()
9497
_, _ = inputFile.WriteString(content)
9598
_, _ = inputFile.Seek(0, io.SeekStart)
9699

97-
err := sl.Encrypt(ctx, inputFile.Name(), outputPath, password)
100+
err := sl.Encrypt(ctx, inputPaths, outputPath, password)
98101
_, isExpectedErr := err.(*myErrs.ErrContextExpired)
99102

100103
assert.NotNil(err)

safelock/examples_test.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ func ExampleSafelock_Encrypt() {
2222

2323
// Prepare files to encrypt (should ignore this)
2424
inputFilePath, encryptedFilePath, clean := getUnencryptedFilePaths()
25+
inputPaths := []string{inputFilePath}
2526
defer clean()
2627

27-
// Encrypt `inputFilePath` with the assigned settings
28-
if err := lock.Encrypt(ctx, inputFilePath, encryptedFilePath, password); err != nil {
28+
// Encrypt `inputPaths` with the assigned settings
29+
if err := lock.Encrypt(ctx, inputPaths, encryptedFilePath, password); err != nil {
2930
fmt.Println("failed!")
3031
}
3132

@@ -76,9 +77,9 @@ func getEncryptedFilePath() (encryptedFilePath string, clean func()) {
7677
ctx := context.Background()
7778
file, _ := os.CreateTemp("", "test_input")
7879
dirPath, _ := os.MkdirTemp("", "test_output")
79-
filePath := file.Name()
80+
filePaths := []string{file.Name()}
8081
encryptedFilePath = filepath.Join(dirPath, "encrypted.sla")
81-
_ = lock.Encrypt(ctx, filePath, encryptedFilePath, password)
82+
_ = lock.Encrypt(ctx, filePaths, encryptedFilePath, password)
8283
clean = func() {
8384
os.Remove(file.Name())
8485
os.RemoveAll(dirPath)

safelock/logger.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ func (sl *Safelock) log(msg string, params ...any) {
1818
}
1919

2020
func (sl *Safelock) logStatus(status string, percent float64) {
21-
sl.log("%s (%.2f)\n", status, percent)
21+
sl.log("%s (%.2f%%)\n", status, percent)
2222
}

safelock/main.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
//
99
// func main() {
1010
// lock := safelock.New()
11-
// inputPath := "/home/testing/important"
11+
// inputPaths := []string{"/home/testing/important"}
1212
// outputPath := "/home/testing/encrypted.sla"
1313
// extractTo := "/home/testing"
1414
// password := "testing123456"
1515
//
16-
// // Encrypts `inputPath` with the default settings
16+
// // Encrypts `inputPaths` with the default settings
1717
// if err := lock.Encrypt(nil, inputPath, outputPath, password); err != nil {
1818
// panic(err)
1919
// }

0 commit comments

Comments
 (0)