blob: 514082e34f907498fe6fb1be4c301311af2d775a [file] [log] [blame]
bigbiff bigbiffe60683a2013-02-22 20:55:50 -05001/*
2 * No copyright is claimed. This code is in the public domain; do with
3 * it what you wish.
4 *
5 * Written by Karel Zak <kzak@redhat.com>
6 */
7#include <sys/types.h>
8#include <sys/stat.h>
9#include <sys/ioctl.h>
10#include <unistd.h>
11#include <stdint.h>
12
13#ifdef HAVE_LINUX_FD_H
14#include <linux/fd.h>
15#endif
16
17#ifdef HAVE_SYS_DISKLABEL_H
18#include <sys/disklabel.h>
19#endif
20
21#ifdef HAVE_SYS_DISK_H
22#ifdef HAVE_SYS_QUEUE_H
23#include <sys/queue.h> /* for LIST_HEAD */
24#endif
25#include <sys/disk.h>
26#endif
27
28#include "blkdev.h"
29#include "c.h"
30#include "linux_version.h"
31#include "xalloc.h"
32
33static long
34blkdev_valid_offset (int fd, off_t offset) {
35 char ch;
36
37 if (lseek (fd, offset, 0) < 0)
38 return 0;
39 if (read (fd, &ch, 1) < 1)
40 return 0;
41 return 1;
42}
43
44int is_blkdev(int fd)
45{
46 struct stat st;
47 return (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode));
48}
49
50off_t
51blkdev_find_size (int fd) {
52 uintmax_t high, low = 0;
53
54 for (high = 1024; blkdev_valid_offset (fd, high); ) {
55 if (high == UINTMAX_MAX)
56 return -1;
57
58 low = high;
59
60 if (high >= UINTMAX_MAX/2)
61 high = UINTMAX_MAX;
62 else
63 high *= 2;
64 }
65
66 while (low < high - 1)
67 {
68 uintmax_t mid = (low + high) / 2;
69
70 if (blkdev_valid_offset (fd, mid))
71 low = mid;
72 else
73 high = mid;
74 }
75 blkdev_valid_offset (fd, 0);
76 return (low + 1);
77}
78
79/* get size in bytes */
80int
81blkdev_get_size(int fd, unsigned long long *bytes)
82{
83#ifdef DKIOCGETBLOCKCOUNT
84 /* Apple Darwin */
85 if (ioctl(fd, DKIOCGETBLOCKCOUNT, bytes) >= 0) {
86 *bytes <<= 9;
87 return 0;
88 }
89#endif
90
91#ifdef BLKGETSIZE64
92 {
93#ifdef __linux__
94 int ver = get_linux_version();
95
96 /* kernels 2.4.15-2.4.17, had a broken BLKGETSIZE64 */
97 if (ver >= KERNEL_VERSION (2,6,0) ||
98 (ver >= KERNEL_VERSION (2,4,18) && ver < KERNEL_VERSION (2,5,0)))
99#endif
100 if (ioctl(fd, BLKGETSIZE64, bytes) >= 0)
101 return 0;
102 }
103#endif /* BLKGETSIZE64 */
104
105#ifdef BLKGETSIZE
106 {
107 unsigned long size;
108
109 if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
110 *bytes = ((unsigned long long)size << 9);
111 return 0;
112 }
113 }
114
115#endif /* BLKGETSIZE */
116
117#ifdef DIOCGMEDIASIZE
118 /* FreeBSD */
119 if (ioctl(fd, DIOCGMEDIASIZE, bytes) >= 0)
120 return 0;
121#endif
122
123#ifdef FDGETPRM
124 {
125 struct floppy_struct this_floppy;
126
127 if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
128 *bytes = this_floppy.size << 9;
129 return 0;
130 }
131 }
132#endif /* FDGETPRM */
133
134#ifdef HAVE_SYS_DISKLABEL_H
135 {
136 /*
137 * This code works for FreeBSD 4.11 i386, except for the full device
138 * (such as /dev/ad0). It doesn't work properly for newer FreeBSD
139 * though. FreeBSD >= 5.0 should be covered by the DIOCGMEDIASIZE
140 * above however.
141 *
142 * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw,
143 * character) devices, so we need to check for S_ISCHR, too.
144 */
145 int part = -1;
146 struct disklabel lab;
147 struct partition *pp;
148 char ch;
149 struct stat st;
150
151 if ((fstat(fd, &st) >= 0) &&
152 (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)))
153 part = st.st_rdev & 7;
154
155 if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
156 pp = &lab.d_partitions[part];
157 if (pp->p_size) {
158 *bytes = pp->p_size << 9;
159 return 0;
160 }
161 }
162 }
163#endif /* HAVE_SYS_DISKLABEL_H */
164
165 {
166 struct stat st;
167
168 if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
169 *bytes = st.st_size;
170 return 0;
171 }
172 if (!S_ISBLK(st.st_mode))
173 return -1;
174 }
175
176 *bytes = blkdev_find_size(fd);
177 return 0;
178}
179
180/* get 512-byte sector count */
181int
182blkdev_get_sectors(int fd, unsigned long long *sectors)
183{
184 unsigned long long bytes;
185
186 if (blkdev_get_size(fd, &bytes) == 0) {
187 *sectors = (bytes >> 9);
188 return 0;
189 }
190
191 return -1;
192}
193
194/*
195 * Get logical sector size.
196 *
197 * This is the smallest unit the storage device can
198 * address. It is typically 512 bytes.
199 */
200int blkdev_get_sector_size(int fd, int *sector_size)
201{
202#ifdef BLKSSZGET
203 if (ioctl(fd, BLKSSZGET, sector_size) >= 0)
204 return 0;
205 return -1;
206#else
207 *sector_size = DEFAULT_SECTOR_SIZE;
208 return 0;
209#endif
210}
211
212/*
213 * Get physical block device size. The BLKPBSZGET is supported since Linux
214 * 2.6.32. For old kernels is probably the best to assume that physical sector
215 * size is the same as logical sector size.
216 *
217 * Example:
218 *
219 * rc = blkdev_get_physector_size(fd, &physec);
220 * if (rc || physec == 0) {
221 * rc = blkdev_get_sector_size(fd, &physec);
222 * if (rc)
223 * physec = DEFAULT_SECTOR_SIZE;
224 * }
225 */
226int blkdev_get_physector_size(int fd, int *sector_size)
227{
228#ifdef BLKPBSZGET
229 if (ioctl(fd, BLKPBSZGET, &sector_size) >= 0)
230 return 0;
231 return -1;
232#else
233 *sector_size = DEFAULT_SECTOR_SIZE;
234 return 0;
235#endif
236}
237
238/*
239 * Return the alignment status of a device
240 */
241int blkdev_is_misaligned(int fd)
242{
243#ifdef BLKALIGNOFF
244 int aligned;
245
246 if (ioctl(fd, BLKALIGNOFF, &aligned) < 0)
247 return 0; /* probably kernel < 2.6.32 */
248 /*
249 * Note that kernel returns -1 as alignement offset if no compatible
250 * sizes and alignments exist for stacked devices
251 */
252 return aligned != 0 ? 1 : 0;
253#else
254 return 0;
255#endif
256}
257
258int blkdev_is_cdrom(int fd)
259{
260#ifdef CDROM_GET_CAPABILITY
261 int ret;
262
263 if ((ret = ioctl(fd, CDROM_GET_CAPABILITY, NULL)) < 0)
264 return 0;
265 else
266 return ret;
267#else
268 return 0;
269#endif
270}
271
272/*
273 * Get kernel's interpretation of the device's geometry.
274 *
275 * Returns the heads and sectors - but not cylinders
276 * as it's truncated for disks with more than 65535 tracks.
277 *
278 * Note that this is deprecated in favor of LBA addressing.
279 */
280int blkdev_get_geometry(int fd, unsigned int *h, unsigned int *s)
281{
282#ifdef HDIO_GETGEO
283 struct hd_geometry geometry;
284
285 if (ioctl(fd, HDIO_GETGEO, &geometry) == 0) {
286 *h = geometry.heads;
287 *s = geometry.sectors;
288 return 0;
289 }
290#else
291 *h = 0;
292 *s = 0;
293#endif
294 return -1;
295}
296
297/*
298 * Convert scsi type to human readable string.
299 */
300const char *blkdev_scsi_type_to_name(int type)
301{
302 switch (type) {
303 case SCSI_TYPE_DISK:
304 return "disk";
305 case SCSI_TYPE_TAPE:
306 return "tape";
307 case SCSI_TYPE_PRINTER:
308 return "printer";
309 case SCSI_TYPE_PROCESSOR:
310 return "processor";
311 case SCSI_TYPE_WORM:
312 return "worm";
313 case SCSI_TYPE_ROM:
314 return "rom";
315 case SCSI_TYPE_SCANNER:
316 return "scanner";
317 case SCSI_TYPE_MOD:
318 return "mo-disk";
319 case SCSI_TYPE_MEDIUM_CHANGER:
320 return "changer";
321 case SCSI_TYPE_COMM:
322 return "comm";
323 case SCSI_TYPE_RAID:
324 return "raid";
325 case SCSI_TYPE_ENCLOSURE:
326 return "enclosure";
327 case SCSI_TYPE_RBC:
328 return "rbc";
329 case SCSI_TYPE_OSD:
330 return "osd";
331 case SCSI_TYPE_NO_LUN:
332 return "no-lun";
333 default:
334 break;
335 }
336 return NULL;
337}
338
339#ifdef TEST_PROGRAM
340#include <stdio.h>
341#include <stdlib.h>
342#include <fcntl.h>
343int
344main(int argc, char **argv)
345{
346 unsigned long long bytes;
347 unsigned long long sectors;
348 int sector_size, phy_sector_size;
349 int fd;
350
351 if (argc != 2) {
352 fprintf(stderr, "usage: %s device\n", argv[0]);
353 exit(EXIT_FAILURE);
354 }
355
356 if ((fd = open(argv[1], O_RDONLY)) < 0)
357 err(EXIT_FAILURE, "open %s failed", argv[1]);
358
359 if (blkdev_get_size(fd, &bytes) < 0)
360 err(EXIT_FAILURE, "blkdev_get_size() failed");
361 if (blkdev_get_sectors(fd, &sectors) < 0)
362 err(EXIT_FAILURE, "blkdev_get_sectors() failed");
363 if (blkdev_get_sector_size(fd, &sector_size) < 0)
364 err(EXIT_FAILURE, "blkdev_get_sector_size() failed");
365 if (blkdev_get_physector_size(fd, &phy_sector_size) < 0)
366 err(EXIT_FAILURE, "blkdev_get_physector_size() failed");
367
368 printf(" bytes: %llu\n", bytes);
369 printf(" sectors: %llu\n", sectors);
370 printf(" sector size: %d\n", sector_size);
371 printf("phy-sector size: %d\n", phy_sector_size);
372
373 return EXIT_SUCCESS;
374}
375#endif /* TEST_PROGRAM */