]>
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> |
3738a48e | 9 | #include <sys/ioctl.h> |
268cefe6 | 10 | #include <unistd.h> |
abbd79ac | 11 | #include <stdint.h> |
3738a48e | 12 | |
30696241 KZ |
13 | #ifdef HAVE_LINUX_FD_H |
14 | #include <linux/fd.h> | |
15 | #endif | |
16 | ||
17 | #ifdef HAVE_SYS_DISKLABEL_H | |
18 | #include <sys/disklabel.h> | |
19 | #endif | |
20 | ||
21 | #ifdef HAVE_SYS_DISK_H | |
4ebac79f | 22 | # include <sys/disk.h> |
62efb93f RM |
23 | #endif |
24 | ||
8fe1e638 SK |
25 | #ifndef EBADFD |
26 | # define EBADFD 77 /* File descriptor in bad state */ | |
27 | #endif | |
28 | ||
3738a48e | 29 | #include "blkdev.h" |
eb76ca98 | 30 | #include "c.h" |
ba32a946 | 31 | #include "linux_version.h" |
8fe1e638 SK |
32 | #include "fileutils.h" |
33 | #include "nls.h" | |
3738a48e | 34 | |
268cefe6 ST |
35 | static long |
36 | blkdev_valid_offset (int fd, off_t offset) { | |
37 | char ch; | |
38 | ||
39 | if (lseek (fd, offset, 0) < 0) | |
40 | return 0; | |
41 | if (read (fd, &ch, 1) < 1) | |
42 | return 0; | |
43 | return 1; | |
44 | } | |
45 | ||
8150beac DB |
46 | int is_blkdev(int fd) |
47 | { | |
48 | struct stat st; | |
49 | return (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode)); | |
50 | } | |
51 | ||
268cefe6 ST |
52 | off_t |
53 | blkdev_find_size (int fd) { | |
abbd79ac KZ |
54 | uintmax_t high, low = 0; |
55 | ||
56 | for (high = 1024; blkdev_valid_offset (fd, high); ) { | |
57 | if (high == UINTMAX_MAX) | |
58 | return -1; | |
268cefe6 | 59 | |
268cefe6 | 60 | low = high; |
abbd79ac KZ |
61 | |
62 | if (high >= UINTMAX_MAX/2) | |
63 | high = UINTMAX_MAX; | |
64 | else | |
65 | high *= 2; | |
66 | } | |
67 | ||
268cefe6 ST |
68 | while (low < high - 1) |
69 | { | |
abbd79ac | 70 | uintmax_t mid = (low + high) / 2; |
268cefe6 ST |
71 | |
72 | if (blkdev_valid_offset (fd, mid)) | |
73 | low = mid; | |
74 | else | |
75 | high = mid; | |
76 | } | |
77 | blkdev_valid_offset (fd, 0); | |
78 | return (low + 1); | |
79 | } | |
80 | ||
3738a48e SK |
81 | /* get size in bytes */ |
82 | int | |
83 | blkdev_get_size(int fd, unsigned long long *bytes) | |
84 | { | |
30696241 KZ |
85 | #ifdef DKIOCGETBLOCKCOUNT |
86 | /* Apple Darwin */ | |
87 | if (ioctl(fd, DKIOCGETBLOCKCOUNT, bytes) >= 0) { | |
88 | *bytes <<= 9; | |
89 | return 0; | |
90 | } | |
91 | #endif | |
3738a48e | 92 | |
268cefe6 | 93 | #ifdef BLKGETSIZE64 |
ad553030 RM |
94 | if (ioctl(fd, BLKGETSIZE64, bytes) >= 0) |
95 | return 0; | |
268cefe6 | 96 | #endif |
268cefe6 ST |
97 | |
98 | #ifdef BLKGETSIZE | |
99 | { | |
100 | unsigned long size; | |
101 | ||
102 | if (ioctl(fd, BLKGETSIZE, &size) >= 0) { | |
103 | *bytes = ((unsigned long long)size << 9); | |
104 | return 0; | |
105 | } | |
3738a48e SK |
106 | } |
107 | ||
268cefe6 ST |
108 | #endif /* BLKGETSIZE */ |
109 | ||
30696241 KZ |
110 | #ifdef DIOCGMEDIASIZE |
111 | /* FreeBSD */ | |
112 | if (ioctl(fd, DIOCGMEDIASIZE, bytes) >= 0) | |
fab1c046 | 113 | return 0; |
30696241 KZ |
114 | #endif |
115 | ||
116 | #ifdef FDGETPRM | |
117 | { | |
118 | struct floppy_struct this_floppy; | |
119 | ||
120 | if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) { | |
09db9538 | 121 | *bytes = ((unsigned long long) this_floppy.size) << 9; |
30696241 KZ |
122 | return 0; |
123 | } | |
124 | } | |
125 | #endif /* FDGETPRM */ | |
126 | ||
6978b2c4 | 127 | #if defined(HAVE_SYS_DISKLABEL_H) && defined(DIOCGDINFO) |
30696241 KZ |
128 | { |
129 | /* | |
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 | |
133 | * above however. | |
134 | * | |
135 | * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw, | |
136 | * character) devices, so we need to check for S_ISCHR, too. | |
137 | */ | |
138 | int part = -1; | |
139 | struct disklabel lab; | |
140 | struct partition *pp; | |
30696241 KZ |
141 | struct stat st; |
142 | ||
143 | if ((fstat(fd, &st) >= 0) && | |
144 | (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))) | |
145 | part = st.st_rdev & 7; | |
146 | ||
147 | if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { | |
148 | pp = &lab.d_partitions[part]; | |
149 | if (pp->p_size) { | |
150 | *bytes = pp->p_size << 9; | |
151 | return 0; | |
152 | } | |
153 | } | |
154 | } | |
6978b2c4 | 155 | #endif /* defined(HAVE_SYS_DISKLABEL_H) && defined(DIOCGDINFO) */ |
30696241 | 156 | |
530381d2 KZ |
157 | { |
158 | struct stat st; | |
159 | ||
160 | if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) { | |
161 | *bytes = st.st_size; | |
162 | return 0; | |
163 | } | |
abbd79ac KZ |
164 | if (!S_ISBLK(st.st_mode)) |
165 | return -1; | |
530381d2 KZ |
166 | } |
167 | ||
268cefe6 ST |
168 | *bytes = blkdev_find_size(fd); |
169 | return 0; | |
3738a48e SK |
170 | } |
171 | ||
172 | /* get 512-byte sector count */ | |
173 | int | |
174 | blkdev_get_sectors(int fd, unsigned long long *sectors) | |
175 | { | |
176 | unsigned long long bytes; | |
177 | ||
178 | if (blkdev_get_size(fd, &bytes) == 0) { | |
179 | *sectors = (bytes >> 9); | |
180 | return 0; | |
181 | } | |
182 | ||
183 | return -1; | |
184 | } | |
185 | ||
c2e05a49 KZ |
186 | /* |
187 | * Get logical sector size. | |
2a1dfbad KZ |
188 | * |
189 | * This is the smallest unit the storage device can | |
190 | * address. It is typically 512 bytes. | |
191 | */ | |
8b9cf26a | 192 | #ifdef BLKSSZGET |
c2e05a49 | 193 | int blkdev_get_sector_size(int fd, int *sector_size) |
3738a48e | 194 | { |
3738a48e SK |
195 | if (ioctl(fd, BLKSSZGET, sector_size) >= 0) |
196 | return 0; | |
3738a48e | 197 | return -1; |
8b9cf26a | 198 | } |
268cefe6 | 199 | #else |
8b9cf26a RM |
200 | int blkdev_get_sector_size(int fd __attribute__((__unused__)), int *sector_size) |
201 | { | |
268cefe6 ST |
202 | *sector_size = DEFAULT_SECTOR_SIZE; |
203 | return 0; | |
3738a48e | 204 | } |
8b9cf26a | 205 | #endif |
3738a48e | 206 | |
0e75337c DB |
207 | /* |
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. | |
211 | * | |
212 | * Example: | |
213 | * | |
214 | * rc = blkdev_get_physector_size(fd, &physec); | |
215 | * if (rc || physec == 0) { | |
216 | * rc = blkdev_get_sector_size(fd, &physec); | |
217 | * if (rc) | |
218 | * physec = DEFAULT_SECTOR_SIZE; | |
219 | * } | |
220 | */ | |
8b9cf26a | 221 | #ifdef BLKPBSZGET |
0e75337c DB |
222 | int blkdev_get_physector_size(int fd, int *sector_size) |
223 | { | |
0e75337c DB |
224 | if (ioctl(fd, BLKPBSZGET, §or_size) >= 0) |
225 | return 0; | |
226 | return -1; | |
8b9cf26a | 227 | } |
0e75337c | 228 | #else |
8b9cf26a RM |
229 | int blkdev_get_physector_size(int fd __attribute__((__unused__)), int *sector_size) |
230 | { | |
0e75337c DB |
231 | *sector_size = DEFAULT_SECTOR_SIZE; |
232 | return 0; | |
0e75337c | 233 | } |
8b9cf26a | 234 | #endif |
3738a48e | 235 | |
ba326929 DB |
236 | /* |
237 | * Return the alignment status of a device | |
238 | */ | |
8b9cf26a | 239 | #ifdef BLKALIGNOFF |
ba326929 DB |
240 | int blkdev_is_misaligned(int fd) |
241 | { | |
242 | int aligned; | |
243 | ||
244 | if (ioctl(fd, BLKALIGNOFF, &aligned) < 0) | |
245 | return 0; /* probably kernel < 2.6.32 */ | |
6678c06f | 246 | /* |
9e930041 | 247 | * Note that kernel returns -1 as alignment offset if no compatible |
6678c06f KZ |
248 | * sizes and alignments exist for stacked devices |
249 | */ | |
250 | return aligned != 0 ? 1 : 0; | |
8b9cf26a | 251 | } |
4585ec0b | 252 | #else |
8b9cf26a RM |
253 | int blkdev_is_misaligned(int fd __attribute__((__unused__))) |
254 | { | |
4585ec0b | 255 | return 0; |
ba326929 | 256 | } |
8b9cf26a | 257 | #endif |
ba326929 | 258 | |
8fe1e638 SK |
259 | int open_blkdev_or_file(const struct stat *st, const char *name, const int oflag) |
260 | { | |
261 | int fd; | |
262 | ||
263 | if (S_ISBLK(st->st_mode)) { | |
264 | fd = open(name, oflag | O_EXCL); | |
265 | } else | |
266 | fd = open(name, oflag); | |
267 | if (-1 < fd && !is_same_inode(fd, st)) { | |
268 | close(fd); | |
269 | errno = EBADFD; | |
270 | return -1; | |
271 | } | |
272 | if (-1 < fd && S_ISBLK(st->st_mode) && blkdev_is_misaligned(fd)) | |
273 | warnx(_("warning: %s is misaligned"), name); | |
274 | return fd; | |
275 | } | |
276 | ||
8b9cf26a | 277 | #ifdef CDROM_GET_CAPABILITY |
3b622ddd DB |
278 | int blkdev_is_cdrom(int fd) |
279 | { | |
3b622ddd DB |
280 | int ret; |
281 | ||
282 | if ((ret = ioctl(fd, CDROM_GET_CAPABILITY, NULL)) < 0) | |
283 | return 0; | |
042f62df RP |
284 | |
285 | return ret; | |
8b9cf26a | 286 | } |
3b622ddd | 287 | #else |
8b9cf26a RM |
288 | int blkdev_is_cdrom(int fd __attribute__((__unused__))) |
289 | { | |
3b622ddd | 290 | return 0; |
3b622ddd | 291 | } |
8b9cf26a | 292 | #endif |
0e75337c | 293 | |
64128b70 DB |
294 | /* |
295 | * Get kernel's interpretation of the device's geometry. | |
296 | * | |
297 | * Returns the heads and sectors - but not cylinders | |
298 | * as it's truncated for disks with more than 65535 tracks. | |
299 | * | |
300 | * Note that this is deprecated in favor of LBA addressing. | |
301 | */ | |
8b9cf26a | 302 | #ifdef HDIO_GETGEO |
64128b70 DB |
303 | int blkdev_get_geometry(int fd, unsigned int *h, unsigned int *s) |
304 | { | |
64128b70 DB |
305 | struct hd_geometry geometry; |
306 | ||
c3443509 | 307 | if (ioctl(fd, HDIO_GETGEO, &geometry) == 0) { |
64128b70 DB |
308 | *h = geometry.heads; |
309 | *s = geometry.sectors; | |
c3443509 | 310 | return 0; |
64128b70 DB |
311 | } |
312 | #else | |
8b9cf26a RM |
313 | int blkdev_get_geometry(int fd __attribute__((__unused__)), |
314 | unsigned int *h, unsigned int *s) | |
315 | { | |
64128b70 DB |
316 | *h = 0; |
317 | *s = 0; | |
318 | #endif | |
c3443509 | 319 | return -1; |
64128b70 DB |
320 | } |
321 | ||
ba32a946 | 322 | /* |
2fc4a256 | 323 | * Convert scsi type to human readable string. |
ba32a946 | 324 | */ |
2fc4a256 | 325 | const char *blkdev_scsi_type_to_name(int type) |
ba32a946 | 326 | { |
ba32a946 SK |
327 | switch (type) { |
328 | case SCSI_TYPE_DISK: | |
2fc4a256 | 329 | return "disk"; |
ba32a946 | 330 | case SCSI_TYPE_TAPE: |
2fc4a256 | 331 | return "tape"; |
ba32a946 | 332 | case SCSI_TYPE_PRINTER: |
2fc4a256 | 333 | return "printer"; |
ba32a946 | 334 | case SCSI_TYPE_PROCESSOR: |
2fc4a256 | 335 | return "processor"; |
ba32a946 | 336 | case SCSI_TYPE_WORM: |
2fc4a256 | 337 | return "worm"; |
ba32a946 | 338 | case SCSI_TYPE_ROM: |
2fc4a256 | 339 | return "rom"; |
ba32a946 | 340 | case SCSI_TYPE_SCANNER: |
2fc4a256 | 341 | return "scanner"; |
ba32a946 | 342 | case SCSI_TYPE_MOD: |
2fc4a256 | 343 | return "mo-disk"; |
ba32a946 | 344 | case SCSI_TYPE_MEDIUM_CHANGER: |
2fc4a256 | 345 | return "changer"; |
ba32a946 | 346 | case SCSI_TYPE_COMM: |
2fc4a256 | 347 | return "comm"; |
ba32a946 | 348 | case SCSI_TYPE_RAID: |
2fc4a256 | 349 | return "raid"; |
ba32a946 | 350 | case SCSI_TYPE_ENCLOSURE: |
2fc4a256 | 351 | return "enclosure"; |
ba32a946 | 352 | case SCSI_TYPE_RBC: |
2fc4a256 | 353 | return "rbc"; |
ba32a946 | 354 | case SCSI_TYPE_OSD: |
2fc4a256 | 355 | return "osd"; |
ba32a946 | 356 | case SCSI_TYPE_NO_LUN: |
2fc4a256 | 357 | return "no-lun"; |
ba32a946 | 358 | default: |
2fc4a256 | 359 | break; |
ba32a946 | 360 | } |
2fc4a256 | 361 | return NULL; |
ba32a946 SK |
362 | } |
363 | ||
293714c0 | 364 | #ifdef TEST_PROGRAM_BLKDEV |
3738a48e SK |
365 | #include <stdio.h> |
366 | #include <stdlib.h> | |
367 | #include <fcntl.h> | |
3738a48e SK |
368 | int |
369 | main(int argc, char **argv) | |
370 | { | |
371 | unsigned long long bytes; | |
372 | unsigned long long sectors; | |
0e75337c | 373 | int sector_size, phy_sector_size; |
3738a48e SK |
374 | int fd; |
375 | ||
376 | if (argc != 2) { | |
377 | fprintf(stderr, "usage: %s device\n", argv[0]); | |
378 | exit(EXIT_FAILURE); | |
379 | } | |
380 | ||
b1fa3e22 | 381 | if ((fd = open(argv[1], O_RDONLY|O_CLOEXEC)) < 0) |
3738a48e SK |
382 | err(EXIT_FAILURE, "open %s failed", argv[1]); |
383 | ||
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"); | |
0e75337c DB |
390 | if (blkdev_get_physector_size(fd, &phy_sector_size) < 0) |
391 | err(EXIT_FAILURE, "blkdev_get_physector_size() failed"); | |
3738a48e | 392 | |
0e75337c DB |
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); | |
3738a48e SK |
397 | |
398 | return EXIT_SUCCESS; | |
399 | } | |
e8f7acb0 | 400 | #endif /* TEST_PROGRAM_BLKDEV */ |