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