]>
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 |
30696241 | 94 | { |
268cefe6 | 95 | #ifdef __linux__ |
30696241 KZ |
96 | int ver = get_linux_version(); |
97 | ||
98 | /* kernels 2.4.15-2.4.17, had a broken BLKGETSIZE64 */ | |
99 | if (ver >= KERNEL_VERSION (2,6,0) || | |
100 | (ver >= KERNEL_VERSION (2,4,18) && ver < KERNEL_VERSION (2,5,0))) | |
268cefe6 | 101 | #endif |
30696241 KZ |
102 | if (ioctl(fd, BLKGETSIZE64, bytes) >= 0) |
103 | return 0; | |
104 | } | |
268cefe6 ST |
105 | #endif /* BLKGETSIZE64 */ |
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 | ||
136 | #ifdef HAVE_SYS_DISKLABEL_H | |
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 | } | |
164 | #endif /* HAVE_SYS_DISKLABEL_H */ | |
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 | } | |
abbd79ac KZ |
173 | if (!S_ISBLK(st.st_mode)) |
174 | return -1; | |
530381d2 KZ |
175 | } |
176 | ||
268cefe6 ST |
177 | *bytes = blkdev_find_size(fd); |
178 | return 0; | |
3738a48e SK |
179 | } |
180 | ||
181 | /* get 512-byte sector count */ | |
182 | int | |
183 | blkdev_get_sectors(int fd, unsigned long long *sectors) | |
184 | { | |
185 | unsigned long long bytes; | |
186 | ||
187 | if (blkdev_get_size(fd, &bytes) == 0) { | |
188 | *sectors = (bytes >> 9); | |
189 | return 0; | |
190 | } | |
191 | ||
192 | return -1; | |
193 | } | |
194 | ||
c2e05a49 KZ |
195 | /* |
196 | * Get logical sector size. | |
2a1dfbad KZ |
197 | * |
198 | * This is the smallest unit the storage device can | |
199 | * address. It is typically 512 bytes. | |
200 | */ | |
8b9cf26a | 201 | #ifdef BLKSSZGET |
c2e05a49 | 202 | int blkdev_get_sector_size(int fd, int *sector_size) |
3738a48e | 203 | { |
3738a48e SK |
204 | if (ioctl(fd, BLKSSZGET, sector_size) >= 0) |
205 | return 0; | |
3738a48e | 206 | return -1; |
8b9cf26a | 207 | } |
268cefe6 | 208 | #else |
8b9cf26a RM |
209 | int blkdev_get_sector_size(int fd __attribute__((__unused__)), int *sector_size) |
210 | { | |
268cefe6 ST |
211 | *sector_size = DEFAULT_SECTOR_SIZE; |
212 | return 0; | |
3738a48e | 213 | } |
8b9cf26a | 214 | #endif |
3738a48e | 215 | |
0e75337c DB |
216 | /* |
217 | * Get physical block device size. The BLKPBSZGET is supported since Linux | |
218 | * 2.6.32. For old kernels is probably the best to assume that physical sector | |
219 | * size is the same as logical sector size. | |
220 | * | |
221 | * Example: | |
222 | * | |
223 | * rc = blkdev_get_physector_size(fd, &physec); | |
224 | * if (rc || physec == 0) { | |
225 | * rc = blkdev_get_sector_size(fd, &physec); | |
226 | * if (rc) | |
227 | * physec = DEFAULT_SECTOR_SIZE; | |
228 | * } | |
229 | */ | |
8b9cf26a | 230 | #ifdef BLKPBSZGET |
0e75337c DB |
231 | int blkdev_get_physector_size(int fd, int *sector_size) |
232 | { | |
0e75337c DB |
233 | if (ioctl(fd, BLKPBSZGET, §or_size) >= 0) |
234 | return 0; | |
235 | return -1; | |
8b9cf26a | 236 | } |
0e75337c | 237 | #else |
8b9cf26a RM |
238 | int blkdev_get_physector_size(int fd __attribute__((__unused__)), int *sector_size) |
239 | { | |
0e75337c DB |
240 | *sector_size = DEFAULT_SECTOR_SIZE; |
241 | return 0; | |
0e75337c | 242 | } |
8b9cf26a | 243 | #endif |
3738a48e | 244 | |
ba326929 DB |
245 | /* |
246 | * Return the alignment status of a device | |
247 | */ | |
8b9cf26a | 248 | #ifdef BLKALIGNOFF |
ba326929 DB |
249 | int blkdev_is_misaligned(int fd) |
250 | { | |
251 | int aligned; | |
252 | ||
253 | if (ioctl(fd, BLKALIGNOFF, &aligned) < 0) | |
254 | return 0; /* probably kernel < 2.6.32 */ | |
6678c06f | 255 | /* |
9e930041 | 256 | * Note that kernel returns -1 as alignment offset if no compatible |
6678c06f KZ |
257 | * sizes and alignments exist for stacked devices |
258 | */ | |
259 | return aligned != 0 ? 1 : 0; | |
8b9cf26a | 260 | } |
4585ec0b | 261 | #else |
8b9cf26a RM |
262 | int blkdev_is_misaligned(int fd __attribute__((__unused__))) |
263 | { | |
4585ec0b | 264 | return 0; |
ba326929 | 265 | } |
8b9cf26a | 266 | #endif |
ba326929 | 267 | |
8fe1e638 SK |
268 | int open_blkdev_or_file(const struct stat *st, const char *name, const int oflag) |
269 | { | |
270 | int fd; | |
271 | ||
272 | if (S_ISBLK(st->st_mode)) { | |
273 | fd = open(name, oflag | O_EXCL); | |
274 | } else | |
275 | fd = open(name, oflag); | |
276 | if (-1 < fd && !is_same_inode(fd, st)) { | |
277 | close(fd); | |
278 | errno = EBADFD; | |
279 | return -1; | |
280 | } | |
281 | if (-1 < fd && S_ISBLK(st->st_mode) && blkdev_is_misaligned(fd)) | |
282 | warnx(_("warning: %s is misaligned"), name); | |
283 | return fd; | |
284 | } | |
285 | ||
8b9cf26a | 286 | #ifdef CDROM_GET_CAPABILITY |
3b622ddd DB |
287 | int blkdev_is_cdrom(int fd) |
288 | { | |
3b622ddd DB |
289 | int ret; |
290 | ||
291 | if ((ret = ioctl(fd, CDROM_GET_CAPABILITY, NULL)) < 0) | |
292 | return 0; | |
293 | else | |
294 | return ret; | |
8b9cf26a | 295 | } |
3b622ddd | 296 | #else |
8b9cf26a RM |
297 | int blkdev_is_cdrom(int fd __attribute__((__unused__))) |
298 | { | |
3b622ddd | 299 | return 0; |
3b622ddd | 300 | } |
8b9cf26a | 301 | #endif |
0e75337c | 302 | |
64128b70 DB |
303 | /* |
304 | * Get kernel's interpretation of the device's geometry. | |
305 | * | |
306 | * Returns the heads and sectors - but not cylinders | |
307 | * as it's truncated for disks with more than 65535 tracks. | |
308 | * | |
309 | * Note that this is deprecated in favor of LBA addressing. | |
310 | */ | |
8b9cf26a | 311 | #ifdef HDIO_GETGEO |
64128b70 DB |
312 | int blkdev_get_geometry(int fd, unsigned int *h, unsigned int *s) |
313 | { | |
64128b70 DB |
314 | struct hd_geometry geometry; |
315 | ||
c3443509 | 316 | if (ioctl(fd, HDIO_GETGEO, &geometry) == 0) { |
64128b70 DB |
317 | *h = geometry.heads; |
318 | *s = geometry.sectors; | |
c3443509 | 319 | return 0; |
64128b70 DB |
320 | } |
321 | #else | |
8b9cf26a RM |
322 | int blkdev_get_geometry(int fd __attribute__((__unused__)), |
323 | unsigned int *h, unsigned int *s) | |
324 | { | |
64128b70 DB |
325 | *h = 0; |
326 | *s = 0; | |
327 | #endif | |
c3443509 | 328 | return -1; |
64128b70 DB |
329 | } |
330 | ||
ba32a946 | 331 | /* |
2fc4a256 | 332 | * Convert scsi type to human readable string. |
ba32a946 | 333 | */ |
2fc4a256 | 334 | const char *blkdev_scsi_type_to_name(int type) |
ba32a946 | 335 | { |
ba32a946 SK |
336 | switch (type) { |
337 | case SCSI_TYPE_DISK: | |
2fc4a256 | 338 | return "disk"; |
ba32a946 | 339 | case SCSI_TYPE_TAPE: |
2fc4a256 | 340 | return "tape"; |
ba32a946 | 341 | case SCSI_TYPE_PRINTER: |
2fc4a256 | 342 | return "printer"; |
ba32a946 | 343 | case SCSI_TYPE_PROCESSOR: |
2fc4a256 | 344 | return "processor"; |
ba32a946 | 345 | case SCSI_TYPE_WORM: |
2fc4a256 | 346 | return "worm"; |
ba32a946 | 347 | case SCSI_TYPE_ROM: |
2fc4a256 | 348 | return "rom"; |
ba32a946 | 349 | case SCSI_TYPE_SCANNER: |
2fc4a256 | 350 | return "scanner"; |
ba32a946 | 351 | case SCSI_TYPE_MOD: |
2fc4a256 | 352 | return "mo-disk"; |
ba32a946 | 353 | case SCSI_TYPE_MEDIUM_CHANGER: |
2fc4a256 | 354 | return "changer"; |
ba32a946 | 355 | case SCSI_TYPE_COMM: |
2fc4a256 | 356 | return "comm"; |
ba32a946 | 357 | case SCSI_TYPE_RAID: |
2fc4a256 | 358 | return "raid"; |
ba32a946 | 359 | case SCSI_TYPE_ENCLOSURE: |
2fc4a256 | 360 | return "enclosure"; |
ba32a946 | 361 | case SCSI_TYPE_RBC: |
2fc4a256 | 362 | return "rbc"; |
ba32a946 | 363 | case SCSI_TYPE_OSD: |
2fc4a256 | 364 | return "osd"; |
ba32a946 | 365 | case SCSI_TYPE_NO_LUN: |
2fc4a256 | 366 | return "no-lun"; |
ba32a946 | 367 | default: |
2fc4a256 | 368 | break; |
ba32a946 | 369 | } |
2fc4a256 | 370 | return NULL; |
ba32a946 SK |
371 | } |
372 | ||
293714c0 | 373 | #ifdef TEST_PROGRAM_BLKDEV |
3738a48e SK |
374 | #include <stdio.h> |
375 | #include <stdlib.h> | |
376 | #include <fcntl.h> | |
3738a48e SK |
377 | int |
378 | main(int argc, char **argv) | |
379 | { | |
380 | unsigned long long bytes; | |
381 | unsigned long long sectors; | |
0e75337c | 382 | int sector_size, phy_sector_size; |
3738a48e SK |
383 | int fd; |
384 | ||
385 | if (argc != 2) { | |
386 | fprintf(stderr, "usage: %s device\n", argv[0]); | |
387 | exit(EXIT_FAILURE); | |
388 | } | |
389 | ||
b1fa3e22 | 390 | if ((fd = open(argv[1], O_RDONLY|O_CLOEXEC)) < 0) |
3738a48e SK |
391 | err(EXIT_FAILURE, "open %s failed", argv[1]); |
392 | ||
393 | if (blkdev_get_size(fd, &bytes) < 0) | |
394 | err(EXIT_FAILURE, "blkdev_get_size() failed"); | |
395 | if (blkdev_get_sectors(fd, §ors) < 0) | |
396 | err(EXIT_FAILURE, "blkdev_get_sectors() failed"); | |
397 | if (blkdev_get_sector_size(fd, §or_size) < 0) | |
398 | err(EXIT_FAILURE, "blkdev_get_sector_size() failed"); | |
0e75337c DB |
399 | if (blkdev_get_physector_size(fd, &phy_sector_size) < 0) |
400 | err(EXIT_FAILURE, "blkdev_get_physector_size() failed"); | |
3738a48e | 401 | |
0e75337c DB |
402 | printf(" bytes: %llu\n", bytes); |
403 | printf(" sectors: %llu\n", sectors); | |
404 | printf(" sector size: %d\n", sector_size); | |
405 | printf("phy-sector size: %d\n", phy_sector_size); | |
3738a48e SK |
406 | |
407 | return EXIT_SUCCESS; | |
408 | } | |
409 | #endif /* TEST_PROGRAM */ |