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