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