1 /* $Id: sizeprobe.c,v 1.4 2005/05/15 20:15:28 harbourn Exp $
2 * dcfldd - The Enhanced Forensic DD
3 * By Nicholas Harbour
4 */
5
6 /* Copyright 85, 90, 91, 1995-2001, 2005 Free Software Foundation, Inc.
7 * Copyright 2014 Vangelis Koukis <vkoukis@gmail.com>
8 * Copyright 2020 David Polverari <david.polverari@gmail.com>
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2, or (at your option)
13 any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software Foundation,
22 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
23
24 /* GNU dd originally written by Paul Rubin, David MacKenzie, and Stuart Kemp. */
25
26 #include "dcfldd.h"
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <fcntl.h>
32 #include "config.h"
33 #include "system.h"
34 #include "sizeprobe.h"
35 #include "log.h"
36
37 static off_t midpoint(off_t a, off_t b, long blksize);
38 static off_t get_dev_size(int, long);
39
40 /* Which file (if any) to probe the size of */
41 int probe = PROBE_NONE;
42 off_t probed_size;
43
44 /*
45 * Compute a block-resolution midpoint (c) of a and b
46 */
midpoint(off_t a,off_t b,long blksize)47 static off_t midpoint(off_t a, off_t b, long blksize)
48 {
49 off_t aprime = a / blksize;
50 off_t bprime = b / blksize;
51 off_t c, cprime;
52
53 cprime = (bprime - aprime) / 2 + aprime;
54 c = cprime * blksize;
55
56 return c;
57 }
58
59 #if defined (__linux__)
60
61 #include <sys/ioctl.h>
62 #include <sys/mount.h>
63
64 /* I stole this from Jesse Kornblum's md5deep */
get_dev_size(int fd,long blksize)65 static off_t get_dev_size(int fd, long blksize)
66 {
67 off_t num_bytes = 0;
68
69 /*
70 * Use BLKGETSIZE64 unconditionally, since dcfldd.h #defines _FILE_OFFSET_BITS 64
71 * and off_t is guaranteed to be large enough to hold the result.
72 */
73 if (ioctl(fd, BLKGETSIZE64, &num_bytes))
74 log_info("%s: ioctl call to BLKGETSIZE64 failed.\n", program_name);
75 else
76 return (num_bytes);
77 }
78
79 #elif defined (__MacOSX__)
80
81 #include <stdint.h>
82 #include <sys/ioctl.h>
83 #include <sys/disk.h>
84 #include <machine/endian.h>
85
86 /* I also stole this from Jesse Kornblum's md5deep */
get_dev_size(int fd,long blksize)87 static static off_t get_dev_size(int fd, long blksize)
88 {
89 FILE *f = fdopen(fd, "r");
90 off_t total = 0;
91 off_t original = ftello(f);
92 int ok = TRUE;
93
94 if (S_ISBLK(info.st_mode)) {
95 daddr_t blocksize = 0;
96 daddr_t blockcount = 0;
97
98
99 /* Get the block size */
100 if (ioctl(fd, DKIOCGETBLOCKSIZE,blocksize) < 0) {
101 ok = FALSE;
102 #if defined(__DEBUG)
103 perror("DKIOCGETBLOCKSIZE failed");
104 #endif
105 }
106
107 /* Get the number of blocks */
108 if (ok) {
109 if (ioctl(fd, DKIOCGETBLOCKCOUNT, blockcount) < 0) {
110 #if defined(__DEBUG)
111 perror("DKIOCGETBLOCKCOUNT failed");
112 #endif
113 }
114 }
115
116 total = blocksize * blockcount;
117
118 } else {
119
120 /* I don't know why, but if you don't initialize this value you'll
121 get wildly innacurate results when you try to run this function */
122
123 if ((fseeko(f,0,SEEK_END)))
124 return 0;
125 total = ftello(f);
126 if ((fseeko(f,original,SEEK_SET)))
127 return 0;
128 }
129
130 return (total - original);
131 }
132
133 #else /* all other *nix */
134 /*
135 * Guess the size of a device file.
136 * Note: this is only used to give time estimates.
137 * Even if this is way off or broken,
138 * the forensic validity of the tool remains.
139 **************************************************
140 * This algorithm works by reading a block starting
141 * at offset 0 then 1, 2, 4, 8, 16, etc and doubles
142 * until it reaches a point where it fails, then it
143 * iteratively backtracks by half the distance to
144 * the last known good read. It goes back and forth
145 * until it knows the last readable block on the
146 * device. Theoretically, this should give EXACTLY
147 * the size of the device considering that the
148 * seeks and reads work. this algorithm will
149 * obviously wreak havok if you try it against a
150 * tape device, you have been warned.
151 */
get_dev_size(int fd,long blksize)152 static off_t get_dev_size(int fd, long blksize)
153 { /* this function is awesome */
154 off_t curr = 0, amount = 0;
155 void *buf;
156 off_t told;
157
158 if (blksize == 0)
159 return 0;
160
161 buf = malloc(blksize);
162
163 for (;;) {
164 ssize_t nread;
165
166 lseek(fd, curr, SEEK_SET);
167 nread = read(fd, buf, blksize);
168 if (nread < blksize) {
169 if (nread <= 0) {
170 if (curr == amount) {
171 free(buf);
172 lseek(fd, 0, SEEK_SET);
173 return amount;
174 }
175 curr = midpoint(amount, curr, blksize);
176 } else { /* 0 < nread < blksize */
177 free(buf);
178 lseek(fd, 0, SEEK_SET);
179 return amount + nread;
180 }
181 } else {
182 amount = curr + blksize;
183 curr = amount * 2;
184 }
185 }
186 free(buf);
187 lseek(fd, 0, SEEK_SET);
188 return amount;
189 }
190
191 #endif /* if defined (__linux__), etc.. */
192
sizeprobe(int fd)193 void sizeprobe(int fd)
194 {
195 struct stat statbuf;
196
197 if (fstat(fd, &statbuf) == -1) {
198 log_info("%s: stating file", strerror(errno));
199 return;
200 }
201
202 if (S_ISREG(statbuf.st_mode) || S_ISDIR(statbuf.st_mode))
203 probed_size = statbuf.st_size;
204 else if (S_ISCHR(statbuf.st_mode) || S_ISBLK(statbuf.st_mode))
205 probed_size = get_dev_size(fd, statbuf.st_blksize);
206 }
207