2 * No copyright is claimed. This code is in the public domain; do with
5 * Written by Karel Zak <kzak@redhat.com>
10 #include <sys/ioctl.h>
15 #ifdef HAVE_LINUX_FD_H
19 #ifdef HAVE_LINUX_BLKZONED_H
20 #include <linux/blkzoned.h>
23 #ifdef HAVE_SYS_DISKLABEL_H
24 #include <sys/disklabel.h>
27 #ifdef HAVE_SYS_DISK_H
28 # include <sys/disk.h>
32 # define EBADFD 77 /* File descriptor in bad state */
38 #include "linux_version.h"
39 #include "fileutils.h"
43 blkdev_valid_offset (int fd
, off_t offset
) {
46 if (lseek (fd
, offset
, 0) < 0)
48 if (read_all (fd
, &ch
, 1) < 1)
56 return (fstat(fd
, &st
) == 0 && S_ISBLK(st
.st_mode
));
60 blkdev_find_size (int fd
) {
63 for (high
= 1024; blkdev_valid_offset (fd
, high
); ) {
64 if (high
== SINT_MAX(off_t
)) {
71 if (high
>= SINT_MAX(off_t
)/2)
72 high
= SINT_MAX(off_t
);
77 while (low
< high
- 1)
79 off_t mid
= (low
+ high
) / 2;
81 if (blkdev_valid_offset (fd
, mid
))
86 blkdev_valid_offset (fd
, 0);
90 /* get size in bytes */
92 blkdev_get_size(int fd
, unsigned long long *bytes
)
94 #ifdef DKIOCGETBLOCKCOUNT
96 if (ioctl(fd
, DKIOCGETBLOCKCOUNT
, bytes
) >= 0) {
103 if (ioctl(fd
, BLKGETSIZE64
, bytes
) >= 0)
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 #if defined(HAVE_SYS_DISKLABEL_H) && defined(DIOCGDINFO)
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 /* defined(HAVE_SYS_DISKLABEL_H) && defined(DIOCGDINFO) */
169 if (fstat(fd
, &st
) == 0 && S_ISREG(st
.st_mode
)) {
173 if (!S_ISBLK(st
.st_mode
)) {
179 *bytes
= blkdev_find_size(fd
);
183 /* get 512-byte sector count */
185 blkdev_get_sectors(int fd
, unsigned long long *sectors
)
187 unsigned long long bytes
;
189 if (blkdev_get_size(fd
, &bytes
) == 0) {
190 *sectors
= (bytes
>> 9);
198 * Get logical sector size.
200 * This is the smallest unit the storage device can
201 * address. It is typically 512 bytes.
204 int blkdev_get_sector_size(int fd
, int *sector_size
)
206 if (ioctl(fd
, BLKSSZGET
, sector_size
) >= 0)
211 int blkdev_get_sector_size(int fd
__attribute__((__unused__
)), int *sector_size
)
213 *sector_size
= DEFAULT_SECTOR_SIZE
;
219 * Get physical block device size. The BLKPBSZGET is supported since Linux
220 * 2.6.32. For old kernels is probably the best to assume that physical sector
221 * size is the same as logical sector size.
225 * rc = blkdev_get_physector_size(fd, &physec);
226 * if (rc || physec == 0) {
227 * rc = blkdev_get_sector_size(fd, &physec);
229 * physec = DEFAULT_SECTOR_SIZE;
233 int blkdev_get_physector_size(int fd
, int *sector_size
)
235 if (ioctl(fd
, BLKPBSZGET
, sector_size
) >= 0)
242 int blkdev_get_physector_size(int fd
__attribute__((__unused__
)), int *sector_size
)
244 *sector_size
= DEFAULT_SECTOR_SIZE
;
250 * Return the alignment status of a device
253 int blkdev_is_misaligned(int fd
)
257 if (ioctl(fd
, BLKALIGNOFF
, &aligned
) < 0)
258 return 0; /* probably kernel < 2.6.32 */
260 * Note that kernel returns -1 as alignment offset if no compatible
261 * sizes and alignments exist for stacked devices
263 return aligned
!= 0 ? 1 : 0;
266 int blkdev_is_misaligned(int fd
__attribute__((__unused__
)))
272 int open_blkdev_or_file(const struct stat
*st
, const char *name
, const int oflag
)
276 if (S_ISBLK(st
->st_mode
)) {
277 fd
= open(name
, oflag
| O_EXCL
);
279 fd
= open(name
, oflag
);
280 if (-1 < fd
&& !is_same_inode(fd
, st
)) {
285 if (-1 < fd
&& S_ISBLK(st
->st_mode
) && blkdev_is_misaligned(fd
))
286 warnx(_("warning: %s is misaligned"), name
);
290 #ifdef CDROM_GET_CAPABILITY
291 int blkdev_is_cdrom(int fd
)
295 if ((ret
= ioctl(fd
, CDROM_GET_CAPABILITY
, NULL
)) < 0)
301 int blkdev_is_cdrom(int fd
__attribute__((__unused__
)))
308 * Get kernel's interpretation of the device's geometry.
310 * Returns the heads and sectors - but not cylinders
311 * as it's truncated for disks with more than 65535 tracks.
313 * Note that this is deprecated in favor of LBA addressing.
316 int blkdev_get_geometry(int fd
, unsigned int *h
, unsigned int *s
)
318 struct hd_geometry geometry
;
320 if (ioctl(fd
, HDIO_GETGEO
, &geometry
) == 0) {
322 *s
= geometry
.sectors
;
326 int blkdev_get_geometry(int fd
__attribute__((__unused__
)),
327 unsigned int *h
, unsigned int *s
)
336 * Convert scsi type to human readable string.
338 const char *blkdev_scsi_type_to_name(int type
)
345 case SCSI_TYPE_PRINTER
:
347 case SCSI_TYPE_PROCESSOR
:
353 case SCSI_TYPE_SCANNER
:
357 case SCSI_TYPE_MEDIUM_CHANGER
:
363 case SCSI_TYPE_ENCLOSURE
:
369 case SCSI_TYPE_NO_LUN
:
377 /* return 0 on success */
378 int blkdev_lock(int fd
, const char *devname
, const char *lockmode
)
380 int oper
, rc
, msg
= 0;
383 lockmode
= getenv("LOCK_BLOCK_DEVICE");
387 if (strcasecmp(lockmode
, "yes") == 0 ||
388 strcmp(lockmode
, "1") == 0)
391 else if (strcasecmp(lockmode
, "nonblock") == 0)
392 oper
= LOCK_EX
| LOCK_NB
;
394 else if (strcasecmp(lockmode
, "no") == 0 ||
395 strcmp(lockmode
, "0") == 0)
398 warnx(_("unsupported lock mode: %s"), lockmode
);
402 if (!(oper
& LOCK_NB
)) {
403 /* Try non-block first to provide message */
404 rc
= flock(fd
, oper
| LOCK_NB
);
407 if (rc
!= 0 && errno
== EWOULDBLOCK
) {
408 fprintf(stderr
, _("%s: %s: device already locked, waiting to get lock ... "),
409 program_invocation_short_name
, devname
);
413 rc
= flock(fd
, oper
);
416 case EWOULDBLOCK
: /* LOCK_NB */
417 warnx(_("%s: device already locked"), devname
);
420 warn(_("%s: failed to get lock"), devname
);
423 fprintf(stderr
, _("OK\n"));
427 #ifdef HAVE_LINUX_BLKZONED_H
428 struct blk_zone_report
*blkdev_get_zonereport(int fd
, uint64_t sector
, uint32_t nzones
)
430 struct blk_zone_report
*rep
;
434 rep_size
= sizeof(struct blk_zone_report
) + sizeof(struct blk_zone
) * 2;
435 rep
= calloc(1, rep_size
);
439 rep
->sector
= sector
;
440 rep
->nr_zones
= nzones
;
442 ret
= ioctl(fd
, BLKREPORTZONE
, rep
);
443 if (ret
|| rep
->nr_zones
!= nzones
) {
453 #ifdef TEST_PROGRAM_BLKDEV
458 main(int argc
, char **argv
)
460 unsigned long long bytes
;
461 unsigned long long sectors
;
462 int sector_size
, phy_sector_size
;
466 fprintf(stderr
, "usage: %s device\n", argv
[0]);
470 if ((fd
= open(argv
[1], O_RDONLY
|O_CLOEXEC
)) < 0)
471 err(EXIT_FAILURE
, "open %s failed", argv
[1]);
473 if (blkdev_get_size(fd
, &bytes
) < 0)
474 err(EXIT_FAILURE
, "blkdev_get_size() failed");
475 if (blkdev_get_sectors(fd
, §ors
) < 0)
476 err(EXIT_FAILURE
, "blkdev_get_sectors() failed");
477 if (blkdev_get_sector_size(fd
, §or_size
) < 0)
478 err(EXIT_FAILURE
, "blkdev_get_sector_size() failed");
479 if (blkdev_get_physector_size(fd
, &phy_sector_size
) < 0)
480 err(EXIT_FAILURE
, "blkdev_get_physector_size() failed");
482 printf(" bytes: %llu\n", bytes
);
483 printf(" sectors: %llu\n", sectors
);
484 printf(" sector size: %d\n", sector_size
);
485 printf("phy-sector size: %d\n", phy_sector_size
);
489 #endif /* TEST_PROGRAM_BLKDEV */