Steve Kondik | 2111ad7 | 2013-07-07 12:07:44 -0700 | [diff] [blame] | 1 | /** |
| 2 | * device.c - Low level device io functions. Originated from the Linux-NTFS project. |
| 3 | * |
Steve Kondik | 79165c3 | 2015-11-09 19:43:00 -0800 | [diff] [blame] | 4 | * Copyright (c) 2004-2013 Anton Altaparmakov |
Steve Kondik | 2111ad7 | 2013-07-07 12:07:44 -0700 | [diff] [blame] | 5 | * Copyright (c) 2004-2006 Szabolcs Szakacsits |
| 6 | * Copyright (c) 2010 Jean-Pierre Andre |
Steve Kondik | 79165c3 | 2015-11-09 19:43:00 -0800 | [diff] [blame] | 7 | * Copyright (c) 2008-2013 Tuxera Inc. |
Steve Kondik | 2111ad7 | 2013-07-07 12:07:44 -0700 | [diff] [blame] | 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 |
Steve Kondik | 79165c3 | 2015-11-09 19:43:00 -0800 | [diff] [blame] | 71 | #ifdef ENABLE_HD |
| 72 | #include <hd.h> |
| 73 | #endif |
Steve Kondik | 2111ad7 | 2013-07-07 12:07:44 -0700 | [diff] [blame] | 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; |
Steve Kondik | 79165c3 | 2015-11-09 19:43:00 -0800 | [diff] [blame] | 135 | dev->d_heads = -1; |
| 136 | dev->d_sectors_per_track = -1; |
Steve Kondik | 2111ad7 | 2013-07-07 12:07:44 -0700 | [diff] [blame] | 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 | |
Steve Kondik | 79165c3 | 2015-11-09 19:43:00 -0800 | [diff] [blame] | 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 | |
Steve Kondik | 2111ad7 | 2013-07-07 12:07:44 -0700 | [diff] [blame] | 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 |
Steve Kondik | 79165c3 | 2015-11-09 19:43:00 -0800 | [diff] [blame] | 788 | * ENOMEM Not enough memory to complete the request |
Steve Kondik | 2111ad7 | 2013-07-07 12:07:44 -0700 | [diff] [blame] | 789 | */ |
| 790 | int ntfs_device_heads_get(struct ntfs_device *dev) |
| 791 | { |
| 792 | if (!dev) { |
| 793 | errno = EINVAL; |
| 794 | return -1; |
| 795 | } |
Steve Kondik | 79165c3 | 2015-11-09 19:43:00 -0800 | [diff] [blame] | 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; |
Steve Kondik | 2111ad7 | 2013-07-07 12:07:44 -0700 | [diff] [blame] | 802 | } |
| 803 | } |
Steve Kondik | 79165c3 | 2015-11-09 19:43:00 -0800 | [diff] [blame] | 804 | return dev->d_heads; |
Steve Kondik | 2111ad7 | 2013-07-07 12:07:44 -0700 | [diff] [blame] | 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 |
Steve Kondik | 79165c3 | 2015-11-09 19:43:00 -0800 | [diff] [blame] | 818 | * ENOMEM Not enough memory to complete the request |
Steve Kondik | 2111ad7 | 2013-07-07 12:07:44 -0700 | [diff] [blame] | 819 | */ |
| 820 | int ntfs_device_sectors_per_track_get(struct ntfs_device *dev) |
| 821 | { |
| 822 | if (!dev) { |
| 823 | errno = EINVAL; |
| 824 | return -1; |
| 825 | } |
Steve Kondik | 79165c3 | 2015-11-09 19:43:00 -0800 | [diff] [blame] | 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; |
Steve Kondik | 2111ad7 | 2013-07-07 12:07:44 -0700 | [diff] [blame] | 832 | } |
| 833 | } |
Steve Kondik | 79165c3 | 2015-11-09 19:43:00 -0800 | [diff] [blame] | 834 | return dev->d_sectors_per_track; |
Steve Kondik | 2111ad7 | 2013-07-07 12:07:44 -0700 | [diff] [blame] | 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 | } |