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