Whamcloud - gitweb
GNU/KFreeBSD portability fixes. (Addresses Debian Bug #239934)
[tools/e2fsprogs.git] / lib / ext2fs / getsize.c
1 /*
2  * getsize.c --- get the size of a partition.
3  * 
4  * Copyright (C) 1995, 1995 Theodore Ts'o.
5  * Copyright (C) 2003 VMware, Inc.
6  *
7  * Windows version of ext2fs_get_device_size by Chris Li, VMware.
8  * 
9  * %Begin-Header%
10  * This file may be redistributed under the terms of the GNU Public
11  * License.
12  * %End-Header%
13  */
14
15 #define _LARGEFILE_SOURCE
16 #define _LARGEFILE64_SOURCE
17
18 #include <stdio.h>
19 #if HAVE_UNISTD_H
20 #include <unistd.h>
21 #endif
22 #if HAVE_ERRNO_H
23 #include <errno.h>
24 #endif
25 #include <fcntl.h>
26 #ifdef HAVE_LINUX_FD_H
27 #include <sys/ioctl.h>
28 #include <linux/fd.h>
29 #endif
30 #ifdef HAVE_SYS_DISKLABEL_H
31 #include <sys/ioctl.h>
32 #include <sys/disklabel.h>
33 #endif /* HAVE_SYS_DISKLABEL_H */
34 #ifdef HAVE_SYS_DISK_H
35 #include <sys/queue.h> /* for LIST_HEAD */
36 #include <sys/disk.h>
37 #endif /* HAVE_SYS_DISK_H */
38 #ifdef __linux__
39 #include <sys/utsname.h>
40 #endif
41
42 #if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE)
43 #define BLKGETSIZE _IO(0x12,96) /* return device size */
44 #endif
45
46 #if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64)
47 #define BLKGETSIZE64 _IOR(0x12,114,size_t)      /* return device size in bytes (u64 *arg) */
48 #endif
49
50 #ifdef APPLE_DARWIN
51 #include <sys/ioctl.h>
52 #include <sys/disk.h>
53
54 #define BLKGETSIZE DKIOCGETBLOCKCOUNT32
55 #endif /* APPLE_DARWIN */
56
57 #include "ext2_fs.h"
58 #include "ext2fs.h"
59
60 #if defined(__CYGWIN__) || defined (WIN32)
61 #include "windows.h"
62 #include "winioctl.h"
63
64 errcode_t ext2fs_get_device_size(const char *file, int blocksize,
65                                  blk_t *retblocks)
66 {
67         HANDLE dev;
68         PARTITION_INFORMATION pi;
69         DISK_GEOMETRY gi;
70         DWORD retbytes;
71         LARGE_INTEGER filesize;
72
73         dev = CreateFile(file, GENERIC_READ, 
74                          FILE_SHARE_READ | FILE_SHARE_WRITE ,
75                          NULL,  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,  NULL); 
76  
77         if (dev == INVALID_HANDLE_VALUE)
78                 return EBADF;
79         if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO,
80                             &pi, sizeof(PARTITION_INFORMATION),
81                             &pi, sizeof(PARTITION_INFORMATION),
82                             &retbytes, NULL)) {
83
84                 *retblocks = pi.PartitionLength.QuadPart / blocksize;
85         
86         } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY,
87                                 &gi, sizeof(DISK_GEOMETRY),
88                                 &gi, sizeof(DISK_GEOMETRY),
89                                 &retbytes, NULL)) {
90
91                 *retblocks = gi.BytesPerSector *
92                              gi.SectorsPerTrack *
93                              gi.TracksPerCylinder *
94                              gi.Cylinders.QuadPart / blocksize;
95
96         } else if (GetFileSizeEx(dev, &filesize)) {
97                 *retblocks = filesize.QuadPart / blocksize;
98         }
99
100         CloseHandle(dev);
101         return 0;
102 }
103
104 #else
105
106 static int valid_offset (int fd, ext2_loff_t offset)
107 {
108         char ch;
109
110         if (ext2fs_llseek (fd, offset, 0) < 0)
111                 return 0;
112         if (read (fd, &ch, 1) < 1)
113                 return 0;
114         return 1;
115 }
116
117 /*
118  * Returns the number of blocks in a partition
119  */
120 errcode_t ext2fs_get_device_size(const char *file, int blocksize,
121                                  blk_t *retblocks)
122 {
123         int     fd;
124         int valid_blkgetsize64 = 1;
125 #ifdef __linux__
126         struct          utsname ut;
127 #endif
128         unsigned long long size64;
129         unsigned long   size;
130         ext2_loff_t high, low;
131 #ifdef FDGETPRM
132         struct floppy_struct this_floppy;
133 #endif
134 #ifdef HAVE_SYS_DISKLABEL_H
135         int part;
136         struct disklabel lab;
137         struct partition *pp;
138         char ch;
139 #endif /* HAVE_SYS_DISKLABEL_H */
140
141 #ifdef HAVE_OPEN64
142         fd = open64(file, O_RDONLY);
143 #else
144         fd = open(file, O_RDONLY);
145 #endif
146         if (fd < 0)
147                 return errno;
148
149 #ifdef DKIOCGETBLOCKCOUNT       /* For Apple Darwin */
150         if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) {
151                 if ((sizeof(*retblocks) < sizeof(unsigned long long))
152                     && ((size64 / (blocksize / 512)) > 0xFFFFFFFF))
153                         return EFBIG;
154                 close(fd);
155                 *retblocks = size64 / (blocksize / 512);
156                 return 0;
157         }
158 #endif
159
160 #ifdef BLKGETSIZE64
161 #ifdef __linux__
162         if ((uname(&ut) == 0) &&
163             ((ut.release[0] == '2') && (ut.release[1] == '.') &&
164              (ut.release[2] < '6') && (ut.release[3] == '.')))
165                 valid_blkgetsize64 = 0;
166 #endif
167         if (valid_blkgetsize64 &&
168             ioctl(fd, BLKGETSIZE64, &size64) >= 0) {
169                 if ((sizeof(*retblocks) < sizeof(unsigned long long))
170                     && ((size64 / blocksize) > 0xFFFFFFFF))
171                         return EFBIG;
172                 close(fd);
173                 *retblocks = size64 / blocksize;
174                 return 0;
175         }
176 #endif
177
178 #ifdef BLKGETSIZE
179         if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
180                 close(fd);
181                 *retblocks = size / (blocksize / 512);
182                 return 0;
183         }
184 #endif
185
186 #ifdef FDGETPRM
187         if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
188                 close(fd);
189                 *retblocks = this_floppy.size / (blocksize / 512);
190                 return 0;
191         }
192 #endif
193
194 #ifdef HAVE_SYS_DISKLABEL_H
195 #if defined(DIOCGMEDIASIZE)
196         {
197             off_t ms;
198             u_int bs;
199             if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) {
200                 *retblocks = ms / blocksize;
201                 return 0;
202             }
203         }
204 #elif defined(DIOCGDINFO)
205         /* old disklabel interface */
206         part = strlen(file) - 1;
207         if (part >= 0) {
208                 ch = file[part];
209                 if (isdigit(ch))
210                         part = 0;
211                 else if (ch >= 'a' && ch <= 'h')
212                         part = ch - 'a';
213                 else
214                         part = -1;
215         }
216         if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
217                 pp = &lab.d_partitions[part];
218                 if (pp->p_size) {
219                         close(fd);
220                         *retblocks = pp->p_size / (blocksize / 512);
221                         return 0;
222                 }
223         }
224 #endif /* defined(DIOCG*) */
225 #endif /* HAVE_SYS_DISKLABEL_H */
226
227         /*
228          * OK, we couldn't figure it out by using a specialized ioctl,
229          * which is generally the best way.  So do binary search to
230          * find the size of the partition.
231          */
232         low = 0;
233         for (high = 1024; valid_offset (fd, high); high *= 2)
234                 low = high;
235         while (low < high - 1)
236         {
237                 const ext2_loff_t mid = (low + high) / 2;
238
239                 if (valid_offset (fd, mid))
240                         low = mid;
241                 else
242                         high = mid;
243         }
244         valid_offset (fd, 0);
245         close(fd);
246         *retblocks = (low + 1) / blocksize;
247         return 0;
248 }
249
250 #endif /* WIN32 */
251
252 #ifdef DEBUG
253 int main(int argc, char **argv)
254 {
255         blk_t   blocks;
256         int     retval;
257         
258         if (argc < 2) {
259                 fprintf(stderr, "Usage: %s device\n", argv[0]);
260                 exit(1);
261         }
262
263         retval = ext2fs_get_device_size(argv[1], 1024, &blocks);
264         if (retval) {
265                 com_err(argv[0], retval,
266                         "while calling ext2fs_get_device_size");
267                 exit(1);
268         }
269         printf("Device %s has %d 1k blocks.\n", argv[1], blocks);
270         exit(0);
271 }
272 #endif