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) {
94 if (ioctl(fd
, BLKGETSIZE64
, bytes
) >= 0)
102 if (ioctl(fd
, BLKGETSIZE
, &size
) >= 0) {
103 *bytes
= ((unsigned long long)size
<< 9);
108 #endif /* BLKGETSIZE */
110 #ifdef DIOCGMEDIASIZE
112 if (ioctl(fd
, DIOCGMEDIASIZE
, bytes
) >= 0)
118 struct floppy_struct this_floppy
;
120 if (ioctl(fd
, FDGETPRM
, &this_floppy
) >= 0) {
121 *bytes
= ((unsigned long long) this_floppy
.size
) << 9;
125 #endif /* FDGETPRM */
127 #if defined(HAVE_SYS_DISKLABEL_H) && defined(DIOCGDINFO)
130 * This code works for FreeBSD 4.11 i386, except for the full device
131 * (such as /dev/ad0). It doesn't work properly for newer FreeBSD
132 * though. FreeBSD >= 5.0 should be covered by the DIOCGMEDIASIZE
135 * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw,
136 * character) devices, so we need to check for S_ISCHR, too.
139 struct disklabel lab
;
140 struct partition
*pp
;
143 if ((fstat(fd
, &st
) >= 0) &&
144 (S_ISBLK(st
.st_mode
) || S_ISCHR(st
.st_mode
)))
145 part
= st
.st_rdev
& 7;
147 if (part
>= 0 && (ioctl(fd
, DIOCGDINFO
, (char *)&lab
) >= 0)) {
148 pp
= &lab
.d_partitions
[part
];
150 *bytes
= pp
->p_size
<< 9;
155 #endif /* defined(HAVE_SYS_DISKLABEL_H) && defined(DIOCGDINFO) */
160 if (fstat(fd
, &st
) == 0 && S_ISREG(st
.st_mode
)) {
164 if (!S_ISBLK(st
.st_mode
))
168 *bytes
= blkdev_find_size(fd
);
172 /* get 512-byte sector count */
174 blkdev_get_sectors(int fd
, unsigned long long *sectors
)
176 unsigned long long bytes
;
178 if (blkdev_get_size(fd
, &bytes
) == 0) {
179 *sectors
= (bytes
>> 9);
187 * Get logical sector size.
189 * This is the smallest unit the storage device can
190 * address. It is typically 512 bytes.
193 int blkdev_get_sector_size(int fd
, int *sector_size
)
195 if (ioctl(fd
, BLKSSZGET
, sector_size
) >= 0)
200 int blkdev_get_sector_size(int fd
__attribute__((__unused__
)), int *sector_size
)
202 *sector_size
= DEFAULT_SECTOR_SIZE
;
208 * Get physical block device size. The BLKPBSZGET is supported since Linux
209 * 2.6.32. For old kernels is probably the best to assume that physical sector
210 * size is the same as logical sector size.
214 * rc = blkdev_get_physector_size(fd, &physec);
215 * if (rc || physec == 0) {
216 * rc = blkdev_get_sector_size(fd, &physec);
218 * physec = DEFAULT_SECTOR_SIZE;
222 int blkdev_get_physector_size(int fd
, int *sector_size
)
224 if (ioctl(fd
, BLKPBSZGET
, §or_size
) >= 0)
229 int blkdev_get_physector_size(int fd
__attribute__((__unused__
)), int *sector_size
)
231 *sector_size
= DEFAULT_SECTOR_SIZE
;
237 * Return the alignment status of a device
240 int blkdev_is_misaligned(int fd
)
244 if (ioctl(fd
, BLKALIGNOFF
, &aligned
) < 0)
245 return 0; /* probably kernel < 2.6.32 */
247 * Note that kernel returns -1 as alignment offset if no compatible
248 * sizes and alignments exist for stacked devices
250 return aligned
!= 0 ? 1 : 0;
253 int blkdev_is_misaligned(int fd
__attribute__((__unused__
)))
259 int open_blkdev_or_file(const struct stat
*st
, const char *name
, const int oflag
)
263 if (S_ISBLK(st
->st_mode
)) {
264 fd
= open(name
, oflag
| O_EXCL
);
266 fd
= open(name
, oflag
);
267 if (-1 < fd
&& !is_same_inode(fd
, st
)) {
272 if (-1 < fd
&& S_ISBLK(st
->st_mode
) && blkdev_is_misaligned(fd
))
273 warnx(_("warning: %s is misaligned"), name
);
277 #ifdef CDROM_GET_CAPABILITY
278 int blkdev_is_cdrom(int fd
)
282 if ((ret
= ioctl(fd
, CDROM_GET_CAPABILITY
, NULL
)) < 0)
288 int blkdev_is_cdrom(int fd
__attribute__((__unused__
)))
295 * Get kernel's interpretation of the device's geometry.
297 * Returns the heads and sectors - but not cylinders
298 * as it's truncated for disks with more than 65535 tracks.
300 * Note that this is deprecated in favor of LBA addressing.
303 int blkdev_get_geometry(int fd
, unsigned int *h
, unsigned int *s
)
305 struct hd_geometry geometry
;
307 if (ioctl(fd
, HDIO_GETGEO
, &geometry
) == 0) {
309 *s
= geometry
.sectors
;
313 int blkdev_get_geometry(int fd
__attribute__((__unused__
)),
314 unsigned int *h
, unsigned int *s
)
323 * Convert scsi type to human readable string.
325 const char *blkdev_scsi_type_to_name(int type
)
332 case SCSI_TYPE_PRINTER
:
334 case SCSI_TYPE_PROCESSOR
:
340 case SCSI_TYPE_SCANNER
:
344 case SCSI_TYPE_MEDIUM_CHANGER
:
350 case SCSI_TYPE_ENCLOSURE
:
356 case SCSI_TYPE_NO_LUN
:
364 #ifdef TEST_PROGRAM_BLKDEV
369 main(int argc
, char **argv
)
371 unsigned long long bytes
;
372 unsigned long long sectors
;
373 int sector_size
, phy_sector_size
;
377 fprintf(stderr
, "usage: %s device\n", argv
[0]);
381 if ((fd
= open(argv
[1], O_RDONLY
|O_CLOEXEC
)) < 0)
382 err(EXIT_FAILURE
, "open %s failed", argv
[1]);
384 if (blkdev_get_size(fd
, &bytes
) < 0)
385 err(EXIT_FAILURE
, "blkdev_get_size() failed");
386 if (blkdev_get_sectors(fd
, §ors
) < 0)
387 err(EXIT_FAILURE
, "blkdev_get_sectors() failed");
388 if (blkdev_get_sector_size(fd
, §or_size
) < 0)
389 err(EXIT_FAILURE
, "blkdev_get_sector_size() failed");
390 if (blkdev_get_physector_size(fd
, &phy_sector_size
) < 0)
391 err(EXIT_FAILURE
, "blkdev_get_physector_size() failed");
393 printf(" bytes: %llu\n", bytes
);
394 printf(" sectors: %llu\n", sectors
);
395 printf(" sector size: %d\n", sector_size
);
396 printf("phy-sector size: %d\n", phy_sector_size
);
400 #endif /* TEST_PROGRAM_BLKDEV */