forked from mandiant/gocrack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfilemanager.go
More file actions
143 lines (120 loc) · 3.63 KB
/
filemanager.go
File metadata and controls
143 lines (120 loc) · 3.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* package filemanager is responsible for the saving of files along with the calculation of metadata */
package filemanager
import (
"crypto/sha1"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/fireeye/gocrack/server/storage"
)
// Context contains the state of the filemanager and provides public methods to interact
// with all filesystem actions within gocrack
type Context struct {
stor FileManagerStor
cfg Config
}
type FileSaveResponse struct {
Size int64
SavedTo string
NumberOfLines int64
SHA1 string
}
type FileManagerStor interface {
NewEngineFileTransaction() (storage.EngineFileTxn, error)
DeleteEngineFile(string) error
DeleteTaskFile(string) error
}
var (
// ErrCannotImport is returned whenever we are told to reload but cannot due to missing import_directory
ErrCannotImport = errors.New("filemanager: cannot import because import_directory is not set in the config")
// SystemUserUUID is the UUID of files uploaded by the GoCrack system
SystemUserUUID = "b2c9e661-74e5-4ba3-b1ce-624894e85622"
)
func splitFilePath(rootPath, filename string) string {
filename = strings.Replace(filename, "-", "", -1)
var parts = []string{rootPath}
for i := 0; i < 14; i += 2 {
parts = append(parts, filename[i:i+2])
}
parts = append(parts, filename)
return filepath.Join(parts...)
}
// New creates a new FileManager for GoCrack
func New(stor FileManagerStor, cfg Config) *Context {
return &Context{
stor: stor,
cfg: cfg,
}
}
// Refresh checks the import directory for new files and saves them into the database
func (s *Context) Refresh() error {
return s.importDirectory()
}
// SaveFile returns metadata about the file and saves the file to disk
func (s *Context) SaveFile(src io.ReadCloser, filename string, fileUUID string, filetype interface{}) (*FileSaveResponse, error) {
var rootpath string
var finalSavePath string
switch t := filetype.(type) {
case storage.TaskFileEngine:
rootpath = s.cfg.TaskUploadPath
case storage.EngineFileType:
rootpath = s.cfg.EngineFilePath
default:
return nil, fmt.Errorf("unknown filetype `%v`", t)
}
// XXX(cschmitt): This is kinda messy how we have to handle file uploads.. we should create something like python's buffered temp file
fd, err := ioutil.TempFile(s.cfg.TempPath, "")
if err != nil {
return nil, err
}
szrec := &WriteSizeLineRecorder{}
hasher := sha1.New()
// Write the file to the temp file, hasher, and our size/line recorder
mw := io.MultiWriter(fd, hasher, szrec)
if _, err = io.Copy(mw, src); err != nil {
fd.Close()
return nil, nil
}
// Close the file we just wrote to disk
if err := fd.Close(); err != nil {
return nil, err
}
fileHash := fmt.Sprintf("%x", hasher.Sum(nil))
finalSavePath = splitFilePath(rootpath, fileUUID)
rootPath, _ := filepath.Split(finalSavePath)
// Create the nested directory
if err := os.MkdirAll(rootPath, 0770); err != nil {
return nil, err
}
// Move the tempfile over to the new path
if err := os.Rename(fd.Name(), finalSavePath); err != nil {
return nil, err
}
return &FileSaveResponse{
SHA1: fileHash,
SavedTo: finalSavePath,
Size: szrec.Size(),
NumberOfLines: szrec.Lines(),
}, nil
}
// DeleteFile removes the file from the system
func (s *Context) DeleteFile(doc interface{}) error {
switch t := doc.(type) {
case storage.EngineFile:
if err := s.stor.DeleteEngineFile(t.FileID); err != nil {
return err
}
return os.Remove(t.SavedAt)
case storage.TaskFile:
if err := s.stor.DeleteTaskFile(t.FileID); err != nil {
return err
}
return os.Remove(t.SavedAt)
default:
return fmt.Errorf("unknown filetype `%v`", t)
}
}