hrev47115 adds 1 changeset to branch 'master' old head: 02cdea9f9d6d42aebb17dfa38c9140e837adbbe0 new head: d68289f7ae3caa4b0d2b87717966b539632b8146 overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=d68289f+%5E02cdea9 ---------------------------------------------------------------------------- d68289f: NTFS: Update libntfs to 2014.2.15 [ Gerasim Troeglazov <3dEyes@xxxxxxxxx> ] ---------------------------------------------------------------------------- Revision: hrev47115 Commit: d68289f7ae3caa4b0d2b87717966b539632b8146 URL: http://cgit.haiku-os.org/haiku/commit/?id=d68289f Author: Gerasim Troeglazov <3dEyes@xxxxxxxxx> Date: Mon Apr 14 11:35:50 2014 UTC ---------------------------------------------------------------------------- 14 files changed, 860 insertions(+), 139 deletions(-) .../kernel/file_systems/ntfs/libntfs/acls.c | 2 +- .../kernel/file_systems/ntfs/libntfs/attrib.c | 48 +- .../kernel/file_systems/ntfs/libntfs/attrib.h | 3 +- .../kernel/file_systems/ntfs/libntfs/compress.c | 9 +- .../kernel/file_systems/ntfs/libntfs/device.c | 174 ++++- .../kernel/file_systems/ntfs/libntfs/device.h | 12 +- .../kernel/file_systems/ntfs/libntfs/device_io.h | 21 +- .../kernel/file_systems/ntfs/libntfs/dir.c | 3 + .../kernel/file_systems/ntfs/libntfs/dir.h | 6 +- .../kernel/file_systems/ntfs/libntfs/logging.c | 2 + .../kernel/file_systems/ntfs/libntfs/reparse.c | 6 +- .../kernel/file_systems/ntfs/libntfs/security.c | 2 + .../kernel/file_systems/ntfs/libntfs/volume.c | 3 +- .../kernel/file_systems/ntfs/libntfs/win32_io.c | 708 +++++++++++++++++-- ---------------------------------------------------------------------------- diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.c index fb6e877..76599b4 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.c @@ -644,7 +644,7 @@ BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz) && ((offgroup + 2) < attrsz) && (!offdacl || ((offdacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) - && (offdacl+sizeof(ACL) < attrsz))) + && (offdacl+sizeof(ACL) <= attrsz))) && (!offsacl || ((offsacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) && (offsacl+sizeof(ACL) <= attrsz))) diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c index c116d9a..1a9ef37 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.c @@ -5,7 +5,7 @@ * Copyright (c) 2002-2005 Richard Russon * Copyright (c) 2002-2008 Szabolcs Szakacsits * Copyright (c) 2004-2007 Yura Pakhuchiy - * Copyright (c) 2007-2011 Jean-Pierre Andre + * Copyright (c) 2007-2013 Jean-Pierre Andre * Copyright (c) 2010 Erik Larsson * * This program/include file is free software; you can redistribute it and/or @@ -454,7 +454,8 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, if (type == AT_ATTRIBUTE_LIST) a->flags = 0; - if ((type == AT_DATA) && !a->initialized_size) { + if ((type == AT_DATA) + && (a->non_resident ? !a->initialized_size : !a->value_length)) { /* * Define/redefine the compression state if stream is * empty, based on the compression mark on parent @@ -4928,7 +4929,7 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize); * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. */ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, - BOOL force_non_resident) + hole_type holes) { ntfs_attr_search_ctx *ctx; ntfs_volume *vol; @@ -4966,7 +4967,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, * attribute non-resident if the attribute type supports it. If it is * smaller we can go ahead and attempt the resize. */ - if ((newsize < vol->mft_record_size) && !force_non_resident) { + if ((newsize < vol->mft_record_size) && (holes != HOLES_NONRES)) { /* Perform the resize of the attribute record. */ if (!(ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, newsize))) { @@ -5011,7 +5012,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, * could cause the attribute to be made resident again, * so size changes are not allowed. */ - if (force_non_resident) { + if (holes == HOLES_NONRES) { ret = 0; if (newsize != na->data_size) { ntfs_log_error("Cannot change size when" @@ -5022,7 +5023,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, return (ret); } /* Resize non-resident attribute */ - return ntfs_attr_truncate_i(na, newsize, HOLES_OK); + return ntfs_attr_truncate_i(na, newsize, holes); } else if (errno != ENOSPC && errno != EPERM) { err = errno; ntfs_log_perror("Failed to make attribute non-resident"); @@ -5078,7 +5079,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, ntfs_inode_mark_dirty(tna->ni); ntfs_attr_close(tna); ntfs_attr_put_search_ctx(ctx); - return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); + return ntfs_resident_attr_resize_i(na, newsize, holes); } /* Check whether error occurred. */ if (errno != ENOENT) { @@ -5098,7 +5099,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, ntfs_log_perror("Could not free space in MFT record"); return -1; } - return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); + return ntfs_resident_attr_resize_i(na, newsize, holes); } /* @@ -5137,7 +5138,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, ntfs_attr_put_search_ctx(ctx); if (ntfs_inode_add_attrlist(ni)) return -1; - return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); + return ntfs_resident_attr_resize_i(na, newsize, holes); } /* Allocate new mft record. */ ni = ntfs_mft_record_alloc(vol, ni); @@ -5158,7 +5159,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, ntfs_attr_put_search_ctx(ctx); /* Try to perform resize once again. */ - return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); + return ntfs_resident_attr_resize_i(na, newsize, holes); resize_done: /* @@ -5179,7 +5180,7 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) int ret; ntfs_log_enter("Entering\n"); - ret = ntfs_resident_attr_resize_i(na, newsize, FALSE); + ret = ntfs_resident_attr_resize_i(na, newsize, HOLES_OK); ntfs_log_leave("\n"); return ret; } @@ -5203,7 +5204,7 @@ int ntfs_attr_force_non_resident(ntfs_attr *na) { int res; - res = ntfs_resident_attr_resize_i(na, na->data_size, TRUE); + res = ntfs_resident_attr_resize_i(na, na->data_size, HOLES_NONRES); if (!res && !NAttrNonResident(na)) { res = -1; errno = EIO; @@ -5566,7 +5567,7 @@ retry: BOOL changed; if (!(na->data_flags & ATTR_IS_SPARSE)) { - int sparse; + int sparse = 0; runlist_element *xrl; /* @@ -5574,10 +5575,18 @@ retry: * have to check whether there is a hole * in the updated region. */ - xrl = na->rl; - if (xrl->lcn == LCN_RL_NOT_MAPPED) - xrl++; - sparse = ntfs_rl_sparse(xrl); + for (xrl = na->rl; xrl->length; xrl++) { + if (xrl->lcn < 0) { + if (xrl->lcn == LCN_HOLE) { + sparse = 1; + break; + } + if (xrl->lcn != LCN_RL_NOT_MAPPED) { + sparse = -1; + break; + } + } + } if (sparse < 0) { ntfs_log_error("Could not check whether sparse\n"); errno = EIO; @@ -6441,7 +6450,7 @@ static int ntfs_attr_truncate_i(ntfs_attr *na, const s64 newsize, else ret = ntfs_non_resident_attr_shrink(na, fullsize); } else - ret = ntfs_resident_attr_resize(na, newsize); + ret = ntfs_resident_attr_resize_i(na, newsize, holes); out: ntfs_log_leave("Return status %d\n", ret); return ret; @@ -6587,7 +6596,8 @@ void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, na = ntfs_attr_open(ni, type, name, name_len); if (!na) { - ntfs_log_perror("ntfs_attr_open failed"); + ntfs_log_perror("ntfs_attr_open failed, inode %lld attr 0x%lx", + (long long)ni->mft_no,(long)le32_to_cpu(type)); goto err_exit; } data = ntfs_malloc(na->data_size); diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.h index 1979ba6..a0bee26 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/attrib.h @@ -61,7 +61,8 @@ typedef enum { typedef enum { /* ways of processing holes when expanding */ HOLES_NO, HOLES_OK, - HOLES_DELAY + HOLES_DELAY, + HOLES_NONRES } hole_type; /** diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/compress.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/compress.c index aeb082d..69b39ed 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/compress.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/compress.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2005 Anton Altaparmakov * Copyright (c) 2004-2006 Szabolcs Szakacsits * Copyright (c) 2005 Yura Pakhuchiy - * Copyright (c) 2009-2011 Jean-Pierre Andre + * Copyright (c) 2009-2013 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -734,6 +734,7 @@ do_next_cb: ofs = 0; } else { s64 tdata_size, tinitialized_size; + u32 decompsz; /* * Compressed cb, decompress it into the temporary buffer, then @@ -791,7 +792,10 @@ do_next_cb: if (cb_pos + 2 <= cb_end) *(u16*)cb_pos = 0; ntfs_log_debug("Successfully read the compression block.\n"); - if (ntfs_decompress(dest, cb_size, cb, cb_size) < 0) { + /* Do not decompress beyond the requested block */ + to_read = min(count, cb_size - ofs); + decompsz = ((ofs + to_read - 1) | (NTFS_SB_SIZE - 1)) + 1; + if (ntfs_decompress(dest, decompsz, cb, cb_size) < 0) { err = errno; free(cb); free(dest); @@ -800,7 +804,6 @@ do_next_cb: errno = err; return -1; } - to_read = min(count, cb_size - ofs); memcpy(b, dest + ofs, to_read); total += to_read; count -= to_read; diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/device.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/device.c index ef7070b..12ca14d 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/device.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/device.c @@ -1,9 +1,10 @@ /** * device.c - Low level device io functions. Originated from the Linux-NTFS project. * - * Copyright (c) 2004-2006 Anton Altaparmakov + * Copyright (c) 2004-2013 Anton Altaparmakov * Copyright (c) 2004-2006 Szabolcs Szakacsits * Copyright (c) 2010 Jean-Pierre Andre + * Copyright (c) 2008-2013 Tuxera Inc. * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -67,6 +68,9 @@ #ifdef HAVE_LINUX_HDREG_H #include <linux/hdreg.h> #endif +#ifdef ENABLE_HD +#include <hd.h> +#endif #ifdef __HAIKU__ #include <Drivers.h> #endif @@ -131,6 +135,8 @@ struct ntfs_device *ntfs_device_alloc(const char *name, const long state, dev->d_ops = dops; dev->d_state = state; dev->d_private = priv_data; + dev->d_heads = -1; + dev->d_sectors_per_track = -1; } return dev; } @@ -663,6 +669,132 @@ s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev) return -1; } +static int ntfs_device_get_geo(struct ntfs_device *dev) +{ + int err; + + if (!dev) { + errno = EINVAL; + return -1; + } + err = EOPNOTSUPP; +#ifdef ENABLE_HD + { + hd_data_t *hddata; + hd_t *hd, *devlist, *partlist = NULL; + str_list_t *names; + hd_res_t *res; + const int d_name_len = strlen(dev->d_name) + 1; + int done = 0; + + hddata = calloc(1, sizeof(*hddata)); + if (!hddata) { + err = ENOMEM; + goto skip_hd; + } + /* List all "disk" class devices on the system. */ + devlist = hd_list(hddata, hw_disk, 1, NULL); + if (!devlist) { + free(hddata); + err = ENOMEM; + goto skip_hd; + } + /* + * Loop over each disk device looking for the device with the + * same unix name as @dev. + */ + for (hd = devlist; hd; hd = hd->next) { + if (hd->unix_dev_name && !strncmp(dev->d_name, + hd->unix_dev_name, d_name_len)) + goto got_hd; + if (hd->unix_dev_name2 && !strncmp(dev->d_name, + hd->unix_dev_name2, d_name_len)) + goto got_hd; + for (names = hd->unix_dev_names; names; + names = names->next) { + if (names->str && !strncmp(dev->d_name, + names->str, d_name_len)) + goto got_hd; + } + } + /* + * Device was not a whole disk device. Unless it is a file it + * is likely to be a partition device. List all "partition" + * class devices on the system. + */ + partlist = hd_list(hddata, hw_partition, 1, NULL); + for (hd = partlist; hd; hd = hd->next) { + if (hd->unix_dev_name && !strncmp(dev->d_name, + hd->unix_dev_name, d_name_len)) + goto got_part_hd; + if (hd->unix_dev_name2 && !strncmp(dev->d_name, + hd->unix_dev_name2, d_name_len)) + goto got_part_hd; + for (names = hd->unix_dev_names; names; + names = names->next) { + if (names->str && !strncmp(dev->d_name, + names->str, d_name_len)) + goto got_part_hd; + } + } + /* Failed to find the device. Stop trying and clean up. */ + goto end_hd; +got_part_hd: + /* Get the whole block device the partition device is on. */ + hd = hd_get_device_by_idx(hddata, hd->attached_to); + if (!hd) + goto end_hd; +got_hd: + /* + * @hd is now the whole block device either being formatted or + * that the partition being formatted is on. + * + * Loop over each resource of the disk device looking for the + * BIOS legacy geometry obtained from EDD which is what Windows + * needs to boot. + */ + for (res = hd->res; res; res = res->next) { + /* geotype 3 is BIOS legacy. */ + if (res->any.type != res_disk_geo || + res->disk_geo.geotype != 3) + continue; + dev->d_heads = res->disk_geo.heads; + dev->d_sectors_per_track = res->disk_geo.sectors; + done = 1; + } +end_hd: + if (partlist) + hd_free_hd_list(partlist); + hd_free_hd_list(devlist); + hd_free_hd_data(hddata); + free(hddata); + if (done) { + ntfs_log_debug("EDD/BIOD legacy heads = %u, sectors " + "per track = %u\n", dev->d_heads, + dev->d_sectors_per_track); + return 0; + } + } +skip_hd: +#endif +#ifdef HDIO_GETGEO + { struct hd_geometry geo; + + if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { + dev->d_heads = geo.heads; + dev->d_sectors_per_track = geo.sectors; + ntfs_log_debug("HDIO_GETGEO heads = %u, sectors per " + "track = %u\n", dev->d_heads, + dev->d_sectors_per_track); + return 0; + } + err = errno; + } +#endif + errno = err; + return -1; +} + /** * ntfs_device_heads_get - get number of heads of device * @dev: open device @@ -674,6 +806,7 @@ s64 ntfs_device_partition_start_sector_get(struct ntfs_device *dev) * EINVAL Input parameter error * EOPNOTSUPP System does not support HDIO_GETGEO ioctl * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO + * ENOMEM Not enough memory to complete the request */ int ntfs_device_heads_get(struct ntfs_device *dev) { @@ -681,20 +814,15 @@ int ntfs_device_heads_get(struct ntfs_device *dev) errno = EINVAL; return -1; } -#ifdef HDIO_GETGEO - { struct hd_geometry geo; - - if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { - ntfs_log_debug("HDIO_GETGEO heads = %u (0x%x)\n", - (unsigned)geo.heads, - (unsigned)geo.heads); - return geo.heads; + if (dev->d_heads == -1) { + if (ntfs_device_get_geo(dev) == -1) + return -1; + if (dev->d_heads == -1) { + errno = EINVAL; + return -1; } } -#else - errno = EOPNOTSUPP; -#endif - return -1; + return dev->d_heads; } /** @@ -708,6 +836,7 @@ int ntfs_device_heads_get(struct ntfs_device *dev) * EINVAL Input parameter error * EOPNOTSUPP System does not support HDIO_GETGEO ioctl * ENOTTY @dev is a file or a device not supporting HDIO_GETGEO + * ENOMEM Not enough memory to complete the request */ int ntfs_device_sectors_per_track_get(struct ntfs_device *dev) { @@ -715,20 +844,15 @@ int ntfs_device_sectors_per_track_get(struct ntfs_device *dev) errno = EINVAL; return -1; } -#ifdef HDIO_GETGEO - { struct hd_geometry geo; - - if (!dev->d_ops->ioctl(dev, HDIO_GETGEO, &geo)) { - ntfs_log_debug("HDIO_GETGEO sectors_per_track = %u (0x%x)\n", - (unsigned)geo.sectors, - (unsigned)geo.sectors); - return geo.sectors; + if (dev->d_sectors_per_track == -1) { + if (ntfs_device_get_geo(dev) == -1) + return -1; + if (dev->d_sectors_per_track == -1) { + errno = EINVAL; + return -1; } } -#else - errno = EOPNOTSUPP; -#endif - return -1; + return dev->d_sectors_per_track; } /** diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/device.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/device.h index ad34ac5..c7cc9b6 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/device.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/device.h @@ -1,7 +1,8 @@ /* * device.h - Exports for low level device io. Originated from the Linux-NTFS project. * - * Copyright (c) 2000-2006 Anton Altaparmakov + * Copyright (c) 2000-2013 Anton Altaparmakov + * Copyright (c) 2008-2013 Tuxera Inc. * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -73,6 +74,11 @@ typedef enum { * * The ntfs device structure defining all operations needed to access the low * level device underlying the ntfs volume. + * + * Note d_heads and d_sectors_per_track are only set as a result of a call to + * either ntfs_device_heads_get() or ntfs_device_sectors_per_track_get() (both + * calls will set up both fields or if getting them failed they will be left at + * -1). */ struct ntfs_device { struct ntfs_device_operations *d_ops; /* Device operations. */ @@ -80,6 +86,10 @@ struct ntfs_device { char *d_name; /* Name of device. */ void *d_private; /* Private data used by the device operations. */ + int d_heads; /* Disk geometry: number of + heads or -1. */ + int d_sectors_per_track; /* Disk geometry: number of + sectors per track or -1. */ }; struct stat; diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/device_io.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/device_io.h index 8437cc2..24f8d9b 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/device_io.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/device_io.h @@ -28,12 +28,18 @@ #ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS -#ifndef __CYGWIN32__ +#if defined(linux) || defined(__uClinux__) || defined(__sun) \ + || defined(__APPLE__) || defined(__DARWIN__) + /* Make sure the presence of <windows.h> means compiling for Windows */ +#undef HAVE_WINDOWS_H +#endif + +#ifndef HAVE_WINDOWS_H -/* Not on Cygwin; use standard Unix style low level device operations. */ +/* Not for Windows use standard Unix style low level device operations. */ #define ntfs_device_default_io_ops ntfs_device_unix_io_ops -#else /* __CYGWIN32__ */ +#else /* HAVE_WINDOWS_H */ #ifndef HDIO_GETGEO # define HDIO_GETGEO 0x301 @@ -60,10 +66,15 @@ struct hd_geometry { # define BLKBSZSET 0x40041271 #endif -/* On Cygwin; use Win32 low level device operations. */ +/* On Windows (and Cygwin) : use Win32 low level device operations. */ #define ntfs_device_default_io_ops ntfs_device_win32_io_ops -#endif /* __CYGWIN32__ */ +/* A few useful functions */ +int ntfs_win32_set_sparse(int); +int ntfs_win32_ftruncate(int fd, s64 size); +int ntfs_win32_device_ftruncate(struct ntfs_device*, s64); + +#endif /* HAVE_WINDOWS_H */ /* Forward declaration. */ diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c index d5f08f2..a13b121 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.c @@ -1047,6 +1047,9 @@ static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits, * Return the mft reference of the parent directory on success or -1 on error * with errno set to the error code. */ +#ifndef __HAIKU__ +static +#endif MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni) { MFT_REF mref; diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.h b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.h index 43308d1..7d196b0 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.h +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/dir.h @@ -88,8 +88,6 @@ extern int ntfs_delete(ntfs_volume *vol, const char *path, extern int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, const ntfschar *name, u8 name_len); -extern MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni); - /* * File types (adapted from include <linux/fs.h>) */ @@ -118,6 +116,10 @@ extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni); +#ifdef __HAIKU__ +MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni); +#endif + int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, char *value, size_t size); int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c index 8102827..76acdbc 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/logging.c @@ -188,6 +188,7 @@ u32 ntfs_log_clear_flags(u32 flags) return old; } + /** * ntfs_log_get_stream - Default output streams for logging levels * @level: Log level @@ -277,6 +278,7 @@ static const char * ntfs_log_get_prefix(u32 level) return prefix; } + /** * ntfs_log_set_handler - Provide an alternate logging handler * @handler: function to perform the logging diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.c index dc44bcd..cdabadd 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/reparse.c @@ -3,7 +3,7 @@ * * This module is part of ntfs-3g library * - * Copyright (c) 2008-2012 Jean-Pierre Andre + * Copyright (c) 2008-2013 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -313,14 +313,14 @@ static char *search_relative(ntfs_inode *ni, ntfschar *path, int count) if ((count >= (pos + 2)) && (path[pos] == const_cpu_to_le16('.')) && (path[pos+1] == const_cpu_to_le16('\\'))) { - path[1] = const_cpu_to_le16('/'); + path[pos+1] = const_cpu_to_le16('/'); pos += 2; } else { if ((count >= (pos + 3)) && (path[pos] == const_cpu_to_le16('.')) &&(path[pos+1] == const_cpu_to_le16('.')) && (path[pos+2] == const_cpu_to_le16('\\'))) { - path[2] = const_cpu_to_le16('/'); + path[pos+2] = const_cpu_to_le16('/'); pos += 3; newni = ntfs_dir_parent_inode(curni); if (curni != ni) diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/security.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/security.c index 2ff3306..8ab23ef 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/security.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/security.c @@ -373,6 +373,8 @@ void ntfs_generate_guid(GUID *guid) unsigned int i; u8 *p = (u8 *)guid; + /* this is called at most once from mkntfs */ + srandom(time((time_t*)NULL) ^ (getpid() << 16)); for (i = 0; i < sizeof(GUID); i++) { p[i] = (u8)(random() & 0xFF); if (i == 7) diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c index 6acef48..1cb58db 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/volume.c @@ -1433,7 +1433,8 @@ static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags) err = errno; goto exit; } - if (!(f = setmntent(MOUNTED, "r"))) { + f = setmntent("/proc/mounts", "r"); + if (!f && !(f = setmntent(MOUNTED, "r"))) { err = errno; goto exit; } diff --git a/src/add-ons/kernel/file_systems/ntfs/libntfs/win32_io.c b/src/add-ons/kernel/file_systems/ntfs/libntfs/win32_io.c index ed9fa52..437a3fc 100644 --- a/src/add-ons/kernel/file_systems/ntfs/libntfs/win32_io.c +++ b/src/add-ons/kernel/file_systems/ntfs/libntfs/win32_io.c @@ -6,6 +6,7 @@ * Copyright (c) 2003-2004 Lode Leroy * Copyright (c) 2003-2006 Anton Altaparmakov * Copyright (c) 2004-2005 Yuval Fledel + * Copyright (c) 2012-2013 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -26,8 +27,33 @@ #include "config.h" #ifdef HAVE_WINDOWS_H +#define BOOL WINBOOL /* avoid conflicting definitions of BOOL */ #include <windows.h> +#undef BOOL #endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +/* + * Definitions needed for <winioctl.h> + */ +#ifndef _ANONYMOUS_UNION +#define _ANONYMOUS_UNION +#define _ANONYMOUS_STRUCT +typedef unsigned long long DWORD64; +#endif + +typedef struct { + DWORD data1; /* The first eight hexadecimal digits of the GUID. */ + WORD data2; /* The first group of four hexadecimal digits. */ + WORD data3; /* The second group of four hexadecimal digits. */ + char data4[8]; /* The first two bytes are the third group of four + hexadecimal digits. The remaining six bytes are the + final 12 hexadecimal digits. */ +} GUID; + #include <winioctl.h> #ifdef HAVE_STDIO_H @@ -42,6 +68,11 @@ #ifdef HAVE_FCNTL_H #include <fcntl.h> #endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#define stat stat64 +#define st_blocks st_rdev /* emulate st_blocks, missing in Windows */ +#endif /* Prevent volume.h from being be loaded, as it conflicts with winnt.h. */ #define _NTFS_VOLUME_H @@ -51,6 +82,10 @@ typedef struct ntfs_volume ntfs_volume; #include "debug.h" #include "types.h" #include "device.h" +#include "misc.h" + +#define cpu_to_le16(x) (x) +#define const_cpu_to_le16(x) (x) #ifndef MAX_PATH #define MAX_PATH 1024 @@ -61,10 +96,26 @@ typedef struct ntfs_volume ntfs_volume; #define NTFS_BLOCK_SIZE_BITS 9 #endif +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + #ifndef IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS #define IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS 5636096 #endif +#ifndef IOCTL_DISK_GET_DRIVE_GEOMETRY +#define IOCTL_DISK_GET_DRIVE_GEOMETRY 0x70000 +#endif + +#ifndef IOCTL_GET_DISK_LENGTH_INFO +#define IOCTL_GET_DISK_LENGTH_INFO 0x7405c +#endif + +#ifndef FSCTL_ALLOW_EXTENDED_DASD_IO +#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x90083 +#endif + /* Windows 2k+ imports. */ typedef HANDLE (WINAPI *LPFN_FINDFIRSTVOLUME)(LPTSTR, DWORD); typedef BOOL (WINAPI *LPFN_FINDNEXTVOLUME)(HANDLE, LPTSTR, DWORD); @@ -83,6 +134,146 @@ static LPFN_SETFILEPOINTEREX fnSetFilePointerEx = NULL; #define FNPOSTFIX "A" #endif +enum { /* see http://msdn.microsoft.com/en-us/library/cc704588(v=prot.10).aspx */ + STATUS_UNKNOWN = -1, + STATUS_SUCCESS = 0x00000000, + STATUS_BUFFER_OVERFLOW = 0x80000005, + STATUS_INVALID_HANDLE = 0xC0000008, + STATUS_INVALID_PARAMETER = 0xC000000D, + STATUS_INVALID_DEVICE_REQUEST = 0xC0000010, + STATUS_END_OF_FILE = 0xC0000011, + STATUS_CONFLICTING_ADDRESSES = 0xC0000018, + STATUS_NO_MATCH = 0xC000001E, + STATUS_ACCESS_DENIED = 0xC0000022, + STATUS_BUFFER_TOO_SMALL = 0xC0000023, + STATUS_OBJECT_TYPE_MISMATCH = 0xC0000024, + STATUS_FILE_NOT_FOUND = 0xC0000028, + STATUS_OBJECT_NAME_INVALID = 0xC0000033, + STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034, + STATUS_INVALID_PARAMETER_1 = 0xC00000EF, + STATUS_IO_DEVICE_ERROR = 0xC0000185, + STATUS_GUARD_PAGE_VIOLATION = 0x80000001 + } ; + +typedef u32 NTSTATUS; /* do not let the compiler choose the size */ +#ifdef __x86_64__ +typedef unsigned long long ULONG_PTR; /* an integer the same size as a pointer */ +#else +typedef unsigned long ULONG_PTR; /* an integer the same size as a pointer */ +#endif + +HANDLE get_osfhandle(int); /* from msvcrt.dll */ + +/* + * A few needed definitions not included in <windows.h> + */ + +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + }; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; +#ifdef __x86_64__ + u32 padding; +#endif + PWSTR Buffer; +} UNICODE_STRING, *PUNICODE_STRING; + +typedef struct _OBJECT_ATTRIBUTES { + ULONG Length; +#ifdef __x86_64__ + u32 padding1; + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + u32 padding2; +#else + HANDLE RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; +#endif + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; + +#define FILE_OPEN 1 +#define FILE_CREATE 2 +#define FILE_OVERWRITE 4 +#define FILE_SYNCHRONOUS_IO_ALERT 0x10 +#define FILE_SYNCHRONOUS_IO_NONALERT 0x20 +#define OBJ_CASE_INSENSITIVE 0x40 + +typedef void (WINAPI *PIO_APC_ROUTINE)(void*, PIO_STATUS_BLOCK, ULONG); + +extern WINAPI NTSTATUS NtOpenFile( + PHANDLE FileHandle, + ACCESS_MASK DesiredAccess, + POBJECT_ATTRIBUTES ObjectAttributes, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG ShareAccess, + ULONG OpenOptions +); + +extern WINAPI NTSTATUS NtReadFile( + HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + PVOID Buffer, + ULONG Length, + PLARGE_INTEGER ByteOffset, + PULONG Key +); + +extern WINAPI NTSTATUS NtWriteFile( + HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + LPCVOID Buffer, + ULONG Length, + PLARGE_INTEGER ByteOffset, + PULONG Key +); + +extern NTSTATUS WINAPI NtClose( + HANDLE Handle +); + +extern NTSTATUS WINAPI NtDeviceIoControlFile( + HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG IoControlCode, + PVOID InputBuffer, + ULONG InputBufferLength, + PVOID OutputBuffer, + ULONG OutputBufferLength +); + +extern NTSTATUS WINAPI NtFsControlFile( + HANDLE FileHandle, + HANDLE Event, + PIO_APC_ROUTINE ApcRoutine, + PVOID ApcContext, + PIO_STATUS_BLOCK IoStatusBlock, + ULONG FsControlCode, + PVOID InputBuffer, + ULONG InputBufferLength, + PVOID OutputBuffer, + ULONG OutputBufferLength +); + /** * struct win32_fd - */ @@ -93,8 +284,11 @@ typedef struct { s64 part_length; int part_hidden_sectors; s64 geo_size, geo_cylinders; + s32 geo_sector_size; + s64 volume_size; DWORD geo_sectors, geo_heads; HANDLE vol_handle; + BOOL ntdll; } win32_fd; /** @@ -146,6 +340,26 @@ static int ntfs_w32error_to_errno(unsigned int w32error) } } +static int ntfs_ntstatus_to_errno(NTSTATUS status) +{ + ntfs_log_trace("Converting w32error 0x%x.\n",w32error); + switch (status) { + case STATUS_INVALID_HANDLE : + case STATUS_INVALID_PARAMETER : + case STATUS_OBJECT_NAME_INVALID : + case STATUS_INVALID_DEVICE_REQUEST : + return (EINVAL); + case STATUS_ACCESS_DENIED : + return (EACCES); + case STATUS_IO_DEVICE_ERROR : + case STATUS_END_OF_FILE : + return (EIO); + default: + /* generic message */ + return ENOMSG; + } +} + /** * libntfs_SetFilePointerEx - emulation for SetFilePointerEx() * @@ -156,10 +370,11 @@ static BOOL WINAPI libntfs_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer, DWORD dwMoveMethod) { - liDistanceToMove.LowPart = SetFilePointer(hFile, - liDistanceToMove.LowPart, &liDistanceToMove.HighPart, - dwMoveMethod); - if (liDistanceToMove.LowPart == INVALID_SET_FILE_POINTER && + liDistanceToMove.u.LowPart = SetFilePointer(hFile, + liDistanceToMove.u.LowPart, + &liDistanceToMove.u.HighPart, dwMoveMethod); + SetLastError(NO_ERROR); + if (liDistanceToMove.u.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { if (lpNewFilePointer) lpNewFilePointer->QuadPart = -1; @@ -199,7 +414,7 @@ static void ntfs_device_win32_init_imports(void) * SetFilePointerEx(). */ if (!fnSetFilePointerEx) { - ntfs_log_debug("SetFilePonterEx() not found in " + ntfs_log_debug("SetFilePointerEx() not found in " "kernel32.dll: Enabling emulation.\n"); fnSetFilePointerEx = libntfs_SetFilePointerEx; } @@ -232,13 +447,13 @@ static __inline__ int ntfs_device_unix_status_flags_to_win32(int flags) switch (flags & O_ACCMODE) { case O_RDONLY: - win_mode = FILE_READ_DATA; + win_mode = GENERIC_READ; break; case O_WRONLY: - win_mode = FILE_WRITE_DATA; + win_mode = GENERIC_WRITE; break; case O_RDWR: - win_mode = FILE_READ_DATA | FILE_WRITE_DATA; + win_mode = GENERIC_READ | GENERIC_WRITE; break; default: /* error */ @@ -267,7 +482,8 @@ static int ntfs_device_win32_simple_open_file(const char *filename, *handle = CreateFile(filename, ntfs_device_unix_status_flags_to_win32(flags), locking ? 0 : (FILE_SHARE_WRITE | FILE_SHARE_READ), - NULL, OPEN_EXISTING, 0, NULL); + NULL, (flags & O_CREAT ? OPEN_ALWAYS : OPEN_EXISTING), + 0, NULL); if (*handle == INVALID_HANDLE_VALUE) { errno = ntfs_w32error_to_errno(GetLastError()); ntfs_log_trace("CreateFile(%s) failed.\n", filename); @@ -321,6 +537,22 @@ static int ntfs_device_win32_unlock(HANDLE handle) return 0; } +static int ntfs_device_win32_setlock(HANDLE handle, ULONG code) +{ + IO_STATUS_BLOCK io_status; + NTSTATUS res; + + io_status.Status = STATUS_SUCCESS; + io_status.Information = 0; + res = NtFsControlFile(handle,(HANDLE)NULL, + (PIO_APC_ROUTINE)NULL,(void*)NULL, + &io_status, code, + (char*)NULL,0,(char*)NULL,0); + if (res != STATUS_SUCCESS) + errno = ntfs_ntstatus_to_errno(res); + return (res == STATUS_SUCCESS ? 0 : -1); +} + /** * ntfs_device_win32_dismount - dismount a volume * @handle: a win32 HANDLE for a volume to dismount @@ -359,15 +591,18 @@ static int ntfs_device_win32_dismount(HANDLE handle) */ static s64 ntfs_device_win32_getsize(HANDLE handle) { - DWORD loword, hiword; + LONG loword, hiword; - loword = GetFileSize(handle, &hiword); - if (loword == INVALID_FILE_SIZE) { + SetLastError(NO_ERROR); + hiword = 0; + loword = SetFilePointer(handle, 0, &hiword, 2); + if ((loword == INVALID_SET_FILE_POINTER) + && (GetLastError() != NO_ERROR)) { errno = ntfs_w32error_to_errno(GetLastError()); ntfs_log_trace("Couldn't get file size.\n"); return -1; } - return ((s64)hiword << 32) + loword; + return ((s64)hiword << 32) + (ULONG)loword; } /** @@ -467,6 +702,7 @@ static int ntfs_device_win32_getgeo(HANDLE handle, win32_fd *fd) fd->geo_cylinders = ((DISK_GEOMETRY*)&b)->Cylinders.QuadPart; fd->geo_sectors = ((DISK_GEOMETRY*)&b)->SectorsPerTrack; fd->geo_size = ((DISK_GEOMETRY_EX*)&b)->DiskSize.QuadPart; + fd->geo_sector_size = NTFS_BLOCK_SIZE; switch (ddi->DetectionType) { case DetectInt13: fd->geo_cylinders = ddi->Int13.MaxCylinders; @@ -493,6 +729,7 @@ static int ntfs_device_win32_getgeo(HANDLE handle, win32_fd *fd) fd->geo_size = fd->geo_cylinders * fd->geo_sectors * ((DISK_GEOMETRY*)&b)->TracksPerCylinder * ((DISK_GEOMETRY*)&b)->BytesPerSector; + fd->geo_sector_size = ((DISK_GEOMETRY*)&b)->BytesPerSector; return 0; } errno = ntfs_w32error_to_errno(GetLastError()); @@ -500,9 +737,51 @@ static int ntfs_device_win32_getgeo(HANDLE handle, win32_fd *fd) fd->geo_cylinders = -1; fd->geo_sectors = -1; fd->geo_size = -1; + fd->geo_sector_size = NTFS_BLOCK_SIZE; return -1; } +static int ntfs_device_win32_getntgeo(HANDLE handle, win32_fd *fd) +{ + DISK_GEOMETRY geo; + NTSTATUS st; + IO_STATUS_BLOCK status; + u64 bytes; + int res; + + res = -1; + fd->geo_cylinders = 0; + fd->geo_sectors = 0; + fd->geo_size = 1073741824; + fd->geo_sectors = fd->geo_size >> 9; + fd->geo_sector_size = NTFS_BLOCK_SIZE; + + st = NtDeviceIoControlFile(handle, (HANDLE)NULL, + (PIO_APC_ROUTINE)NULL, (void*)NULL, + &status, IOCTL_DISK_GET_DRIVE_GEOMETRY, (void*)NULL, 0, + (void*)&geo, sizeof(geo)); + if (st == STATUS_SUCCESS) { + /* over-estimate the (rounded) number of cylinders */ + fd->geo_cylinders = geo.Cylinders.QuadPart + 1; + fd->geo_sectors = fd->geo_cylinders + *geo.TracksPerCylinder*geo.SectorsPerTrack; + fd->geo_size = fd->geo_sectors*geo.BytesPerSector; + fd->geo_sector_size = geo.BytesPerSector; + res = 0; + /* try to get the exact sector count */ + st = NtDeviceIoControlFile(handle, (HANDLE)NULL, + (PIO_APC_ROUTINE)NULL, (void*)NULL, + &status, IOCTL_GET_DISK_LENGTH_INFO, + (void*)NULL, 0, + (void*)&bytes, sizeof(bytes)); + if (st == STATUS_SUCCESS) { + fd->geo_size = bytes; + fd->geo_sectors = bytes/geo.BytesPerSector; + } + } + return (res); +} + /** * ntfs_device_win32_open_file - open a file via win32 API * @filename: name of the file to open @@ -516,12 +795,22 @@ static __inline__ int ntfs_device_win32_open_file(char *filename, win32_fd *fd, int flags) { HANDLE handle; + int mode; if (ntfs_device_win32_simple_open_file(filename, &handle, flags, FALSE)) { /* open error */ return -1; } + mode = flags & O_ACCMODE; + if ((mode == O_RDWR) || (mode == O_WRONLY)) { + DWORD bytes; + + /* try making sparse (but ignore errors) */ + DeviceIoControl(handle, FSCTL_SET_SPARSE, + (void*)NULL, 0, (void*)NULL, 0, + &bytes, (LPOVERLAPPED)NULL); + } /* fill fd */ fd->handle = handle; fd->part_start = 0; @@ -530,6 +819,8 @@ static __inline__ int ntfs_device_win32_open_file(char *filename, win32_fd *fd, fd->part_hidden_sectors = -1; fd->geo_size = -1; /* used as a marker that this is a file */ fd->vol_handle = INVALID_HANDLE_VALUE; + fd->geo_sector_size = 512; /* will be adjusted from the boot sector */ + fd->ntdll = FALSE; return 0; } @@ -561,6 +852,90 @@ static __inline__ int ntfs_device_win32_open_drive(int drive_id, win32_fd *fd, if (fd->geo_size == -1) fd->geo_size = ntfs_device_win32_getdisklength(handle); /* fill fd */ + fd->ntdll = FALSE; + fd->handle = handle; + fd->part_start = 0; + fd->part_length = fd->geo_size; + fd->pos = 0; + fd->part_hidden_sectors = -1; + fd->vol_handle = INVALID_HANDLE_VALUE; + return 0; +} + +/** + * ntfs_device_win32_open_lowlevel - open a drive via low level win32 API + * @drive_id: drive to open + * @fd: pointer to win32 file device in which to put the result + * @flags: unix open status flags + * + * return 0 if o.k. + * -1 if not, and errno set. + */ +static __inline__ int ntfs_device_win32_open_lowlevel(int drive_id, + win32_fd *fd, int flags) +{ + HANDLE handle; + NTSTATUS st; + ACCESS_MASK access; + ULONG share; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io_status; + UNICODE_STRING unicode_name; + ntfschar unicode_buffer[7]; + int mode; + static const ntfschar unicode_init[] = { + const_cpu_to_le16('\\'), const_cpu_to_le16('?'), + const_cpu_to_le16('?'), const_cpu_to_le16('\\'), + const_cpu_to_le16(' '), const_cpu_to_le16(':'), + const_cpu_to_le16(0) + }; + + memcpy(unicode_buffer, unicode_init, sizeof(unicode_buffer)); + unicode_buffer[4] = cpu_to_le16(drive_id + 'A'); + unicode_name.Buffer = unicode_buffer; + unicode_name.Length = 6*sizeof(ntfschar); + unicode_name.MaximumLength = 6*sizeof(ntfschar); + + attr.Length = sizeof(OBJECT_ATTRIBUTES); + attr.RootDirectory = (HANDLE*)NULL; + attr.ObjectName = &unicode_name; + attr.Attributes = OBJ_CASE_INSENSITIVE; + attr.SecurityDescriptor = (void*)NULL; + attr.SecurityQualityOfService = (void*)NULL; + + io_status.Status = 0; + io_status.Information = 0; + mode = flags & O_ACCMODE; + share = (mode == O_RDWR ? + 0 : FILE_SHARE_READ | FILE_SHARE_WRITE); + access = (mode == O_RDWR ? + FILE_READ_DATA | FILE_WRITE_DATA | SYNCHRONIZE + : FILE_READ_DATA | SYNCHRONIZE); + + st = NtOpenFile(&handle, access, + &attr, &io_status, + share, + FILE_SYNCHRONOUS_IO_ALERT); + if (st != STATUS_SUCCESS) { + errno = ntfs_ntstatus_to_errno(st); + return (-1); + } + ntfs_device_win32_setlock(handle,FSCTL_LOCK_VOLUME); + /* store the drive geometry */ + ntfs_device_win32_getntgeo(handle, fd); + fd->ntdll = TRUE; + /* allow accessing the full partition */ + st = NtFsControlFile(handle, (HANDLE)NULL, + (PIO_APC_ROUTINE)NULL, + (PVOID)NULL, &io_status, + FSCTL_ALLOW_EXTENDED_DASD_IO, + NULL, 0, NULL, 0); + if (st != STATUS_SUCCESS) { + errno = ntfs_ntstatus_to_errno(st); + NtClose(handle); + return (-1); + } + /* fill fd */ fd->handle = handle; fd->part_start = 0; fd->part_length = fd->geo_size; @@ -694,7 +1069,7 @@ static BOOL ntfs_device_win32_find_partition(HANDLE handle, DWORD partition_id, do { buf_size = sizeof(DRIVE_LAYOUT_INFORMATION) + part_count * sizeof(PARTITION_INFORMATION); - drive_layout = malloc(buf_size); + drive_layout = (DRIVE_LAYOUT_INFORMATION*)ntfs_malloc(buf_size); if (!drive_layout) { errno = ENOMEM; return FALSE; @@ -775,6 +1150,8 @@ static int ntfs_device_win32_open_partition(int drive_id, fd->part_start = part_start; fd->part_length = part_length; fd->part_hidden_sectors = hidden_sectors; + fd->geo_sector_size = 512; + fd->ntdll = FALSE; tmp = ntfs_device_win32_getntfssize(vol_handle); if (tmp > 0) fd->geo_size = tmp; @@ -837,7 +1214,13 @@ static int ntfs_device_win32_open(struct ntfs_device *dev, int flags) } ntfs_device_win32_init_imports(); numparams = sscanf(dev->d_name, "/dev/hd%c%u", &drive_char, &part); - drive_id = toupper(drive_char) - 'A'; + if (!numparams + && (dev->d_name[1] == ':') + && (dev->d_name[2] == '\0')) { + drive_char = dev->d_name[0]; + numparams = 3; + drive_id = toupper(drive_char) - 'A'; + } switch (numparams) { case 0: ntfs_log_debug("win32_open(%s) -> file.\n", dev->d_name); @@ -854,6 +1237,12 @@ static int ntfs_device_win32_open(struct ntfs_device *dev, int flags) err = ntfs_device_win32_open_partition(drive_id, part, &fd, flags); break; + case 3: + ntfs_log_debug("win32_open(%s) -> drive %c:\n", + dev->d_name, drive_char); + err = ntfs_device_win32_open_lowlevel(drive_id, &fd, + flags); + break; default: ntfs_log_debug("win32_open(%s) -> unknwon file format.\n", dev->d_name); @@ -866,7 +1255,7 @@ static int ntfs_device_win32_open(struct ntfs_device *dev, int flags) /* Setup our read-only flag. */ if ((flags & O_RDWR) != O_RDWR) NDevSetReadOnly(dev); - dev->d_private = malloc(sizeof(win32_fd)); + dev->d_private = (win32_fd*)ntfs_malloc(sizeof(win32_fd)); memcpy(dev->d_private, &fd, sizeof(win32_fd)); NDevSetOpen(dev); NDevClearDirty(dev); @@ -916,7 +1305,8 @@ static s64 ntfs_device_win32_seek(struct ntfs_device *dev, s64 offset, errno = EINVAL; return -1; } - if (abs_ofs < 0 || abs_ofs > fd->part_length) { + if ((abs_ofs < 0) + || (fd->ntdll && (abs_ofs > fd->part_length))) { ntfs_log_trace("Seeking outsize seekable area.\n"); errno = EINVAL; return -1; @@ -937,18 +1327,19 @@ static s64 ntfs_device_win32_seek(struct ntfs_device *dev, s64 offset, * error returns -1 and errno set. Transfer starts from position @pos on @fd. * * Notes: - * - @pos, @buf, and @count must be aligned to NTFS_BLOCK_SIZE. + * - @pos, @buf, and @count must be aligned to geo_sector_size * - When dealing with volumes, a single call must not span both volume * and disk extents. * - Does not use/set @fd->pos. */ static s64 ntfs_device_win32_pio(win32_fd *fd, const s64 pos, - const s64 count, void *b, const BOOL write) + const s64 count, void *rbuf, const void *wbuf) { LARGE_INTEGER li; HANDLE handle; DWORD bt; BOOL res; + s64 bytes; ntfs_log_trace("pos = 0x%llx, count = 0x%llx, direction = %s.\n", (long long)pos, (long long)count, write ? "write" : @@ -962,21 +1353,57 @@ static s64 ntfs_device_win32_pio(win32_fd *fd, const s64 pos, handle = fd->handle; li.QuadPart += fd->part_start; } - if (!fnSetFilePointerEx(handle, li, NULL, FILE_BEGIN)) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("SetFilePointer failed.\n"); - return -1; - } - if (write) - res = WriteFile(handle, b, count, &bt, NULL); - else - res = ReadFile(handle, b, count, &bt, NULL); - if (!res) { - errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("%sFile() failed.\n", write ? "Write" : "Read"); - return -1; + + if (fd->ntdll) { + IO_STATUS_BLOCK io_status; + NTSTATUS res; + LARGE_INTEGER offset; + + io_status.Status = STATUS_SUCCESS; + io_status.Information = 0; + offset.QuadPart = pos; + if (wbuf) { + res = NtWriteFile(fd->handle,(HANDLE)NULL, + (PIO_APC_ROUTINE)NULL,(void*)NULL, + &io_status, wbuf, count, + &offset, (PULONG)NULL); + } else { + res = NtReadFile(fd->handle,(HANDLE)NULL, + (PIO_APC_ROUTINE)NULL,(void*)NULL, + &io_status, rbuf, count, + &offset, (PULONG)NULL); + } + if (res == STATUS_SUCCESS) { + bytes = io_status.Information; + } else { + bytes = -1; + errno = ntfs_ntstatus_to_errno(res); + } + } else { + if (!fnSetFilePointerEx(handle, li, NULL, FILE_BEGIN)) { + errno = ntfs_w32error_to_errno(GetLastError()); + ntfs_log_trace("SetFilePointer failed.\n"); + return -1; + } + if (wbuf) + res = WriteFile(handle, wbuf, count, &bt, NULL); + else + res = ReadFile(handle, rbuf, count, &bt, NULL); + bytes = bt; + if (!res) { + errno = ntfs_w32error_to_errno(GetLastError()); + ntfs_log_trace("%sFile() failed.\n", write ? + "Write" : "Read"); + return -1; + } + if (rbuf && !pos) { + /* get the sector size from the boot sector */ + char *boot = (char*)rbuf; + fd->geo_sector_size = (boot[11] & 255) + + ((boot[12] & 255) << 8); + } } - return bt; + return bytes; } /** @@ -990,7 +1417,7 @@ static s64 ntfs_device_win32_pio(win32_fd *fd, const s64 pos, * returns -1 and errno set. Read starts from position @pos. * * Notes: - * - @pos, @buf, and @count must be aligned to NTFS_BLOCK_SIZE. + * - @pos, @buf, and @count must be aligned to geo_sector_size. * - When dealing with volumes, a single call must not span both volume * and disk extents. * - Does not use/set @fd->pos. @@ -998,7 +1425,7 @@ static s64 ntfs_device_win32_pio(win32_fd *fd, const s64 pos, static inline s64 ntfs_device_win32_pread_simple(win32_fd *fd, const s64 pos, const s64 count, void *b) { - return ntfs_device_win32_pio(fd, pos, count, b, FALSE); + return ntfs_device_win32_pio(fd, pos, count, b, (void*)NULL); } /** @@ -1018,9 +1445,9 @@ static s64 ntfs_device_win32_read(struct ntfs_device *dev, void *b, s64 count) int old_ofs, ofs; old_pos = fd->pos; - old_ofs = ofs = old_pos & (NTFS_BLOCK_SIZE - 1); - to_read = (ofs + count + NTFS_BLOCK_SIZE - 1) & - ~(s64)(NTFS_BLOCK_SIZE - 1); + old_ofs = ofs = old_pos & (fd->geo_sector_size - 1); + to_read = (ofs + count + fd->geo_sector_size - 1) & + ~(s64)(fd->geo_sector_size - 1); /* Impose maximum of 2GB to be on the safe side. */ if (to_read > 0x80000000) { int delta = to_read - count; @@ -1031,8 +1458,8 @@ static s64 ntfs_device_win32_read(struct ntfs_device *dev, void *b, s64 count) "ofs = %i, to_read = 0x%llx.\n", fd, b, (long long)count, (long long)old_pos, ofs, (long long)to_read); - if (!((unsigned long)b & (NTFS_BLOCK_SIZE - 1)) && !old_ofs && - !(count & (NTFS_BLOCK_SIZE - 1))) + if (!((unsigned long)b & (fd->geo_sector_size - 1)) && !old_ofs && + !(count & (fd->geo_sector_size - 1))) alignedbuffer = b; else { alignedbuffer = (BYTE *)VirtualAlloc(NULL, to_read, MEM_COMMIT, @@ -1047,7 +1474,7 @@ static s64 ntfs_device_win32_read(struct ntfs_device *dev, void *b, s64 count) s64 vol_to_read = fd->geo_size - old_pos; if (count > vol_to_read) { br = ntfs_device_win32_pread_simple(fd, - old_pos & ~(s64)(NTFS_BLOCK_SIZE - 1), + old_pos & ~(s64)(fd->geo_sector_size - 1), ofs + vol_to_read, alignedbuffer); if (br == -1) goto read_error; @@ -1058,13 +1485,13 @@ static s64 ntfs_device_win32_read(struct ntfs_device *dev, void *b, s64 count) } br -= ofs; fd->pos += br; - ofs = fd->pos & (NTFS_BLOCK_SIZE - 1); + ofs = fd->pos & (fd->geo_sector_size - 1); if (br != vol_to_read) goto read_partial; } } i = ntfs_device_win32_pread_simple(fd, - fd->pos & ~(s64)(NTFS_BLOCK_SIZE - 1), to_read, + fd->pos & ~(s64)(fd->geo_sector_size - 1), to_read, alignedbuffer + br); if (i == -1) { if (br) @@ -1115,11 +1542,18 @@ static int ntfs_device_win32_close(struct ntfs_device *dev) if (!CloseHandle(fd->vol_handle)) ntfs_log_trace("CloseHandle() failed for volume.\n"); } - rvl = CloseHandle(fd->handle); + if (fd->ntdll) { + ntfs_device_win32_setlock(fd->handle,FSCTL_UNLOCK_VOLUME); + rvl = NtClose(fd->handle) == STATUS_SUCCESS; + } else + rvl = CloseHandle(fd->handle); free(fd); if (!rvl) { errno = ntfs_w32error_to_errno(GetLastError()); - ntfs_log_trace("CloseHandle() failed.\n"); + if (fd->ntdll) + ntfs_log_trace("NtClose() failed.\n"); + else + ntfs_log_trace("CloseHandle() failed.\n"); return -1; } return 0; @@ -1174,7 +1608,7 @@ static int ntfs_device_win32_sync(struct ntfs_device *dev) * errno set. Write starts from position @pos. * * Notes: - * - @pos, @buf, and @count must be aligned to NTFS_BLOCK_SIZE. + * - @pos, @buf, and @count must be aligned to geo_sector_size. * - When dealing with volumes, a single call must not span both volume * and disk extents. * - Does not use/set @fd->pos. @@ -1182,7 +1616,7 @@ static int ntfs_device_win32_sync(struct ntfs_device *dev) static inline s64 ntfs_device_win32_pwrite_simple(win32_fd *fd, const s64 pos, const s64 count, const void *b) { - return ntfs_device_win32_pio(fd, pos, count, (void *)b, TRUE); + return ntfs_device_win32_pio(fd, pos, count, (void*)NULL, b); } /** @@ -1203,9 +1637,9 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, int old_ofs, ofs; old_pos = fd->pos; - old_ofs = ofs = old_pos & (NTFS_BLOCK_SIZE - 1); - to_write = (ofs + count + NTFS_BLOCK_SIZE - 1) & - ~(s64)(NTFS_BLOCK_SIZE - 1); + old_ofs = ofs = old_pos & (fd->geo_sector_size - 1); + to_write = (ofs + count + fd->geo_sector_size - 1) & + ~(s64)(fd->geo_sector_size - 1); /* Impose maximum of 2GB to be on the safe side. */ if (to_write > 0x80000000) { int delta = to_write - count; @@ -1224,8 +1658,8 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, if (!count) return 0; NDevSetDirty(dev); - if (!((unsigned long)b & (NTFS_BLOCK_SIZE - 1)) && !old_ofs && - !(count & (NTFS_BLOCK_SIZE - 1))) + if (!((unsigned long)b & (fd->geo_sector_size - 1)) && !old_ofs && + !(count & (fd->geo_sector_size - 1))) alignedbuffer = (BYTE *)b; else { s64 end; @@ -1240,9 +1674,9 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, /* Read first sector if start of write not sector aligned. */ if (ofs) { i = ntfs_device_win32_pread_simple(fd, - old_pos & ~(s64)(NTFS_BLOCK_SIZE - 1), - NTFS_BLOCK_SIZE, alignedbuffer); - if (i != NTFS_BLOCK_SIZE) { + old_pos & ~(s64)(fd->geo_sector_size - 1), + fd->geo_sector_size, alignedbuffer); + if (i != fd->geo_sector_size) { if (i >= 0) errno = EIO; goto write_error; @@ -1255,13 +1689,13 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, * yet, i.e. the start of the write is sector aligned. */ end = old_pos + count; - if ((end & (NTFS_BLOCK_SIZE - 1)) && - ((to_write > NTFS_BLOCK_SIZE) || !ofs)) { + if ((end & (fd->geo_sector_size - 1)) && + ((to_write > fd->geo_sector_size) || !ofs)) { i = ntfs_device_win32_pread_simple(fd, - end & ~(s64)(NTFS_BLOCK_SIZE - 1), - NTFS_BLOCK_SIZE, alignedbuffer + - to_write - NTFS_BLOCK_SIZE); - if (i != NTFS_BLOCK_SIZE) { + end & ~(s64)(fd->geo_sector_size - 1), + fd->geo_sector_size, alignedbuffer + + to_write - fd->geo_sector_size); + if (i != fd->geo_sector_size) { if (i >= 0) errno = EIO; goto write_error; @@ -1274,7 +1708,7 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, s64 vol_to_write = fd->geo_size - old_pos; if (count > vol_to_write) { bw = ntfs_device_win32_pwrite_simple(fd, - old_pos & ~(s64)(NTFS_BLOCK_SIZE - 1), + old_pos & ~(s64)(fd->geo_sector_size - 1), ofs + vol_to_write, alignedbuffer); if (bw == -1) goto write_error; @@ -1285,13 +1719,13 @@ static s64 ntfs_device_win32_write(struct ntfs_device *dev, const void *b, } bw -= ofs; fd->pos += bw; - ofs = fd->pos & (NTFS_BLOCK_SIZE - 1); + ofs = fd->pos & (fd->geo_sector_size - 1); if (bw != vol_to_write) goto write_partial; } } i = ntfs_device_win32_pwrite_simple(fd, - fd->pos & ~(s64)(NTFS_BLOCK_SIZE - 1), to_write, + fd->pos & ~(s64)(fd->geo_sector_size - 1), to_write, alignedbuffer + bw); if (i == -1) { if (bw) @@ -1329,19 +1763,22 @@ static int ntfs_device_win32_stat(struct ntfs_device *dev, struct stat *buf) win32_fd *fd = (win32_fd *)dev->d_private; mode_t st_mode; - switch (GetFileType(fd->handle)) { - case FILE_TYPE_CHAR: - st_mode = S_IFCHR; - break; - case FILE_TYPE_DISK: + if ((dev->d_name[1] == ':') && (dev->d_name[2] == '\0')) st_mode = S_IFBLK; - break; - case FILE_TYPE_PIPE: - st_mode = S_IFIFO; - break; - default: - st_mode = 0; - } + else + switch (GetFileType(fd->handle)) { + case FILE_TYPE_CHAR: + st_mode = S_IFCHR; + break; + case FILE_TYPE_DISK: + st_mode = S_IFREG; + break; + case FILE_TYPE_PIPE: + st_mode = S_IFIFO; + break; + default: + st_mode = 0; + } memset(buf, 0, sizeof(struct stat)); buf->st_mode = st_mode; buf->st_size = fd->part_length; @@ -1352,6 +1789,7 @@ static int ntfs_device_win32_stat(struct ntfs_device *dev, struct stat *buf) return 0; } +#ifdef HDIO_GETGEO /** * ntfs_win32_hdio_getgeo - get drive geometry * @dev: ntfs device obtained via ->open @@ -1373,6 +1811,7 @@ static __inline__ int ntfs_win32_hdio_getgeo(struct ntfs_device *dev, argp->start = fd->part_hidden_sectors; return 0; } +#endif /** * ntfs_win32_blksszget - get block device sector size @@ -1404,7 +1843,9 @@ static __inline__ int ntfs_win32_blksszget(struct ntfs_device *dev,int *argp) static int ntfs_device_win32_ioctl(struct ntfs_device *dev, int request, void *argp) { +#if defined(BLKGETSIZE) | defined(BLKGETSIZE64) win32_fd *fd = (win32_fd *)dev->d_private; +#endif ntfs_log_trace("win32_ioctl(%d) called.\n", request); switch (request) { @@ -1454,13 +1895,40 @@ static int ntfs_device_win32_ioctl(struct ntfs_device *dev, int request, static s64 ntfs_device_win32_pread(struct ntfs_device *dev, void *b, s64 count, s64 offset) { - return ntfs_pread(dev, offset, count, b); + s64 got; + win32_fd *fd; + + /* read the fast way if sector aligned */ + fd = (win32_fd*)dev->d_private; + if (!((count | offset) & (fd->geo_sector_size - 1))) { + got = ntfs_device_win32_pio(fd, offset, count, b, (void*)NULL); + } else { + if (ntfs_device_win32_seek(dev, offset, 0) == -1) + got = 0; + else + got = ntfs_device_win32_read(dev, b, count); + } + + return (got); } static s64 ntfs_device_win32_pwrite(struct ntfs_device *dev, const void *b, s64 count, s64 offset) { - return ntfs_pwrite(dev, offset, count, b); + s64 put; + win32_fd *fd; + + /* write the fast way if sector aligned */ + fd = (win32_fd*)dev->d_private; + if (!((count | offset) & (fd->geo_sector_size - 1))) { + put = ntfs_device_win32_pio(fd, offset, count, (void*)NULL, b); + } else { + if (ntfs_device_win32_seek(dev, offset, 0) == -1) + put = 0; + else + put = ntfs_device_win32_write(dev, b, count); + } + return (put); } struct ntfs_device_operations ntfs_device_win32_io_ops = { @@ -1475,3 +1943,87 @@ struct ntfs_device_operations ntfs_device_win32_io_ops = { .stat = ntfs_device_win32_stat, .ioctl = ntfs_device_win32_ioctl }; + +/* + * Mark an open file as sparse + * + * This is only called by ntfsclone when cloning a volume to a file. + * The argument is the target file, not a volume. + * + * Returns 0 if successful. + */ + +int ntfs_win32_set_sparse(int fd) +{ + BOOL ok; + HANDLE handle; + DWORD bytes; + + handle = get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) + ok = FALSE; + else + ok = DeviceIoControl(handle, FSCTL_SET_SPARSE, + (void*)NULL, 0, (void*)NULL, 0, + &bytes, (LPOVERLAPPED)NULL); + return (!ok); +} + +/* + * Resize an open file + * + * This is only called by ntfsclone when cloning a volume to a file. + * The argument must designate a file, not a volume. + * + * Returns 0 if successful. + */ + +static int win32_ftruncate(HANDLE handle, s64 size) +{ + BOOL ok; + LONG hsize, lsize; + LONG ohsize, olsize; + + if (handle == INVALID_HANDLE_VALUE) + ok = FALSE; + else { + SetLastError(NO_ERROR); + /* save original position */ + ohsize = 0; + olsize = SetFilePointer(handle, 0, &ohsize, 1); + hsize = size >> 32; + lsize = size & 0xffffffff; + ok = (SetFilePointer(handle, lsize, &hsize, 0) == (DWORD)lsize) + && (GetLastError() == NO_ERROR) + && SetEndOfFile(handle); + /* restore original position, even if above failed */ + SetFilePointer(handle, olsize, &ohsize, 0); + if (GetLastError() != NO_ERROR) + ok = FALSE; + } + if (!ok) + errno = EINVAL; + return (ok ? 0 : -1); +} + +int ntfs_device_win32_ftruncate(struct ntfs_device *dev, s64 size) +{ + win32_fd *fd; + int ret; + + ret = -1; + fd = (win32_fd*)dev->d_private; + if (fd && !fd->ntdll) + ret = win32_ftruncate(fd->handle, size); + return (ret); +} + +int ntfs_win32_ftruncate(int fd, s64 size) +{ + int ret; + HANDLE handle; + + handle = get_osfhandle(fd); + ret = win32_ftruncate(handle, size); + return (ret); +}