blkid_getsize.c 3.9 KB

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