]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/blkdev.c
Merge branch 'clock' of https://github.com/t-8ch/util-linux
[thirdparty/util-linux.git] / lib / blkdev.c
CommitLineData
0f23ee0c
KZ
1/*
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 */
3738a48e 7#include <sys/types.h>
1675ed78 8#include <sys/stat.h>
37b30204 9#include <sys/file.h>
3738a48e 10#include <sys/ioctl.h>
2f7450f6 11#include <errno.h>
268cefe6 12#include <unistd.h>
abbd79ac 13#include <stdint.h>
3738a48e 14
30696241
KZ
15#ifdef HAVE_LINUX_FD_H
16#include <linux/fd.h>
17#endif
18
6be38dcc
NA
19#ifdef HAVE_LINUX_BLKZONED_H
20#include <linux/blkzoned.h>
21#endif
22
30696241
KZ
23#ifdef HAVE_SYS_DISKLABEL_H
24#include <sys/disklabel.h>
25#endif
26
27#ifdef HAVE_SYS_DISK_H
4ebac79f 28# include <sys/disk.h>
62efb93f
RM
29#endif
30
8fe1e638
SK
31#ifndef EBADFD
32# define EBADFD 77 /* File descriptor in bad state */
33#endif
34
5d5cd07b 35#include "all-io.h"
3738a48e 36#include "blkdev.h"
eb76ca98 37#include "c.h"
ba32a946 38#include "linux_version.h"
8fe1e638
SK
39#include "fileutils.h"
40#include "nls.h"
3738a48e 41
268cefe6
ST
42static long
43blkdev_valid_offset (int fd, off_t offset) {
44 char ch;
45
46 if (lseek (fd, offset, 0) < 0)
47 return 0;
5d5cd07b 48 if (read_all (fd, &ch, 1) < 1)
268cefe6
ST
49 return 0;
50 return 1;
51}
52
8150beac
DB
53int is_blkdev(int fd)
54{
55 struct stat st;
56 return (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode));
57}
58
268cefe6
ST
59off_t
60blkdev_find_size (int fd) {
ce621f16 61 off_t high, low = 0;
abbd79ac
KZ
62
63 for (high = 1024; blkdev_valid_offset (fd, high); ) {
ce621f16 64 if (high == SINT_MAX(off_t)) {
2f7450f6 65 errno = EFBIG;
abbd79ac 66 return -1;
2f7450f6 67 }
268cefe6 68
268cefe6 69 low = high;
abbd79ac 70
ce621f16
SN
71 if (high >= SINT_MAX(off_t)/2)
72 high = SINT_MAX(off_t);
abbd79ac
KZ
73 else
74 high *= 2;
75 }
76
268cefe6
ST
77 while (low < high - 1)
78 {
ce621f16 79 off_t mid = (low + high) / 2;
268cefe6
ST
80
81 if (blkdev_valid_offset (fd, mid))
82 low = mid;
83 else
84 high = mid;
85 }
86 blkdev_valid_offset (fd, 0);
87 return (low + 1);
88}
89
3738a48e
SK
90/* get size in bytes */
91int
92blkdev_get_size(int fd, unsigned long long *bytes)
93{
30696241
KZ
94#ifdef DKIOCGETBLOCKCOUNT
95 /* Apple Darwin */
96 if (ioctl(fd, DKIOCGETBLOCKCOUNT, bytes) >= 0) {
97 *bytes <<= 9;
98 return 0;
99 }
100#endif
3738a48e 101
268cefe6 102#ifdef BLKGETSIZE64
ad553030
RM
103 if (ioctl(fd, BLKGETSIZE64, bytes) >= 0)
104 return 0;
268cefe6 105#endif
268cefe6
ST
106
107#ifdef BLKGETSIZE
108 {
109 unsigned long size;
110
111 if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
112 *bytes = ((unsigned long long)size << 9);
113 return 0;
114 }
3738a48e
SK
115 }
116
268cefe6
ST
117#endif /* BLKGETSIZE */
118
30696241
KZ
119#ifdef DIOCGMEDIASIZE
120 /* FreeBSD */
121 if (ioctl(fd, DIOCGMEDIASIZE, bytes) >= 0)
fab1c046 122 return 0;
30696241
KZ
123#endif
124
125#ifdef FDGETPRM
126 {
127 struct floppy_struct this_floppy;
128
129 if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
09db9538 130 *bytes = ((unsigned long long) this_floppy.size) << 9;
30696241
KZ
131 return 0;
132 }
133 }
134#endif /* FDGETPRM */
135
6978b2c4 136#if defined(HAVE_SYS_DISKLABEL_H) && defined(DIOCGDINFO)
30696241
KZ
137 {
138 /*
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
142 * above however.
143 *
144 * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw,
145 * character) devices, so we need to check for S_ISCHR, too.
146 */
147 int part = -1;
148 struct disklabel lab;
149 struct partition *pp;
30696241
KZ
150 struct stat st;
151
152 if ((fstat(fd, &st) >= 0) &&
153 (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)))
154 part = st.st_rdev & 7;
155
156 if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
157 pp = &lab.d_partitions[part];
158 if (pp->p_size) {
159 *bytes = pp->p_size << 9;
160 return 0;
161 }
162 }
163 }
6978b2c4 164#endif /* defined(HAVE_SYS_DISKLABEL_H) && defined(DIOCGDINFO) */
30696241 165
530381d2
KZ
166 {
167 struct stat st;
168
169 if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
170 *bytes = st.st_size;
171 return 0;
172 }
2f7450f6
SN
173 if (!S_ISBLK(st.st_mode)) {
174 errno = ENOTBLK;
abbd79ac 175 return -1;
2f7450f6 176 }
530381d2
KZ
177 }
178
268cefe6
ST
179 *bytes = blkdev_find_size(fd);
180 return 0;
3738a48e
SK
181}
182
183/* get 512-byte sector count */
184int
185blkdev_get_sectors(int fd, unsigned long long *sectors)
186{
187 unsigned long long bytes;
188
189 if (blkdev_get_size(fd, &bytes) == 0) {
190 *sectors = (bytes >> 9);
191 return 0;
192 }
193
194 return -1;
195}
196
c2e05a49
KZ
197/*
198 * Get logical sector size.
2a1dfbad
KZ
199 *
200 * This is the smallest unit the storage device can
201 * address. It is typically 512 bytes.
202 */
8b9cf26a 203#ifdef BLKSSZGET
c2e05a49 204int blkdev_get_sector_size(int fd, int *sector_size)
3738a48e 205{
3738a48e
SK
206 if (ioctl(fd, BLKSSZGET, sector_size) >= 0)
207 return 0;
3738a48e 208 return -1;
8b9cf26a 209}
268cefe6 210#else
8b9cf26a
RM
211int blkdev_get_sector_size(int fd __attribute__((__unused__)), int *sector_size)
212{
268cefe6
ST
213 *sector_size = DEFAULT_SECTOR_SIZE;
214 return 0;
3738a48e 215}
8b9cf26a 216#endif
3738a48e 217
0e75337c
DB
218/*
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.
222 *
223 * Example:
224 *
225 * rc = blkdev_get_physector_size(fd, &physec);
226 * if (rc || physec == 0) {
227 * rc = blkdev_get_sector_size(fd, &physec);
228 * if (rc)
229 * physec = DEFAULT_SECTOR_SIZE;
230 * }
231 */
8b9cf26a 232#ifdef BLKPBSZGET
0e75337c
DB
233int blkdev_get_physector_size(int fd, int *sector_size)
234{
b5456d16
NM
235 if (ioctl(fd, BLKPBSZGET, sector_size) >= 0)
236 {
0e75337c 237 return 0;
b5456d16 238 }
0e75337c 239 return -1;
8b9cf26a 240}
0e75337c 241#else
8b9cf26a
RM
242int blkdev_get_physector_size(int fd __attribute__((__unused__)), int *sector_size)
243{
0e75337c
DB
244 *sector_size = DEFAULT_SECTOR_SIZE;
245 return 0;
0e75337c 246}
8b9cf26a 247#endif
3738a48e 248
ba326929
DB
249/*
250 * Return the alignment status of a device
251 */
8b9cf26a 252#ifdef BLKALIGNOFF
ba326929
DB
253int blkdev_is_misaligned(int fd)
254{
255 int aligned;
256
257 if (ioctl(fd, BLKALIGNOFF, &aligned) < 0)
258 return 0; /* probably kernel < 2.6.32 */
6678c06f 259 /*
9e930041 260 * Note that kernel returns -1 as alignment offset if no compatible
6678c06f
KZ
261 * sizes and alignments exist for stacked devices
262 */
263 return aligned != 0 ? 1 : 0;
8b9cf26a 264}
4585ec0b 265#else
8b9cf26a
RM
266int blkdev_is_misaligned(int fd __attribute__((__unused__)))
267{
4585ec0b 268 return 0;
ba326929 269}
8b9cf26a 270#endif
ba326929 271
8fe1e638
SK
272int open_blkdev_or_file(const struct stat *st, const char *name, const int oflag)
273{
274 int fd;
275
276 if (S_ISBLK(st->st_mode)) {
277 fd = open(name, oflag | O_EXCL);
278 } else
279 fd = open(name, oflag);
280 if (-1 < fd && !is_same_inode(fd, st)) {
281 close(fd);
282 errno = EBADFD;
283 return -1;
284 }
285 if (-1 < fd && S_ISBLK(st->st_mode) && blkdev_is_misaligned(fd))
286 warnx(_("warning: %s is misaligned"), name);
287 return fd;
288}
289
8b9cf26a 290#ifdef CDROM_GET_CAPABILITY
3b622ddd
DB
291int blkdev_is_cdrom(int fd)
292{
3b622ddd
DB
293 int ret;
294
295 if ((ret = ioctl(fd, CDROM_GET_CAPABILITY, NULL)) < 0)
296 return 0;
042f62df
RP
297
298 return ret;
8b9cf26a 299}
3b622ddd 300#else
8b9cf26a
RM
301int blkdev_is_cdrom(int fd __attribute__((__unused__)))
302{
3b622ddd 303 return 0;
3b622ddd 304}
8b9cf26a 305#endif
0e75337c 306
64128b70
DB
307/*
308 * Get kernel's interpretation of the device's geometry.
309 *
310 * Returns the heads and sectors - but not cylinders
311 * as it's truncated for disks with more than 65535 tracks.
312 *
313 * Note that this is deprecated in favor of LBA addressing.
314 */
8b9cf26a 315#ifdef HDIO_GETGEO
64128b70
DB
316int blkdev_get_geometry(int fd, unsigned int *h, unsigned int *s)
317{
64128b70
DB
318 struct hd_geometry geometry;
319
c3443509 320 if (ioctl(fd, HDIO_GETGEO, &geometry) == 0) {
64128b70
DB
321 *h = geometry.heads;
322 *s = geometry.sectors;
c3443509 323 return 0;
64128b70
DB
324 }
325#else
8b9cf26a
RM
326int blkdev_get_geometry(int fd __attribute__((__unused__)),
327 unsigned int *h, unsigned int *s)
328{
64128b70
DB
329 *h = 0;
330 *s = 0;
331#endif
c3443509 332 return -1;
64128b70
DB
333}
334
ba32a946 335/*
2fc4a256 336 * Convert scsi type to human readable string.
ba32a946 337 */
2fc4a256 338const char *blkdev_scsi_type_to_name(int type)
ba32a946 339{
ba32a946
SK
340 switch (type) {
341 case SCSI_TYPE_DISK:
2fc4a256 342 return "disk";
ba32a946 343 case SCSI_TYPE_TAPE:
2fc4a256 344 return "tape";
ba32a946 345 case SCSI_TYPE_PRINTER:
2fc4a256 346 return "printer";
ba32a946 347 case SCSI_TYPE_PROCESSOR:
2fc4a256 348 return "processor";
ba32a946 349 case SCSI_TYPE_WORM:
2fc4a256 350 return "worm";
ba32a946 351 case SCSI_TYPE_ROM:
2fc4a256 352 return "rom";
ba32a946 353 case SCSI_TYPE_SCANNER:
2fc4a256 354 return "scanner";
ba32a946 355 case SCSI_TYPE_MOD:
2fc4a256 356 return "mo-disk";
ba32a946 357 case SCSI_TYPE_MEDIUM_CHANGER:
2fc4a256 358 return "changer";
ba32a946 359 case SCSI_TYPE_COMM:
2fc4a256 360 return "comm";
ba32a946 361 case SCSI_TYPE_RAID:
2fc4a256 362 return "raid";
ba32a946 363 case SCSI_TYPE_ENCLOSURE:
2fc4a256 364 return "enclosure";
ba32a946 365 case SCSI_TYPE_RBC:
2fc4a256 366 return "rbc";
ba32a946 367 case SCSI_TYPE_OSD:
2fc4a256 368 return "osd";
ba32a946 369 case SCSI_TYPE_NO_LUN:
2fc4a256 370 return "no-lun";
ba32a946 371 default:
2fc4a256 372 break;
ba32a946 373 }
2fc4a256 374 return NULL;
ba32a946
SK
375}
376
37b30204
KZ
377/* return 0 on success */
378int blkdev_lock(int fd, const char *devname, const char *lockmode)
379{
380 int oper, rc, msg = 0;
381
382 if (!lockmode)
383 lockmode = getenv("LOCK_BLOCK_DEVICE");
384 if (!lockmode)
385 return 0;
386
387 if (strcasecmp(lockmode, "yes") == 0 ||
388 strcmp(lockmode, "1") == 0)
389 oper = LOCK_EX;
390
391 else if (strcasecmp(lockmode, "nonblock") == 0)
392 oper = LOCK_EX | LOCK_NB;
393
394 else if (strcasecmp(lockmode, "no") == 0 ||
395 strcmp(lockmode, "0") == 0)
396 return 0;
397 else {
398 warnx(_("unsupported lock mode: %s"), lockmode);
399 return -EINVAL;
400 }
401
402 if (!(oper & LOCK_NB)) {
403 /* Try non-block first to provide message */
404 rc = flock(fd, oper | LOCK_NB);
405 if (rc == 0)
406 return 0;
407 if (rc != 0 && errno == EWOULDBLOCK) {
408 fprintf(stderr, _("%s: %s: device already locked, waiting to get lock ... "),
409 program_invocation_short_name, devname);
410 msg = 1;
411 }
412 }
413 rc = flock(fd, oper);
414 if (rc != 0) {
415 switch (errno) {
416 case EWOULDBLOCK: /* LOCK_NB */
417 warnx(_("%s: device already locked"), devname);
418 break;
419 default:
420 warn(_("%s: failed to get lock"), devname);
421 }
422 } else if (msg)
423 fprintf(stderr, _("OK\n"));
424 return rc;
425}
426
6be38dcc
NA
427#ifdef HAVE_LINUX_BLKZONED_H
428struct blk_zone_report *blkdev_get_zonereport(int fd, uint64_t sector, uint32_t nzones)
429{
430 struct blk_zone_report *rep;
431 size_t rep_size;
432 int ret;
433
434 rep_size = sizeof(struct blk_zone_report) + sizeof(struct blk_zone) * 2;
435 rep = calloc(1, rep_size);
436 if (!rep)
437 return NULL;
438
439 rep->sector = sector;
440 rep->nr_zones = nzones;
441
442 ret = ioctl(fd, BLKREPORTZONE, rep);
443 if (ret || rep->nr_zones != nzones) {
444 free(rep);
445 return NULL;
446 }
447
448 return rep;
449}
450#endif
451
37b30204 452
293714c0 453#ifdef TEST_PROGRAM_BLKDEV
3738a48e
SK
454#include <stdio.h>
455#include <stdlib.h>
456#include <fcntl.h>
3738a48e
SK
457int
458main(int argc, char **argv)
459{
460 unsigned long long bytes;
461 unsigned long long sectors;
0e75337c 462 int sector_size, phy_sector_size;
3738a48e
SK
463 int fd;
464
465 if (argc != 2) {
466 fprintf(stderr, "usage: %s device\n", argv[0]);
467 exit(EXIT_FAILURE);
468 }
469
b1fa3e22 470 if ((fd = open(argv[1], O_RDONLY|O_CLOEXEC)) < 0)
3738a48e
SK
471 err(EXIT_FAILURE, "open %s failed", argv[1]);
472
473 if (blkdev_get_size(fd, &bytes) < 0)
474 err(EXIT_FAILURE, "blkdev_get_size() failed");
475 if (blkdev_get_sectors(fd, &sectors) < 0)
476 err(EXIT_FAILURE, "blkdev_get_sectors() failed");
477 if (blkdev_get_sector_size(fd, &sector_size) < 0)
478 err(EXIT_FAILURE, "blkdev_get_sector_size() failed");
0e75337c
DB
479 if (blkdev_get_physector_size(fd, &phy_sector_size) < 0)
480 err(EXIT_FAILURE, "blkdev_get_physector_size() failed");
3738a48e 481
0e75337c
DB
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);
3738a48e
SK
486
487 return EXIT_SUCCESS;
488}
e8f7acb0 489#endif /* TEST_PROGRAM_BLKDEV */