]>
Commit | Line | Data |
---|---|---|
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 |
42 | static long |
43 | blkdev_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 |
53 | int is_blkdev(int fd) |
54 | { | |
55 | struct stat st; | |
56 | return (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode)); | |
57 | } | |
58 | ||
268cefe6 ST |
59 | off_t |
60 | blkdev_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 */ |
91 | int | |
92 | blkdev_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 */ | |
184 | int | |
185 | blkdev_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 | 204 | int 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 |
211 | int 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 |
233 | int 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 |
242 | int 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 |
253 | int 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 |
266 | int blkdev_is_misaligned(int fd __attribute__((__unused__))) |
267 | { | |
4585ec0b | 268 | return 0; |
ba326929 | 269 | } |
8b9cf26a | 270 | #endif |
ba326929 | 271 | |
8fe1e638 SK |
272 | int 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 |
291 | int 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 |
301 | int 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 |
316 | int 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 |
326 | int 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 | 338 | const 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 */ |
378 | int 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 |
428 | struct 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 |
457 | int |
458 | main(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, §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"); | |
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 */ |