blob: e1f6ba6d2aaced7cc6e2dfdd0d557ad7ec010f55
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * getsize.c --- get the size of a partition. |
4 | * |
5 | * Copyright (C) 1995, 1995 Theodore Ts'o. |
6 | * |
7 | * %Begin-Header% |
8 | * This file may be redistributed under the terms of the |
9 | * GNU Lesser General Public License. |
10 | * %End-Header% |
11 | */ |
12 | |
13 | /* include this before sys/queues.h! */ |
14 | #include "blkidP.h" |
15 | |
16 | #include <stdio.h> |
17 | #include <unistd.h> |
18 | #ifdef HAVE_ERRNO_H |
19 | #include <errno.h> |
20 | #endif |
21 | #include <fcntl.h> |
22 | #ifdef HAVE_SYS_IOCTL_H |
23 | #include <sys/ioctl.h> |
24 | #endif |
25 | #ifdef HAVE_LINUX_FD_H |
26 | #include <linux/fd.h> |
27 | #endif |
28 | #ifdef HAVE_SYS_DISKLABEL_H |
29 | #include <sys/disklabel.h> |
30 | #include <sys/stat.h> |
31 | #endif |
32 | #ifdef HAVE_SYS_DISK_H |
33 | #ifdef HAVE_SYS_QUEUE_H |
34 | #include <sys/queue.h> /* for LIST_HEAD */ |
35 | #endif |
36 | #include <sys/disk.h> |
37 | #endif |
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 | #define BLKGETSIZE DKIOCGETBLOCKCOUNT32 |
52 | #endif /* APPLE_DARWIN */ |
53 | |
54 | static int valid_offset(int fd, blkid_loff_t offset) |
55 | { |
56 | char ch; |
57 | |
58 | if (blkid_llseek(fd, offset, 0) < 0) |
59 | return 0; |
60 | if (read(fd, &ch, 1) < 1) |
61 | return 0; |
62 | return 1; |
63 | } |
64 | |
65 | /* |
66 | * Returns the number of blocks in a partition |
67 | */ |
68 | blkid_loff_t blkid_get_dev_size(int fd) |
69 | { |
70 | int valid_blkgetsize64 = 1; |
71 | #ifdef __linux__ |
72 | struct utsname ut; |
73 | #endif |
74 | unsigned long long size64; |
75 | unsigned long size; |
76 | blkid_loff_t high, low; |
77 | #ifdef FDGETPRM |
78 | struct floppy_struct this_floppy; |
79 | #endif |
80 | #ifdef HAVE_SYS_DISKLABEL_H |
81 | int part = -1; |
82 | struct disklabel lab; |
83 | struct partition *pp; |
84 | char ch; |
85 | struct stat st; |
86 | #endif /* HAVE_SYS_DISKLABEL_H */ |
87 | |
88 | #ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */ |
89 | if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) { |
90 | if ((sizeof(blkid_loff_t) < sizeof(unsigned long long)) |
91 | && (size64 << 9 > 0xFFFFFFFF)) |
92 | return 0; /* EFBIG */ |
93 | return (blkid_loff_t) size64 << 9; |
94 | } |
95 | #endif |
96 | |
97 | #ifdef BLKGETSIZE64 |
98 | #ifdef __linux__ |
99 | uname(&ut); |
100 | if ((ut.release[0] == '2') && (ut.release[1] == '.') && |
101 | (ut.release[2] < '6') && (ut.release[3] == '.')) |
102 | valid_blkgetsize64 = 0; |
103 | #endif |
104 | if (valid_blkgetsize64 && |
105 | ioctl(fd, BLKGETSIZE64, &size64) >= 0) { |
106 | if ((sizeof(blkid_loff_t) < sizeof(unsigned long long)) |
107 | && ((size64) > 0xFFFFFFFF)) |
108 | return 0; /* EFBIG */ |
109 | return size64; |
110 | } |
111 | #endif |
112 | |
113 | #ifdef BLKGETSIZE |
114 | if (ioctl(fd, BLKGETSIZE, &size) >= 0) |
115 | return (blkid_loff_t)size << 9; |
116 | #endif |
117 | |
118 | #ifdef FDGETPRM |
119 | if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) |
120 | return (blkid_loff_t)this_floppy.size << 9; |
121 | #endif |
122 | #ifdef HAVE_SYS_DISKLABEL_H |
123 | #if 0 |
124 | /* |
125 | * This should work in theory but I haven't tested it. Anyone |
126 | * on a BSD system want to test this for me? In the meantime, |
127 | * binary search mechanism should work just fine. |
128 | */ |
129 | if ((fstat(fd, &st) >= 0) && S_ISBLK(st.st_mode)) |
130 | part = st.st_rdev & 7; |
131 | if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { |
132 | pp = &lab.d_partitions[part]; |
133 | if (pp->p_size) |
134 | return pp->p_size << 9; |
135 | } |
136 | #endif |
137 | #endif /* HAVE_SYS_DISKLABEL_H */ |
138 | |
139 | /* |
140 | * OK, we couldn't figure it out by using a specialized ioctl, |
141 | * which is generally the best way. So do binary search to |
142 | * find the size of the partition. |
143 | */ |
144 | low = 0; |
145 | for (high = 1024; valid_offset(fd, high); high *= 2) |
146 | low = high; |
147 | while (low < high - 1) |
148 | { |
149 | const blkid_loff_t mid = (low + high) / 2; |
150 | |
151 | if (valid_offset(fd, mid)) |
152 | low = mid; |
153 | else |
154 | high = mid; |
155 | } |
156 | return low + 1; |
157 | } |
158 | |
159 | #ifdef TEST_PROGRAM |
160 | int main(int argc, char **argv) |
161 | { |
162 | blkid_loff_t bytes; |
163 | int fd; |
164 | |
165 | if (argc < 2) { |
166 | fprintf(stderr, "Usage: %s device\n" |
167 | "Determine the size of a device\n", argv[0]); |
168 | return 1; |
169 | } |
170 | |
171 | if ((fd = open(argv[1], O_RDONLY)) < 0) |
172 | perror(argv[0]); |
173 | |
174 | bytes = blkid_get_dev_size(fd); |
175 | printf("Device %s has %lld 1k blocks.\n", argv[1], bytes >> 10); |
176 | |
177 | return 0; |
178 | } |
179 | #endif |
180 |