dosfstools  4.2
About: dosfstools are utilities to create, check and label (MS-DOS) FAT filesystems.
  Fossies Dox: dosfstools-4.2.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

blkdev.c
Go to the documentation of this file.
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  */
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <sys/ioctl.h>
10 #include <sys/sysmacros.h>
11 #include <unistd.h>
12 #include <stdint.h>
13 #include <stdio.h>
14 #include <limits.h>
15 
16 #ifdef HAVE_LINUX_HDREG_H
17 #include <linux/hdreg.h>
18 #endif
19 
20 #ifdef HAVE_LINUX_FD_H
21 #include <linux/fd.h>
22 #endif
23 
24 #ifdef HAVE_SYS_DISKLABEL_H
25 #include <sys/disklabel.h>
26 #endif
27 
28 #ifdef HAVE_SYS_DISK_H
29 # ifdef HAVE_SYS_QUEUE_H
30 # include <sys/queue.h> /* for LIST_HEAD */
31 # endif
32 # include <sys/disk.h>
33 #endif
34 
35 #include "blkdev.h"
36 #include "linux_version.h"
37 
38 static long
39 blkdev_valid_offset (int fd, off_t offset) {
40  char ch;
41 
42  if (lseek (fd, offset, 0) < 0)
43  return 0;
44  if (read (fd, &ch, 1) < 1)
45  return 0;
46  return 1;
47 }
48 
49 int is_blkdev(int fd)
50 {
51  struct stat st;
52  return (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode));
53 }
54 
55 off_t
57  uintmax_t high, low = 0;
58 
59  for (high = 1024; blkdev_valid_offset (fd, high); ) {
60  if (high == UINTMAX_MAX)
61  return -1;
62 
63  low = high;
64 
65  if (high >= UINTMAX_MAX/2)
66  high = UINTMAX_MAX;
67  else
68  high *= 2;
69  }
70 
71  while (low < high - 1)
72  {
73  uintmax_t mid = (low + high) / 2;
74 
75  if (blkdev_valid_offset (fd, mid))
76  low = mid;
77  else
78  high = mid;
79  }
81  return (low + 1);
82 }
83 
84 /* get size in bytes */
85 int
86 blkdev_get_size(int fd, unsigned long long *bytes)
87 {
88 #ifdef DKIOCGETBLOCKCOUNT
89  /* Apple Darwin */
90  if (ioctl(fd, DKIOCGETBLOCKCOUNT, bytes) >= 0) {
91  *bytes <<= 9;
92  return 0;
93  }
94 #endif
95 
96 #ifdef BLKGETSIZE64
97  {
98 #ifdef __linux__
99  int ver = get_linux_version();
100 
101  /* kernels 2.4.15-2.4.17, had a broken BLKGETSIZE64 */
102  if (ver >= KERNEL_VERSION (2,6,0) ||
103  (ver >= KERNEL_VERSION (2,4,18) && ver < KERNEL_VERSION (2,5,0)))
104 #endif
105  if (ioctl(fd, BLKGETSIZE64, bytes) >= 0)
106  return 0;
107  }
108 #endif /* BLKGETSIZE64 */
109 
110 #ifdef BLKGETSIZE
111  {
112  unsigned long size;
113 
114  if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
115  *bytes = ((unsigned long long)size << 9);
116  return 0;
117  }
118  }
119 
120 #endif /* BLKGETSIZE */
121 
122 #ifdef DIOCGMEDIASIZE
123  /* FreeBSD */
124  if (ioctl(fd, DIOCGMEDIASIZE, bytes) >= 0)
125  return 0;
126 #endif
127 
128 #ifdef FDGETPRM
129  {
130  struct floppy_struct this_floppy;
131 
132  if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
133  *bytes = ((unsigned long long) this_floppy.size) << 9;
134  return 0;
135  }
136  }
137 #endif /* FDGETPRM */
138 
139 #ifdef HAVE_SYS_DISKLABEL_H
140  {
141  /*
142  * This code works for FreeBSD 4.11 i386, except for the full device
143  * (such as /dev/ad0). It doesn't work properly for newer FreeBSD
144  * though. FreeBSD >= 5.0 should be covered by the DIOCGMEDIASIZE
145  * above however.
146  *
147  * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw,
148  * character) devices, so we need to check for S_ISCHR, too.
149  */
150  int part = -1;
151  struct disklabel lab;
152  struct partition *pp;
153  struct stat st;
154 
155  if ((fstat(fd, &st) >= 0) &&
156  (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)))
157  part = st.st_rdev & 7;
158 
159  if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
160  pp = &lab.d_partitions[part];
161  if (pp->p_size) {
162  *bytes = pp->p_size << 9;
163  return 0;
164  }
165  }
166  }
167 #endif /* HAVE_SYS_DISKLABEL_H */
168 
169  {
170  struct stat st;
171 
172  if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
173  *bytes = st.st_size;
174  return 0;
175  }
176  if (!S_ISBLK(st.st_mode))
177  return -1;
178  }
179 
180  *bytes = blkdev_find_size(fd);
181  return 0;
182 }
183 
184 /* get 512-byte sector count */
185 int
186 blkdev_get_sectors(int fd, unsigned long long *sectors)
187 {
188  unsigned long long bytes;
189 
190  if (blkdev_get_size(fd, &bytes) == 0) {
191  *sectors = (bytes >> 9);
192  return 0;
193  }
194 
195  return -1;
196 }
197 
198 /*
199  * Get logical sector size.
200  *
201  * This is the smallest unit the storage device can
202  * address. It is typically 512 bytes.
203  */
205 {
206 #ifdef BLKSSZGET
207  if (ioctl(fd, BLKSSZGET, sector_size) >= 0)
208  return 0;
209  return -1;
210 #else
211  (void)fd; /* prevent unused parameter warning */
213  return 0;
214 #endif
215 }
216 
217 /*
218  * Get physical block device size. The BLKPBSZGET is supported since Linux
219  * 2.6.32. For old kernels is probably the best to assume that physical sector
220  * size is the same as logical sector size.
221  *
222  * Example:
223  *
224  * rc = blkdev_get_physector_size(fd, &physec);
225  * if (rc || physec == 0) {
226  * rc = blkdev_get_sector_size(fd, &physec);
227  * if (rc)
228  * physec = DEFAULT_SECTOR_SIZE;
229  * }
230  */
232 {
233 #ifdef BLKPBSZGET
234  if (ioctl(fd, BLKPBSZGET, &sector_size) >= 0)
235  return 0;
236  return -1;
237 #else
238  (void)fd; /* prevent unused parameter warning */
240  return 0;
241 #endif
242 }
243 
244 /*
245  * Return the alignment status of a device
246  */
248 {
249 #ifdef BLKALIGNOFF
250  int aligned;
251 
252  if (ioctl(fd, BLKALIGNOFF, &aligned) < 0)
253  return 0; /* probably kernel < 2.6.32 */
254  /*
255  * Note that kernel returns -1 as alignement offset if no compatible
256  * sizes and alignments exist for stacked devices
257  */
258  return aligned != 0 ? 1 : 0;
259 #else
260  (void)fd; /* prevent unused parameter warning */
261  return 0;
262 #endif
263 }
264 
266 {
267 #ifdef CDROM_GET_CAPABILITY
268  int ret;
269 
270  if ((ret = ioctl(fd, CDROM_GET_CAPABILITY, NULL)) < 0)
271  return 0;
272  else
273  return ret;
274 #else
275  (void)fd; /* prevent unused parameter warning */
276  return 0;
277 #endif
278 }
279 
280 /*
281  * Get kernel's interpretation of the device's geometry.
282  *
283  * Returns the heads and sectors - but not cylinders
284  * as it's truncated for disks with more than 65535 tracks.
285  *
286  * Note that this is deprecated in favor of LBA addressing.
287  */
288 int blkdev_get_geometry(int fd, unsigned int *h, unsigned int *s)
289 {
290 #ifdef HDIO_GETGEO
291  {
292  struct hd_geometry geometry;
293 
294  if (ioctl(fd, HDIO_GETGEO, &geometry) == 0) {
295  *h = geometry.heads;
296  *s = geometry.sectors;
297  return 0;
298  }
299  }
300 #endif
301 
302 #ifdef FDGETPRM
303  {
304  struct floppy_struct fdparam;
305 
306  if (ioctl(fd, FDGETPRM, &fdparam) == 0) {
307  *h = fdparam.head;
308  *s = fdparam.sect;
309  return 0;
310  }
311  }
312 #endif
313 
314  (void)fd; /* prevent unused parameter warning */
315  *h = 0;
316  *s = 0;
317  return -1;
318 }
319 
320 /*
321  * Get start offset of partition
322  */
323 int blkdev_get_start(int fd, dev_t rdev, unsigned long long *s)
324 {
325 #ifdef __linux__
326  {
327  char path[PATH_MAX];
328  FILE *file;
329  int ret;
330 
331  snprintf(path, sizeof(path), "/sys/dev/block/%d:%d/start", major(rdev), minor(rdev));
332  file = fopen(path, "r");
333  if (file) {
334  ret = fscanf(file, "%llu", s);
335  fclose(file);
336  if (ret == 1)
337  return 0;
338  }
339  }
340 #endif
341 
342 #ifdef HDIO_GETGEO
343  {
344  struct hd_geometry geometry;
345 
346  if (ioctl(fd, HDIO_GETGEO, &geometry) == 0) {
347  *s = geometry.start;
348  return 0;
349  }
350  }
351 #endif
352 
353  (void)rdev; /* prevent unused parameter warning */
354  (void)fd; /* prevent unused parameter warning */
355  *s = 0;
356  return -1;
357 }
358 
359 /*
360  * Convert scsi type to human readable string.
361  */
362 const char *blkdev_scsi_type_to_name(int type)
363 {
364  switch (type) {
365  case SCSI_TYPE_DISK:
366  return "disk";
367  case SCSI_TYPE_TAPE:
368  return "tape";
369  case SCSI_TYPE_PRINTER:
370  return "printer";
371  case SCSI_TYPE_PROCESSOR:
372  return "processor";
373  case SCSI_TYPE_WORM:
374  return "worm";
375  case SCSI_TYPE_ROM:
376  return "rom";
377  case SCSI_TYPE_SCANNER:
378  return "scanner";
379  case SCSI_TYPE_MOD:
380  return "mo-disk";
382  return "changer";
383  case SCSI_TYPE_COMM:
384  return "comm";
385  case SCSI_TYPE_RAID:
386  return "raid";
387  case SCSI_TYPE_ENCLOSURE:
388  return "enclosure";
389  case SCSI_TYPE_RBC:
390  return "rbc";
391  case SCSI_TYPE_OSD:
392  return "osd";
393  case SCSI_TYPE_NO_LUN:
394  return "no-lun";
395  default:
396  break;
397  }
398  return NULL;
399 }
off_t blkdev_find_size(int fd)
Definition: blkdev.c:56
int is_blkdev(int fd)
Definition: blkdev.c:49
const char * blkdev_scsi_type_to_name(int type)
Definition: blkdev.c:362
int blkdev_get_sectors(int fd, unsigned long long *sectors)
Definition: blkdev.c:186
int blkdev_get_size(int fd, unsigned long long *bytes)
Definition: blkdev.c:86
int blkdev_get_geometry(int fd, unsigned int *h, unsigned int *s)
Definition: blkdev.c:288
static long blkdev_valid_offset(int fd, off_t offset)
Definition: blkdev.c:39
int blkdev_get_sector_size(int fd, int *sector_size)
Definition: blkdev.c:204
int blkdev_get_start(int fd, dev_t rdev, unsigned long long *s)
Definition: blkdev.c:323
int blkdev_is_misaligned(int fd)
Definition: blkdev.c:247
int blkdev_get_physector_size(int fd, int *sector_size)
Definition: blkdev.c:231
int blkdev_is_cdrom(int fd)
Definition: blkdev.c:265
#define SCSI_TYPE_SCANNER
Definition: blkdev.h:135
#define SCSI_TYPE_WORM
Definition: blkdev.h:133
#define SCSI_TYPE_OSD
Definition: blkdev.h:142
#define SCSI_TYPE_COMM
Definition: blkdev.h:138
#define SCSI_TYPE_PRINTER
Definition: blkdev.h:131
#define SCSI_TYPE_MEDIUM_CHANGER
Definition: blkdev.h:137
#define SCSI_TYPE_RAID
Definition: blkdev.h:139
#define SCSI_TYPE_RBC
Definition: blkdev.h:141
#define SCSI_TYPE_ROM
Definition: blkdev.h:134
#define SCSI_TYPE_NO_LUN
Definition: blkdev.h:143
#define SCSI_TYPE_ENCLOSURE
Definition: blkdev.h:140
#define SCSI_TYPE_DISK
Definition: blkdev.h:129
#define DEFAULT_SECTOR_SIZE
Definition: blkdev.h:22
#define SCSI_TYPE_PROCESSOR
Definition: blkdev.h:132
#define SCSI_TYPE_TAPE
Definition: blkdev.h:130
#define SCSI_TYPE_MOD
Definition: blkdev.h:136
static int fd
Definition: io.c:56
int get_linux_version(void)
Definition: linux_version.c:6
#define KERNEL_VERSION(a, b, c)
Definition: linux_version.h:9
static unsigned sector_size
Definition: mkfs.fat.c:240
unsigned long start
Definition: blkdev.h:92
unsigned char heads
Definition: blkdev.h:89
unsigned char sectors
Definition: blkdev.h:90