paul@181 | 1 | /* |
paul@181 | 2 | * getsize.c --- get the size of a partition. |
paul@181 | 3 | * |
paul@181 | 4 | * Copyright (C) 1995, 1995 Theodore Ts'o. |
paul@181 | 5 | * |
paul@181 | 6 | * %Begin-Header% |
paul@181 | 7 | * This file may be redistributed under the terms of the |
paul@181 | 8 | * GNU Lesser General Public License. |
paul@181 | 9 | * %End-Header% |
paul@181 | 10 | */ |
paul@181 | 11 | |
paul@181 | 12 | #ifndef _LARGEFILE_SOURCE |
paul@181 | 13 | #define _LARGEFILE_SOURCE |
paul@181 | 14 | #endif |
paul@181 | 15 | #ifndef _LARGEFILE64_SOURCE |
paul@181 | 16 | #define _LARGEFILE64_SOURCE |
paul@181 | 17 | #endif |
paul@181 | 18 | |
paul@181 | 19 | #include "config.h" |
paul@181 | 20 | #include "blkidP.h" |
paul@181 | 21 | |
paul@181 | 22 | #include <stdio.h> |
paul@181 | 23 | #if HAVE_UNISTD_H |
paul@181 | 24 | #include <unistd.h> |
paul@181 | 25 | #endif |
paul@181 | 26 | #if HAVE_ERRNO_H |
paul@181 | 27 | #include <errno.h> |
paul@181 | 28 | #endif |
paul@181 | 29 | #include <fcntl.h> |
paul@181 | 30 | #ifdef HAVE_SYS_IOCTL_H |
paul@181 | 31 | #include <sys/ioctl.h> |
paul@181 | 32 | #endif |
paul@181 | 33 | #ifdef HAVE_LINUX_FD_H |
paul@181 | 34 | #include <linux/fd.h> |
paul@181 | 35 | #endif |
paul@181 | 36 | #ifdef HAVE_SYS_DISKLABEL_H |
paul@181 | 37 | #include <sys/disklabel.h> |
paul@181 | 38 | #endif |
paul@181 | 39 | #ifdef HAVE_SYS_DISK_H |
paul@181 | 40 | #include <sys/disk.h> |
paul@181 | 41 | #endif |
paul@181 | 42 | #ifdef __linux__ |
paul@181 | 43 | #include <sys/utsname.h> |
paul@181 | 44 | #endif |
paul@181 | 45 | #if HAVE_SYS_STAT_H |
paul@181 | 46 | #include <sys/stat.h> |
paul@181 | 47 | #endif |
paul@181 | 48 | |
paul@181 | 49 | |
paul@181 | 50 | #if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) |
paul@181 | 51 | #define BLKGETSIZE _IO(0x12,96) /* return device size */ |
paul@181 | 52 | #endif |
paul@181 | 53 | |
paul@181 | 54 | #if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) |
paul@181 | 55 | #define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ |
paul@181 | 56 | #endif |
paul@181 | 57 | |
paul@181 | 58 | #ifdef APPLE_DARWIN |
paul@181 | 59 | #define BLKGETSIZE DKIOCGETBLOCKCOUNT32 |
paul@181 | 60 | #endif /* APPLE_DARWIN */ |
paul@181 | 61 | |
paul@181 | 62 | static int valid_offset(int fd, blkid_loff_t offset) |
paul@181 | 63 | { |
paul@181 | 64 | char ch; |
paul@181 | 65 | |
paul@181 | 66 | if (blkid_llseek(fd, offset, 0) < 0) |
paul@181 | 67 | return 0; |
paul@181 | 68 | if (read(fd, &ch, 1) < 1) |
paul@181 | 69 | return 0; |
paul@181 | 70 | return 1; |
paul@181 | 71 | } |
paul@181 | 72 | |
paul@181 | 73 | /* |
paul@181 | 74 | * Returns the number of bytes in a partition |
paul@181 | 75 | */ |
paul@181 | 76 | blkid_loff_t blkid_get_dev_size(int fd) |
paul@181 | 77 | { |
paul@181 | 78 | unsigned long long size64; |
paul@181 | 79 | blkid_loff_t high, low; |
paul@181 | 80 | |
paul@181 | 81 | #if defined DKIOCGETBLOCKCOUNT && defined DKIOCGETBLOCKSIZE /* For Apple Darwin */ |
paul@181 | 82 | unsigned int size; |
paul@181 | 83 | |
paul@181 | 84 | if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0 && |
paul@181 | 85 | ioctl(fd, DKIOCGETBLOCKSIZE, &size) >= 0) { |
paul@181 | 86 | if (sizeof(blkid_loff_t) < sizeof(unsigned long long) && |
paul@181 | 87 | (size64 * size) > 0xFFFFFFFF) |
paul@181 | 88 | return 0; /* EFBIG */ |
paul@181 | 89 | return (blkid_loff_t)size64 * size; |
paul@181 | 90 | } |
paul@181 | 91 | #endif |
paul@181 | 92 | |
paul@181 | 93 | #ifdef BLKGETSIZE64 |
paul@181 | 94 | { |
paul@181 | 95 | int valid_blkgetsize64 = 1; |
paul@181 | 96 | #ifdef __linux__ |
paul@181 | 97 | struct utsname ut; |
paul@181 | 98 | |
paul@181 | 99 | if ((uname(&ut) == 0) && |
paul@181 | 100 | ((ut.release[0] == '2') && (ut.release[1] == '.') && |
paul@181 | 101 | (ut.release[2] < '6') && (ut.release[3] == '.'))) |
paul@181 | 102 | valid_blkgetsize64 = 0; |
paul@181 | 103 | #endif |
paul@181 | 104 | if (valid_blkgetsize64 && |
paul@181 | 105 | ioctl(fd, BLKGETSIZE64, &size64) >= 0) { |
paul@181 | 106 | if (sizeof(blkid_loff_t) < sizeof(unsigned long long) && |
paul@181 | 107 | (size64 > 0xFFFFFFFF)) |
paul@181 | 108 | return 0; /* EFBIG */ |
paul@181 | 109 | return size64; |
paul@181 | 110 | } |
paul@181 | 111 | } |
paul@181 | 112 | #endif /* BLKGETSIZE64 */ |
paul@181 | 113 | |
paul@181 | 114 | #ifdef BLKGETSIZE |
paul@181 | 115 | { |
paul@181 | 116 | unsigned long size; |
paul@181 | 117 | |
paul@181 | 118 | if (ioctl(fd, BLKGETSIZE, &size) >= 0) |
paul@181 | 119 | return (blkid_loff_t)size << 9; |
paul@181 | 120 | } |
paul@181 | 121 | #endif |
paul@181 | 122 | |
paul@181 | 123 | /* tested on FreeBSD 6.1-RELEASE i386 */ |
paul@181 | 124 | #ifdef DIOCGMEDIASIZE |
paul@181 | 125 | if (ioctl(fd, DIOCGMEDIASIZE, &size64) >= 0) |
paul@181 | 126 | return (off_t)size64; |
paul@181 | 127 | #endif /* DIOCGMEDIASIZE */ |
paul@181 | 128 | |
paul@181 | 129 | #ifdef FDGETPRM |
paul@181 | 130 | { |
paul@181 | 131 | struct floppy_struct this_floppy; |
paul@181 | 132 | |
paul@181 | 133 | if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) |
paul@181 | 134 | return (blkid_loff_t)this_floppy.size << 9; |
paul@181 | 135 | } |
paul@181 | 136 | #endif |
paul@181 | 137 | #if defined(HAVE_SYS_DISKLABEL_H) && defined(DIOCGDINFO) |
paul@181 | 138 | { |
paul@181 | 139 | int part = -1; |
paul@181 | 140 | struct disklabel lab; |
paul@181 | 141 | struct partition *pp; |
paul@181 | 142 | char ch; |
paul@181 | 143 | struct stat st; |
paul@181 | 144 | |
paul@181 | 145 | /* |
paul@181 | 146 | * This code works for FreeBSD 4.11 i386, except for the full |
paul@181 | 147 | * device (such as /dev/ad0). It doesn't work properly for |
paul@181 | 148 | * newer FreeBSD though. FreeBSD >= 5.0 should be covered by |
paul@181 | 149 | * the DIOCGMEDIASIZE above however. |
paul@181 | 150 | * |
paul@181 | 151 | * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw, |
paul@181 | 152 | * character) devices, so we need to check for S_ISCHR, too. |
paul@181 | 153 | */ |
paul@181 | 154 | if (fstat(fd, &st) >= 0 && |
paul@181 | 155 | blkidP_is_disk_device(st.st_mode)) |
paul@181 | 156 | part = st.st_rdev & 7; |
paul@181 | 157 | |
paul@181 | 158 | if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { |
paul@181 | 159 | pp = &lab.d_partitions[part]; |
paul@181 | 160 | if (pp->p_size) |
paul@181 | 161 | return pp->p_size << 9; |
paul@181 | 162 | } |
paul@181 | 163 | } |
paul@181 | 164 | #endif /* defined(HAVE_SYS_DISKLABEL_H) && defined(DIOCGDINFO) */ |
paul@181 | 165 | { |
paul@181 | 166 | #if defined(HAVE_FSTAT64) && !defined(__OSX_AVAILABLE_BUT_DEPRECATED) |
paul@181 | 167 | struct stat64 st; |
paul@181 | 168 | if (fstat64(fd, &st) == 0) |
paul@181 | 169 | #else |
paul@181 | 170 | struct stat st; |
paul@181 | 171 | if (fstat(fd, &st) == 0) |
paul@181 | 172 | #endif |
paul@181 | 173 | if (S_ISREG(st.st_mode)) |
paul@181 | 174 | return st.st_size; |
paul@181 | 175 | } |
paul@181 | 176 | |
paul@181 | 177 | /* |
paul@181 | 178 | * OK, we couldn't figure it out by using a specialized ioctl, |
paul@181 | 179 | * which is generally the best way. So do binary search to |
paul@181 | 180 | * find the size of the partition. |
paul@181 | 181 | */ |
paul@181 | 182 | low = 0; |
paul@181 | 183 | for (high = 1024; valid_offset(fd, high); high *= 2) |
paul@181 | 184 | low = high; |
paul@181 | 185 | while (low < high - 1) { |
paul@181 | 186 | const blkid_loff_t mid = (low + high) / 2; |
paul@181 | 187 | |
paul@181 | 188 | if (valid_offset(fd, mid)) |
paul@181 | 189 | low = mid; |
paul@181 | 190 | else |
paul@181 | 191 | high = mid; |
paul@181 | 192 | } |
paul@181 | 193 | return low + 1; |
paul@181 | 194 | } |
paul@181 | 195 | |
paul@181 | 196 | #ifdef TEST_PROGRAM |
paul@181 | 197 | int main(int argc, char **argv) |
paul@181 | 198 | { |
paul@181 | 199 | long long bytes; |
paul@181 | 200 | int fd; |
paul@181 | 201 | |
paul@181 | 202 | if (argc < 2) { |
paul@181 | 203 | fprintf(stderr, "Usage: %s device\n" |
paul@181 | 204 | "Determine the size of a device\n", argv[0]); |
paul@181 | 205 | return 1; |
paul@181 | 206 | } |
paul@181 | 207 | |
paul@181 | 208 | if ((fd = open(argv[1], O_RDONLY)) < 0) |
paul@181 | 209 | perror(argv[0]); |
paul@181 | 210 | |
paul@181 | 211 | bytes = blkid_get_dev_size(fd); |
paul@181 | 212 | printf("Device %s has %lld 1k blocks.\n", argv[1], |
paul@181 | 213 | (unsigned long long)bytes >> 10); |
paul@181 | 214 | |
paul@181 | 215 | return 0; |
paul@181 | 216 | } |
paul@181 | 217 | #endif |