1 // Scalpel Copyright (C) 2005-11 by Golden G. Richard III and
2 // 2007-11 by Vico Marziale.
3 // Written by Golden G. Richard III and Vico Marziale.
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301, USA.
19 //
20 // Thanks to Kris Kendall, Jesse Kornblum, et al for their work
21 // on Foremost. Foremost 0.69 was used as the starting point for
22 // Scalpel, in 2005.
23
24 #include "scalpel.h"
25
26 // Returns TRUE if the directory exists and is empty.
27 // If the directory does not exist, an attempt is made to
28 // create it. On error, returns FALSE
outputDirectoryOK(char * dir)29 int outputDirectoryOK(char *dir) {
30
31 DIR *temp;
32 struct dirent *entry;
33 int i;
34 mode_t newDirectoryMode;
35
36 if((temp = opendir(dir)) == NULL) {
37 // If the directory doesn't exist (ENOENT), we will create it
38 if(errno == ENOENT) {
39 // The directory mode values come from the chmod(2) man page
40 #ifdef __MINGW32__
41 newDirectoryMode = 0;
42 if(mkdir(dir)) {
43 #else
44 newDirectoryMode = (S_IRUSR | S_IWUSR | S_IXUSR |
45 S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH);
46 if(mkdir(dir, newDirectoryMode)) {
47 #endif
48
49 fprintf(stderr,
50 "An error occured while trying to create %s - %s\n",
51 dir, strerror(errno));
52 return FALSE;
53 }
54
55 // try to open directory
56 if((temp = opendir(dir)) == NULL) {
57 fprintf(stderr,
58 "An error occured while trying to open %s - %s\n", dir,
59 strerror(errno));
60 return FALSE;
61 }
62 }
63 else {
64 fprintf(stderr, "An error occured while trying to open %s - %s\n",
65 dir, strerror(errno));
66 return FALSE;
67 }
68 }
69
70 // verify directory is empty--there should be only two entries,
71 // "." and ".."
72 i = 0;
73 while ((entry = readdir(temp))) {
74 if(i > 1) {
75 return FALSE;
76 }
77 i++;
78 }
79 closedir(temp);
80 return TRUE;
81 }
82
83 // open audit file and add initial entries
84 int openAuditFile(struct scalpelState *state) {
85 time_t now = time(NULL);
86 char *timestring = ctime(&now);
87 char fn[MAX_STRING_LENGTH];
88 char *buf;
89 int err = SCALPEL_OK;
90
91 buf = (char *)malloc(NUM_SEARCH_SPEC_ELEMENTS * MAX_STRING_LENGTH);
92 checkMemoryAllocation(state, buf, __LINE__, __FILE__, "buf");
93
94 if(!outputDirectoryOK(state->outputdirectory)) {
95 err = SCALPEL_ERROR_NONEMPTY_DIRECTORY;
96 goto out;
97 }
98
99 snprintf(fn, MAX_STRING_LENGTH, "%s/audit.txt", state->outputdirectory);
100
101 FILE *f;
102
103 if(!(state->auditFile = fopen(fn, "w"))) {
104 fprintf(stderr, "Couldn't open audit file\n%s -- %s\n", fn,
105 strerror(errno));
106 err = SCALPEL_ERROR_FATAL_READ;
107 goto out;
108 }
109
110 fprintf(state->auditFile,
111 "\nScalpel version %s audit file\n"
112 "Started at %sCommand line:\n%s\n\n"
113 "Output directory: %s\n"
114 "Configuration file: %s\n",
115 SCALPEL_VERSION, timestring, state->invocation,
116 state->outputdirectory, state->conffile);
117
118 // copy config file into audit log
119
120 f = fopen(state->conffile, "r");
121 if(f == NULL) {
122 fprintf(stderr,
123 "ERROR: Couldn't open configuration file:\n%s -- %s\n",
124 state->conffile, strerror(errno));
125 err = SCALPEL_ERROR_FATAL_READ;
126 goto out;
127 }
128
129 fprintf(state->auditFile, "\n------ BEGIN COPY OF CONFIG FILE USED ------\n");
130
131 while (fgets(buf, NUM_SEARCH_SPEC_ELEMENTS * MAX_STRING_LENGTH, f)) {
132 fprintf(state->auditFile, "%s", buf);
133 }
134
135 fprintf(state->auditFile, "------ END COPY OF CONFIG FILE USED ------\n\n");
136 fclose(f);
137
138 out:
139 free(buf);
140 return err;
141 }
142
143 // write final completion message and close the audit file, if it's
144 // open
145 int closeAuditFile(FILE * f) {
146
147 time_t now = time(NULL);
148 char *timestring = ctime(&now);
149
150 if(f) {
151 fprintf(f, "\n\nCompleted at %s", timestring);
152 fclose(f);
153 }
154
155 return SCALPEL_OK;
156 }
157
158
159 #if ! defined(__linux)
160
161 // helper function for measureOpenFile(), based on e2fsprogs utility
162 // function valid_offset()
163
164 static int valid_offset(int fd, off64_t offset) {
165 char ch;
166 if(lseek(fd, offset, SEEK_SET) < 0) {
167 return 0;
168 }
169 if(read(fd, &ch, 1) < 1) {
170 return 0;
171 }
172 return 1;
173 }
174
175 #endif
176
177
178 // Return the remaining size, in bytes, of an open file stream. On
179 // error, return -1. Handling raw device files is substantially more
180 // complicated than image files. For Linux, an ioctl() is used. For
181 // other operating systems, a "binary search" technique similar to
182 // that in e2fsprogs getsize.c is used.
183 long long measureOpenFile(FILE * f, struct scalpelState *state) {
184
185 unsigned long long total = 0, original = ftello(f);
186 int descriptor = 0;
187 struct stat *info;
188 unsigned long long numsectors = 0;
189
190 if((fseeko(f, 0, SEEK_END))) {
191 if(state->modeVerbose) {
192 fprintf(stdout, "fseeko() call failed on image file.\n");
193 fprintf(stdout, "Diagnosis: %s\n", strerror(errno));
194 }
195 return -1;
196 }
197 total = ftello(f);
198
199 // for block devices (e.g., raw disk devices), calculating size by
200 // seeking the end of the opened stream doesn't work. For Linux, we use
201 // an ioctl() call. For others (e.g., OS X), we use binary search.
202
203 // is it a block device?
204 descriptor = fileno(f);
205 info = (struct stat *)malloc(sizeof(struct stat));
206 checkMemoryAllocation(state, info, __LINE__, __FILE__, "info");
207 fstat(descriptor, info);
208 if(S_ISBLK(info->st_mode)) {
209
210 #if defined (__linux)
211 if(ioctl(descriptor, BLKGETSIZE, &numsectors) < 0) {
212 if(state->modeVerbose) {
213 fprintf(stdout, "Using ioctl() call to measure block device size.\n");
214 }
215 #if defined(__DEBUG)
216 perror("BLKGETSIZE failed");
217 #endif
218 }
219 #else // non-Linux, use binary search
220
221 {
222 unsigned long long low, high, mid;
223
224 fprintf(stdout, "Using binary search to measure block device size.\n");
225 low = 0;
226 for(high = 512; valid_offset(descriptor, high); high *= 2) {
227 low = high;
228 }
229
230 while (low < high - 1) {
231 mid = (low + high) / 2;
232 if(valid_offset(descriptor, mid)) {
233 low = mid;
234 }
235 else {
236 high = mid;
237 }
238 }
239 numsectors = (low + 1) >> 9;
240 }
241 #endif
242
243 // assume device has 512 byte sectors
244 total = numsectors * 512;
245
246 free(info);
247
248 }
249
250 // restore file position
251
252 if((fseeko(f, original, SEEK_SET))) {
253 if(state->modeVerbose) {
254 fprintf(stdout,
255 "fseeko() call to restore file position failed on image file.\n");
256 }
257 return -1;
258 }
259
260 return (total - original);
261 }
262