1 // Fuzzy Hashing by Jesse Kornblum
2 // Copyright (C) 2012 Kyrus
3 // Copyright (C) 2008 ManTech International Corporation
4 //
5 // $Id: find-file-size.c 144 2012-04-24 14:59:33Z jessekornblum $
6 //
7 
8 #include "main.h"
9 
10 #ifndef _WIN32
11 
12 // Return the size, in bytes of an open file stream. On error, return 0
13 #if defined (__LINUX__)
14 
15 
find_file_size(FILE * f)16 off_t find_file_size(FILE *f)
17 {
18   off_t num_sectors = 0, sector_size = 0;
19   int fd = fileno(f);
20   struct stat sb;
21 
22   if (fstat(fd,&sb))
23     return 0;
24 
25   if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))
26     return sb.st_size;
27 
28 #ifdef HAVE_SYS_IOCTL_H
29 #ifdef HAVE_SYS_MOUNT_H
30   if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode))
31   {
32 #if defined(_IO) && defined(BLKGETSIZE)
33     if (ioctl(fd, BLKGETSIZE, &num_sectors))
34     {
35       return 0;
36     }
37 #else
38     // If we can't run the ioctl call, we can't do anything here
39     return 0;
40 #endif // ifdefined _IO and BLKGETSIZE
41 
42 
43 #if defined(_IO) && defined(BLKSSZGET)
44     if (ioctl(fd, BLKSSZGET, &sector_size))
45     {
46       return 0;
47     }
48     if (0 == sector_size)
49       sector_size = 512;
50 #else
51     sector_size = 512;
52 #endif  // ifdef _IO and BLKSSZGET
53 
54     return (num_sectors * sector_size);
55   }
56 #endif // #ifdef HAVE_SYS_MOUNT_H
57 #endif // #ifdef HAVE_SYS_IOCTL_H
58 
59   return 0;
60 }
61 
62 #elif defined (__APPLE__)
63 
find_file_size(FILE * f)64 off_t find_file_size(FILE *f) {
65   struct stat info;
66   off_t total = 0;
67   off_t original = ftello(f);
68   int fd = fileno(f);
69   uint32_t blocksize = 0;
70   uint64_t blockcount = 0;
71 
72   // I'd prefer not to use fstat as it will follow symbolic links. We don't
73   // follow symbolic links. That being said, all symbolic links *should*
74   // have been caught before we got here.
75 
76   if (fstat(fd, &info))
77   {
78     return 0;
79   }
80 
81 #ifdef HAVE_SYS_IOCTL_H
82   // Block devices, like /dev/hda, don't return a normal filesize.
83   // If we are working with a block device, we have to ask the operating
84   // system to tell us the true size of the device.
85   //
86   // This isn't the recommended way to do check for block devices,
87   // but using S_ISBLK(info.stmode) wasn't working.
88   if (info.st_mode & S_IFBLK)
89   {
90     // Get the block size
91     if (ioctl(fd, DKIOCGETBLOCKSIZE,&blocksize) < 0)
92     {
93       return 0;
94     }
95 
96     // Get the number of blocks
97     if (ioctl(fd, DKIOCGETBLOCKCOUNT, &blockcount) < 0)
98     {
99     }
100 
101     total = blocksize * blockcount;
102   }
103 #endif     // ifdef HAVE_IOCTL_H
104 
105   else
106   {
107     if ((fseeko(f,0,SEEK_END)))
108       return 0;
109     total = ftello(f);
110     if ((fseeko(f,original,SEEK_SET)))
111       return 0;
112   }
113 
114   return (total - original);
115 }
116 
117 
118 #else   // ifdef __APPLE__
119 
120 // This is code for general UNIX systems
121 // (e.g. NetBSD, FreeBSD, OpenBSD, etc)
122 
123 static off_t
midpoint(off_t a,off_t b,long blksize)124 midpoint (off_t a, off_t b, long blksize)
125 {
126   off_t aprime = a / blksize;
127   off_t bprime = b / blksize;
128   off_t c, cprime;
129 
130   cprime = (bprime - aprime) / 2 + aprime;
131   c = cprime * blksize;
132 
133   return c;
134 }
135 
136 
137 
find_dev_size(int fd,int blk_size)138 off_t find_dev_size(int fd, int blk_size)
139 {
140 
141   off_t curr = 0, amount = 0;
142   void *buf;
143 
144   if (blk_size == 0)
145     return 0;
146 
147   buf = malloc(blk_size);
148 
149   for (;;) {
150     ssize_t nread;
151 
152     lseek(fd, curr, SEEK_SET);
153     nread = read(fd, buf, blk_size);
154     if (nread < blk_size)
155     {
156       if (nread <= 0)
157 	{
158 	  if (curr == amount)
159 	  {
160 	    free(buf);
161 	    lseek(fd, 0, SEEK_SET);
162 	    return amount;
163 	  }
164 	  curr = midpoint(amount, curr, blk_size);
165 	}
166       else
167 	{ // 0 < nread < blk_size
168 	  free(buf);
169 	  lseek(fd, 0, SEEK_SET);
170 	  return amount + nread;
171 	}
172     }
173     else
174     {
175       amount = curr + blk_size;
176       curr = amount * 2;
177     }
178   }
179 
180   free(buf);
181   lseek(fd, 0, SEEK_SET);
182   return amount;
183 }
184 
185 
find_file_size(FILE * f)186 off_t find_file_size(FILE *f)
187 {
188   int fd = fileno(f);
189   struct stat sb;
190 
191   if (fstat(fd,&sb))
192     return 0;
193 
194   if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))
195     return sb.st_size;
196   else if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode))
197     return find_dev_size(fd,sb.st_blksize);
198 
199   return 0;
200 }
201 
202 #endif // ifdef __LINUX__
203 #endif // ifndef _WIN32
204 
205 #if defined(_WIN32)
find_file_size(FILE * f)206 off_t find_file_size(FILE *f)
207 {
208   off_t total = 0, original = ftello(f);
209 
210   // Windows does not support running fstat on block devices,
211   // so there's no point in mucking about with them.
212 
213   if ((fseeko(f,0,SEEK_END)))
214     return 0;
215 
216   total = ftello(f);
217   if ((fseeko(f,original,SEEK_SET)))
218     return 0;
219 
220   return total;
221 }
222 #endif // ifdef _WIN32
223