getsize.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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. /* include this before sys/queues.h! */
  12. #include "blkidP.h"
  13. #include <stdio.h>
  14. #if HAVE_UNISTD_H
  15. #include <unistd.h>
  16. #endif
  17. #if HAVE_ERRNO_H
  18. #include <errno.h>
  19. #endif
  20. #include <fcntl.h>
  21. #ifdef HAVE_SYS_IOCTL_H
  22. #include <sys/ioctl.h>
  23. #endif
  24. #ifdef HAVE_LINUX_FD_H
  25. #include <linux/fd.h>
  26. #endif
  27. #ifdef HAVE_SYS_DISKLABEL_H
  28. #include <sys/disklabel.h>
  29. #include <sys/stat.h>
  30. #endif
  31. #ifdef HAVE_SYS_DISK_H
  32. #ifdef HAVE_SYS_QUEUE_H
  33. #include <sys/queue.h> /* for LIST_HEAD */
  34. #endif
  35. #include <sys/disk.h>
  36. #endif
  37. #ifdef __linux__
  38. #include <sys/utsname.h>
  39. #endif
  40. #if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE)
  41. #define BLKGETSIZE _IO(0x12,96) /* return device size */
  42. #endif
  43. #if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64)
  44. #define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */
  45. #endif
  46. #ifdef APPLE_DARWIN
  47. #define BLKGETSIZE DKIOCGETBLOCKCOUNT32
  48. #endif /* APPLE_DARWIN */
  49. static int valid_offset(int fd, blkid_loff_t offset)
  50. {
  51. char ch;
  52. if (blkid_llseek(fd, offset, 0) < 0)
  53. return 0;
  54. if (read(fd, &ch, 1) < 1)
  55. return 0;
  56. return 1;
  57. }
  58. /*
  59. * Returns the number of blocks in a partition
  60. */
  61. blkid_loff_t blkid_get_dev_size(int fd)
  62. {
  63. int valid_blkgetsize64 = 1;
  64. #ifdef __linux__
  65. struct utsname ut;
  66. #endif
  67. unsigned long long size64;
  68. unsigned long size;
  69. blkid_loff_t high, low;
  70. #ifdef FDGETPRM
  71. struct floppy_struct this_floppy;
  72. #endif
  73. #ifdef HAVE_SYS_DISKLABEL_H
  74. int part = -1;
  75. struct disklabel lab;
  76. struct partition *pp;
  77. char ch;
  78. struct stat st;
  79. #endif /* HAVE_SYS_DISKLABEL_H */
  80. #ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */
  81. if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) {
  82. if ((sizeof(blkid_loff_t) < sizeof(unsigned long long))
  83. && (size64 << 9 > 0xFFFFFFFF))
  84. return 0; /* EFBIG */
  85. return (blkid_loff_t) size64 << 9;
  86. }
  87. #endif
  88. #ifdef BLKGETSIZE64
  89. #ifdef __linux__
  90. if ((uname(&ut) == 0) &&
  91. ((ut.release[0] == '2') && (ut.release[1] == '.') &&
  92. (ut.release[2] < '6') && (ut.release[3] == '.')))
  93. valid_blkgetsize64 = 0;
  94. #endif
  95. if (valid_blkgetsize64 &&
  96. ioctl(fd, BLKGETSIZE64, &size64) >= 0) {
  97. if ((sizeof(blkid_loff_t) < sizeof(unsigned long long))
  98. && ((size64) > 0xFFFFFFFF))
  99. return 0; /* EFBIG */
  100. return size64;
  101. }
  102. #endif
  103. #ifdef BLKGETSIZE
  104. if (ioctl(fd, BLKGETSIZE, &size) >= 0)
  105. return (blkid_loff_t)size << 9;
  106. #endif
  107. #ifdef FDGETPRM
  108. if (ioctl(fd, FDGETPRM, &this_floppy) >= 0)
  109. return (blkid_loff_t)this_floppy.size << 9;
  110. #endif
  111. #ifdef HAVE_SYS_DISKLABEL_H
  112. #if 0
  113. /*
  114. * This should work in theory but I haven't tested it. Anyone
  115. * on a BSD system want to test this for me? In the meantime,
  116. * binary search mechanism should work just fine.
  117. */
  118. if ((fstat(fd, &st) >= 0) && S_ISBLK(st.st_mode))
  119. part = st.st_rdev & 7;
  120. if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
  121. pp = &lab.d_partitions[part];
  122. if (pp->p_size)
  123. return pp->p_size << 9;
  124. }
  125. #endif
  126. #endif /* HAVE_SYS_DISKLABEL_H */
  127. /*
  128. * OK, we couldn't figure it out by using a specialized ioctl,
  129. * which is generally the best way. So do binary search to
  130. * find the size of the partition.
  131. */
  132. low = 0;
  133. for (high = 1024; valid_offset(fd, high); high *= 2)
  134. low = high;
  135. while (low < high - 1)
  136. {
  137. const blkid_loff_t mid = (low + high) / 2;
  138. if (valid_offset(fd, mid))
  139. low = mid;
  140. else
  141. high = mid;
  142. }
  143. return low + 1;
  144. }
  145. #ifdef TEST_PROGRAM
  146. int main(int argc, char **argv)
  147. {
  148. blkid_loff_t bytes;
  149. int fd;
  150. if (argc < 2) {
  151. fprintf(stderr, "Usage: %s device\n"
  152. "Determine the size of a device\n", argv[0]);
  153. return 1;
  154. }
  155. if ((fd = open(argv[1], O_RDONLY)) < 0)
  156. perror(argv[0]);
  157. bytes = blkid_get_dev_size(fd);
  158. printf("Device %s has %Ld 1k blocks.\n", argv[1], bytes >> 10);
  159. return 0;
  160. }
  161. #endif