xref: /haiku/src/add-ons/kernel/file_systems/ntfs/libntfs/device.c (revision 579f1dbca962a2a03df54f69fdc6e9423f91f20e)
1 /**
2  * device.c - Low level device io functions. Originated from the Linux-NTFS project.
3  *
4  * Copyright (c) 2004-2006 Anton Altaparmakov
5  * Copyright (c) 2004-2006 Szabolcs Szakacsits
6  * Copyright (c) 2010      Jean-Pierre Andre
7  *
8  * This program/include file is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as published
10  * by the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program/include file is distributed in the hope that it will be
14  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program (in the main directory of the NTFS-3G
20  * distribution in the file COPYING); if not, write to the Free Software
21  * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #endif
37 #ifdef HAVE_ERRNO_H
38 #include <errno.h>
39 #endif
40 #ifdef HAVE_STDIO_H
41 #include <stdio.h>
42 #endif
43 #ifdef HAVE_SYS_TYPES_H
44 #include <sys/types.h>
45 #endif
46 #ifdef HAVE_SYS_STAT_H
47 #include <sys/stat.h>
48 #endif
49 #ifdef HAVE_FCNTL_H
50 #include <fcntl.h>
51 #endif
52 #ifdef HAVE_SYS_IOCTL_H
53 #include <sys/ioctl.h>
54 #endif
55 #ifdef HAVE_SYS_PARAM_H
56 #include <sys/param.h>
57 #endif
58 #ifdef HAVE_SYS_MOUNT_H
59 #include <sys/mount.h>
60 #endif
61 #ifdef HAVE_SYS_DISK_H
62 #include <sys/disk.h>
63 #endif
64 #ifdef HAVE_LINUX_FD_H
65 #include <linux/fd.h>
66 #endif
67 #ifdef HAVE_LINUX_HDREG_H
68 #include <linux/hdreg.h>
69 #endif
70 #ifdef __HAIKU__
71 #include <Drivers.h>
72 #endif
73 
74 #include "types.h"
75 #include "mst.h"
76 #include "debug.h"
77 #include "device.h"
78 #include "logging.h"
79 #include "misc.h"
80 
81 #if defined(linux) && defined(_IO) && !defined(BLKGETSIZE)
82 #define BLKGETSIZE	_IO(0x12,96)  /* Get device size in 512-byte blocks. */
83 #endif
84 #if defined(linux) && defined(_IOR) && !defined(BLKGETSIZE64)
85 #define BLKGETSIZE64	_IOR(0x12,114,size_t)	/* Get device size in bytes. */
86 #endif
87 #if defined(linux) && !defined(HDIO_GETGEO)
88 #define HDIO_GETGEO	0x0301	/* Get device geometry. */
89 #endif
90 #if defined(linux) && defined(_IO) && !defined(BLKSSZGET)
91 #	define BLKSSZGET _IO(0x12,104) /* Get device sector size in bytes. */
92 #endif
93 #if defined(linux) && defined(_IO) && !defined(BLKBSZSET)
94 #	define BLKBSZSET _IOW(0x12,113,size_t) /* Set device block size in bytes. */
95 #endif
96 
97 /**
98  * ntfs_device_alloc - allocate an ntfs device structure and pre-initialize it
99  * @name:	name of the device (must be present)
100  * @state:	initial device state (usually zero)
101  * @dops:	ntfs device operations to use with the device (must be present)
102  * @priv_data:	pointer to private data (optional)
103  *
104  * Allocate an ntfs device structure and pre-initialize it with the user-
105  * specified device operations @dops, device state @state, device name @name,
106  * and optional private data @priv_data.
107  *
108  * Note, @name is copied and can hence be freed after this functions returns.
109  *
110  * On success return a pointer to the allocated ntfs device structure and on
111  * error return NULL with errno set to the error code returned by ntfs_malloc().
112  */
113 struct ntfs_device *ntfs_device_alloc(const char *name, const long state,
114 		struct ntfs_device_operations *dops, void *priv_data)
115 {
116 	struct ntfs_device *dev;
117 
118 	if (!name) {
119 		errno = EINVAL;
120 		return NULL;
121 	}
122 
123 	dev = ntfs_malloc(sizeof(struct ntfs_device));
124 	if (dev) {
125 		if (!(dev->d_name = strdup(name))) {
126 			int eo = errno;
127 			free(dev);
128 			errno = eo;
129 			return NULL;
130 		}
131 		dev->d_ops = dops;
132 		dev->d_state = state;
133 		dev->d_private = priv_data;
134 	}
135 	return dev;
136 }
137 
138 /**
139  * ntfs_device_free - free an ntfs device structure
140  * @dev:	ntfs device structure to free
141  *
142  * Free the ntfs device structure @dev.
143  *
144  * Return 0 on success or -1 on error with errno set to the error code. The
145  * following error codes are defined:
146  *	EINVAL		Invalid pointer @dev.
147  *	EBUSY		Device is still open. Close it before freeing it!
148  */
149 int ntfs_device_free(struct ntfs_device *dev)
150 {
151 	if (!dev) {
152 		errno = EINVAL;
153 		return -1;
154 	}
155 	if (NDevOpen(dev)) {
156 		errno = EBUSY;
157 		return -1;
158 	}
159 	free(dev->d_name);
160 	free(dev);
161 	return 0;
162 }
163 
164 /*
165  *		Sync the device
166  *
167  *	returns zero if successful.
168  */
169 
170 int ntfs_device_sync(struct ntfs_device *dev)
171 {
172 	int ret;
173 	struct ntfs_device_operations *dops;
174 
175 	if (NDevDirty(dev)) {
176 		dops = dev->d_ops;
177 		ret = dops->sync(dev);
178 	} else
179 		ret = 0;
180 	return ret;
181 }
182 
183 /**
184  * ntfs_pread - positioned read from disk
185  * @dev:	device to read from
186  * @pos:	position in device to read from
187  * @count:	number of bytes to read
188  * @b:		output data buffer
189  *
190  * This function will read @count bytes from device @dev at position @pos into
191  * the data buffer @b.
192  *
193  * On success, return the number of successfully read bytes. If this number is
194  * lower than @count this means that we have either reached end of file or
195  * encountered an error during the read so that the read is partial. 0 means
196  * end of file or nothing to read (@count is 0).
197  *
198  * On error and nothing has been read, return -1 with errno set appropriately
199  * to the return code of either seek, read, or set to EINVAL in case of
200  * invalid arguments.
201  */
202 s64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, void *b)
203 {
204 	s64 br, total;
205 	struct ntfs_device_operations *dops;
206 
207 	ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count);
208 
209 	if (!b || count < 0 || pos < 0) {
210 		errno = EINVAL;
211 		return -1;
212 	}
213 	if (!count)
214 		return 0;
215 
216 	dops = dev->d_ops;
217 
218 	for (total = 0; count; count -= br, total += br) {
219 		br = dops->pread(dev, (char*)b + total, count, pos + total);
220 		/* If everything ok, continue. */
221 		if (br > 0)
222 			continue;
223 		/* If EOF or error return number of bytes read. */
224 		if (!br || total)
225 			return total;
226 		/* Nothing read and error, return error status. */
227 		return br;
228 	}
229 	/* Finally, return the number of bytes read. */
230 	return total;
231 }
232 
233 /**
234  * ntfs_pwrite - positioned write to disk
235  * @dev:	device to write to
236  * @pos:	position in file descriptor to write to
237  * @count:	number of bytes to write
238  * @b:		data buffer to write to disk
239  *
240  * This function will write @count bytes from data buffer @b to the device @dev
241  * at position @pos.
242  *
243  * On success, return the number of successfully written bytes. If this number
244  * is lower than @count this means that the write has been interrupted in
245  * flight or that an error was encountered during the write so that the write
246  * is partial. 0 means nothing was written (also return 0 when @count is 0).
247  *
248  * On error and nothing has been written, return -1 with errno set
249  * appropriately to the return code of either seek, write, or set
250  * to EINVAL in case of invalid arguments.
251  */
252 s64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
253 		const void *b)
254 {
255 	s64 written, total, ret = -1;
256 	struct ntfs_device_operations *dops;
257 
258 	ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count);
259 
260 	if (!b || count < 0 || pos < 0) {
261 		errno = EINVAL;
262 		goto out;
263 	}
264 	if (!count)
265 		return 0;
266 	if (NDevReadOnly(dev)) {
267 		errno = EROFS;
268 		goto out;
269 	}
270 
271 	dops = dev->d_ops;
272 
273 	NDevSetDirty(dev);
274 	for (total = 0; count; count -= written, total += written) {
275 		written = dops->pwrite(dev, (const char*)b + total, count,
276 				       pos + total);
277 		/* If everything ok, continue. */
278 		if (written > 0)
279 			continue;
280 		/*
281 		 * If nothing written or error return number of bytes written.
282 		 */
283 		if (!written || total)
284 			break;
285 		/* Nothing written and error, return error status. */
286 		total = written;
287 		break;
288 	}
289 	if (NDevSync(dev) && total && dops->sync(dev)) {
290 		total--; /* on sync error, return partially written */
291 	}
292 	ret = total;
293 out:
294 	return ret;
295 }
296 
297 /**
298  * ntfs_mst_pread - multi sector transfer (mst) positioned read
299  * @dev:	device to read from
300  * @pos:	position in file descriptor to read from
301  * @count:	number of blocks to read
302  * @bksize:	size of each block that needs mst deprotecting
303  * @b:		output data buffer
304  *
305  * Multi sector transfer (mst) positioned read. This function will read @count
306  * blocks of size @bksize bytes each from device @dev at position @pos into the
307  * the data buffer @b.
308  *
309  * On success, return the number of successfully read blocks. If this number is
310  * lower than @count this means that we have reached end of file, that the read
311  * was interrupted, or that an error was encountered during the read so that
312  * the read is partial. 0 means end of file or nothing was read (also return 0
313  * when @count or @bksize are 0).
314  *
315  * On error and nothing was read, return -1 with errno set appropriately to the
316  * return code of either seek, read, or set to EINVAL in case of invalid
317  * arguments.
318  *
319  * NOTE: If an incomplete multi sector transfer has been detected the magic
320  * will have been changed to magic_BAAD but no error will be returned. Thus it
321  * is possible that we return count blocks as being read but that any number
322  * (between zero and count!) of these blocks is actually subject to a multi
323  * sector transfer error. This should be detected by the caller by checking for
324  * the magic being "BAAD".
325  */
326 s64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count,
327 		const u32 bksize, void *b)
328 {
329 	s64 br, i;
330 
331 	if (bksize & (bksize - 1) || bksize % NTFS_BLOCK_SIZE) {
332 		errno = EINVAL;
333 		return -1;
334 	}
335 	/* Do the read. */
336 	br = ntfs_pread(dev, pos, count * bksize, b);
337 	if (br < 0)
338 		return br;
339 	/*
340 	 * Apply fixups to successfully read data, disregarding any errors
341 	 * returned from the MST fixup function. This is because we want to
342 	 * fixup everything possible and we rely on the fact that the "BAAD"
343 	 * magic will be detected later on.
344 	 */
345 	count = br / bksize;
346 	for (i = 0; i < count; ++i)
347 		ntfs_mst_post_read_fixup((NTFS_RECORD*)
348 				((u8*)b + i * bksize), bksize);
349 	/* Finally, return the number of complete blocks read. */
350 	return count;
351 }
352 
353 /**
354  * ntfs_mst_pwrite - multi sector transfer (mst) positioned write
355  * @dev:	device to write to
356  * @pos:	position in file descriptor to write to
357  * @count:	number of blocks to write
358  * @bksize:	size of each block that needs mst protecting
359  * @b:		data buffer to write to disk
360  *
361  * Multi sector transfer (mst) positioned write. This function will write
362  * @count blocks of size @bksize bytes each from data buffer @b to the device
363  * @dev at position @pos.
364  *
365  * On success, return the number of successfully written blocks. If this number
366  * is lower than @count this means that the write has been interrupted or that
367  * an error was encountered during the write so that the write is partial. 0
368  * means nothing was written (also return 0 when @count or @bksize are 0).
369  *
370  * On error and nothing has been written, return -1 with errno set
371  * appropriately to the return code of either seek, write, or set
372  * to EINVAL in case of invalid arguments.
373  *
374  * NOTE: We mst protect the data, write it, then mst deprotect it using a quick
375  * deprotect algorithm (no checking). This saves us from making a copy before
376  * the write and at the same time causes the usn to be incremented in the
377  * buffer. This conceptually fits in better with the idea that cached data is
378  * always deprotected and protection is performed when the data is actually
379  * going to hit the disk and the cache is immediately deprotected again
380  * simulating an mst read on the written data. This way cache coherency is
381  * achieved.
382  */
383 s64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
384 		const u32 bksize, void *b)
385 {
386 	s64 written, i;
387 
388 	if (count < 0 || bksize % NTFS_BLOCK_SIZE) {
389 		errno = EINVAL;
390 		return -1;
391 	}
392 	if (!count)
393 		return 0;
394 	/* Prepare data for writing. */
395 	for (i = 0; i < count; ++i) {
396 		int err;
397 
398 		err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)
399 				((u8*)b + i * bksize), bksize);
400 		if (err < 0) {
401 			/* Abort write at this position. */
402 			if (!i)
403 				return err;
404 			count = i;
405 			break;
406 		}
407 	}
408 	/* Write the prepared data. */
409 	written = ntfs_pwrite(dev, pos, count * bksize, b);
410 	/* Quickly deprotect the data again. */
411 	for (i = 0; i < count; ++i)
412 		ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)b + i * bksize));
413 	if (written <= 0)
414 		return written;
415 	/* Finally, return the number of complete blocks written. */
416 	return written / bksize;
417 }
418 
419 /**
420  * ntfs_cluster_read - read ntfs clusters
421  * @vol:	volume to read from
422  * @lcn:	starting logical cluster number
423  * @count:	number of clusters to read
424  * @b:		output data buffer
425  *
426  * Read @count ntfs clusters starting at logical cluster number @lcn from
427  * volume @vol into buffer @b. Return number of clusters read or -1 on error,
428  * with errno set to the error code.
429  */
430 s64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, const s64 count,
431 		void *b)
432 {
433 	s64 br;
434 
435 	if (!vol || lcn < 0 || count < 0) {
436 		errno = EINVAL;
437 		return -1;
438 	}
439 	if (vol->nr_clusters < lcn + count) {
440 		errno = ESPIPE;
441 		ntfs_log_perror("Trying to read outside of volume "
442 				"(%lld < %lld)", (long long)vol->nr_clusters,
443 			        (long long)lcn + count);
444 		return -1;
445 	}
446 	br = ntfs_pread(vol->dev, lcn << vol->cluster_size_bits,
447 			count << vol->cluster_size_bits, b);
448 	if (br < 0) {
449 		ntfs_log_perror("Error reading cluster(s)");
450 		return br;
451 	}
452 	return br >> vol->cluster_size_bits;
453 }
454 
455 /**
456  * ntfs_cluster_write - write ntfs clusters
457  * @vol:	volume to write to
458  * @lcn:	starting logical cluster number
459  * @count:	number of clusters to write
460  * @b:		data buffer to write to disk
461  *
462  * Write @count ntfs clusters starting at logical cluster number @lcn from
463  * buffer @b to volume @vol. Return the number of clusters written or -1 on
464  * error, with errno set to the error code.
465  */
466 s64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn,
467 		const s64 count, const void *b)
468 {
469 	s64 bw;
470 
471 	if (!vol || lcn < 0 || count < 0) {
472 		errno = EINVAL;
473 		return -1;
474 	}
475 	if (vol->nr_clusters < lcn + count) {
476 		errno = ESPIPE;
477 		ntfs_log_perror("Trying to write outside of volume "
478 				"(%lld < %lld)", (long long)vol->nr_clusters,
479 			        (long long)lcn + count);
480 		return -1;
481 	}
482 	if (!NVolReadOnly(vol))
483 		bw = ntfs_pwrite(vol->dev, lcn << vol->cluster_size_bits,
484 				count << vol->cluster_size_bits, b);
485 	else
486 		bw = count << vol->cluster_size_bits;
487 	if (bw < 0) {
488 		ntfs_log_perror("Error writing cluster(s)");
489 		return bw;
490 	}
491 	return bw >> vol->cluster_size_bits;
492 }
493 
494 /**
495  * ntfs_device_offset_valid - test if a device offset is valid
496  * @dev:	open device
497  * @ofs:	offset to test for validity
498  *
499  * Test if the offset @ofs is an existing location on the device described
500  * by the open device structure @dev.
501  *
502  * Return 0 if it is valid and -1 if it is not valid.
503  */
504 static int ntfs_device_offset_valid(struct ntfs_device *dev, s64 ofs)
505 {
506 	char ch;
507 
508 	if (dev->d_ops->seek(dev, ofs, SEEK_SET) >= 0 &&
509 			dev->d_ops->read(dev, &ch, 1) == 1)
510 		return 0;
511 	return -1;
512 }
513 
514 /**
515  * ntfs_device_size_get - return the size of a device in blocks
516  * @dev:	open device
517  * @block_size:	block size in bytes in which to return the result
518  *
519  * Return the number of @block_size sized blocks in the device described by the
520  * open device @dev.
521  *
522  * Adapted from e2fsutils-1.19, Copyright (C) 1995 Theodore Ts'o.
523  *
524  * On error return -1 with errno set to the error code.
525  */
526 s64 ntfs_device_size_get(struct ntfs_device *dev, int block_size)
527 {
528 	s64 high, low;
529 
530 	if (!dev || block_size <= 0 || (block_size - 1) & block_size) {
531 		errno = EINVAL;
532 		return -1;
533 	}
534 #ifdef BLKGETSIZE64
535 	{	u64 size;
536 
537 		if (dev->d_ops->ioctl(dev, BLKGETSIZE64, &size) >= 0) {
538 			ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu (0x%llx)\n",
539 					(unsigned long long)size,
540 					(unsigned long long)size);
541 			return (s64)size / block_size;
542 		}
543 	}
544 #endif
545 #ifdef BLKGETSIZE
546 	{	unsigned long size;
547 
548 		if (dev->d_ops->ioctl(dev, BLKGETSIZE, &size) >= 0) {
549 			ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu (0x%lx)\n",
550 					size, size);
551 			return (s64)size * 512 / block_size;
552 		}
553 	}
554 #endif
555 #ifdef FDGETPRM
556 	{       struct floppy_struct this_floppy;
557 
558 		if (dev->d_ops->ioctl(dev, FDGETPRM, &this_floppy) >= 0) {
559 			ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu (0x%lx)\n",
560 					(unsigned long)this_floppy.size,
561 					(unsigned long)this_floppy.size);
562 			return (s64)this_floppy.size * 512 / block_size;
563 		}
564 	}
565 #endif
566 #ifdef DIOCGMEDIASIZE
567 	{
568 		/* FreeBSD */
569 		off_t size;
570 
571 		if (dev->d_ops->ioctl(dev, DIOCGMEDIASIZE, &size) >= 0) {
572 			ntfs_log_debug("DIOCGMEDIASIZE nr bytes = %llu (0x%llx)\n",
573 					(unsigned long long)size,
574 					(unsigned long long)size);
575 			return (s64)size / block_size;
576 		}
577 	}
578 #endif
579 #ifdef DKIOCGETBLOCKCOUNT
580 	{
581 		/* Mac OS X */
582 		uint64_t blocks;
583 		int sector_size;
584 
585 		sector_size = ntfs_device_sector_size_get(dev);
586 		if (sector_size >= 0 && dev->d_ops->ioctl(dev,
587 			DKIOCGETBLOCKCOUNT, &blocks) >= 0)
588 		{
589 			ntfs_log_debug("DKIOCGETBLOCKCOUNT nr blocks = %llu (0x%llx)\n",
590 				(unsigned long long) blocks,
591 				(unsigned long long) blocks);
592 			return blocks * sector_size / block_size;
593 		}
594 	}
595 #endif
596 #ifdef __HAIKU__
597 	{
598 		off_t size = 0;
599 
600 		partition_info partitionInfo;
601 		device_geometry geometry;
602 
603 		if (dev->d_ops->ioctl(dev, B_GET_PARTITION_INFO, &partitionInfo) == 0)
604 			size = partitionInfo.size;
605 		else if (dev->d_ops->ioctl(dev, B_GET_GEOMETRY, &geometry) == 0) {
606 			size = (off_t)geometry.cylinder_count * geometry.sectors_per_track
607 				* geometry.head_count * geometry.bytes_per_sector;
608 		}
609 
610 		if (size > 0)
611 			return (s64)size / block_size;
612 	}
613 #endif
614 	/*
615 	 * We couldn't figure it out by using a specialized ioctl,
616 	 * so do binary search to find the size of the device.
617 	 */
618 	low = 0LL;
619 	for (high = 1024LL; !ntfs_device_offset_valid(dev, high); high <<= 1)
620 		low = high;
621 	while (low < high - 1LL) {
622 		const s64 mid = (low + high) / 2;
623 
624 		if (!ntfs_device_offset_valid(dev, mid))
625 			low = mid;
626 		else
627 			high = mid;
628 	}
629 	dev->d_ops->seek(dev, 0LL, SEEK_SET);
630 	return (low + 1LL) / block_size;
631 }
632 
633 /**
634  * ntfs_device_partition_start_sector_get - get starting sector of a partition
635  * @dev:	open device
636  *
637  * On success, return the starting sector of the partition @dev in the parent
638  * block device of @dev.  On error return -1 with errno set to the error code.
639  *
640  * The following error codes are defined:
641  *	EINVAL		Input parameter error
642  *	EOPNOTSUPP	System does not support HDIO_GETGEO ioctl
643  *	ENOTTY		@dev is a file or a device not supporting HDIO_GETGEO
644  */
645 s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev)
646 {
647 	if (!dev) {
648 		errno = EINVAL;
649 		return -1;
650 	}
651 #ifdef HDIO_GETGEO
652 	{	struct hd_geometry geo;
653 
654 		if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) {
655 			ntfs_log_debug("HDIO_GETGEO start_sect = %lu (0x%lx)\n",
656 					geo.start, geo.start);
657 			return geo.start;
658 		}
659 	}
660 #else
661 	errno = EOPNOTSUPP;
662 #endif
663 	return -1;
664 }
665 
666 /**
667  * ntfs_device_heads_get - get number of heads of device
668  * @dev:		open device
669  *
670  * On success, return the number of heads on the device @dev.  On error return
671  * -1 with errno set to the error code.
672  *
673  * The following error codes are defined:
674  *	EINVAL		Input parameter error
675  *	EOPNOTSUPP	System does not support HDIO_GETGEO ioctl
676  *	ENOTTY		@dev is a file or a device not supporting HDIO_GETGEO
677  */
678 int ntfs_device_heads_get(struct ntfs_device *dev)
679 {
680 	if (!dev) {
681 		errno = EINVAL;
682 		return -1;
683 	}
684 #ifdef HDIO_GETGEO
685 	{	struct hd_geometry geo;
686 
687 		if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) {
688 			ntfs_log_debug("HDIO_GETGEO heads = %u (0x%x)\n",
689 					(unsigned)geo.heads,
690 					(unsigned)geo.heads);
691 			return geo.heads;
692 		}
693 	}
694 #else
695 	errno = EOPNOTSUPP;
696 #endif
697 	return -1;
698 }
699 
700 /**
701  * ntfs_device_sectors_per_track_get - get number of sectors per track of device
702  * @dev:		open device
703  *
704  * On success, return the number of sectors per track on the device @dev.  On
705  * error return -1 with errno set to the error code.
706  *
707  * The following error codes are defined:
708  *	EINVAL		Input parameter error
709  *	EOPNOTSUPP	System does not support HDIO_GETGEO ioctl
710  *	ENOTTY		@dev is a file or a device not supporting HDIO_GETGEO
711  */
712 int ntfs_device_sectors_per_track_get(struct ntfs_device *dev)
713 {
714 	if (!dev) {
715 		errno = EINVAL;
716 		return -1;
717 	}
718 #ifdef HDIO_GETGEO
719 	{	struct hd_geometry geo;
720 
721 		if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) {
722 			ntfs_log_debug("HDIO_GETGEO sectors_per_track = %u (0x%x)\n",
723 					(unsigned)geo.sectors,
724 					(unsigned)geo.sectors);
725 			return geo.sectors;
726 		}
727 	}
728 #else
729 	errno = EOPNOTSUPP;
730 #endif
731 	return -1;
732 }
733 
734 /**
735  * ntfs_device_sector_size_get - get sector size of a device
736  * @dev:	open device
737  *
738  * On success, return the sector size in bytes of the device @dev.
739  * On error return -1 with errno set to the error code.
740  *
741  * The following error codes are defined:
742  *	EINVAL		Input parameter error
743  *	EOPNOTSUPP	System does not support BLKSSZGET ioctl
744  *	ENOTTY		@dev is a file or a device not supporting BLKSSZGET
745  */
746 int ntfs_device_sector_size_get(struct ntfs_device *dev)
747 {
748 	if (!dev) {
749 		errno = EINVAL;
750 		return -1;
751 	}
752 #ifdef BLKSSZGET
753 	{
754 		int sect_size = 0;
755 
756 		if (!dev->d_ops->ioctl(dev, BLKSSZGET, &sect_size)) {
757 			ntfs_log_debug("BLKSSZGET sector size = %d bytes\n",
758 					sect_size);
759 			return sect_size;
760 		}
761 	}
762 #elif defined(DIOCGSECTORSIZE)
763 	{
764 		/* FreeBSD */
765 		size_t sect_size = 0;
766 
767 		if (!dev->d_ops->ioctl(dev, DIOCGSECTORSIZE, &sect_size)) {
768 			ntfs_log_debug("DIOCGSECTORSIZE sector size = %d bytes\n",
769 					(int) sect_size);
770 			return sect_size;
771 		}
772 	}
773 #elif defined(DKIOCGETBLOCKSIZE)
774 	{
775 		/* Mac OS X */
776 		uint32_t sect_size = 0;
777 
778 		if (!dev->d_ops->ioctl(dev, DKIOCGETBLOCKSIZE, &sect_size)) {
779 			ntfs_log_debug("DKIOCGETBLOCKSIZE sector size = %d bytes\n",
780 					(int) sect_size);
781 			return sect_size;
782 		}
783 	}
784 #else
785 	errno = EOPNOTSUPP;
786 #endif
787 	return -1;
788 }
789 
790 /**
791  * ntfs_device_block_size_set - set block size of a device
792  * @dev:	open device
793  * @block_size: block size to set @dev to
794  *
795  * On success, return 0.
796  * On error return -1 with errno set to the error code.
797  *
798  * The following error codes are defined:
799  *	EINVAL		Input parameter error
800  *	EOPNOTSUPP	System does not support BLKBSZSET ioctl
801  *	ENOTTY		@dev is a file or a device not supporting BLKBSZSET
802  */
803 int ntfs_device_block_size_set(struct ntfs_device *dev,
804 		int block_size __attribute__((unused)))
805 {
806 	if (!dev) {
807 		errno = EINVAL;
808 		return -1;
809 	}
810 #ifdef BLKBSZSET
811 	{
812 		size_t s_block_size = block_size;
813 		if (!dev->d_ops->ioctl(dev, BLKBSZSET, &s_block_size)) {
814 			ntfs_log_debug("Used BLKBSZSET to set block size to "
815 					"%d bytes.\n", block_size);
816 			return 0;
817 		}
818 		/* If not a block device, pretend it was successful. */
819 		if (!NDevBlock(dev))
820 			return 0;
821 	}
822 #else
823 	/* If not a block device, pretend it was successful. */
824 	if (!NDevBlock(dev))
825 		return 0;
826 	errno = EOPNOTSUPP;
827 #endif
828 	return -1;
829 }
830