1987da915Sopenharmony_ci/**
2987da915Sopenharmony_ci * device.c - Low level device io functions. Originated from the Linux-NTFS project.
3987da915Sopenharmony_ci *
4987da915Sopenharmony_ci * Copyright (c) 2004-2013 Anton Altaparmakov
5987da915Sopenharmony_ci * Copyright (c) 2004-2006 Szabolcs Szakacsits
6987da915Sopenharmony_ci * Copyright (c) 2010      Jean-Pierre Andre
7987da915Sopenharmony_ci * Copyright (c) 2008-2013 Tuxera Inc.
8987da915Sopenharmony_ci *
9987da915Sopenharmony_ci * This program/include file is free software; you can redistribute it and/or
10987da915Sopenharmony_ci * modify it under the terms of the GNU General Public License as published
11987da915Sopenharmony_ci * by the Free Software Foundation; either version 2 of the License, or
12987da915Sopenharmony_ci * (at your option) any later version.
13987da915Sopenharmony_ci *
14987da915Sopenharmony_ci * This program/include file is distributed in the hope that it will be
15987da915Sopenharmony_ci * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16987da915Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17987da915Sopenharmony_ci * GNU General Public License for more details.
18987da915Sopenharmony_ci *
19987da915Sopenharmony_ci * You should have received a copy of the GNU General Public License
20987da915Sopenharmony_ci * along with this program (in the main directory of the NTFS-3G
21987da915Sopenharmony_ci * distribution in the file COPYING); if not, write to the Free Software
22987da915Sopenharmony_ci * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23987da915Sopenharmony_ci */
24987da915Sopenharmony_ci
25987da915Sopenharmony_ci#ifdef HAVE_CONFIG_H
26987da915Sopenharmony_ci#include "config.h"
27987da915Sopenharmony_ci#endif
28987da915Sopenharmony_ci
29987da915Sopenharmony_ci#ifdef HAVE_UNISTD_H
30987da915Sopenharmony_ci#include <unistd.h>
31987da915Sopenharmony_ci#endif
32987da915Sopenharmony_ci#ifdef HAVE_STDLIB_H
33987da915Sopenharmony_ci#include <stdlib.h>
34987da915Sopenharmony_ci#endif
35987da915Sopenharmony_ci#ifdef HAVE_STRING_H
36987da915Sopenharmony_ci#include <string.h>
37987da915Sopenharmony_ci#endif
38987da915Sopenharmony_ci#ifdef HAVE_ERRNO_H
39987da915Sopenharmony_ci#include <errno.h>
40987da915Sopenharmony_ci#endif
41987da915Sopenharmony_ci#ifdef HAVE_STDIO_H
42987da915Sopenharmony_ci#include <stdio.h>
43987da915Sopenharmony_ci#endif
44987da915Sopenharmony_ci#ifdef HAVE_SYS_TYPES_H
45987da915Sopenharmony_ci#include <sys/types.h>
46987da915Sopenharmony_ci#endif
47987da915Sopenharmony_ci#ifdef HAVE_SYS_STAT_H
48987da915Sopenharmony_ci#include <sys/stat.h>
49987da915Sopenharmony_ci#endif
50987da915Sopenharmony_ci#ifdef HAVE_FCNTL_H
51987da915Sopenharmony_ci#include <fcntl.h>
52987da915Sopenharmony_ci#endif
53987da915Sopenharmony_ci#ifdef HAVE_SYS_IOCTL_H
54987da915Sopenharmony_ci#include <sys/ioctl.h>
55987da915Sopenharmony_ci#endif
56987da915Sopenharmony_ci#ifdef HAVE_SYS_PARAM_H
57987da915Sopenharmony_ci#include <sys/param.h>
58987da915Sopenharmony_ci#endif
59987da915Sopenharmony_ci#ifdef HAVE_SYS_MOUNT_H
60987da915Sopenharmony_ci#include <sys/mount.h>
61987da915Sopenharmony_ci#endif
62987da915Sopenharmony_ci#ifdef HAVE_SYS_DISK_H
63987da915Sopenharmony_ci#include <sys/disk.h>
64987da915Sopenharmony_ci#endif
65987da915Sopenharmony_ci#ifdef HAVE_LINUX_FD_H
66987da915Sopenharmony_ci#include <linux/fd.h>
67987da915Sopenharmony_ci#endif
68987da915Sopenharmony_ci#ifdef HAVE_LINUX_HDREG_H
69987da915Sopenharmony_ci#include <linux/hdreg.h>
70987da915Sopenharmony_ci#endif
71987da915Sopenharmony_ci#ifdef ENABLE_HD
72987da915Sopenharmony_ci#include <hd.h>
73987da915Sopenharmony_ci#endif
74987da915Sopenharmony_ci
75987da915Sopenharmony_ci#include "types.h"
76987da915Sopenharmony_ci#include "mst.h"
77987da915Sopenharmony_ci#include "debug.h"
78987da915Sopenharmony_ci#include "device.h"
79987da915Sopenharmony_ci#include "logging.h"
80987da915Sopenharmony_ci#include "misc.h"
81987da915Sopenharmony_ci
82987da915Sopenharmony_ci#if defined(linux) && defined(_IO) && !defined(BLKGETSIZE)
83987da915Sopenharmony_ci#define BLKGETSIZE	_IO(0x12,96)  /* Get device size in 512-byte blocks. */
84987da915Sopenharmony_ci#endif
85987da915Sopenharmony_ci#if defined(linux) && defined(_IOR) && !defined(BLKGETSIZE64)
86987da915Sopenharmony_ci#define BLKGETSIZE64	_IOR(0x12,114,size_t)	/* Get device size in bytes. */
87987da915Sopenharmony_ci#endif
88987da915Sopenharmony_ci#if defined(linux) && !defined(HDIO_GETGEO)
89987da915Sopenharmony_ci#define HDIO_GETGEO	0x0301	/* Get device geometry. */
90987da915Sopenharmony_ci#endif
91987da915Sopenharmony_ci#if defined(linux) && defined(_IO) && !defined(BLKSSZGET)
92987da915Sopenharmony_ci#	define BLKSSZGET _IO(0x12,104) /* Get device sector size in bytes. */
93987da915Sopenharmony_ci#endif
94987da915Sopenharmony_ci#if defined(linux) && defined(_IO) && !defined(BLKBSZSET)
95987da915Sopenharmony_ci#	define BLKBSZSET _IOW(0x12,113,size_t) /* Set device block size in bytes. */
96987da915Sopenharmony_ci#endif
97987da915Sopenharmony_ci
98987da915Sopenharmony_ci/**
99987da915Sopenharmony_ci * ntfs_device_alloc - allocate an ntfs device structure and pre-initialize it
100987da915Sopenharmony_ci * @name:	name of the device (must be present)
101987da915Sopenharmony_ci * @state:	initial device state (usually zero)
102987da915Sopenharmony_ci * @dops:	ntfs device operations to use with the device (must be present)
103987da915Sopenharmony_ci * @priv_data:	pointer to private data (optional)
104987da915Sopenharmony_ci *
105987da915Sopenharmony_ci * Allocate an ntfs device structure and pre-initialize it with the user-
106987da915Sopenharmony_ci * specified device operations @dops, device state @state, device name @name,
107987da915Sopenharmony_ci * and optional private data @priv_data.
108987da915Sopenharmony_ci *
109987da915Sopenharmony_ci * Note, @name is copied and can hence be freed after this functions returns.
110987da915Sopenharmony_ci *
111987da915Sopenharmony_ci * On success return a pointer to the allocated ntfs device structure and on
112987da915Sopenharmony_ci * error return NULL with errno set to the error code returned by ntfs_malloc().
113987da915Sopenharmony_ci */
114987da915Sopenharmony_cistruct ntfs_device *ntfs_device_alloc(const char *name, const long state,
115987da915Sopenharmony_ci		struct ntfs_device_operations *dops, void *priv_data)
116987da915Sopenharmony_ci{
117987da915Sopenharmony_ci	struct ntfs_device *dev;
118987da915Sopenharmony_ci
119987da915Sopenharmony_ci	if (!name) {
120987da915Sopenharmony_ci		errno = EINVAL;
121987da915Sopenharmony_ci		return NULL;
122987da915Sopenharmony_ci	}
123987da915Sopenharmony_ci
124987da915Sopenharmony_ci	dev = ntfs_malloc(sizeof(struct ntfs_device));
125987da915Sopenharmony_ci	if (dev) {
126987da915Sopenharmony_ci		if (!(dev->d_name = strdup(name))) {
127987da915Sopenharmony_ci			int eo = errno;
128987da915Sopenharmony_ci			free(dev);
129987da915Sopenharmony_ci			errno = eo;
130987da915Sopenharmony_ci			return NULL;
131987da915Sopenharmony_ci		}
132987da915Sopenharmony_ci		dev->d_ops = dops;
133987da915Sopenharmony_ci		dev->d_state = state;
134987da915Sopenharmony_ci		dev->d_private = priv_data;
135987da915Sopenharmony_ci		dev->d_heads = -1;
136987da915Sopenharmony_ci		dev->d_sectors_per_track = -1;
137987da915Sopenharmony_ci	}
138987da915Sopenharmony_ci	return dev;
139987da915Sopenharmony_ci}
140987da915Sopenharmony_ci
141987da915Sopenharmony_ci/**
142987da915Sopenharmony_ci * ntfs_device_free - free an ntfs device structure
143987da915Sopenharmony_ci * @dev:	ntfs device structure to free
144987da915Sopenharmony_ci *
145987da915Sopenharmony_ci * Free the ntfs device structure @dev.
146987da915Sopenharmony_ci *
147987da915Sopenharmony_ci * Return 0 on success or -1 on error with errno set to the error code. The
148987da915Sopenharmony_ci * following error codes are defined:
149987da915Sopenharmony_ci *	EINVAL		Invalid pointer @dev.
150987da915Sopenharmony_ci *	EBUSY		Device is still open. Close it before freeing it!
151987da915Sopenharmony_ci */
152987da915Sopenharmony_ciint ntfs_device_free(struct ntfs_device *dev)
153987da915Sopenharmony_ci{
154987da915Sopenharmony_ci	if (!dev) {
155987da915Sopenharmony_ci		errno = EINVAL;
156987da915Sopenharmony_ci		return -1;
157987da915Sopenharmony_ci	}
158987da915Sopenharmony_ci	if (NDevOpen(dev)) {
159987da915Sopenharmony_ci		errno = EBUSY;
160987da915Sopenharmony_ci		return -1;
161987da915Sopenharmony_ci	}
162987da915Sopenharmony_ci	free(dev->d_name);
163987da915Sopenharmony_ci	free(dev);
164987da915Sopenharmony_ci	return 0;
165987da915Sopenharmony_ci}
166987da915Sopenharmony_ci
167987da915Sopenharmony_ci/*
168987da915Sopenharmony_ci *		Sync the device
169987da915Sopenharmony_ci *
170987da915Sopenharmony_ci *	returns zero if successful.
171987da915Sopenharmony_ci */
172987da915Sopenharmony_ci
173987da915Sopenharmony_ciint ntfs_device_sync(struct ntfs_device *dev)
174987da915Sopenharmony_ci{
175987da915Sopenharmony_ci	int ret;
176987da915Sopenharmony_ci	struct ntfs_device_operations *dops;
177987da915Sopenharmony_ci
178987da915Sopenharmony_ci	if (NDevDirty(dev)) {
179987da915Sopenharmony_ci		dops = dev->d_ops;
180987da915Sopenharmony_ci		ret = dops->sync(dev);
181987da915Sopenharmony_ci	} else
182987da915Sopenharmony_ci		ret = 0;
183987da915Sopenharmony_ci	return ret;
184987da915Sopenharmony_ci}
185987da915Sopenharmony_ci
186987da915Sopenharmony_ci/**
187987da915Sopenharmony_ci * ntfs_pread - positioned read from disk
188987da915Sopenharmony_ci * @dev:	device to read from
189987da915Sopenharmony_ci * @pos:	position in device to read from
190987da915Sopenharmony_ci * @count:	number of bytes to read
191987da915Sopenharmony_ci * @b:		output data buffer
192987da915Sopenharmony_ci *
193987da915Sopenharmony_ci * This function will read @count bytes from device @dev at position @pos into
194987da915Sopenharmony_ci * the data buffer @b.
195987da915Sopenharmony_ci *
196987da915Sopenharmony_ci * On success, return the number of successfully read bytes. If this number is
197987da915Sopenharmony_ci * lower than @count this means that we have either reached end of file or
198987da915Sopenharmony_ci * encountered an error during the read so that the read is partial. 0 means
199987da915Sopenharmony_ci * end of file or nothing to read (@count is 0).
200987da915Sopenharmony_ci *
201987da915Sopenharmony_ci * On error and nothing has been read, return -1 with errno set appropriately
202987da915Sopenharmony_ci * to the return code of either seek, read, or set to EINVAL in case of
203987da915Sopenharmony_ci * invalid arguments.
204987da915Sopenharmony_ci */
205987da915Sopenharmony_cis64 ntfs_pread(struct ntfs_device *dev, const s64 pos, s64 count, void *b)
206987da915Sopenharmony_ci{
207987da915Sopenharmony_ci	s64 br, total;
208987da915Sopenharmony_ci	struct ntfs_device_operations *dops;
209987da915Sopenharmony_ci
210987da915Sopenharmony_ci	ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count);
211987da915Sopenharmony_ci
212987da915Sopenharmony_ci	if (!b || count < 0 || pos < 0) {
213987da915Sopenharmony_ci		errno = EINVAL;
214987da915Sopenharmony_ci		return -1;
215987da915Sopenharmony_ci	}
216987da915Sopenharmony_ci	if (!count)
217987da915Sopenharmony_ci		return 0;
218987da915Sopenharmony_ci
219987da915Sopenharmony_ci	dops = dev->d_ops;
220987da915Sopenharmony_ci
221987da915Sopenharmony_ci	for (total = 0; count; count -= br, total += br) {
222987da915Sopenharmony_ci		br = dops->pread(dev, (char*)b + total, count, pos + total);
223987da915Sopenharmony_ci		/* If everything ok, continue. */
224987da915Sopenharmony_ci		if (br > 0)
225987da915Sopenharmony_ci			continue;
226987da915Sopenharmony_ci		/* If EOF or error return number of bytes read. */
227987da915Sopenharmony_ci		if (!br || total)
228987da915Sopenharmony_ci			return total;
229987da915Sopenharmony_ci		/* Nothing read and error, return error status. */
230987da915Sopenharmony_ci		return br;
231987da915Sopenharmony_ci	}
232987da915Sopenharmony_ci	/* Finally, return the number of bytes read. */
233987da915Sopenharmony_ci	return total;
234987da915Sopenharmony_ci}
235987da915Sopenharmony_ci
236987da915Sopenharmony_ci/**
237987da915Sopenharmony_ci * ntfs_pwrite - positioned write to disk
238987da915Sopenharmony_ci * @dev:	device to write to
239987da915Sopenharmony_ci * @pos:	position in file descriptor to write to
240987da915Sopenharmony_ci * @count:	number of bytes to write
241987da915Sopenharmony_ci * @b:		data buffer to write to disk
242987da915Sopenharmony_ci *
243987da915Sopenharmony_ci * This function will write @count bytes from data buffer @b to the device @dev
244987da915Sopenharmony_ci * at position @pos.
245987da915Sopenharmony_ci *
246987da915Sopenharmony_ci * On success, return the number of successfully written bytes. If this number
247987da915Sopenharmony_ci * is lower than @count this means that the write has been interrupted in
248987da915Sopenharmony_ci * flight or that an error was encountered during the write so that the write
249987da915Sopenharmony_ci * is partial. 0 means nothing was written (also return 0 when @count is 0).
250987da915Sopenharmony_ci *
251987da915Sopenharmony_ci * On error and nothing has been written, return -1 with errno set
252987da915Sopenharmony_ci * appropriately to the return code of either seek, write, or set
253987da915Sopenharmony_ci * to EINVAL in case of invalid arguments.
254987da915Sopenharmony_ci */
255987da915Sopenharmony_cis64 ntfs_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
256987da915Sopenharmony_ci		const void *b)
257987da915Sopenharmony_ci{
258987da915Sopenharmony_ci	s64 written, total, ret = -1;
259987da915Sopenharmony_ci	struct ntfs_device_operations *dops;
260987da915Sopenharmony_ci
261987da915Sopenharmony_ci	ntfs_log_trace("pos %lld, count %lld\n",(long long)pos,(long long)count);
262987da915Sopenharmony_ci
263987da915Sopenharmony_ci	if (!b || count < 0 || pos < 0) {
264987da915Sopenharmony_ci		errno = EINVAL;
265987da915Sopenharmony_ci		goto out;
266987da915Sopenharmony_ci	}
267987da915Sopenharmony_ci	if (!count)
268987da915Sopenharmony_ci		return 0;
269987da915Sopenharmony_ci	if (NDevReadOnly(dev)) {
270987da915Sopenharmony_ci		errno = EROFS;
271987da915Sopenharmony_ci		goto out;
272987da915Sopenharmony_ci	}
273987da915Sopenharmony_ci
274987da915Sopenharmony_ci	dops = dev->d_ops;
275987da915Sopenharmony_ci
276987da915Sopenharmony_ci	NDevSetDirty(dev);
277987da915Sopenharmony_ci	for (total = 0; count; count -= written, total += written) {
278987da915Sopenharmony_ci		written = dops->pwrite(dev, (const char*)b + total, count,
279987da915Sopenharmony_ci				       pos + total);
280987da915Sopenharmony_ci		/* If everything ok, continue. */
281987da915Sopenharmony_ci		if (written > 0)
282987da915Sopenharmony_ci			continue;
283987da915Sopenharmony_ci		/*
284987da915Sopenharmony_ci		 * If nothing written or error return number of bytes written.
285987da915Sopenharmony_ci		 */
286987da915Sopenharmony_ci		if (!written || total)
287987da915Sopenharmony_ci			break;
288987da915Sopenharmony_ci		/* Nothing written and error, return error status. */
289987da915Sopenharmony_ci		total = written;
290987da915Sopenharmony_ci		break;
291987da915Sopenharmony_ci	}
292987da915Sopenharmony_ci	if (NDevSync(dev) && total && dops->sync(dev)) {
293987da915Sopenharmony_ci		total--; /* on sync error, return partially written */
294987da915Sopenharmony_ci	}
295987da915Sopenharmony_ci	ret = total;
296987da915Sopenharmony_ciout:
297987da915Sopenharmony_ci	return ret;
298987da915Sopenharmony_ci}
299987da915Sopenharmony_ci
300987da915Sopenharmony_ci/**
301987da915Sopenharmony_ci * ntfs_mst_pread - multi sector transfer (mst) positioned read
302987da915Sopenharmony_ci * @dev:	device to read from
303987da915Sopenharmony_ci * @pos:	position in file descriptor to read from
304987da915Sopenharmony_ci * @count:	number of blocks to read
305987da915Sopenharmony_ci * @bksize:	size of each block that needs mst deprotecting
306987da915Sopenharmony_ci * @b:		output data buffer
307987da915Sopenharmony_ci *
308987da915Sopenharmony_ci * Multi sector transfer (mst) positioned read. This function will read @count
309987da915Sopenharmony_ci * blocks of size @bksize bytes each from device @dev at position @pos into the
310987da915Sopenharmony_ci * the data buffer @b.
311987da915Sopenharmony_ci *
312987da915Sopenharmony_ci * On success, return the number of successfully read blocks. If this number is
313987da915Sopenharmony_ci * lower than @count this means that we have reached end of file, that the read
314987da915Sopenharmony_ci * was interrupted, or that an error was encountered during the read so that
315987da915Sopenharmony_ci * the read is partial. 0 means end of file or nothing was read (also return 0
316987da915Sopenharmony_ci * when @count or @bksize are 0).
317987da915Sopenharmony_ci *
318987da915Sopenharmony_ci * On error and nothing was read, return -1 with errno set appropriately to the
319987da915Sopenharmony_ci * return code of either seek, read, or set to EINVAL in case of invalid
320987da915Sopenharmony_ci * arguments.
321987da915Sopenharmony_ci *
322987da915Sopenharmony_ci * NOTE: If an incomplete multi sector transfer has been detected the magic
323987da915Sopenharmony_ci * will have been changed to magic_BAAD but no error will be returned. Thus it
324987da915Sopenharmony_ci * is possible that we return count blocks as being read but that any number
325987da915Sopenharmony_ci * (between zero and count!) of these blocks is actually subject to a multi
326987da915Sopenharmony_ci * sector transfer error. This should be detected by the caller by checking for
327987da915Sopenharmony_ci * the magic being "BAAD".
328987da915Sopenharmony_ci */
329987da915Sopenharmony_cis64 ntfs_mst_pread(struct ntfs_device *dev, const s64 pos, s64 count,
330987da915Sopenharmony_ci		const u32 bksize, void *b)
331987da915Sopenharmony_ci{
332987da915Sopenharmony_ci	s64 br, i;
333987da915Sopenharmony_ci
334987da915Sopenharmony_ci	if (bksize & (bksize - 1) || bksize % NTFS_BLOCK_SIZE) {
335987da915Sopenharmony_ci		errno = EINVAL;
336987da915Sopenharmony_ci		return -1;
337987da915Sopenharmony_ci	}
338987da915Sopenharmony_ci	/* Do the read. */
339987da915Sopenharmony_ci	br = ntfs_pread(dev, pos, count * bksize, b);
340987da915Sopenharmony_ci	if (br < 0)
341987da915Sopenharmony_ci		return br;
342987da915Sopenharmony_ci	/*
343987da915Sopenharmony_ci	 * Apply fixups to successfully read data, disregarding any errors
344987da915Sopenharmony_ci	 * returned from the MST fixup function. This is because we want to
345987da915Sopenharmony_ci	 * fixup everything possible and we rely on the fact that the "BAAD"
346987da915Sopenharmony_ci	 * magic will be detected later on.
347987da915Sopenharmony_ci	 */
348987da915Sopenharmony_ci	count = br / bksize;
349987da915Sopenharmony_ci	for (i = 0; i < count; ++i)
350987da915Sopenharmony_ci		ntfs_mst_post_read_fixup((NTFS_RECORD*)
351987da915Sopenharmony_ci				((u8*)b + i * bksize), bksize);
352987da915Sopenharmony_ci	/* Finally, return the number of complete blocks read. */
353987da915Sopenharmony_ci	return count;
354987da915Sopenharmony_ci}
355987da915Sopenharmony_ci
356987da915Sopenharmony_ci/**
357987da915Sopenharmony_ci * ntfs_mst_pwrite - multi sector transfer (mst) positioned write
358987da915Sopenharmony_ci * @dev:	device to write to
359987da915Sopenharmony_ci * @pos:	position in file descriptor to write to
360987da915Sopenharmony_ci * @count:	number of blocks to write
361987da915Sopenharmony_ci * @bksize:	size of each block that needs mst protecting
362987da915Sopenharmony_ci * @b:		data buffer to write to disk
363987da915Sopenharmony_ci *
364987da915Sopenharmony_ci * Multi sector transfer (mst) positioned write. This function will write
365987da915Sopenharmony_ci * @count blocks of size @bksize bytes each from data buffer @b to the device
366987da915Sopenharmony_ci * @dev at position @pos.
367987da915Sopenharmony_ci *
368987da915Sopenharmony_ci * On success, return the number of successfully written blocks. If this number
369987da915Sopenharmony_ci * is lower than @count this means that the write has been interrupted or that
370987da915Sopenharmony_ci * an error was encountered during the write so that the write is partial. 0
371987da915Sopenharmony_ci * means nothing was written (also return 0 when @count or @bksize are 0).
372987da915Sopenharmony_ci *
373987da915Sopenharmony_ci * On error and nothing has been written, return -1 with errno set
374987da915Sopenharmony_ci * appropriately to the return code of either seek, write, or set
375987da915Sopenharmony_ci * to EINVAL in case of invalid arguments.
376987da915Sopenharmony_ci *
377987da915Sopenharmony_ci * NOTE: We mst protect the data, write it, then mst deprotect it using a quick
378987da915Sopenharmony_ci * deprotect algorithm (no checking). This saves us from making a copy before
379987da915Sopenharmony_ci * the write and at the same time causes the usn to be incremented in the
380987da915Sopenharmony_ci * buffer. This conceptually fits in better with the idea that cached data is
381987da915Sopenharmony_ci * always deprotected and protection is performed when the data is actually
382987da915Sopenharmony_ci * going to hit the disk and the cache is immediately deprotected again
383987da915Sopenharmony_ci * simulating an mst read on the written data. This way cache coherency is
384987da915Sopenharmony_ci * achieved.
385987da915Sopenharmony_ci */
386987da915Sopenharmony_cis64 ntfs_mst_pwrite(struct ntfs_device *dev, const s64 pos, s64 count,
387987da915Sopenharmony_ci		const u32 bksize, void *b)
388987da915Sopenharmony_ci{
389987da915Sopenharmony_ci	s64 written, i;
390987da915Sopenharmony_ci
391987da915Sopenharmony_ci	if (count < 0 || bksize % NTFS_BLOCK_SIZE) {
392987da915Sopenharmony_ci		errno = EINVAL;
393987da915Sopenharmony_ci		return -1;
394987da915Sopenharmony_ci	}
395987da915Sopenharmony_ci	if (!count)
396987da915Sopenharmony_ci		return 0;
397987da915Sopenharmony_ci	/* Prepare data for writing. */
398987da915Sopenharmony_ci	for (i = 0; i < count; ++i) {
399987da915Sopenharmony_ci		int err;
400987da915Sopenharmony_ci
401987da915Sopenharmony_ci		err = ntfs_mst_pre_write_fixup((NTFS_RECORD*)
402987da915Sopenharmony_ci				((u8*)b + i * bksize), bksize);
403987da915Sopenharmony_ci		if (err < 0) {
404987da915Sopenharmony_ci			/* Abort write at this position. */
405987da915Sopenharmony_ci			if (!i)
406987da915Sopenharmony_ci				return err;
407987da915Sopenharmony_ci			count = i;
408987da915Sopenharmony_ci			break;
409987da915Sopenharmony_ci		}
410987da915Sopenharmony_ci	}
411987da915Sopenharmony_ci	/* Write the prepared data. */
412987da915Sopenharmony_ci	written = ntfs_pwrite(dev, pos, count * bksize, b);
413987da915Sopenharmony_ci	/* Quickly deprotect the data again. */
414987da915Sopenharmony_ci	for (i = 0; i < count; ++i)
415987da915Sopenharmony_ci		ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)b + i * bksize));
416987da915Sopenharmony_ci	if (written <= 0)
417987da915Sopenharmony_ci		return written;
418987da915Sopenharmony_ci	/* Finally, return the number of complete blocks written. */
419987da915Sopenharmony_ci	return written / bksize;
420987da915Sopenharmony_ci}
421987da915Sopenharmony_ci
422987da915Sopenharmony_ci/**
423987da915Sopenharmony_ci * ntfs_cluster_read - read ntfs clusters
424987da915Sopenharmony_ci * @vol:	volume to read from
425987da915Sopenharmony_ci * @lcn:	starting logical cluster number
426987da915Sopenharmony_ci * @count:	number of clusters to read
427987da915Sopenharmony_ci * @b:		output data buffer
428987da915Sopenharmony_ci *
429987da915Sopenharmony_ci * Read @count ntfs clusters starting at logical cluster number @lcn from
430987da915Sopenharmony_ci * volume @vol into buffer @b. Return number of clusters read or -1 on error,
431987da915Sopenharmony_ci * with errno set to the error code.
432987da915Sopenharmony_ci */
433987da915Sopenharmony_cis64 ntfs_cluster_read(const ntfs_volume *vol, const s64 lcn, const s64 count,
434987da915Sopenharmony_ci		void *b)
435987da915Sopenharmony_ci{
436987da915Sopenharmony_ci	s64 br;
437987da915Sopenharmony_ci
438987da915Sopenharmony_ci	if (!vol || lcn < 0 || count < 0) {
439987da915Sopenharmony_ci		errno = EINVAL;
440987da915Sopenharmony_ci		return -1;
441987da915Sopenharmony_ci	}
442987da915Sopenharmony_ci	if (vol->nr_clusters < lcn + count) {
443987da915Sopenharmony_ci		errno = ESPIPE;
444987da915Sopenharmony_ci		ntfs_log_perror("Trying to read outside of volume "
445987da915Sopenharmony_ci				"(%lld < %lld)", (long long)vol->nr_clusters,
446987da915Sopenharmony_ci			        (long long)lcn + count);
447987da915Sopenharmony_ci		return -1;
448987da915Sopenharmony_ci	}
449987da915Sopenharmony_ci	br = ntfs_pread(vol->dev, lcn << vol->cluster_size_bits,
450987da915Sopenharmony_ci			count << vol->cluster_size_bits, b);
451987da915Sopenharmony_ci	if (br < 0) {
452987da915Sopenharmony_ci		ntfs_log_perror("Error reading cluster(s)");
453987da915Sopenharmony_ci		return br;
454987da915Sopenharmony_ci	}
455987da915Sopenharmony_ci	return br >> vol->cluster_size_bits;
456987da915Sopenharmony_ci}
457987da915Sopenharmony_ci
458987da915Sopenharmony_ci/**
459987da915Sopenharmony_ci * ntfs_cluster_write - write ntfs clusters
460987da915Sopenharmony_ci * @vol:	volume to write to
461987da915Sopenharmony_ci * @lcn:	starting logical cluster number
462987da915Sopenharmony_ci * @count:	number of clusters to write
463987da915Sopenharmony_ci * @b:		data buffer to write to disk
464987da915Sopenharmony_ci *
465987da915Sopenharmony_ci * Write @count ntfs clusters starting at logical cluster number @lcn from
466987da915Sopenharmony_ci * buffer @b to volume @vol. Return the number of clusters written or -1 on
467987da915Sopenharmony_ci * error, with errno set to the error code.
468987da915Sopenharmony_ci */
469987da915Sopenharmony_cis64 ntfs_cluster_write(const ntfs_volume *vol, const s64 lcn,
470987da915Sopenharmony_ci		const s64 count, const void *b)
471987da915Sopenharmony_ci{
472987da915Sopenharmony_ci	s64 bw;
473987da915Sopenharmony_ci
474987da915Sopenharmony_ci	if (!vol || lcn < 0 || count < 0) {
475987da915Sopenharmony_ci		errno = EINVAL;
476987da915Sopenharmony_ci		return -1;
477987da915Sopenharmony_ci	}
478987da915Sopenharmony_ci	if (vol->nr_clusters < lcn + count) {
479987da915Sopenharmony_ci		errno = ESPIPE;
480987da915Sopenharmony_ci		ntfs_log_perror("Trying to write outside of volume "
481987da915Sopenharmony_ci				"(%lld < %lld)", (long long)vol->nr_clusters,
482987da915Sopenharmony_ci			        (long long)lcn + count);
483987da915Sopenharmony_ci		return -1;
484987da915Sopenharmony_ci	}
485987da915Sopenharmony_ci	if (!NVolReadOnly(vol))
486987da915Sopenharmony_ci		bw = ntfs_pwrite(vol->dev, lcn << vol->cluster_size_bits,
487987da915Sopenharmony_ci				count << vol->cluster_size_bits, b);
488987da915Sopenharmony_ci	else
489987da915Sopenharmony_ci		bw = count << vol->cluster_size_bits;
490987da915Sopenharmony_ci	if (bw < 0) {
491987da915Sopenharmony_ci		ntfs_log_perror("Error writing cluster(s)");
492987da915Sopenharmony_ci		return bw;
493987da915Sopenharmony_ci	}
494987da915Sopenharmony_ci	return bw >> vol->cluster_size_bits;
495987da915Sopenharmony_ci}
496987da915Sopenharmony_ci
497987da915Sopenharmony_ci/**
498987da915Sopenharmony_ci * ntfs_device_offset_valid - test if a device offset is valid
499987da915Sopenharmony_ci * @dev:	open device
500987da915Sopenharmony_ci * @ofs:	offset to test for validity
501987da915Sopenharmony_ci *
502987da915Sopenharmony_ci * Test if the offset @ofs is an existing location on the device described
503987da915Sopenharmony_ci * by the open device structure @dev.
504987da915Sopenharmony_ci *
505987da915Sopenharmony_ci * Return 0 if it is valid and -1 if it is not valid.
506987da915Sopenharmony_ci */
507987da915Sopenharmony_cistatic int ntfs_device_offset_valid(struct ntfs_device *dev, s64 ofs)
508987da915Sopenharmony_ci{
509987da915Sopenharmony_ci	char ch;
510987da915Sopenharmony_ci
511987da915Sopenharmony_ci	if (dev->d_ops->seek(dev, ofs, SEEK_SET) >= 0 &&
512987da915Sopenharmony_ci			dev->d_ops->read(dev, &ch, 1) == 1)
513987da915Sopenharmony_ci		return 0;
514987da915Sopenharmony_ci	return -1;
515987da915Sopenharmony_ci}
516987da915Sopenharmony_ci
517987da915Sopenharmony_ci/**
518987da915Sopenharmony_ci * ntfs_device_size_get - return the size of a device in blocks
519987da915Sopenharmony_ci * @dev:	open device
520987da915Sopenharmony_ci * @block_size:	block size in bytes in which to return the result
521987da915Sopenharmony_ci *
522987da915Sopenharmony_ci * Return the number of @block_size sized blocks in the device described by the
523987da915Sopenharmony_ci * open device @dev.
524987da915Sopenharmony_ci *
525987da915Sopenharmony_ci * Adapted from e2fsutils-1.19, Copyright (C) 1995 Theodore Ts'o.
526987da915Sopenharmony_ci *
527987da915Sopenharmony_ci * On error return -1 with errno set to the error code.
528987da915Sopenharmony_ci */
529987da915Sopenharmony_cis64 ntfs_device_size_get(struct ntfs_device *dev, int block_size)
530987da915Sopenharmony_ci{
531987da915Sopenharmony_ci	s64 high, low;
532987da915Sopenharmony_ci
533987da915Sopenharmony_ci	if (!dev || block_size <= 0 || (block_size - 1) & block_size) {
534987da915Sopenharmony_ci		errno = EINVAL;
535987da915Sopenharmony_ci		return -1;
536987da915Sopenharmony_ci	}
537987da915Sopenharmony_ci#ifdef BLKGETSIZE64
538987da915Sopenharmony_ci	{	u64 size;
539987da915Sopenharmony_ci
540987da915Sopenharmony_ci		if (dev->d_ops->ioctl(dev, BLKGETSIZE64, &size) >= 0) {
541987da915Sopenharmony_ci			ntfs_log_debug("BLKGETSIZE64 nr bytes = %llu (0x%llx)\n",
542987da915Sopenharmony_ci					(unsigned long long)size,
543987da915Sopenharmony_ci					(unsigned long long)size);
544987da915Sopenharmony_ci			return (s64)size / block_size;
545987da915Sopenharmony_ci		}
546987da915Sopenharmony_ci	}
547987da915Sopenharmony_ci#endif
548987da915Sopenharmony_ci#ifdef BLKGETSIZE
549987da915Sopenharmony_ci	{	unsigned long size;
550987da915Sopenharmony_ci
551987da915Sopenharmony_ci		if (dev->d_ops->ioctl(dev, BLKGETSIZE, &size) >= 0) {
552987da915Sopenharmony_ci			ntfs_log_debug("BLKGETSIZE nr 512 byte blocks = %lu (0x%lx)\n",
553987da915Sopenharmony_ci					size, size);
554987da915Sopenharmony_ci			return (s64)size * 512 / block_size;
555987da915Sopenharmony_ci		}
556987da915Sopenharmony_ci	}
557987da915Sopenharmony_ci#endif
558987da915Sopenharmony_ci#ifdef FDGETPRM
559987da915Sopenharmony_ci	{       struct floppy_struct this_floppy;
560987da915Sopenharmony_ci
561987da915Sopenharmony_ci		if (dev->d_ops->ioctl(dev, FDGETPRM, &this_floppy) >= 0) {
562987da915Sopenharmony_ci			ntfs_log_debug("FDGETPRM nr 512 byte blocks = %lu (0x%lx)\n",
563987da915Sopenharmony_ci					(unsigned long)this_floppy.size,
564987da915Sopenharmony_ci					(unsigned long)this_floppy.size);
565987da915Sopenharmony_ci			return (s64)this_floppy.size * 512 / block_size;
566987da915Sopenharmony_ci		}
567987da915Sopenharmony_ci	}
568987da915Sopenharmony_ci#endif
569987da915Sopenharmony_ci#ifdef DIOCGMEDIASIZE
570987da915Sopenharmony_ci	{
571987da915Sopenharmony_ci		/* FreeBSD */
572987da915Sopenharmony_ci		off_t size;
573987da915Sopenharmony_ci
574987da915Sopenharmony_ci		if (dev->d_ops->ioctl(dev, DIOCGMEDIASIZE, &size) >= 0) {
575987da915Sopenharmony_ci			ntfs_log_debug("DIOCGMEDIASIZE nr bytes = %llu (0x%llx)\n",
576987da915Sopenharmony_ci					(unsigned long long)size,
577987da915Sopenharmony_ci					(unsigned long long)size);
578987da915Sopenharmony_ci			return (s64)size / block_size;
579987da915Sopenharmony_ci		}
580987da915Sopenharmony_ci	}
581987da915Sopenharmony_ci#endif
582987da915Sopenharmony_ci#ifdef DKIOCGETBLOCKCOUNT
583987da915Sopenharmony_ci	{
584987da915Sopenharmony_ci		/* Mac OS X */
585987da915Sopenharmony_ci		uint64_t blocks;
586987da915Sopenharmony_ci		int sector_size;
587987da915Sopenharmony_ci
588987da915Sopenharmony_ci		sector_size = ntfs_device_sector_size_get(dev);
589987da915Sopenharmony_ci		if (sector_size >= 0 && dev->d_ops->ioctl(dev,
590987da915Sopenharmony_ci			DKIOCGETBLOCKCOUNT, &blocks) >= 0)
591987da915Sopenharmony_ci		{
592987da915Sopenharmony_ci			ntfs_log_debug("DKIOCGETBLOCKCOUNT nr blocks = %llu (0x%llx)\n",
593987da915Sopenharmony_ci				(unsigned long long) blocks,
594987da915Sopenharmony_ci				(unsigned long long) blocks);
595987da915Sopenharmony_ci			return blocks * sector_size / block_size;
596987da915Sopenharmony_ci		}
597987da915Sopenharmony_ci	}
598987da915Sopenharmony_ci#endif
599987da915Sopenharmony_ci	/*
600987da915Sopenharmony_ci	 * We couldn't figure it out by using a specialized ioctl,
601987da915Sopenharmony_ci	 * so do binary search to find the size of the device.
602987da915Sopenharmony_ci	 */
603987da915Sopenharmony_ci	low = 0LL;
604987da915Sopenharmony_ci	for (high = 1024LL; !ntfs_device_offset_valid(dev, high); high <<= 1)
605987da915Sopenharmony_ci		low = high;
606987da915Sopenharmony_ci	while (low < high - 1LL) {
607987da915Sopenharmony_ci		const s64 mid = (low + high) / 2;
608987da915Sopenharmony_ci
609987da915Sopenharmony_ci		if (!ntfs_device_offset_valid(dev, mid))
610987da915Sopenharmony_ci			low = mid;
611987da915Sopenharmony_ci		else
612987da915Sopenharmony_ci			high = mid;
613987da915Sopenharmony_ci	}
614987da915Sopenharmony_ci	dev->d_ops->seek(dev, 0LL, SEEK_SET);
615987da915Sopenharmony_ci	return (low + 1LL) / block_size;
616987da915Sopenharmony_ci}
617987da915Sopenharmony_ci
618987da915Sopenharmony_ci/**
619987da915Sopenharmony_ci * ntfs_device_partition_start_sector_get - get starting sector of a partition
620987da915Sopenharmony_ci * @dev:	open device
621987da915Sopenharmony_ci *
622987da915Sopenharmony_ci * On success, return the starting sector of the partition @dev in the parent
623987da915Sopenharmony_ci * block device of @dev.  On error return -1 with errno set to the error code.
624987da915Sopenharmony_ci *
625987da915Sopenharmony_ci * The following error codes are defined:
626987da915Sopenharmony_ci *	EINVAL		Input parameter error
627987da915Sopenharmony_ci *	EOPNOTSUPP	System does not support HDIO_GETGEO ioctl
628987da915Sopenharmony_ci *	ENOTTY		@dev is a file or a device not supporting HDIO_GETGEO
629987da915Sopenharmony_ci */
630987da915Sopenharmony_cis64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev)
631987da915Sopenharmony_ci{
632987da915Sopenharmony_ci	if (!dev) {
633987da915Sopenharmony_ci		errno = EINVAL;
634987da915Sopenharmony_ci		return -1;
635987da915Sopenharmony_ci	}
636987da915Sopenharmony_ci#ifdef HDIO_GETGEO
637987da915Sopenharmony_ci	{	struct hd_geometry geo;
638987da915Sopenharmony_ci
639987da915Sopenharmony_ci		if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) {
640987da915Sopenharmony_ci			ntfs_log_debug("HDIO_GETGEO start_sect = %lu (0x%lx)\n",
641987da915Sopenharmony_ci					geo.start, geo.start);
642987da915Sopenharmony_ci			return geo.start;
643987da915Sopenharmony_ci		}
644987da915Sopenharmony_ci	}
645987da915Sopenharmony_ci#else
646987da915Sopenharmony_ci	errno = EOPNOTSUPP;
647987da915Sopenharmony_ci#endif
648987da915Sopenharmony_ci	return -1;
649987da915Sopenharmony_ci}
650987da915Sopenharmony_ci
651987da915Sopenharmony_cistatic int ntfs_device_get_geo(struct ntfs_device *dev)
652987da915Sopenharmony_ci{
653987da915Sopenharmony_ci	int err;
654987da915Sopenharmony_ci
655987da915Sopenharmony_ci	if (!dev) {
656987da915Sopenharmony_ci		errno = EINVAL;
657987da915Sopenharmony_ci		return -1;
658987da915Sopenharmony_ci	}
659987da915Sopenharmony_ci	err = EOPNOTSUPP;
660987da915Sopenharmony_ci#ifdef ENABLE_HD
661987da915Sopenharmony_ci	{
662987da915Sopenharmony_ci		hd_data_t *hddata;
663987da915Sopenharmony_ci		hd_t *hd, *devlist, *partlist = NULL;
664987da915Sopenharmony_ci		str_list_t *names;
665987da915Sopenharmony_ci		hd_res_t *res;
666987da915Sopenharmony_ci		const int d_name_len = strlen(dev->d_name) + 1;
667987da915Sopenharmony_ci		int done = 0;
668987da915Sopenharmony_ci
669987da915Sopenharmony_ci		hddata = calloc(1, sizeof(*hddata));
670987da915Sopenharmony_ci		if (!hddata) {
671987da915Sopenharmony_ci			err = ENOMEM;
672987da915Sopenharmony_ci			goto skip_hd;
673987da915Sopenharmony_ci		}
674987da915Sopenharmony_ci		/* List all "disk" class devices on the system. */
675987da915Sopenharmony_ci		devlist = hd_list(hddata, hw_disk, 1, NULL);
676987da915Sopenharmony_ci		if (!devlist) {
677987da915Sopenharmony_ci			free(hddata);
678987da915Sopenharmony_ci			err = ENOMEM;
679987da915Sopenharmony_ci			goto skip_hd;
680987da915Sopenharmony_ci		}
681987da915Sopenharmony_ci		/*
682987da915Sopenharmony_ci		 * Loop over each disk device looking for the device with the
683987da915Sopenharmony_ci		 * same unix name as @dev.
684987da915Sopenharmony_ci		 */
685987da915Sopenharmony_ci		for (hd = devlist; hd; hd = hd->next) {
686987da915Sopenharmony_ci			if (hd->unix_dev_name && !strncmp(dev->d_name,
687987da915Sopenharmony_ci					hd->unix_dev_name, d_name_len))
688987da915Sopenharmony_ci				goto got_hd;
689987da915Sopenharmony_ci			if (hd->unix_dev_name2 && !strncmp(dev->d_name,
690987da915Sopenharmony_ci					hd->unix_dev_name2, d_name_len))
691987da915Sopenharmony_ci				goto got_hd;
692987da915Sopenharmony_ci			for (names = hd->unix_dev_names; names;
693987da915Sopenharmony_ci					names = names->next) {
694987da915Sopenharmony_ci				if (names->str && !strncmp(dev->d_name,
695987da915Sopenharmony_ci						names->str, d_name_len))
696987da915Sopenharmony_ci					goto got_hd;
697987da915Sopenharmony_ci			}
698987da915Sopenharmony_ci		}
699987da915Sopenharmony_ci		/*
700987da915Sopenharmony_ci		 * Device was not a whole disk device.  Unless it is a file it
701987da915Sopenharmony_ci		 * is likely to be a partition device.  List all "partition"
702987da915Sopenharmony_ci		 * class devices on the system.
703987da915Sopenharmony_ci		 */
704987da915Sopenharmony_ci		partlist = hd_list(hddata, hw_partition, 1, NULL);
705987da915Sopenharmony_ci		for (hd = partlist; hd; hd = hd->next) {
706987da915Sopenharmony_ci			if (hd->unix_dev_name && !strncmp(dev->d_name,
707987da915Sopenharmony_ci					hd->unix_dev_name, d_name_len))
708987da915Sopenharmony_ci				goto got_part_hd;
709987da915Sopenharmony_ci			if (hd->unix_dev_name2 && !strncmp(dev->d_name,
710987da915Sopenharmony_ci					hd->unix_dev_name2, d_name_len))
711987da915Sopenharmony_ci				goto got_part_hd;
712987da915Sopenharmony_ci			for (names = hd->unix_dev_names; names;
713987da915Sopenharmony_ci					names = names->next) {
714987da915Sopenharmony_ci				if (names->str && !strncmp(dev->d_name,
715987da915Sopenharmony_ci						names->str, d_name_len))
716987da915Sopenharmony_ci					goto got_part_hd;
717987da915Sopenharmony_ci			}
718987da915Sopenharmony_ci		}
719987da915Sopenharmony_ci		/* Failed to find the device.  Stop trying and clean up. */
720987da915Sopenharmony_ci		goto end_hd;
721987da915Sopenharmony_cigot_part_hd:
722987da915Sopenharmony_ci		/* Get the whole block device the partition device is on. */
723987da915Sopenharmony_ci		hd = hd_get_device_by_idx(hddata, hd->attached_to);
724987da915Sopenharmony_ci		if (!hd)
725987da915Sopenharmony_ci			goto end_hd;
726987da915Sopenharmony_cigot_hd:
727987da915Sopenharmony_ci		/*
728987da915Sopenharmony_ci		 * @hd is now the whole block device either being formatted or
729987da915Sopenharmony_ci		 * that the partition being formatted is on.
730987da915Sopenharmony_ci		 *
731987da915Sopenharmony_ci		 * Loop over each resource of the disk device looking for the
732987da915Sopenharmony_ci		 * BIOS legacy geometry obtained from EDD which is what Windows
733987da915Sopenharmony_ci		 * needs to boot.
734987da915Sopenharmony_ci		 */
735987da915Sopenharmony_ci		for (res = hd->res; res; res = res->next) {
736987da915Sopenharmony_ci			/* geotype 3 is BIOS legacy. */
737987da915Sopenharmony_ci			if (res->any.type != res_disk_geo ||
738987da915Sopenharmony_ci					res->disk_geo.geotype != 3)
739987da915Sopenharmony_ci				continue;
740987da915Sopenharmony_ci			dev->d_heads = res->disk_geo.heads;
741987da915Sopenharmony_ci			dev->d_sectors_per_track = res->disk_geo.sectors;
742987da915Sopenharmony_ci			done = 1;
743987da915Sopenharmony_ci		}
744987da915Sopenharmony_ciend_hd:
745987da915Sopenharmony_ci		if (partlist)
746987da915Sopenharmony_ci			hd_free_hd_list(partlist);
747987da915Sopenharmony_ci		hd_free_hd_list(devlist);
748987da915Sopenharmony_ci		hd_free_hd_data(hddata);
749987da915Sopenharmony_ci		free(hddata);
750987da915Sopenharmony_ci		if (done) {
751987da915Sopenharmony_ci			ntfs_log_debug("EDD/BIOD legacy heads = %u, sectors "
752987da915Sopenharmony_ci					"per track = %u\n", dev->d_heads,
753987da915Sopenharmony_ci					dev->d_sectors_per_track);
754987da915Sopenharmony_ci			return 0;
755987da915Sopenharmony_ci		}
756987da915Sopenharmony_ci	}
757987da915Sopenharmony_ciskip_hd:
758987da915Sopenharmony_ci#endif
759987da915Sopenharmony_ci#ifdef HDIO_GETGEO
760987da915Sopenharmony_ci	{	struct hd_geometry geo;
761987da915Sopenharmony_ci
762987da915Sopenharmony_ci		if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) {
763987da915Sopenharmony_ci			dev->d_heads = geo.heads;
764987da915Sopenharmony_ci			dev->d_sectors_per_track = geo.sectors;
765987da915Sopenharmony_ci			ntfs_log_debug("HDIO_GETGEO heads = %u, sectors per "
766987da915Sopenharmony_ci					"track = %u\n", dev->d_heads,
767987da915Sopenharmony_ci					dev->d_sectors_per_track);
768987da915Sopenharmony_ci			return 0;
769987da915Sopenharmony_ci		}
770987da915Sopenharmony_ci		err = errno;
771987da915Sopenharmony_ci	}
772987da915Sopenharmony_ci#endif
773987da915Sopenharmony_ci	errno = err;
774987da915Sopenharmony_ci	return -1;
775987da915Sopenharmony_ci}
776987da915Sopenharmony_ci
777987da915Sopenharmony_ci/**
778987da915Sopenharmony_ci * ntfs_device_heads_get - get number of heads of device
779987da915Sopenharmony_ci * @dev:		open device
780987da915Sopenharmony_ci *
781987da915Sopenharmony_ci * On success, return the number of heads on the device @dev.  On error return
782987da915Sopenharmony_ci * -1 with errno set to the error code.
783987da915Sopenharmony_ci *
784987da915Sopenharmony_ci * The following error codes are defined:
785987da915Sopenharmony_ci *	EINVAL		Input parameter error
786987da915Sopenharmony_ci *	EOPNOTSUPP	System does not support HDIO_GETGEO ioctl
787987da915Sopenharmony_ci *	ENOTTY		@dev is a file or a device not supporting HDIO_GETGEO
788987da915Sopenharmony_ci *	ENOMEM		Not enough memory to complete the request
789987da915Sopenharmony_ci */
790987da915Sopenharmony_ciint ntfs_device_heads_get(struct ntfs_device *dev)
791987da915Sopenharmony_ci{
792987da915Sopenharmony_ci	if (!dev) {
793987da915Sopenharmony_ci		errno = EINVAL;
794987da915Sopenharmony_ci		return -1;
795987da915Sopenharmony_ci	}
796987da915Sopenharmony_ci	if (dev->d_heads == -1) {
797987da915Sopenharmony_ci		if (ntfs_device_get_geo(dev) == -1)
798987da915Sopenharmony_ci			return -1;
799987da915Sopenharmony_ci		if (dev->d_heads == -1) {
800987da915Sopenharmony_ci			errno = EINVAL;
801987da915Sopenharmony_ci			return -1;
802987da915Sopenharmony_ci		}
803987da915Sopenharmony_ci	}
804987da915Sopenharmony_ci	return dev->d_heads;
805987da915Sopenharmony_ci}
806987da915Sopenharmony_ci
807987da915Sopenharmony_ci/**
808987da915Sopenharmony_ci * ntfs_device_sectors_per_track_get - get number of sectors per track of device
809987da915Sopenharmony_ci * @dev:		open device
810987da915Sopenharmony_ci *
811987da915Sopenharmony_ci * On success, return the number of sectors per track on the device @dev.  On
812987da915Sopenharmony_ci * error return -1 with errno set to the error code.
813987da915Sopenharmony_ci *
814987da915Sopenharmony_ci * The following error codes are defined:
815987da915Sopenharmony_ci *	EINVAL		Input parameter error
816987da915Sopenharmony_ci *	EOPNOTSUPP	System does not support HDIO_GETGEO ioctl
817987da915Sopenharmony_ci *	ENOTTY		@dev is a file or a device not supporting HDIO_GETGEO
818987da915Sopenharmony_ci *	ENOMEM		Not enough memory to complete the request
819987da915Sopenharmony_ci */
820987da915Sopenharmony_ciint ntfs_device_sectors_per_track_get(struct ntfs_device *dev)
821987da915Sopenharmony_ci{
822987da915Sopenharmony_ci	if (!dev) {
823987da915Sopenharmony_ci		errno = EINVAL;
824987da915Sopenharmony_ci		return -1;
825987da915Sopenharmony_ci	}
826987da915Sopenharmony_ci	if (dev->d_sectors_per_track == -1) {
827987da915Sopenharmony_ci		if (ntfs_device_get_geo(dev) == -1)
828987da915Sopenharmony_ci			return -1;
829987da915Sopenharmony_ci		if (dev->d_sectors_per_track == -1) {
830987da915Sopenharmony_ci			errno = EINVAL;
831987da915Sopenharmony_ci			return -1;
832987da915Sopenharmony_ci		}
833987da915Sopenharmony_ci	}
834987da915Sopenharmony_ci	return dev->d_sectors_per_track;
835987da915Sopenharmony_ci}
836987da915Sopenharmony_ci
837987da915Sopenharmony_ci/**
838987da915Sopenharmony_ci * ntfs_device_sector_size_get - get sector size of a device
839987da915Sopenharmony_ci * @dev:	open device
840987da915Sopenharmony_ci *
841987da915Sopenharmony_ci * On success, return the sector size in bytes of the device @dev.
842987da915Sopenharmony_ci * On error return -1 with errno set to the error code.
843987da915Sopenharmony_ci *
844987da915Sopenharmony_ci * The following error codes are defined:
845987da915Sopenharmony_ci *	EINVAL		Input parameter error
846987da915Sopenharmony_ci *	EOPNOTSUPP	System does not support BLKSSZGET ioctl
847987da915Sopenharmony_ci *	ENOTTY		@dev is a file or a device not supporting BLKSSZGET
848987da915Sopenharmony_ci */
849987da915Sopenharmony_ciint ntfs_device_sector_size_get(struct ntfs_device *dev)
850987da915Sopenharmony_ci{
851987da915Sopenharmony_ci	if (!dev) {
852987da915Sopenharmony_ci		errno = EINVAL;
853987da915Sopenharmony_ci		return -1;
854987da915Sopenharmony_ci	}
855987da915Sopenharmony_ci#ifdef BLKSSZGET
856987da915Sopenharmony_ci	{
857987da915Sopenharmony_ci		int sect_size = 0;
858987da915Sopenharmony_ci
859987da915Sopenharmony_ci		if (!dev->d_ops->ioctl(dev, BLKSSZGET, &sect_size)) {
860987da915Sopenharmony_ci			ntfs_log_debug("BLKSSZGET sector size = %d bytes\n",
861987da915Sopenharmony_ci					sect_size);
862987da915Sopenharmony_ci			return sect_size;
863987da915Sopenharmony_ci		}
864987da915Sopenharmony_ci	}
865987da915Sopenharmony_ci#elif defined(DIOCGSECTORSIZE)
866987da915Sopenharmony_ci	{
867987da915Sopenharmony_ci		/* FreeBSD */
868987da915Sopenharmony_ci		size_t sect_size = 0;
869987da915Sopenharmony_ci
870987da915Sopenharmony_ci		if (!dev->d_ops->ioctl(dev, DIOCGSECTORSIZE, &sect_size)) {
871987da915Sopenharmony_ci			ntfs_log_debug("DIOCGSECTORSIZE sector size = %d bytes\n",
872987da915Sopenharmony_ci					(int) sect_size);
873987da915Sopenharmony_ci			return sect_size;
874987da915Sopenharmony_ci		}
875987da915Sopenharmony_ci	}
876987da915Sopenharmony_ci#elif defined(DKIOCGETBLOCKSIZE)
877987da915Sopenharmony_ci	{
878987da915Sopenharmony_ci		/* Mac OS X */
879987da915Sopenharmony_ci		uint32_t sect_size = 0;
880987da915Sopenharmony_ci
881987da915Sopenharmony_ci		if (!dev->d_ops->ioctl(dev, DKIOCGETBLOCKSIZE, &sect_size)) {
882987da915Sopenharmony_ci			ntfs_log_debug("DKIOCGETBLOCKSIZE sector size = %d bytes\n",
883987da915Sopenharmony_ci					(int) sect_size);
884987da915Sopenharmony_ci			return sect_size;
885987da915Sopenharmony_ci		}
886987da915Sopenharmony_ci	}
887987da915Sopenharmony_ci#else
888987da915Sopenharmony_ci	errno = EOPNOTSUPP;
889987da915Sopenharmony_ci#endif
890987da915Sopenharmony_ci	return -1;
891987da915Sopenharmony_ci}
892987da915Sopenharmony_ci
893987da915Sopenharmony_ci/**
894987da915Sopenharmony_ci * ntfs_device_block_size_set - set block size of a device
895987da915Sopenharmony_ci * @dev:	open device
896987da915Sopenharmony_ci * @block_size: block size to set @dev to
897987da915Sopenharmony_ci *
898987da915Sopenharmony_ci * On success, return 0.
899987da915Sopenharmony_ci * On error return -1 with errno set to the error code.
900987da915Sopenharmony_ci *
901987da915Sopenharmony_ci * The following error codes are defined:
902987da915Sopenharmony_ci *	EINVAL		Input parameter error
903987da915Sopenharmony_ci *	EOPNOTSUPP	System does not support BLKBSZSET ioctl
904987da915Sopenharmony_ci *	ENOTTY		@dev is a file or a device not supporting BLKBSZSET
905987da915Sopenharmony_ci */
906987da915Sopenharmony_ciint ntfs_device_block_size_set(struct ntfs_device *dev,
907987da915Sopenharmony_ci		int block_size __attribute__((unused)))
908987da915Sopenharmony_ci{
909987da915Sopenharmony_ci	if (!dev) {
910987da915Sopenharmony_ci		errno = EINVAL;
911987da915Sopenharmony_ci		return -1;
912987da915Sopenharmony_ci	}
913987da915Sopenharmony_ci#ifdef BLKBSZSET
914987da915Sopenharmony_ci	{
915987da915Sopenharmony_ci		size_t s_block_size = block_size;
916987da915Sopenharmony_ci		if (!dev->d_ops->ioctl(dev, BLKBSZSET, &s_block_size)) {
917987da915Sopenharmony_ci			ntfs_log_debug("Used BLKBSZSET to set block size to "
918987da915Sopenharmony_ci					"%d bytes.\n", block_size);
919987da915Sopenharmony_ci			return 0;
920987da915Sopenharmony_ci		}
921987da915Sopenharmony_ci		/* If not a block device, pretend it was successful. */
922987da915Sopenharmony_ci		if (!NDevBlock(dev))
923987da915Sopenharmony_ci			return 0;
924987da915Sopenharmony_ci	}
925987da915Sopenharmony_ci#else
926987da915Sopenharmony_ci	/* If not a block device, pretend it was successful. */
927987da915Sopenharmony_ci	if (!NDevBlock(dev))
928987da915Sopenharmony_ci		return 0;
929987da915Sopenharmony_ci	errno = EOPNOTSUPP;
930987da915Sopenharmony_ci#endif
931987da915Sopenharmony_ci	return -1;
932987da915Sopenharmony_ci}
933