2 * No copyright is claimed. This code is in the public domain; do with
5 * Written by Karel Zak <kzak@redhat.com>
13 #ifdef HAVE_LINUX_FD_H
17 #ifdef HAVE_SYS_DISKLABEL_H
18 #include <sys/disklabel.h>
21 #ifdef HAVE_SYS_DISK_H
22 # include <sys/disk.h>
26 # define EBADFD 77 /* File descriptor in bad state */
31 #include "linux_version.h"
32 #include "fileutils.h"
36 blkdev_valid_offset (int fd
, off_t offset
) {
39 if (lseek (fd
, offset
, 0) < 0)
41 if (read (fd
, &ch
, 1) < 1)
49 return (fstat(fd
, &st
) == 0 && S_ISBLK(st
.st_mode
));
53 blkdev_find_size (int fd
) {
54 uintmax_t high
, low
= 0;
56 for (high
= 1024; blkdev_valid_offset (fd
, high
); ) {
57 if (high
== UINTMAX_MAX
)
62 if (high
>= UINTMAX_MAX
/2)
68 while (low
< high
- 1)
70 uintmax_t mid
= (low
+ high
) / 2;
72 if (blkdev_valid_offset (fd
, mid
))
77 blkdev_valid_offset (fd
, 0);
81 /* get size in bytes */
83 blkdev_get_size(int fd
, unsigned long long *bytes
)
85 #ifdef DKIOCGETBLOCKCOUNT
87 if (ioctl(fd
, DKIOCGETBLOCKCOUNT
, bytes
) >= 0) {
96 int ver
= get_linux_version();
98 /* kernels 2.4.15-2.4.17, had a broken BLKGETSIZE64 */
99 if (ver
>= KERNEL_VERSION (2,6,0) ||
100 (ver
>= KERNEL_VERSION (2,4,18) && ver
< KERNEL_VERSION (2,5,0)))
102 if (ioctl(fd
, BLKGETSIZE64
, bytes
) >= 0)
105 #endif /* BLKGETSIZE64 */
111 if (ioctl(fd
, BLKGETSIZE
, &size
) >= 0) {
112 *bytes
= ((unsigned long long)size
<< 9);
117 #endif /* BLKGETSIZE */
119 #ifdef DIOCGMEDIASIZE
121 if (ioctl(fd
, DIOCGMEDIASIZE
, bytes
) >= 0)
127 struct floppy_struct this_floppy
;
129 if (ioctl(fd
, FDGETPRM
, &this_floppy
) >= 0) {
130 *bytes
= ((unsigned long long) this_floppy
.size
) << 9;
134 #endif /* FDGETPRM */
136 #ifdef HAVE_SYS_DISKLABEL_H
139 * This code works for FreeBSD 4.11 i386, except for the full device
140 * (such as /dev/ad0). It doesn't work properly for newer FreeBSD
141 * though. FreeBSD >= 5.0 should be covered by the DIOCGMEDIASIZE
144 * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw,
145 * character) devices, so we need to check for S_ISCHR, too.
148 struct disklabel lab
;
149 struct partition
*pp
;
152 if ((fstat(fd
, &st
) >= 0) &&
153 (S_ISBLK(st
.st_mode
) || S_ISCHR(st
.st_mode
)))
154 part
= st
.st_rdev
& 7;
156 if (part
>= 0 && (ioctl(fd
, DIOCGDINFO
, (char *)&lab
) >= 0)) {
157 pp
= &lab
.d_partitions
[part
];
159 *bytes
= pp
->p_size
<< 9;
164 #endif /* HAVE_SYS_DISKLABEL_H */
169 if (fstat(fd
, &st
) == 0 && S_ISREG(st
.st_mode
)) {
173 if (!S_ISBLK(st
.st_mode
))
177 *bytes
= blkdev_find_size(fd
);
181 /* get 512-byte sector count */
183 blkdev_get_sectors(int fd
, unsigned long long *sectors
)
185 unsigned long long bytes
;
187 if (blkdev_get_size(fd
, &bytes
) == 0) {
188 *sectors
= (bytes
>> 9);
196 * Get logical sector size.
198 * This is the smallest unit the storage device can
199 * address. It is typically 512 bytes.
202 int blkdev_get_sector_size(int fd
, int *sector_size
)
204 if (ioctl(fd
, BLKSSZGET
, sector_size
) >= 0)
209 int blkdev_get_sector_size(int fd
__attribute__((__unused__
)), int *sector_size
)
211 *sector_size
= DEFAULT_SECTOR_SIZE
;
217 * Get physical block device size. The BLKPBSZGET is supported since Linux
218 * 2.6.32. For old kernels is probably the best to assume that physical sector
219 * size is the same as logical sector size.
223 * rc = blkdev_get_physector_size(fd, &physec);
224 * if (rc || physec == 0) {
225 * rc = blkdev_get_sector_size(fd, &physec);
227 * physec = DEFAULT_SECTOR_SIZE;
231 int blkdev_get_physector_size(int fd
, int *sector_size
)
233 if (ioctl(fd
, BLKPBSZGET
, §or_size
) >= 0)
238 int blkdev_get_physector_size(int fd
__attribute__((__unused__
)), int *sector_size
)
240 *sector_size
= DEFAULT_SECTOR_SIZE
;
246 * Return the alignment status of a device
249 int blkdev_is_misaligned(int fd
)
253 if (ioctl(fd
, BLKALIGNOFF
, &aligned
) < 0)
254 return 0; /* probably kernel < 2.6.32 */
256 * Note that kernel returns -1 as alignement offset if no compatible
257 * sizes and alignments exist for stacked devices
259 return aligned
!= 0 ? 1 : 0;
262 int blkdev_is_misaligned(int fd
__attribute__((__unused__
)))
268 int open_blkdev_or_file(const struct stat
*st
, const char *name
, const int oflag
)
272 if (S_ISBLK(st
->st_mode
)) {
273 fd
= open(name
, oflag
| O_EXCL
);
275 fd
= open(name
, oflag
);
276 if (-1 < fd
&& !is_same_inode(fd
, st
)) {
281 if (-1 < fd
&& S_ISBLK(st
->st_mode
) && blkdev_is_misaligned(fd
))
282 warnx(_("warning: %s is misaligned"), name
);
286 #ifdef CDROM_GET_CAPABILITY
287 int blkdev_is_cdrom(int fd
)
291 if ((ret
= ioctl(fd
, CDROM_GET_CAPABILITY
, NULL
)) < 0)
297 int blkdev_is_cdrom(int fd
__attribute__((__unused__
)))
304 * Get kernel's interpretation of the device's geometry.
306 * Returns the heads and sectors - but not cylinders
307 * as it's truncated for disks with more than 65535 tracks.
309 * Note that this is deprecated in favor of LBA addressing.
312 int blkdev_get_geometry(int fd
, unsigned int *h
, unsigned int *s
)
314 struct hd_geometry geometry
;
316 if (ioctl(fd
, HDIO_GETGEO
, &geometry
) == 0) {
318 *s
= geometry
.sectors
;
322 int blkdev_get_geometry(int fd
__attribute__((__unused__
)),
323 unsigned int *h
, unsigned int *s
)
332 * Convert scsi type to human readable string.
334 const char *blkdev_scsi_type_to_name(int type
)
341 case SCSI_TYPE_PRINTER
:
343 case SCSI_TYPE_PROCESSOR
:
349 case SCSI_TYPE_SCANNER
:
353 case SCSI_TYPE_MEDIUM_CHANGER
:
359 case SCSI_TYPE_ENCLOSURE
:
365 case SCSI_TYPE_NO_LUN
:
373 #ifdef TEST_PROGRAM_BLKDEV
378 main(int argc
, char **argv
)
380 unsigned long long bytes
;
381 unsigned long long sectors
;
382 int sector_size
, phy_sector_size
;
386 fprintf(stderr
, "usage: %s device\n", argv
[0]);
390 if ((fd
= open(argv
[1], O_RDONLY
|O_CLOEXEC
)) < 0)
391 err(EXIT_FAILURE
, "open %s failed", argv
[1]);
393 if (blkdev_get_size(fd
, &bytes
) < 0)
394 err(EXIT_FAILURE
, "blkdev_get_size() failed");
395 if (blkdev_get_sectors(fd
, §ors
) < 0)
396 err(EXIT_FAILURE
, "blkdev_get_sectors() failed");
397 if (blkdev_get_sector_size(fd
, §or_size
) < 0)
398 err(EXIT_FAILURE
, "blkdev_get_sector_size() failed");
399 if (blkdev_get_physector_size(fd
, &phy_sector_size
) < 0)
400 err(EXIT_FAILURE
, "blkdev_get_physector_size() failed");
402 printf(" bytes: %llu\n", bytes
);
403 printf(" sectors: %llu\n", sectors
);
404 printf(" sector size: %d\n", sector_size
);
405 printf("phy-sector size: %d\n", phy_sector_size
);
409 #endif /* TEST_PROGRAM */