19663SMark.Logan@Sun.COM /** 29663SMark.Logan@Sun.COM * volume.c - NTFS volume handling code. Part of the Linux-NTFS project. 39663SMark.Logan@Sun.COM * 49663SMark.Logan@Sun.COM * Copyright (c) 2000-2006 Anton Altaparmakov 59663SMark.Logan@Sun.COM * Copyright (c) 2002-2006 Szabolcs Szakacsits 69663SMark.Logan@Sun.COM * Copyright (c) 2004-2005 Richard Russon 79663SMark.Logan@Sun.COM * Copyright (c) 2005-2007 Yura Pakhuchiy 89663SMark.Logan@Sun.COM * 99663SMark.Logan@Sun.COM * This program/include file is free software; you can redistribute it and/or 109663SMark.Logan@Sun.COM * modify it under the terms of the GNU General Public License as published 119663SMark.Logan@Sun.COM * by the Free Software Foundation; either version 2 of the License, or 129663SMark.Logan@Sun.COM * (at your option) any later version. 139663SMark.Logan@Sun.COM * 149663SMark.Logan@Sun.COM * This program/include file is distributed in the hope that it will be 159663SMark.Logan@Sun.COM * useful, but WITHOUT ANY WARRANTY; without even the implied warranty 169663SMark.Logan@Sun.COM * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 179663SMark.Logan@Sun.COM * GNU General Public License for more details. 189663SMark.Logan@Sun.COM * 199663SMark.Logan@Sun.COM * You should have received a copy of the GNU General Public License 209663SMark.Logan@Sun.COM * along with this program (in the main directory of the Linux-NTFS 219663SMark.Logan@Sun.COM * distribution in the file COPYING); if not, write to the Free Software 229663SMark.Logan@Sun.COM * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 239663SMark.Logan@Sun.COM */ 249663SMark.Logan@Sun.COM 259663SMark.Logan@Sun.COM #ifdef HAVE_CONFIG_H 269663SMark.Logan@Sun.COM #include "config.h" 279663SMark.Logan@Sun.COM #endif 289663SMark.Logan@Sun.COM 299663SMark.Logan@Sun.COM #ifdef HAVE_STDLIB_H 309663SMark.Logan@Sun.COM #include <stdlib.h> 319663SMark.Logan@Sun.COM #endif 329663SMark.Logan@Sun.COM #ifdef HAVE_STDIO_H 339663SMark.Logan@Sun.COM #include <stdio.h> 349663SMark.Logan@Sun.COM #endif 359663SMark.Logan@Sun.COM #ifdef HAVE_STRING_H 369663SMark.Logan@Sun.COM #include <string.h> 379663SMark.Logan@Sun.COM #endif 389663SMark.Logan@Sun.COM #ifdef HAVE_FCNTL_H 399663SMark.Logan@Sun.COM #include <fcntl.h> 409663SMark.Logan@Sun.COM #endif 419663SMark.Logan@Sun.COM #ifdef HAVE_UNISTD_H 429663SMark.Logan@Sun.COM #include <unistd.h> 439663SMark.Logan@Sun.COM #endif 449663SMark.Logan@Sun.COM #ifdef HAVE_ERRNO_H 459663SMark.Logan@Sun.COM #include <errno.h> 469663SMark.Logan@Sun.COM #endif 479663SMark.Logan@Sun.COM #ifdef HAVE_SYS_STAT_H 489663SMark.Logan@Sun.COM #include <sys/stat.h> 499663SMark.Logan@Sun.COM #endif 509663SMark.Logan@Sun.COM #ifdef HAVE_LIMITS_H 519663SMark.Logan@Sun.COM #include <limits.h> 529663SMark.Logan@Sun.COM #endif 539663SMark.Logan@Sun.COM 54*10214SMark.Logan@Sun.COM #include "compat.h" 559663SMark.Logan@Sun.COM #include "volume.h" 569663SMark.Logan@Sun.COM #include "attrib.h" 579663SMark.Logan@Sun.COM #include "mft.h" 589663SMark.Logan@Sun.COM #include "bootsect.h" 599663SMark.Logan@Sun.COM #include "device.h" 609663SMark.Logan@Sun.COM #include "debug.h" 619663SMark.Logan@Sun.COM #include "inode.h" 629663SMark.Logan@Sun.COM #include "runlist.h" 639663SMark.Logan@Sun.COM #include "logfile.h" 649663SMark.Logan@Sun.COM #include "dir.h" 659663SMark.Logan@Sun.COM #include "logging.h" 669663SMark.Logan@Sun.COM 679663SMark.Logan@Sun.COM #ifndef PATH_MAX 689663SMark.Logan@Sun.COM #define PATH_MAX 4096 699663SMark.Logan@Sun.COM #endif 709663SMark.Logan@Sun.COM 719663SMark.Logan@Sun.COM /** 729663SMark.Logan@Sun.COM * ntfs_volume_alloc - Create an NTFS volume object and initialise it 739663SMark.Logan@Sun.COM * 749663SMark.Logan@Sun.COM * Description... 759663SMark.Logan@Sun.COM * 769663SMark.Logan@Sun.COM * Returns: 779663SMark.Logan@Sun.COM */ 789663SMark.Logan@Sun.COM ntfs_volume *ntfs_volume_alloc(void) 799663SMark.Logan@Sun.COM { 809663SMark.Logan@Sun.COM ntfs_volume *vol; 819663SMark.Logan@Sun.COM int i; 829663SMark.Logan@Sun.COM 839663SMark.Logan@Sun.COM vol = calloc(1, sizeof(ntfs_volume)); 849663SMark.Logan@Sun.COM if (vol) { 859663SMark.Logan@Sun.COM for (i = 0; i < NTFS_INODE_CACHE_SIZE; i++) 869663SMark.Logan@Sun.COM INIT_LIST_HEAD(&vol->inode_cache[i]); 879663SMark.Logan@Sun.COM } 889663SMark.Logan@Sun.COM return vol; 899663SMark.Logan@Sun.COM } 909663SMark.Logan@Sun.COM 919663SMark.Logan@Sun.COM /** 929663SMark.Logan@Sun.COM * __ntfs_volume_release - Destroy an NTFS volume object 939663SMark.Logan@Sun.COM * @v: 949663SMark.Logan@Sun.COM * 959663SMark.Logan@Sun.COM * Description... 969663SMark.Logan@Sun.COM * 979663SMark.Logan@Sun.COM * Returns: 989663SMark.Logan@Sun.COM */ 999663SMark.Logan@Sun.COM static void __ntfs_volume_release(ntfs_volume *v) 1009663SMark.Logan@Sun.COM { 1019663SMark.Logan@Sun.COM struct list_head *pos, *tmp; 1029663SMark.Logan@Sun.COM int i; 1039663SMark.Logan@Sun.COM 1049663SMark.Logan@Sun.COM /* Sync and print error about not detached inodes. */ 1059663SMark.Logan@Sun.COM for (i = 0; i < NTFS_INODE_CACHE_SIZE; i++) 1069663SMark.Logan@Sun.COM list_for_each_safe(pos, tmp, &v->inode_cache[i]) { 1079663SMark.Logan@Sun.COM ntfs_inode *ni = 1089663SMark.Logan@Sun.COM list_entry(pos, ntfs_inode, list_entry); 1099663SMark.Logan@Sun.COM 1109663SMark.Logan@Sun.COM switch (ni->mft_no) { 1119663SMark.Logan@Sun.COM case FILE_Volume: 1129663SMark.Logan@Sun.COM case FILE_Bitmap: 1139663SMark.Logan@Sun.COM case FILE_MFT: 1149663SMark.Logan@Sun.COM case FILE_MFTMirr: 1159663SMark.Logan@Sun.COM if (ni->nr_references == 1) 1169663SMark.Logan@Sun.COM continue; 1179663SMark.Logan@Sun.COM break; 1189663SMark.Logan@Sun.COM } 1199663SMark.Logan@Sun.COM 1209663SMark.Logan@Sun.COM ntfs_log_error("%s(): Inode %llu still have %d " 1219663SMark.Logan@Sun.COM "references.\n", "__ntfs_volume_release", 1229663SMark.Logan@Sun.COM ni->mft_no, ni->nr_references); 1239663SMark.Logan@Sun.COM ntfs_inode_sync(ni); 1249663SMark.Logan@Sun.COM } 1259663SMark.Logan@Sun.COM /* 1269663SMark.Logan@Sun.COM * Clear the dirty bit if it was not set before we mounted and this is 1279663SMark.Logan@Sun.COM * not a forensic mount. 1289663SMark.Logan@Sun.COM */ 1299663SMark.Logan@Sun.COM if (!NVolReadOnly(v) && !NVolWasDirty(v) && !NVolForensicMount(v)) { 1309663SMark.Logan@Sun.COM v->flags &= ~VOLUME_IS_DIRTY; 1319663SMark.Logan@Sun.COM (void)ntfs_volume_write_flags(v, v->flags); 1329663SMark.Logan@Sun.COM } 1339663SMark.Logan@Sun.COM if (v->lcnbmp_ni && NInoDirty(v->lcnbmp_ni)) 1349663SMark.Logan@Sun.COM ntfs_inode_sync(v->lcnbmp_ni); 1359663SMark.Logan@Sun.COM if (v->vol_ni) 1369663SMark.Logan@Sun.COM ntfs_inode_close(v->vol_ni); 1379663SMark.Logan@Sun.COM if (v->lcnbmp_na) 1389663SMark.Logan@Sun.COM ntfs_attr_close(v->lcnbmp_na); 1399663SMark.Logan@Sun.COM if (v->lcnbmp_ni) 1409663SMark.Logan@Sun.COM ntfs_inode_close(v->lcnbmp_ni); 1419663SMark.Logan@Sun.COM if (v->mft_ni && NInoDirty(v->mft_ni)) 1429663SMark.Logan@Sun.COM ntfs_inode_sync(v->mft_ni); 1439663SMark.Logan@Sun.COM if (v->mftbmp_na) 1449663SMark.Logan@Sun.COM ntfs_attr_close(v->mftbmp_na); 1459663SMark.Logan@Sun.COM if (v->mft_na) 1469663SMark.Logan@Sun.COM ntfs_attr_close(v->mft_na); 1479663SMark.Logan@Sun.COM if (v->mft_ni) 1489663SMark.Logan@Sun.COM ntfs_inode_close(v->mft_ni); 1499663SMark.Logan@Sun.COM if (v->mftmirr_ni && NInoDirty(v->mftmirr_ni)) 1509663SMark.Logan@Sun.COM ntfs_inode_sync(v->mftmirr_ni); 1519663SMark.Logan@Sun.COM if (v->mftmirr_na) 1529663SMark.Logan@Sun.COM ntfs_attr_close(v->mftmirr_na); 1539663SMark.Logan@Sun.COM if (v->mftmirr_ni) 1549663SMark.Logan@Sun.COM ntfs_inode_close(v->mftmirr_ni); 1559663SMark.Logan@Sun.COM if (v->u.dev) { 1569663SMark.Logan@Sun.COM struct ntfs_device *dev = v->u.dev; 1579663SMark.Logan@Sun.COM 1589663SMark.Logan@Sun.COM if (NDevDirty(dev)) 1599663SMark.Logan@Sun.COM dev->d_ops->sync(dev); 1609663SMark.Logan@Sun.COM if (dev->d_ops->close(dev)) 1619663SMark.Logan@Sun.COM ntfs_log_perror("Failed to close the device"); 1629663SMark.Logan@Sun.COM } 1639663SMark.Logan@Sun.COM free(v->vol_name); 1649663SMark.Logan@Sun.COM free(v->upcase); 1659663SMark.Logan@Sun.COM free(v->attrdef); 1669663SMark.Logan@Sun.COM free(v); 1679663SMark.Logan@Sun.COM } 1689663SMark.Logan@Sun.COM 1699663SMark.Logan@Sun.COM /** 1709663SMark.Logan@Sun.COM * ntfs_mft_load - load the $MFT and setup the ntfs volume with it 1719663SMark.Logan@Sun.COM * @vol: ntfs volume whose $MFT to load 1729663SMark.Logan@Sun.COM * 1739663SMark.Logan@Sun.COM * Load $MFT from @vol and setup @vol with it. After calling this function the 1749663SMark.Logan@Sun.COM * volume @vol is ready for use by all read access functions provided by the 1759663SMark.Logan@Sun.COM * ntfs library. 1769663SMark.Logan@Sun.COM * 1779663SMark.Logan@Sun.COM * Return 0 on success and -1 on error with errno set to the error code. 1789663SMark.Logan@Sun.COM */ 1799663SMark.Logan@Sun.COM static int ntfs_mft_load(ntfs_volume *vol) 1809663SMark.Logan@Sun.COM { 1819663SMark.Logan@Sun.COM VCN next_vcn, last_vcn, highest_vcn; 1829663SMark.Logan@Sun.COM s64 l; 1839663SMark.Logan@Sun.COM MFT_RECORD *mb = NULL; 1849663SMark.Logan@Sun.COM ntfs_attr_search_ctx *ctx = NULL; 1859663SMark.Logan@Sun.COM ATTR_RECORD *a; 1869663SMark.Logan@Sun.COM STANDARD_INFORMATION *std_info; 1879663SMark.Logan@Sun.COM int eo; 1889663SMark.Logan@Sun.COM 1899663SMark.Logan@Sun.COM /* Manually setup an ntfs_inode. */ 1909663SMark.Logan@Sun.COM vol->mft_ni = ntfs_inode_allocate(vol); 1919663SMark.Logan@Sun.COM mb = (MFT_RECORD*)ntfs_malloc(vol->mft_record_size); 1929663SMark.Logan@Sun.COM if (!vol->mft_ni || !mb) { 1939663SMark.Logan@Sun.COM ntfs_log_perror("Error allocating memory for $MFT"); 1949663SMark.Logan@Sun.COM goto error_exit; 1959663SMark.Logan@Sun.COM } 1969663SMark.Logan@Sun.COM vol->mft_ni->mft_no = 0; 1979663SMark.Logan@Sun.COM vol->mft_ni->mrec = mb; 1989663SMark.Logan@Sun.COM __ntfs_inode_add_to_cache(vol->mft_ni); 1999663SMark.Logan@Sun.COM /* Can't use any of the higher level functions yet! */ 2009663SMark.Logan@Sun.COM l = ntfs_mst_pread(vol->u.dev, vol->mft_lcn << vol->cluster_size_bits, 1, 2019663SMark.Logan@Sun.COM vol->mft_record_size, mb); 2029663SMark.Logan@Sun.COM if (l != 1) { 2039663SMark.Logan@Sun.COM if (l != -1) 2049663SMark.Logan@Sun.COM errno = EIO; 2059663SMark.Logan@Sun.COM ntfs_log_perror("Error reading $MFT"); 2069663SMark.Logan@Sun.COM goto error_exit; 2079663SMark.Logan@Sun.COM } 2089663SMark.Logan@Sun.COM if (ntfs_is_baad_record(mb->magic)) { 2099663SMark.Logan@Sun.COM ntfs_log_error("Incomplete multi sector transfer detected in " 2109663SMark.Logan@Sun.COM "$MFT.\n"); 2119663SMark.Logan@Sun.COM goto io_error_exit; 2129663SMark.Logan@Sun.COM } 2139663SMark.Logan@Sun.COM if (!ntfs_is_mft_record(mb->magic)) { 2149663SMark.Logan@Sun.COM ntfs_log_error("$MFT has invalid magic.\n"); 2159663SMark.Logan@Sun.COM goto io_error_exit; 2169663SMark.Logan@Sun.COM } 2179663SMark.Logan@Sun.COM ctx = ntfs_attr_get_search_ctx(vol->mft_ni, NULL); 2189663SMark.Logan@Sun.COM if (!ctx) { 2199663SMark.Logan@Sun.COM ntfs_log_perror("Failed to allocate attribute search context"); 2209663SMark.Logan@Sun.COM goto error_exit; 2219663SMark.Logan@Sun.COM } 2229663SMark.Logan@Sun.COM if (p2n(ctx->attr) < p2n(mb) || 2239663SMark.Logan@Sun.COM (char*)ctx->attr > (char*)mb + vol->mft_record_size) { 2249663SMark.Logan@Sun.COM ntfs_log_error("$MFT is corrupt.\n"); 2259663SMark.Logan@Sun.COM goto io_error_exit; 2269663SMark.Logan@Sun.COM } 2279663SMark.Logan@Sun.COM /* Find the $ATTRIBUTE_LIST attribute in $MFT if present. */ 2289663SMark.Logan@Sun.COM if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0, 2299663SMark.Logan@Sun.COM ctx)) { 2309663SMark.Logan@Sun.COM if (errno != ENOENT) { 2319663SMark.Logan@Sun.COM ntfs_log_error("$MFT has corrupt attribute list.\n"); 2329663SMark.Logan@Sun.COM goto io_error_exit; 2339663SMark.Logan@Sun.COM } 2349663SMark.Logan@Sun.COM goto mft_has_no_attr_list; 2359663SMark.Logan@Sun.COM } 2369663SMark.Logan@Sun.COM NInoSetAttrList(vol->mft_ni); 2379663SMark.Logan@Sun.COM l = ntfs_get_attribute_value_length(ctx->attr); 2389663SMark.Logan@Sun.COM if (l <= 0 || l > 0x40000) { 2399663SMark.Logan@Sun.COM ntfs_log_error("$MFT/$ATTRIBUTE_LIST has invalid length.\n"); 2409663SMark.Logan@Sun.COM goto io_error_exit; 2419663SMark.Logan@Sun.COM } 2429663SMark.Logan@Sun.COM vol->mft_ni->attr_list_size = l; 2439663SMark.Logan@Sun.COM vol->mft_ni->attr_list = ntfs_malloc(l); 2449663SMark.Logan@Sun.COM if (!vol->mft_ni->attr_list) 2459663SMark.Logan@Sun.COM goto error_exit; 2469663SMark.Logan@Sun.COM 2479663SMark.Logan@Sun.COM l = ntfs_get_attribute_value(vol, ctx->attr, vol->mft_ni->attr_list); 2489663SMark.Logan@Sun.COM if (!l) { 2499663SMark.Logan@Sun.COM ntfs_log_error("Failed to get value of " 2509663SMark.Logan@Sun.COM "$MFT/$ATTRIBUTE_LIST.\n"); 2519663SMark.Logan@Sun.COM goto io_error_exit; 2529663SMark.Logan@Sun.COM } 2539663SMark.Logan@Sun.COM if (l != vol->mft_ni->attr_list_size) { 2549663SMark.Logan@Sun.COM ntfs_log_error("Got unexpected amount of data when " 2559663SMark.Logan@Sun.COM "reading $MFT/$ATTRIBUTE_LIST.\n"); 2569663SMark.Logan@Sun.COM goto io_error_exit; 2579663SMark.Logan@Sun.COM } 2589663SMark.Logan@Sun.COM mft_has_no_attr_list: 2599663SMark.Logan@Sun.COM /* Receive attributes from STANDARD_INFORMATION. */ 2609663SMark.Logan@Sun.COM std_info = ntfs_attr_readall(vol->mft_ni, AT_STANDARD_INFORMATION, 2619663SMark.Logan@Sun.COM AT_UNNAMED, 0, NULL); 2629663SMark.Logan@Sun.COM vol->mft_ni->flags = std_info->file_attributes; 2639663SMark.Logan@Sun.COM free(std_info); 2649663SMark.Logan@Sun.COM 2659663SMark.Logan@Sun.COM /* We now have a fully setup ntfs inode for $MFT in vol->mft_ni. */ 2669663SMark.Logan@Sun.COM 2679663SMark.Logan@Sun.COM /* Get an ntfs attribute for $MFT/$DATA and set it up, too. */ 2689663SMark.Logan@Sun.COM vol->mft_na = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0); 2699663SMark.Logan@Sun.COM if (!vol->mft_na) { 2709663SMark.Logan@Sun.COM ntfs_log_perror("Failed to open ntfs attribute"); 2719663SMark.Logan@Sun.COM goto error_exit; 2729663SMark.Logan@Sun.COM } 2739663SMark.Logan@Sun.COM /* Read all extents from the $DATA attribute in $MFT. */ 2749663SMark.Logan@Sun.COM ntfs_attr_reinit_search_ctx(ctx); 2759663SMark.Logan@Sun.COM last_vcn = vol->mft_na->allocated_size >> vol->cluster_size_bits; 2769663SMark.Logan@Sun.COM highest_vcn = next_vcn = 0; 2779663SMark.Logan@Sun.COM a = NULL; 2789663SMark.Logan@Sun.COM while (!ntfs_attr_lookup(AT_DATA, AT_UNNAMED, 0, 0, next_vcn, NULL, 0, 2799663SMark.Logan@Sun.COM ctx)) { 2809663SMark.Logan@Sun.COM runlist_element *nrl; 2819663SMark.Logan@Sun.COM 2829663SMark.Logan@Sun.COM a = ctx->attr; 2839663SMark.Logan@Sun.COM /* $MFT must be non-resident. */ 2849663SMark.Logan@Sun.COM if (!a->non_resident) { 2859663SMark.Logan@Sun.COM ntfs_log_error("$MFT must be non-resident but a " 2869663SMark.Logan@Sun.COM "resident extent was found. $MFT is " 2879663SMark.Logan@Sun.COM "corrupt. Run chkdsk.\n"); 2889663SMark.Logan@Sun.COM goto io_error_exit; 2899663SMark.Logan@Sun.COM } 2909663SMark.Logan@Sun.COM /* $MFT must be uncompressed and unencrypted. */ 2919663SMark.Logan@Sun.COM if (a->flags & ATTR_COMPRESSION_MASK || 2929663SMark.Logan@Sun.COM a->flags & ATTR_IS_ENCRYPTED) { 2939663SMark.Logan@Sun.COM ntfs_log_error("$MFT must be uncompressed and " 2949663SMark.Logan@Sun.COM "unencrypted but a compressed/encrypted" 2959663SMark.Logan@Sun.COM " extent was found. $MFT is corrupt. " 2969663SMark.Logan@Sun.COM "Run chkdsk.\n"); 2979663SMark.Logan@Sun.COM goto io_error_exit; 2989663SMark.Logan@Sun.COM } 2999663SMark.Logan@Sun.COM /* 3009663SMark.Logan@Sun.COM * Decompress the mapping pairs array of this extent and merge 3019663SMark.Logan@Sun.COM * the result into the existing runlist. No need for locking 3029663SMark.Logan@Sun.COM * as we have exclusive access to the inode at this time and we 3039663SMark.Logan@Sun.COM * are a mount in progress task, too. 3049663SMark.Logan@Sun.COM */ 3059663SMark.Logan@Sun.COM nrl = ntfs_mapping_pairs_decompress(vol, a, vol->mft_na->rl); 3069663SMark.Logan@Sun.COM if (!nrl) { 3079663SMark.Logan@Sun.COM ntfs_log_perror("ntfs_mapping_pairs_decompress() " 3089663SMark.Logan@Sun.COM "failed"); 3099663SMark.Logan@Sun.COM goto error_exit; 3109663SMark.Logan@Sun.COM } 3119663SMark.Logan@Sun.COM vol->mft_na->rl = nrl; 3129663SMark.Logan@Sun.COM 3139663SMark.Logan@Sun.COM /* Get the lowest vcn for the next extent. */ 3149663SMark.Logan@Sun.COM highest_vcn = sle64_to_cpu(a->u.nonres.highest_vcn); 3159663SMark.Logan@Sun.COM next_vcn = highest_vcn + 1; 3169663SMark.Logan@Sun.COM 3179663SMark.Logan@Sun.COM /* Only one extent or error, which we catch below. */ 3189663SMark.Logan@Sun.COM if (next_vcn <= 0) 3199663SMark.Logan@Sun.COM break; 3209663SMark.Logan@Sun.COM 3219663SMark.Logan@Sun.COM /* Avoid endless loops due to corruption. */ 3229663SMark.Logan@Sun.COM if (next_vcn < sle64_to_cpu(a->u.nonres.lowest_vcn)) { 3239663SMark.Logan@Sun.COM ntfs_log_error("$MFT has corrupt attribute list " 3249663SMark.Logan@Sun.COM "attribute. Run chkdsk.\n"); 3259663SMark.Logan@Sun.COM goto io_error_exit; 3269663SMark.Logan@Sun.COM } 3279663SMark.Logan@Sun.COM } 3289663SMark.Logan@Sun.COM if (!a) { 3299663SMark.Logan@Sun.COM ntfs_log_error("$MFT/$DATA attribute not found. " 3309663SMark.Logan@Sun.COM "$MFT is corrupt. Run chkdsk.\n"); 3319663SMark.Logan@Sun.COM goto io_error_exit; 3329663SMark.Logan@Sun.COM } 3339663SMark.Logan@Sun.COM if (highest_vcn && highest_vcn != last_vcn - 1) { 3349663SMark.Logan@Sun.COM ntfs_log_error("Failed to load the complete runlist for " 3359663SMark.Logan@Sun.COM "$MFT/$DATA. Bug or corrupt $MFT. " 3369663SMark.Logan@Sun.COM "Run chkdsk.\n highest_vcn = 0x%llx, " 3379663SMark.Logan@Sun.COM "last_vcn - 1 = 0x%llx\n", (long long) 3389663SMark.Logan@Sun.COM highest_vcn, (long long)last_vcn - 1); 3399663SMark.Logan@Sun.COM goto io_error_exit; 3409663SMark.Logan@Sun.COM } 3419663SMark.Logan@Sun.COM /* Done with the $Mft mft record. */ 3429663SMark.Logan@Sun.COM ntfs_attr_put_search_ctx(ctx); 3439663SMark.Logan@Sun.COM ctx = NULL; 3449663SMark.Logan@Sun.COM /* 3459663SMark.Logan@Sun.COM * The volume is now setup so we can use all read access functions. 3469663SMark.Logan@Sun.COM */ 3479663SMark.Logan@Sun.COM vol->mftbmp_na = ntfs_attr_open(vol->mft_ni, AT_BITMAP, AT_UNNAMED, 0); 3489663SMark.Logan@Sun.COM if (!vol->mftbmp_na) { 3499663SMark.Logan@Sun.COM ntfs_log_perror("Failed to open $MFT/$BITMAP"); 3509663SMark.Logan@Sun.COM goto error_exit; 3519663SMark.Logan@Sun.COM } 3529663SMark.Logan@Sun.COM return 0; 3539663SMark.Logan@Sun.COM io_error_exit: 3549663SMark.Logan@Sun.COM errno = EIO; 3559663SMark.Logan@Sun.COM error_exit: 3569663SMark.Logan@Sun.COM eo = errno; 3579663SMark.Logan@Sun.COM if (ctx) 3589663SMark.Logan@Sun.COM ntfs_attr_put_search_ctx(ctx); 3599663SMark.Logan@Sun.COM if (vol->mft_na) { 3609663SMark.Logan@Sun.COM ntfs_attr_close(vol->mft_na); 3619663SMark.Logan@Sun.COM vol->mft_na = NULL; 3629663SMark.Logan@Sun.COM } 3639663SMark.Logan@Sun.COM if (vol->mft_ni) { 3649663SMark.Logan@Sun.COM ntfs_inode_close(vol->mft_ni); 3659663SMark.Logan@Sun.COM vol->mft_ni = NULL; 3669663SMark.Logan@Sun.COM } 3679663SMark.Logan@Sun.COM ntfs_log_error("%s(): Failed.\n", "ntfs_mft_load"); 3689663SMark.Logan@Sun.COM errno = eo; 3699663SMark.Logan@Sun.COM return -1; 3709663SMark.Logan@Sun.COM } 3719663SMark.Logan@Sun.COM 3729663SMark.Logan@Sun.COM /** 3739663SMark.Logan@Sun.COM * ntfs_mftmirr_load - load the $MFTMirr and setup the ntfs volume with it 3749663SMark.Logan@Sun.COM * @vol: ntfs volume whose $MFTMirr to load 3759663SMark.Logan@Sun.COM * 3769663SMark.Logan@Sun.COM * Load $MFTMirr from @vol and setup @vol with it. After calling this function 3779663SMark.Logan@Sun.COM * the volume @vol is ready for use by all write access functions provided by 3789663SMark.Logan@Sun.COM * the ntfs library (assuming ntfs_mft_load() has been called successfully 3799663SMark.Logan@Sun.COM * beforehand). 3809663SMark.Logan@Sun.COM * 3819663SMark.Logan@Sun.COM * Return 0 on success and -1 on error with errno set to the error code. 3829663SMark.Logan@Sun.COM */ 3839663SMark.Logan@Sun.COM static int ntfs_mftmirr_load(ntfs_volume *vol) 3849663SMark.Logan@Sun.COM { 3859663SMark.Logan@Sun.COM int err; 3869663SMark.Logan@Sun.COM 3879663SMark.Logan@Sun.COM vol->mftmirr_ni = ntfs_inode_open(vol, FILE_MFTMirr); 3889663SMark.Logan@Sun.COM if (!vol->mftmirr_ni) { 3899663SMark.Logan@Sun.COM ntfs_log_perror("Failed to open inode $MFTMirr"); 3909663SMark.Logan@Sun.COM return -1; 3919663SMark.Logan@Sun.COM } 3929663SMark.Logan@Sun.COM /* Get an ntfs attribute for $MFTMirr/$DATA, too. */ 3939663SMark.Logan@Sun.COM vol->mftmirr_na = ntfs_attr_open(vol->mftmirr_ni, AT_DATA, 3949663SMark.Logan@Sun.COM AT_UNNAMED, 0); 3959663SMark.Logan@Sun.COM if (!vol->mftmirr_na) { 3969663SMark.Logan@Sun.COM ntfs_log_perror("Failed to open $MFTMirr/$DATA"); 3979663SMark.Logan@Sun.COM goto error_exit; 3989663SMark.Logan@Sun.COM } 3999663SMark.Logan@Sun.COM if (ntfs_attr_map_runlist(vol->mftmirr_na, 0) < 0) { 4009663SMark.Logan@Sun.COM ntfs_log_perror("Failed to map runlist of $MFTMirr/$DATA"); 4019663SMark.Logan@Sun.COM goto error_exit; 4029663SMark.Logan@Sun.COM } 4039663SMark.Logan@Sun.COM /* Check $MFTMirr runlist. */ 4049663SMark.Logan@Sun.COM if (vol->mftmirr_na->rl[0].lcn != vol->mftmirr_lcn || 4059663SMark.Logan@Sun.COM vol->mftmirr_na->rl[0].length < (vol->mftmirr_size * 4069663SMark.Logan@Sun.COM vol->mft_record_size + vol->cluster_size - 1) / 4079663SMark.Logan@Sun.COM vol->cluster_size) { 4089663SMark.Logan@Sun.COM ntfs_log_error("$MFTMirr location mismatch or first 4 records " 4099663SMark.Logan@Sun.COM "are fragmented. Run chkdsk.\n"); 4109663SMark.Logan@Sun.COM errno = EIO; 4119663SMark.Logan@Sun.COM goto error_exit; 4129663SMark.Logan@Sun.COM 4139663SMark.Logan@Sun.COM } 4149663SMark.Logan@Sun.COM return 0; 4159663SMark.Logan@Sun.COM error_exit: 4169663SMark.Logan@Sun.COM err = errno; 4179663SMark.Logan@Sun.COM if (vol->mftmirr_na) { 4189663SMark.Logan@Sun.COM ntfs_attr_close(vol->mftmirr_na); 4199663SMark.Logan@Sun.COM vol->mftmirr_na = NULL; 4209663SMark.Logan@Sun.COM } 4219663SMark.Logan@Sun.COM ntfs_inode_close(vol->mftmirr_ni); 4229663SMark.Logan@Sun.COM vol->mftmirr_ni = NULL; 4239663SMark.Logan@Sun.COM errno = err; 4249663SMark.Logan@Sun.COM return -1; 4259663SMark.Logan@Sun.COM } 4269663SMark.Logan@Sun.COM 4279663SMark.Logan@Sun.COM /** 4289663SMark.Logan@Sun.COM * ntfs_volume_startup - allocate and setup an ntfs volume 4299663SMark.Logan@Sun.COM * @dev: device to open 4309663SMark.Logan@Sun.COM * @flags: optional mount flags 4319663SMark.Logan@Sun.COM * 4329663SMark.Logan@Sun.COM * Load, verify, and parse bootsector; load and setup $MFT and $MFTMirr. After 4339663SMark.Logan@Sun.COM * calling this function, the volume is setup sufficiently to call all read 4349663SMark.Logan@Sun.COM * and write access functions provided by the library. 4359663SMark.Logan@Sun.COM * 4369663SMark.Logan@Sun.COM * Return the allocated volume structure on success and NULL on error with 4379663SMark.Logan@Sun.COM * errno set to the error code. 4389663SMark.Logan@Sun.COM */ 4399663SMark.Logan@Sun.COM ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, 4409663SMark.Logan@Sun.COM ntfs_mount_flags flags) 4419663SMark.Logan@Sun.COM { 4429663SMark.Logan@Sun.COM LCN mft_zone_size, mft_lcn; 4439663SMark.Logan@Sun.COM s64 br; 4449663SMark.Logan@Sun.COM ntfs_volume *vol; 4459663SMark.Logan@Sun.COM NTFS_BOOT_SECTOR *bs; 4469663SMark.Logan@Sun.COM int eo; 4479663SMark.Logan@Sun.COM #ifdef DEBUG 4489663SMark.Logan@Sun.COM const char *OK = "OK\n"; 4499663SMark.Logan@Sun.COM const char *FAILED = "FAILED\n"; 4509663SMark.Logan@Sun.COM BOOL debug = 1; 4519663SMark.Logan@Sun.COM #else 4529663SMark.Logan@Sun.COM BOOL debug = 0; 4539663SMark.Logan@Sun.COM #endif 4549663SMark.Logan@Sun.COM 4559663SMark.Logan@Sun.COM if (!dev || !dev->d_ops || !dev->d_name) { 4569663SMark.Logan@Sun.COM errno = EINVAL; 4579663SMark.Logan@Sun.COM return NULL; 4589663SMark.Logan@Sun.COM } 4599663SMark.Logan@Sun.COM 4609663SMark.Logan@Sun.COM if (!(bs = (NTFS_BOOT_SECTOR *)ntfs_malloc(sizeof(NTFS_BOOT_SECTOR)))) 4619663SMark.Logan@Sun.COM return NULL; 4629663SMark.Logan@Sun.COM 4639663SMark.Logan@Sun.COM /* Allocate the volume structure. */ 4649663SMark.Logan@Sun.COM vol = ntfs_volume_alloc(); 4659663SMark.Logan@Sun.COM if (!vol) 4669663SMark.Logan@Sun.COM goto error_exit; 4679663SMark.Logan@Sun.COM /* Create the default upcase table. */ 4689663SMark.Logan@Sun.COM vol->upcase_len = 65536; 4699663SMark.Logan@Sun.COM vol->upcase = (ntfschar*)ntfs_malloc(vol->upcase_len * 4709663SMark.Logan@Sun.COM sizeof(ntfschar)); 4719663SMark.Logan@Sun.COM if (!vol->upcase) 4729663SMark.Logan@Sun.COM goto error_exit; 4739663SMark.Logan@Sun.COM ntfs_upcase_table_build(vol->upcase, 4749663SMark.Logan@Sun.COM vol->upcase_len * sizeof(ntfschar)); 4759663SMark.Logan@Sun.COM if (flags & NTFS_MNT_RDONLY) 4769663SMark.Logan@Sun.COM NVolSetReadOnly(vol); 4779663SMark.Logan@Sun.COM if (flags & NTFS_MNT_CASE_SENSITIVE) 4789663SMark.Logan@Sun.COM NVolSetCaseSensitive(vol); 4799663SMark.Logan@Sun.COM if (flags & NTFS_MNT_INTERIX) 4809663SMark.Logan@Sun.COM NVolSetInterix(vol); 4819663SMark.Logan@Sun.COM ntfs_log_debug("Reading bootsector... "); 4829663SMark.Logan@Sun.COM if (dev->d_ops->open(dev, NVolReadOnly(vol) ? O_RDONLY : 4839663SMark.Logan@Sun.COM ((flags & NTFS_MNT_NOT_EXCLUSIVE) ? O_RDWR : 4849663SMark.Logan@Sun.COM (O_RDWR | O_EXCL)))) { 4859663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 4869663SMark.Logan@Sun.COM ntfs_log_perror("Error opening partition device"); 4879663SMark.Logan@Sun.COM goto error_exit; 4889663SMark.Logan@Sun.COM } 4899663SMark.Logan@Sun.COM /* Attach the device to the volume. */ 4909663SMark.Logan@Sun.COM vol->u.dev = dev; 4919663SMark.Logan@Sun.COM /* Now read the bootsector. */ 4929663SMark.Logan@Sun.COM br = ntfs_pread(dev, 0, sizeof(NTFS_BOOT_SECTOR), bs); 4939663SMark.Logan@Sun.COM if (br != sizeof(NTFS_BOOT_SECTOR)) { 4949663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 4959663SMark.Logan@Sun.COM if (br != -1) 4969663SMark.Logan@Sun.COM errno = EINVAL; 4979663SMark.Logan@Sun.COM if (!br) 4989663SMark.Logan@Sun.COM ntfs_log_debug("Error: partition is smaller than " 4999663SMark.Logan@Sun.COM "bootsector size. Weird!\n"); 5009663SMark.Logan@Sun.COM else 5019663SMark.Logan@Sun.COM ntfs_log_perror("Error reading bootsector"); 5029663SMark.Logan@Sun.COM goto error_exit; 5039663SMark.Logan@Sun.COM } 5049663SMark.Logan@Sun.COM ntfs_log_debug(OK); 5059663SMark.Logan@Sun.COM if (!ntfs_boot_sector_is_ntfs(bs, !debug)) { 5069663SMark.Logan@Sun.COM ntfs_log_debug("Error: %s is not a valid NTFS partition!\n", 5079663SMark.Logan@Sun.COM dev->d_name); 5089663SMark.Logan@Sun.COM errno = EINVAL; 5099663SMark.Logan@Sun.COM goto error_exit; 5109663SMark.Logan@Sun.COM } 5119663SMark.Logan@Sun.COM if (ntfs_boot_sector_parse(vol, bs) < 0) { 5129663SMark.Logan@Sun.COM ntfs_log_perror("Failed to parse ntfs bootsector"); 5139663SMark.Logan@Sun.COM goto error_exit; 5149663SMark.Logan@Sun.COM } 5159663SMark.Logan@Sun.COM free(bs); 5169663SMark.Logan@Sun.COM bs = NULL; 5179663SMark.Logan@Sun.COM /* Now set the device block size to the sector size. */ 5189663SMark.Logan@Sun.COM if (ntfs_device_block_size_set(vol->u.dev, vol->sector_size)) 5199663SMark.Logan@Sun.COM ntfs_log_debug("Failed to set the device block size to the " 5209663SMark.Logan@Sun.COM "sector size. This may affect performance " 5219663SMark.Logan@Sun.COM "but should be harmless otherwise. Error: " 5229663SMark.Logan@Sun.COM "%s\n", strerror(errno)); 5239663SMark.Logan@Sun.COM /* 5249663SMark.Logan@Sun.COM * We now initialize the cluster allocator. 5259663SMark.Logan@Sun.COM * 5269663SMark.Logan@Sun.COM * FIXME: Move this to its own function? (AIA) 5279663SMark.Logan@Sun.COM */ 5289663SMark.Logan@Sun.COM 5299663SMark.Logan@Sun.COM // TODO: Make this tunable at mount time. (AIA) 5309663SMark.Logan@Sun.COM vol->mft_zone_multiplier = 1; 5319663SMark.Logan@Sun.COM 5329663SMark.Logan@Sun.COM /* Determine the size of the MFT zone. */ 5339663SMark.Logan@Sun.COM mft_zone_size = vol->nr_clusters; 5349663SMark.Logan@Sun.COM switch (vol->mft_zone_multiplier) { /* % of volume size in clusters */ 5359663SMark.Logan@Sun.COM case 4: 5369663SMark.Logan@Sun.COM mft_zone_size >>= 1; /* 50% */ 5379663SMark.Logan@Sun.COM break; 5389663SMark.Logan@Sun.COM case 3: 5399663SMark.Logan@Sun.COM mft_zone_size = mft_zone_size * 3 >> 3; /* 37.5% */ 5409663SMark.Logan@Sun.COM break; 5419663SMark.Logan@Sun.COM case 2: 5429663SMark.Logan@Sun.COM mft_zone_size >>= 2; /* 25% */ 5439663SMark.Logan@Sun.COM break; 5449663SMark.Logan@Sun.COM /* case 1: */ 5459663SMark.Logan@Sun.COM default: 5469663SMark.Logan@Sun.COM mft_zone_size >>= 3; /* 12.5% */ 5479663SMark.Logan@Sun.COM break; 5489663SMark.Logan@Sun.COM } 5499663SMark.Logan@Sun.COM 5509663SMark.Logan@Sun.COM /* Setup the mft zone. */ 5519663SMark.Logan@Sun.COM vol->mft_zone_start = vol->mft_zone_pos = vol->mft_lcn; 5529663SMark.Logan@Sun.COM ntfs_log_debug("mft_zone_pos = 0x%llx\n", (long long)vol->mft_zone_pos); 5539663SMark.Logan@Sun.COM 5549663SMark.Logan@Sun.COM /* 5559663SMark.Logan@Sun.COM * Calculate the mft_lcn for an unmodified NTFS volume (see mkntfs 5569663SMark.Logan@Sun.COM * source) and if the actual mft_lcn is in the expected place or even 5579663SMark.Logan@Sun.COM * further to the front of the volume, extend the mft_zone to cover the 5589663SMark.Logan@Sun.COM * beginning of the volume as well. This is in order to protect the 5599663SMark.Logan@Sun.COM * area reserved for the mft bitmap as well within the mft_zone itself. 5609663SMark.Logan@Sun.COM * On non-standard volumes we don't protect it as the overhead would be 5619663SMark.Logan@Sun.COM * higher than the speed increase we would get by doing it. 5629663SMark.Logan@Sun.COM */ 5639663SMark.Logan@Sun.COM mft_lcn = (8192 + 2 * vol->cluster_size - 1) / vol->cluster_size; 5649663SMark.Logan@Sun.COM if (mft_lcn * vol->cluster_size < 16 * 1024) 5659663SMark.Logan@Sun.COM mft_lcn = (16 * 1024 + vol->cluster_size - 1) / 5669663SMark.Logan@Sun.COM vol->cluster_size; 5679663SMark.Logan@Sun.COM if (vol->mft_zone_start <= mft_lcn) 5689663SMark.Logan@Sun.COM vol->mft_zone_start = 0; 5699663SMark.Logan@Sun.COM ntfs_log_debug("mft_zone_start = 0x%llx\n", 5709663SMark.Logan@Sun.COM (long long)vol->mft_zone_start); 5719663SMark.Logan@Sun.COM 5729663SMark.Logan@Sun.COM /* 5739663SMark.Logan@Sun.COM * Need to cap the mft zone on non-standard volumes so that it does 5749663SMark.Logan@Sun.COM * not point outside the boundaries of the volume. We do this by 5759663SMark.Logan@Sun.COM * halving the zone size until we are inside the volume. 5769663SMark.Logan@Sun.COM */ 5779663SMark.Logan@Sun.COM vol->mft_zone_end = vol->mft_lcn + mft_zone_size; 5789663SMark.Logan@Sun.COM while (vol->mft_zone_end >= vol->nr_clusters) { 5799663SMark.Logan@Sun.COM mft_zone_size >>= 1; 5809663SMark.Logan@Sun.COM vol->mft_zone_end = vol->mft_lcn + mft_zone_size; 5819663SMark.Logan@Sun.COM } 5829663SMark.Logan@Sun.COM ntfs_log_debug("mft_zone_end = 0x%llx\n", (long long)vol->mft_zone_end); 5839663SMark.Logan@Sun.COM 5849663SMark.Logan@Sun.COM /* 5859663SMark.Logan@Sun.COM * Set the current position within each data zone to the start of the 5869663SMark.Logan@Sun.COM * respective zone. 5879663SMark.Logan@Sun.COM */ 5889663SMark.Logan@Sun.COM vol->data1_zone_pos = vol->mft_zone_end; 5899663SMark.Logan@Sun.COM ntfs_log_debug("data1_zone_pos = 0x%llx\n", vol->data1_zone_pos); 5909663SMark.Logan@Sun.COM vol->data2_zone_pos = 0; 5919663SMark.Logan@Sun.COM ntfs_log_debug("data2_zone_pos = 0x%llx\n", vol->data2_zone_pos); 5929663SMark.Logan@Sun.COM 5939663SMark.Logan@Sun.COM /* Set the mft data allocation position to mft record 24. */ 5949663SMark.Logan@Sun.COM vol->mft_data_pos = 24; 5959663SMark.Logan@Sun.COM 5969663SMark.Logan@Sun.COM /* 5979663SMark.Logan@Sun.COM * The cluster allocator is now fully operational. 5989663SMark.Logan@Sun.COM */ 5999663SMark.Logan@Sun.COM 6009663SMark.Logan@Sun.COM /* Need to setup $MFT so we can use the library read functions. */ 6019663SMark.Logan@Sun.COM ntfs_log_debug("Loading $MFT... "); 6029663SMark.Logan@Sun.COM if (ntfs_mft_load(vol) < 0) { 6039663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 6049663SMark.Logan@Sun.COM ntfs_log_perror("Failed to load $MFT"); 6059663SMark.Logan@Sun.COM goto error_exit; 6069663SMark.Logan@Sun.COM } 6079663SMark.Logan@Sun.COM ntfs_log_debug(OK); 6089663SMark.Logan@Sun.COM 6099663SMark.Logan@Sun.COM /* Need to setup $MFTMirr so we can use the write functions, too. */ 6109663SMark.Logan@Sun.COM ntfs_log_debug("Loading $MFTMirr... "); 6119663SMark.Logan@Sun.COM if (ntfs_mftmirr_load(vol) < 0) { 6129663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 6139663SMark.Logan@Sun.COM ntfs_log_perror("Failed to load $MFTMirr"); 6149663SMark.Logan@Sun.COM goto error_exit; 6159663SMark.Logan@Sun.COM } 6169663SMark.Logan@Sun.COM ntfs_log_debug(OK); 6179663SMark.Logan@Sun.COM return vol; 6189663SMark.Logan@Sun.COM error_exit: 6199663SMark.Logan@Sun.COM eo = errno; 6209663SMark.Logan@Sun.COM free(bs); 6219663SMark.Logan@Sun.COM if (vol) 6229663SMark.Logan@Sun.COM __ntfs_volume_release(vol); 6239663SMark.Logan@Sun.COM errno = eo; 6249663SMark.Logan@Sun.COM return NULL; 6259663SMark.Logan@Sun.COM } 6269663SMark.Logan@Sun.COM 6279663SMark.Logan@Sun.COM /** 6289663SMark.Logan@Sun.COM * ntfs_volume_check_logfile - check logfile on target volume 6299663SMark.Logan@Sun.COM * @vol: volume on which to check logfile 6309663SMark.Logan@Sun.COM * 6319663SMark.Logan@Sun.COM * Return 0 on success and -1 on error with errno set error code. 6329663SMark.Logan@Sun.COM */ 6339663SMark.Logan@Sun.COM static int ntfs_volume_check_logfile(ntfs_volume *vol) 6349663SMark.Logan@Sun.COM { 6359663SMark.Logan@Sun.COM ntfs_inode *ni; 6369663SMark.Logan@Sun.COM ntfs_attr *na = NULL; 6379663SMark.Logan@Sun.COM RESTART_PAGE_HEADER *rp = NULL; 6389663SMark.Logan@Sun.COM int err = 0; 6399663SMark.Logan@Sun.COM 6409663SMark.Logan@Sun.COM if ((ni = ntfs_inode_open(vol, FILE_LogFile)) == NULL) { 6419663SMark.Logan@Sun.COM ntfs_log_debug("Failed to open inode FILE_LogFile.\n"); 6429663SMark.Logan@Sun.COM errno = EIO; 6439663SMark.Logan@Sun.COM return -1; 6449663SMark.Logan@Sun.COM } 6459663SMark.Logan@Sun.COM if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) { 6469663SMark.Logan@Sun.COM ntfs_log_debug("Failed to open $FILE_LogFile/$DATA\n"); 6479663SMark.Logan@Sun.COM err = EIO; 6489663SMark.Logan@Sun.COM goto exit; 6499663SMark.Logan@Sun.COM } 6509663SMark.Logan@Sun.COM if (!ntfs_check_logfile(na, &rp) || !ntfs_is_logfile_clean(na, rp)) 6519663SMark.Logan@Sun.COM err = EOPNOTSUPP; 6529663SMark.Logan@Sun.COM free(rp); 6539663SMark.Logan@Sun.COM exit: 6549663SMark.Logan@Sun.COM if (na) 6559663SMark.Logan@Sun.COM ntfs_attr_close(na); 6569663SMark.Logan@Sun.COM ntfs_inode_close(ni); 6579663SMark.Logan@Sun.COM if (err) { 6589663SMark.Logan@Sun.COM errno = err; 6599663SMark.Logan@Sun.COM return -1; 6609663SMark.Logan@Sun.COM } 6619663SMark.Logan@Sun.COM return 0; 6629663SMark.Logan@Sun.COM } 6639663SMark.Logan@Sun.COM 6649663SMark.Logan@Sun.COM /** 6659663SMark.Logan@Sun.COM * ntfs_hiberfile_open - Find and open '/hiberfil.sys' 6669663SMark.Logan@Sun.COM * @vol: An ntfs volume obtained from ntfs_mount 6679663SMark.Logan@Sun.COM * 6689663SMark.Logan@Sun.COM * Return: inode Success, hiberfil.sys is valid 6699663SMark.Logan@Sun.COM * NULL hiberfil.sys doesn't exist or some other error occurred 6709663SMark.Logan@Sun.COM */ 6719663SMark.Logan@Sun.COM static ntfs_inode *ntfs_hiberfile_open(ntfs_volume *vol) 6729663SMark.Logan@Sun.COM { 6739663SMark.Logan@Sun.COM u64 inode; 6749663SMark.Logan@Sun.COM ntfs_inode *ni_root; 6759663SMark.Logan@Sun.COM ntfs_inode *ni_hibr = NULL; 6769663SMark.Logan@Sun.COM ntfschar *unicode = NULL; 6779663SMark.Logan@Sun.COM int unicode_len; 6789663SMark.Logan@Sun.COM const char *hiberfile = "hiberfil.sys"; 6799663SMark.Logan@Sun.COM 6809663SMark.Logan@Sun.COM if (!vol) { 6819663SMark.Logan@Sun.COM errno = EINVAL; 6829663SMark.Logan@Sun.COM return NULL; 6839663SMark.Logan@Sun.COM } 6849663SMark.Logan@Sun.COM 6859663SMark.Logan@Sun.COM ni_root = ntfs_inode_open(vol, FILE_root); 6869663SMark.Logan@Sun.COM if (!ni_root) { 6879663SMark.Logan@Sun.COM ntfs_log_debug("Couldn't open the root directory.\n"); 6889663SMark.Logan@Sun.COM return NULL; 6899663SMark.Logan@Sun.COM } 6909663SMark.Logan@Sun.COM 6919663SMark.Logan@Sun.COM unicode_len = ntfs_mbstoucs(hiberfile, &unicode, 0); 6929663SMark.Logan@Sun.COM if (unicode_len < 0) { 6939663SMark.Logan@Sun.COM ntfs_log_perror("Couldn't convert 'hiberfil.sys' to Unicode"); 6949663SMark.Logan@Sun.COM goto out; 6959663SMark.Logan@Sun.COM } 6969663SMark.Logan@Sun.COM 6979663SMark.Logan@Sun.COM inode = ntfs_inode_lookup_by_name(ni_root, unicode, unicode_len); 6989663SMark.Logan@Sun.COM if (inode == (u64)-1) { 6999663SMark.Logan@Sun.COM ntfs_log_debug("Couldn't find file '%s'.\n", hiberfile); 7009663SMark.Logan@Sun.COM goto out; 7019663SMark.Logan@Sun.COM } 7029663SMark.Logan@Sun.COM 7039663SMark.Logan@Sun.COM inode = MREF(inode); 7049663SMark.Logan@Sun.COM ni_hibr = ntfs_inode_open(vol, inode); 7059663SMark.Logan@Sun.COM if (!ni_hibr) { 7069663SMark.Logan@Sun.COM ntfs_log_debug("Couldn't open inode %lld.\n", (long long)inode); 7079663SMark.Logan@Sun.COM goto out; 7089663SMark.Logan@Sun.COM } 7099663SMark.Logan@Sun.COM out: 7109663SMark.Logan@Sun.COM ntfs_inode_close(ni_root); 7119663SMark.Logan@Sun.COM free(unicode); 7129663SMark.Logan@Sun.COM return ni_hibr; 7139663SMark.Logan@Sun.COM } 7149663SMark.Logan@Sun.COM 7159663SMark.Logan@Sun.COM 7169663SMark.Logan@Sun.COM #define NTFS_HIBERFILE_HEADER_SIZE 4096 7179663SMark.Logan@Sun.COM 7189663SMark.Logan@Sun.COM /** 7199663SMark.Logan@Sun.COM * ntfs_volume_check_hiberfile - check hiberfil.sys whether Windows is 7209663SMark.Logan@Sun.COM * hibernated on the target volume 7219663SMark.Logan@Sun.COM * @vol: volume on which to check hiberfil.sys 7229663SMark.Logan@Sun.COM * 7239663SMark.Logan@Sun.COM * Return: 0 if Windows isn't hibernated for sure 7249663SMark.Logan@Sun.COM * -1 otherwise and errno is set to the appropriate value 7259663SMark.Logan@Sun.COM */ 7269663SMark.Logan@Sun.COM static int ntfs_volume_check_hiberfile(ntfs_volume *vol) 7279663SMark.Logan@Sun.COM { 7289663SMark.Logan@Sun.COM ntfs_inode *ni; 7299663SMark.Logan@Sun.COM ntfs_attr *na = NULL; 7309663SMark.Logan@Sun.COM int bytes_read, ret = -1; 7319663SMark.Logan@Sun.COM char *buf = NULL; 7329663SMark.Logan@Sun.COM 7339663SMark.Logan@Sun.COM ni = ntfs_hiberfile_open(vol); 7349663SMark.Logan@Sun.COM if (!ni) { 7359663SMark.Logan@Sun.COM if (errno == ENOENT) 7369663SMark.Logan@Sun.COM return 0; 7379663SMark.Logan@Sun.COM return -1; 7389663SMark.Logan@Sun.COM } 7399663SMark.Logan@Sun.COM 7409663SMark.Logan@Sun.COM buf = ntfs_malloc(NTFS_HIBERFILE_HEADER_SIZE); 7419663SMark.Logan@Sun.COM if (!buf) 7429663SMark.Logan@Sun.COM goto out; 7439663SMark.Logan@Sun.COM 7449663SMark.Logan@Sun.COM na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); 7459663SMark.Logan@Sun.COM if (!na) { 7469663SMark.Logan@Sun.COM ntfs_log_perror("Failed to open hiberfil.sys data attribute"); 7479663SMark.Logan@Sun.COM goto out; 7489663SMark.Logan@Sun.COM } 7499663SMark.Logan@Sun.COM 7509663SMark.Logan@Sun.COM bytes_read = ntfs_attr_pread(na, 0, NTFS_HIBERFILE_HEADER_SIZE, buf); 7519663SMark.Logan@Sun.COM if (bytes_read == -1) { 7529663SMark.Logan@Sun.COM ntfs_log_perror("Failed to read hiberfil.sys"); 7539663SMark.Logan@Sun.COM goto out; 7549663SMark.Logan@Sun.COM } 7559663SMark.Logan@Sun.COM if (bytes_read < NTFS_HIBERFILE_HEADER_SIZE) { 7569663SMark.Logan@Sun.COM ntfs_log_debug("Hibernated non-system partition, refused to " 7579663SMark.Logan@Sun.COM "mount!\n"); 7589663SMark.Logan@Sun.COM errno = EPERM; 7599663SMark.Logan@Sun.COM goto out; 7609663SMark.Logan@Sun.COM } 7619663SMark.Logan@Sun.COM if (memcmp(buf, "hibr", 4) == 0) { 7629663SMark.Logan@Sun.COM ntfs_log_debug("Windows is hibernated, refused to mount!\n"); 7639663SMark.Logan@Sun.COM errno = EPERM; 7649663SMark.Logan@Sun.COM goto out; 7659663SMark.Logan@Sun.COM } 7669663SMark.Logan@Sun.COM ret = 0; 7679663SMark.Logan@Sun.COM out: 7689663SMark.Logan@Sun.COM if (na) 7699663SMark.Logan@Sun.COM ntfs_attr_close(na); 7709663SMark.Logan@Sun.COM free(buf); 7719663SMark.Logan@Sun.COM ntfs_inode_close(ni); 7729663SMark.Logan@Sun.COM return ret; 7739663SMark.Logan@Sun.COM } 7749663SMark.Logan@Sun.COM 7759663SMark.Logan@Sun.COM /** 7769663SMark.Logan@Sun.COM * ntfs_volume_get_nr_free_mft_records - calculate number of free MFT records 7779663SMark.Logan@Sun.COM * vol: ntfs volume for which perform calculations. 7789663SMark.Logan@Sun.COM * 7799663SMark.Logan@Sun.COM * This function initializes @vol->nr_free_mft_records. @vol->mftbmp_na should 7809663SMark.Logan@Sun.COM * be already opened upon call to this function. 7819663SMark.Logan@Sun.COM * 7829663SMark.Logan@Sun.COM * Return 0 on success. On error return -1 with errno set appropriately and 7839663SMark.Logan@Sun.COM * @vol->nr_free_mft_records is not touched in this case. 7849663SMark.Logan@Sun.COM */ 7859663SMark.Logan@Sun.COM static int ntfs_volume_get_nr_free_mft_records(ntfs_volume *vol) 7869663SMark.Logan@Sun.COM { 7879663SMark.Logan@Sun.COM long nr_free = vol->mft_na->data_size >> vol->mft_record_size_bits; 7889663SMark.Logan@Sun.COM s64 br, total = 0; 7899663SMark.Logan@Sun.COM u8 *buf; 7909663SMark.Logan@Sun.COM 7919663SMark.Logan@Sun.COM buf = ntfs_malloc(vol->cluster_size); 7929663SMark.Logan@Sun.COM if (!buf) 7939663SMark.Logan@Sun.COM return -1; 7949663SMark.Logan@Sun.COM while (1) { 7959663SMark.Logan@Sun.COM int i, j; 7969663SMark.Logan@Sun.COM 7979663SMark.Logan@Sun.COM br = ntfs_attr_pread(vol->mftbmp_na, total, 7989663SMark.Logan@Sun.COM vol->cluster_size, buf); 7999663SMark.Logan@Sun.COM if (br <= 0) 8009663SMark.Logan@Sun.COM break; 8019663SMark.Logan@Sun.COM total += br; 8029663SMark.Logan@Sun.COM for (i = 0; i < br; i++) 8039663SMark.Logan@Sun.COM for (j = 0; j < 8; j++) 8049663SMark.Logan@Sun.COM if ((buf[i] >> j) & 1) 8059663SMark.Logan@Sun.COM nr_free--; 8069663SMark.Logan@Sun.COM } 8079663SMark.Logan@Sun.COM free(buf); 8089663SMark.Logan@Sun.COM if (!total || br < 0) { 8099663SMark.Logan@Sun.COM ntfs_log_error("pread: %s\n", strerror(errno)); 8109663SMark.Logan@Sun.COM return -1; 8119663SMark.Logan@Sun.COM } 8129663SMark.Logan@Sun.COM vol->nr_free_mft_records = nr_free; 8139663SMark.Logan@Sun.COM return 0; 8149663SMark.Logan@Sun.COM } 8159663SMark.Logan@Sun.COM 8169663SMark.Logan@Sun.COM /** 8179663SMark.Logan@Sun.COM * ntfs_volume_get_nr_free_clusters - calculate number of free clusters 8189663SMark.Logan@Sun.COM * vol: ntfs volume for which perform calculations. 8199663SMark.Logan@Sun.COM * 8209663SMark.Logan@Sun.COM * This function initializes @vol->nr_free_clusters. @vol->lcnbmp_na should be 8219663SMark.Logan@Sun.COM * already opened upon call to this function. 8229663SMark.Logan@Sun.COM * 8239663SMark.Logan@Sun.COM * Return 0 on success. On error return -1 with errno set appropriately and 8249663SMark.Logan@Sun.COM * @vol->nr_free_clusters is not touched in this case. 8259663SMark.Logan@Sun.COM */ 8269663SMark.Logan@Sun.COM static long ntfs_volume_get_nr_free_clusters(ntfs_volume *vol) 8279663SMark.Logan@Sun.COM { 8289663SMark.Logan@Sun.COM long nr_free = vol->nr_clusters; 8299663SMark.Logan@Sun.COM s64 br, total = 0; 8309663SMark.Logan@Sun.COM u8 *buf; 8319663SMark.Logan@Sun.COM 8329663SMark.Logan@Sun.COM buf = ntfs_malloc(vol->cluster_size); 8339663SMark.Logan@Sun.COM if (!buf) 8349663SMark.Logan@Sun.COM return -1; 8359663SMark.Logan@Sun.COM while (1) { 8369663SMark.Logan@Sun.COM int i, j; 8379663SMark.Logan@Sun.COM 8389663SMark.Logan@Sun.COM br = ntfs_attr_pread(vol->lcnbmp_na, total, 8399663SMark.Logan@Sun.COM vol->cluster_size, buf); 8409663SMark.Logan@Sun.COM if (br <= 0) 8419663SMark.Logan@Sun.COM break; 8429663SMark.Logan@Sun.COM total += br; 8439663SMark.Logan@Sun.COM for (i = 0; i < br; i++) 8449663SMark.Logan@Sun.COM for (j = 0; j < 8; j++) 8459663SMark.Logan@Sun.COM if ((buf[i] >> j) & 1) 8469663SMark.Logan@Sun.COM nr_free--; 8479663SMark.Logan@Sun.COM } 8489663SMark.Logan@Sun.COM free(buf); 8499663SMark.Logan@Sun.COM if (!total || br < 0) { 8509663SMark.Logan@Sun.COM ntfs_log_error("pread: %s\n", strerror(errno)); 8519663SMark.Logan@Sun.COM return -1; 8529663SMark.Logan@Sun.COM } 8539663SMark.Logan@Sun.COM vol->nr_free_clusters = nr_free; 8549663SMark.Logan@Sun.COM return 0; 8559663SMark.Logan@Sun.COM } 8569663SMark.Logan@Sun.COM 8579663SMark.Logan@Sun.COM /** 8589663SMark.Logan@Sun.COM * ntfs_device_mount - open ntfs volume 8599663SMark.Logan@Sun.COM * @dev: device to open 8609663SMark.Logan@Sun.COM * @flags: optional mount flags 8619663SMark.Logan@Sun.COM * 8629663SMark.Logan@Sun.COM * This function mounts an ntfs volume. @dev should describe the device which 8639663SMark.Logan@Sun.COM * to mount as the ntfs volume. 8649663SMark.Logan@Sun.COM * 8659663SMark.Logan@Sun.COM * @flags is an optional second parameter. Some flags are similar to flags used 8669663SMark.Logan@Sun.COM * as for the mount system call (man 2 mount). Currently the following flags 8679663SMark.Logan@Sun.COM * are implemented: 8689663SMark.Logan@Sun.COM * NTFS_MNT_RDONLY - mount volume read-only 8699663SMark.Logan@Sun.COM * NTFS_MNT_CASE_SENSITIVE - treat filenames as case sensitive even if 8709663SMark.Logan@Sun.COM * they are not in POSIX namespace 8719663SMark.Logan@Sun.COM * NTFS_MNT_NOT_EXCLUSIVE - (unix only) do not open volume exclusively 8729663SMark.Logan@Sun.COM * NTFS_MNT_FORENSIC - mount for forensic purposes, i.e. do not do 8739663SMark.Logan@Sun.COM * any writing at all during the mount, i.e. no 8749663SMark.Logan@Sun.COM * journal emptying, no dirty bit setting, etc. 8759663SMark.Logan@Sun.COM * NTFS_MNT_INTERIX - make libntfs recognize special Interix files 8769663SMark.Logan@Sun.COM * 8779663SMark.Logan@Sun.COM * The function opens the device @dev and verifies that it contains a valid 8789663SMark.Logan@Sun.COM * bootsector. Then, it allocates an ntfs_volume structure and initializes 8799663SMark.Logan@Sun.COM * some of the values inside the structure from the information stored in the 8809663SMark.Logan@Sun.COM * bootsector. It proceeds to load the necessary system files and completes 8819663SMark.Logan@Sun.COM * setting up the structure. 8829663SMark.Logan@Sun.COM * 8839663SMark.Logan@Sun.COM * Return the allocated volume structure on success and NULL on error with 8849663SMark.Logan@Sun.COM * errno set to the error code. 8859663SMark.Logan@Sun.COM */ 8869663SMark.Logan@Sun.COM ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags) 8879663SMark.Logan@Sun.COM { 8889663SMark.Logan@Sun.COM s64 l; 8899663SMark.Logan@Sun.COM #ifdef DEBUG 8909663SMark.Logan@Sun.COM const char *OK = "OK\n"; 8919663SMark.Logan@Sun.COM const char *FAILED = "FAILED\n"; 8929663SMark.Logan@Sun.COM #endif 8939663SMark.Logan@Sun.COM ntfs_volume *vol; 8949663SMark.Logan@Sun.COM u8 *m = NULL, *m2 = NULL; 8959663SMark.Logan@Sun.COM ntfs_attr_search_ctx *ctx = NULL; 8969663SMark.Logan@Sun.COM ntfs_inode *ni; 8979663SMark.Logan@Sun.COM ntfs_attr *na; 8989663SMark.Logan@Sun.COM ATTR_RECORD *a; 8999663SMark.Logan@Sun.COM VOLUME_INFORMATION *vinf; 9009663SMark.Logan@Sun.COM ntfschar *vname; 9019663SMark.Logan@Sun.COM int i, j, eo; 9029663SMark.Logan@Sun.COM u32 u; 9039663SMark.Logan@Sun.COM 9049663SMark.Logan@Sun.COM vol = ntfs_volume_startup(dev, flags); 9059663SMark.Logan@Sun.COM if (!vol) { 9069663SMark.Logan@Sun.COM ntfs_log_perror("Failed to startup volume"); 9079663SMark.Logan@Sun.COM return NULL; 9089663SMark.Logan@Sun.COM } 9099663SMark.Logan@Sun.COM /* Record whether this is a forensic mount. */ 9109663SMark.Logan@Sun.COM if (flags & NTFS_MNT_FORENSIC) 9119663SMark.Logan@Sun.COM NVolSetForensicMount(vol); 9129663SMark.Logan@Sun.COM /* Load data from $MFT and $MFTMirr and compare the contents. */ 9139663SMark.Logan@Sun.COM m = (u8*)ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits); 9149663SMark.Logan@Sun.COM m2 = (u8*)ntfs_malloc(vol->mftmirr_size << vol->mft_record_size_bits); 9159663SMark.Logan@Sun.COM if (!m || !m2) 9169663SMark.Logan@Sun.COM goto error_exit; 9179663SMark.Logan@Sun.COM 9189663SMark.Logan@Sun.COM l = ntfs_attr_mst_pread(vol->mft_na, 0, vol->mftmirr_size, 9199663SMark.Logan@Sun.COM vol->mft_record_size, m); 9209663SMark.Logan@Sun.COM if (l != vol->mftmirr_size) { 9219663SMark.Logan@Sun.COM if (l == -1) 9229663SMark.Logan@Sun.COM ntfs_log_perror("Failed to read $MFT"); 9239663SMark.Logan@Sun.COM else { 9249663SMark.Logan@Sun.COM ntfs_log_debug("Failed to read $MFT, unexpected length " 9259663SMark.Logan@Sun.COM "(%d != %lld).\n", vol->mftmirr_size, l); 9269663SMark.Logan@Sun.COM errno = EIO; 9279663SMark.Logan@Sun.COM } 9289663SMark.Logan@Sun.COM goto error_exit; 9299663SMark.Logan@Sun.COM } 9309663SMark.Logan@Sun.COM l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size, 9319663SMark.Logan@Sun.COM vol->mft_record_size, m2); 9329663SMark.Logan@Sun.COM if (l != vol->mftmirr_size) { 9339663SMark.Logan@Sun.COM if (l == -1) 9349663SMark.Logan@Sun.COM ntfs_log_perror("Failed to read $MFTMirr"); 9359663SMark.Logan@Sun.COM else { 9369663SMark.Logan@Sun.COM ntfs_log_debug("Failed to read $MFTMirr, unexpected " 9379663SMark.Logan@Sun.COM "length (%d != %lld).\n", 9389663SMark.Logan@Sun.COM vol->mftmirr_size, l); 9399663SMark.Logan@Sun.COM errno = EIO; 9409663SMark.Logan@Sun.COM } 9419663SMark.Logan@Sun.COM goto error_exit; 9429663SMark.Logan@Sun.COM } 9439663SMark.Logan@Sun.COM ntfs_log_debug("Comparing $MFTMirr to $MFT... "); 9449663SMark.Logan@Sun.COM for (i = 0; i < vol->mftmirr_size; ++i) { 9459663SMark.Logan@Sun.COM MFT_RECORD *mrec, *mrec2; 9469663SMark.Logan@Sun.COM const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", 9479663SMark.Logan@Sun.COM "$Volume", "$AttrDef", "root directory", "$Bitmap", 9489663SMark.Logan@Sun.COM "$Boot", "$BadClus", "$Secure", "$UpCase", "$Extend" }; 9499663SMark.Logan@Sun.COM const char *s; 9509663SMark.Logan@Sun.COM 9519663SMark.Logan@Sun.COM if (i < 12) 9529663SMark.Logan@Sun.COM s = ESTR[i]; 9539663SMark.Logan@Sun.COM else if (i < 16) 9549663SMark.Logan@Sun.COM s = "system file"; 9559663SMark.Logan@Sun.COM else 9569663SMark.Logan@Sun.COM s = "mft record"; 9579663SMark.Logan@Sun.COM 9589663SMark.Logan@Sun.COM mrec = (MFT_RECORD*)(m + i * vol->mft_record_size); 9599663SMark.Logan@Sun.COM if (mrec->flags & MFT_RECORD_IN_USE) { 9609663SMark.Logan@Sun.COM if (ntfs_is_baad_record(mrec->magic)) { 9619663SMark.Logan@Sun.COM ntfs_log_debug("FAILED\n"); 9629663SMark.Logan@Sun.COM ntfs_log_debug("$MFT error: Incomplete multi " 9639663SMark.Logan@Sun.COM "sector transfer detected in " 9649663SMark.Logan@Sun.COM "%s.\n", s); 9659663SMark.Logan@Sun.COM goto io_error_exit; 9669663SMark.Logan@Sun.COM } 9679663SMark.Logan@Sun.COM if (!ntfs_is_mft_record(mrec->magic)) { 9689663SMark.Logan@Sun.COM ntfs_log_debug("FAILED\n"); 9699663SMark.Logan@Sun.COM ntfs_log_debug("$MFT error: Invalid mft " 9709663SMark.Logan@Sun.COM "record for %s.\n", s); 9719663SMark.Logan@Sun.COM goto io_error_exit; 9729663SMark.Logan@Sun.COM } 9739663SMark.Logan@Sun.COM } 9749663SMark.Logan@Sun.COM mrec2 = (MFT_RECORD*)(m2 + i * vol->mft_record_size); 9759663SMark.Logan@Sun.COM if (mrec2->flags & MFT_RECORD_IN_USE) { 9769663SMark.Logan@Sun.COM if (ntfs_is_baad_record(mrec2->magic)) { 9779663SMark.Logan@Sun.COM ntfs_log_debug("FAILED\n"); 9789663SMark.Logan@Sun.COM ntfs_log_debug("$MFTMirr error: Incomplete " 9799663SMark.Logan@Sun.COM "multi sector transfer " 9809663SMark.Logan@Sun.COM "detected in %s.\n", s); 9819663SMark.Logan@Sun.COM goto io_error_exit; 9829663SMark.Logan@Sun.COM } 9839663SMark.Logan@Sun.COM if (!ntfs_is_mft_record(mrec2->magic)) { 9849663SMark.Logan@Sun.COM ntfs_log_debug("FAILED\n"); 9859663SMark.Logan@Sun.COM ntfs_log_debug("$MFTMirr error: Invalid mft " 9869663SMark.Logan@Sun.COM "record for %s.\n", s); 9879663SMark.Logan@Sun.COM goto io_error_exit; 9889663SMark.Logan@Sun.COM } 9899663SMark.Logan@Sun.COM } 9909663SMark.Logan@Sun.COM if (memcmp(mrec, mrec2, ntfs_mft_record_get_data_size(mrec))) { 9919663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 9929663SMark.Logan@Sun.COM ntfs_log_debug("$MFTMirr does not match $MFT. Run " 9939663SMark.Logan@Sun.COM "chkdsk.\n"); 9949663SMark.Logan@Sun.COM goto io_error_exit; 9959663SMark.Logan@Sun.COM } 9969663SMark.Logan@Sun.COM } 9979663SMark.Logan@Sun.COM ntfs_log_debug(OK); 9989663SMark.Logan@Sun.COM 9999663SMark.Logan@Sun.COM free(m2); 10009663SMark.Logan@Sun.COM free(m); 10019663SMark.Logan@Sun.COM m = m2 = NULL; 10029663SMark.Logan@Sun.COM 10039663SMark.Logan@Sun.COM /* Now load the bitmap from $Bitmap. */ 10049663SMark.Logan@Sun.COM ntfs_log_debug("Loading $Bitmap... "); 10059663SMark.Logan@Sun.COM vol->lcnbmp_ni = ntfs_inode_open(vol, FILE_Bitmap); 10069663SMark.Logan@Sun.COM if (!vol->lcnbmp_ni) { 10079663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 10089663SMark.Logan@Sun.COM ntfs_log_perror("Failed to open inode"); 10099663SMark.Logan@Sun.COM goto error_exit; 10109663SMark.Logan@Sun.COM } 10119663SMark.Logan@Sun.COM /* Get an ntfs attribute for $Bitmap/$DATA. */ 10129663SMark.Logan@Sun.COM vol->lcnbmp_na = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0); 10139663SMark.Logan@Sun.COM if (!vol->lcnbmp_na) { 10149663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 10159663SMark.Logan@Sun.COM ntfs_log_perror("Failed to open ntfs attribute"); 10169663SMark.Logan@Sun.COM goto error_exit; 10179663SMark.Logan@Sun.COM } 10189663SMark.Logan@Sun.COM /* Done with the $Bitmap mft record. */ 10199663SMark.Logan@Sun.COM ntfs_log_debug(OK); 10209663SMark.Logan@Sun.COM 10219663SMark.Logan@Sun.COM /* Now load the upcase table from $UpCase. */ 10229663SMark.Logan@Sun.COM ntfs_log_debug("Loading $UpCase... "); 10239663SMark.Logan@Sun.COM ni = ntfs_inode_open(vol, FILE_UpCase); 10249663SMark.Logan@Sun.COM if (!ni) { 10259663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 10269663SMark.Logan@Sun.COM ntfs_log_perror("Failed to open inode"); 10279663SMark.Logan@Sun.COM goto error_exit; 10289663SMark.Logan@Sun.COM } 10299663SMark.Logan@Sun.COM /* Get an ntfs attribute for $UpCase/$DATA. */ 10309663SMark.Logan@Sun.COM na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); 10319663SMark.Logan@Sun.COM if (!na) { 10329663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 10339663SMark.Logan@Sun.COM ntfs_log_perror("Failed to open ntfs attribute"); 10349663SMark.Logan@Sun.COM goto error_exit; 10359663SMark.Logan@Sun.COM } 10369663SMark.Logan@Sun.COM /* 10379663SMark.Logan@Sun.COM * Note: Normally, the upcase table has a length equal to 65536 10389663SMark.Logan@Sun.COM * 2-byte Unicode characters but allow for different cases, so no 10399663SMark.Logan@Sun.COM * checks done. Just check we don't overflow 32-bits worth of Unicode 10409663SMark.Logan@Sun.COM * characters. 10419663SMark.Logan@Sun.COM */ 10429663SMark.Logan@Sun.COM if (na->data_size & ~0x1ffffffffULL) { 10439663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 10449663SMark.Logan@Sun.COM ntfs_log_debug("Error: Upcase table is too big (max 32-bit " 10459663SMark.Logan@Sun.COM "allowed).\n"); 10469663SMark.Logan@Sun.COM errno = EINVAL; 10479663SMark.Logan@Sun.COM goto error_exit; 10489663SMark.Logan@Sun.COM } 10499663SMark.Logan@Sun.COM if (vol->upcase_len != na->data_size >> 1) { 10509663SMark.Logan@Sun.COM vol->upcase_len = na->data_size >> 1; 10519663SMark.Logan@Sun.COM /* Throw away default table. */ 10529663SMark.Logan@Sun.COM free(vol->upcase); 10539663SMark.Logan@Sun.COM vol->upcase = (ntfschar*)ntfs_malloc(na->data_size); 10549663SMark.Logan@Sun.COM if (!vol->upcase) { 10559663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 10569663SMark.Logan@Sun.COM goto error_exit; 10579663SMark.Logan@Sun.COM } 10589663SMark.Logan@Sun.COM } 10599663SMark.Logan@Sun.COM /* Read in the $DATA attribute value into the buffer. */ 10609663SMark.Logan@Sun.COM l = ntfs_attr_pread(na, 0, na->data_size, vol->upcase); 10619663SMark.Logan@Sun.COM if (l != na->data_size) { 10629663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 10639663SMark.Logan@Sun.COM ntfs_log_debug("Amount of data read does not correspond to " 10649663SMark.Logan@Sun.COM "expected length!\n"); 10659663SMark.Logan@Sun.COM errno = EIO; 10669663SMark.Logan@Sun.COM goto error_exit; 10679663SMark.Logan@Sun.COM } 10689663SMark.Logan@Sun.COM /* Done with the $UpCase mft record. */ 10699663SMark.Logan@Sun.COM ntfs_log_debug(OK); 10709663SMark.Logan@Sun.COM ntfs_attr_close(na); 10719663SMark.Logan@Sun.COM if (ntfs_inode_close(ni)) 10729663SMark.Logan@Sun.COM ntfs_log_perror("Failed to close inode, leaking memory"); 10739663SMark.Logan@Sun.COM 10749663SMark.Logan@Sun.COM /* 10759663SMark.Logan@Sun.COM * Now load $Volume and set the version information and flags in the 10769663SMark.Logan@Sun.COM * vol structure accordingly. 10779663SMark.Logan@Sun.COM */ 10789663SMark.Logan@Sun.COM ntfs_log_debug("Loading $Volume... "); 10799663SMark.Logan@Sun.COM vol->vol_ni = ntfs_inode_open(vol, FILE_Volume); 10809663SMark.Logan@Sun.COM if (!vol->vol_ni) { 10819663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 10829663SMark.Logan@Sun.COM ntfs_log_perror("Failed to open inode"); 10839663SMark.Logan@Sun.COM goto error_exit; 10849663SMark.Logan@Sun.COM } 10859663SMark.Logan@Sun.COM /* Get a search context for the $Volume/$VOLUME_INFORMATION lookup. */ 10869663SMark.Logan@Sun.COM ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); 10879663SMark.Logan@Sun.COM if (!ctx) { 10889663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 10899663SMark.Logan@Sun.COM ntfs_log_perror("Failed to allocate attribute search context"); 10909663SMark.Logan@Sun.COM goto error_exit; 10919663SMark.Logan@Sun.COM } 10929663SMark.Logan@Sun.COM /* Find the $VOLUME_INFORMATION attribute. */ 10939663SMark.Logan@Sun.COM if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, 10949663SMark.Logan@Sun.COM 0, ctx)) { 10959663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 10969663SMark.Logan@Sun.COM ntfs_log_debug("$VOLUME_INFORMATION attribute not found in " 10979663SMark.Logan@Sun.COM "$Volume?!?\n"); 10989663SMark.Logan@Sun.COM goto error_exit; 10999663SMark.Logan@Sun.COM } 11009663SMark.Logan@Sun.COM a = ctx->attr; 11019663SMark.Logan@Sun.COM /* Has to be resident. */ 11029663SMark.Logan@Sun.COM if (a->non_resident) { 11039663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 11049663SMark.Logan@Sun.COM ntfs_log_debug("Error: Attribute $VOLUME_INFORMATION must be " 11059663SMark.Logan@Sun.COM "resident (and it isn't)!\n"); 11069663SMark.Logan@Sun.COM errno = EIO; 11079663SMark.Logan@Sun.COM goto error_exit; 11089663SMark.Logan@Sun.COM } 11099663SMark.Logan@Sun.COM /* Get a pointer to the value of the attribute. */ 11109663SMark.Logan@Sun.COM vinf = (VOLUME_INFORMATION*)(le16_to_cpu(a->u.res.value_offset) + (char*)a); 11119663SMark.Logan@Sun.COM /* Sanity checks. */ 11129663SMark.Logan@Sun.COM if ((char*)vinf + le32_to_cpu(a->u.res.value_length) > (char*)ctx->mrec + 11139663SMark.Logan@Sun.COM le32_to_cpu(ctx->mrec->bytes_in_use) || 11149663SMark.Logan@Sun.COM le16_to_cpu(a->u.res.value_offset) + le32_to_cpu( 11159663SMark.Logan@Sun.COM a->u.res.value_length) > le32_to_cpu(a->length)) { 11169663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 11179663SMark.Logan@Sun.COM ntfs_log_debug("Error: Attribute $VOLUME_INFORMATION in " 11189663SMark.Logan@Sun.COM "$Volume is corrupt!\n"); 11199663SMark.Logan@Sun.COM errno = EIO; 11209663SMark.Logan@Sun.COM goto error_exit; 11219663SMark.Logan@Sun.COM } 11229663SMark.Logan@Sun.COM /* Setup vol from the volume information attribute value. */ 11239663SMark.Logan@Sun.COM vol->major_ver = vinf->major_ver; 11249663SMark.Logan@Sun.COM vol->minor_ver = vinf->minor_ver; 11259663SMark.Logan@Sun.COM /* 11269663SMark.Logan@Sun.COM * Do not use le16_to_cpu() macro here as our VOLUME_FLAGS are defined 11279663SMark.Logan@Sun.COM * using cpu_to_le16() macro and hence are consistent. 11289663SMark.Logan@Sun.COM */ 11299663SMark.Logan@Sun.COM vol->flags = vinf->flags; 11309663SMark.Logan@Sun.COM /* Record whether the volume was dirty or not. */ 11319663SMark.Logan@Sun.COM if (vol->flags & VOLUME_IS_DIRTY) 11329663SMark.Logan@Sun.COM NVolSetWasDirty(vol); 11339663SMark.Logan@Sun.COM /* 11349663SMark.Logan@Sun.COM * Reinitialize the search context for the $Volume/$VOLUME_NAME lookup. 11359663SMark.Logan@Sun.COM */ 11369663SMark.Logan@Sun.COM ntfs_attr_reinit_search_ctx(ctx); 11379663SMark.Logan@Sun.COM if (ntfs_attr_lookup(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, 11389663SMark.Logan@Sun.COM ctx)) { 11399663SMark.Logan@Sun.COM if (errno != ENOENT) { 11409663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 11419663SMark.Logan@Sun.COM ntfs_log_debug("Error: Lookup of $VOLUME_NAME " 11429663SMark.Logan@Sun.COM "attribute in $Volume failed. " 11439663SMark.Logan@Sun.COM "This probably means something is " 11449663SMark.Logan@Sun.COM "corrupt. Run chkdsk.\n"); 11459663SMark.Logan@Sun.COM goto error_exit; 11469663SMark.Logan@Sun.COM } 11479663SMark.Logan@Sun.COM /* 11489663SMark.Logan@Sun.COM * Attribute not present. This has been seen in the field. 11499663SMark.Logan@Sun.COM * Treat this the same way as if the attribute was present but 11509663SMark.Logan@Sun.COM * had zero length. 11519663SMark.Logan@Sun.COM */ 11529663SMark.Logan@Sun.COM vol->vol_name = ntfs_malloc(1); 11539663SMark.Logan@Sun.COM if (!vol->vol_name) { 11549663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 11559663SMark.Logan@Sun.COM goto error_exit; 11569663SMark.Logan@Sun.COM } 11579663SMark.Logan@Sun.COM vol->vol_name[0] = '\0'; 11589663SMark.Logan@Sun.COM } else { 11599663SMark.Logan@Sun.COM a = ctx->attr; 11609663SMark.Logan@Sun.COM /* Has to be resident. */ 11619663SMark.Logan@Sun.COM if (a->non_resident) { 11629663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 11639663SMark.Logan@Sun.COM ntfs_log_debug("Error: Attribute $VOLUME_NAME must be " 11649663SMark.Logan@Sun.COM "resident!\n"); 11659663SMark.Logan@Sun.COM errno = EIO; 11669663SMark.Logan@Sun.COM goto error_exit; 11679663SMark.Logan@Sun.COM } 11689663SMark.Logan@Sun.COM /* Get a pointer to the value of the attribute. */ 11699663SMark.Logan@Sun.COM vname = (ntfschar*)(le16_to_cpu(a->u.res.value_offset) + (char*)a); 11709663SMark.Logan@Sun.COM u = le32_to_cpu(a->u.res.value_length) / 2; 11719663SMark.Logan@Sun.COM /* 11729663SMark.Logan@Sun.COM * Convert Unicode volume name to current locale multibyte 11739663SMark.Logan@Sun.COM * format. 11749663SMark.Logan@Sun.COM */ 11759663SMark.Logan@Sun.COM vol->vol_name = NULL; 11769663SMark.Logan@Sun.COM if (ntfs_ucstombs(vname, u, &vol->vol_name, 0) == -1) { 11779663SMark.Logan@Sun.COM ntfs_log_perror("Error: Volume name could not be " 11789663SMark.Logan@Sun.COM "converted to current locale"); 11799663SMark.Logan@Sun.COM ntfs_log_debug("Forcing name into ASCII by replacing " 11809663SMark.Logan@Sun.COM "non-ASCII characters with underscores.\n"); 11819663SMark.Logan@Sun.COM vol->vol_name = ntfs_malloc(u + 1); 11829663SMark.Logan@Sun.COM if (!vol->vol_name) { 11839663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 11849663SMark.Logan@Sun.COM goto error_exit; 11859663SMark.Logan@Sun.COM } 11869663SMark.Logan@Sun.COM for (j = 0; j < (s32)u; j++) { 11879663SMark.Logan@Sun.COM u16 uc = le16_to_cpu(vname[j]); 11889663SMark.Logan@Sun.COM if (uc > 0xff) 11899663SMark.Logan@Sun.COM uc = (u16)'_'; 11909663SMark.Logan@Sun.COM vol->vol_name[j] = (char)uc; 11919663SMark.Logan@Sun.COM } 11929663SMark.Logan@Sun.COM vol->vol_name[u] = 0; 11939663SMark.Logan@Sun.COM } 11949663SMark.Logan@Sun.COM } 11959663SMark.Logan@Sun.COM ntfs_log_debug(OK); 11969663SMark.Logan@Sun.COM ntfs_attr_put_search_ctx(ctx); 11979663SMark.Logan@Sun.COM ctx = NULL; 11989663SMark.Logan@Sun.COM /* Now load the attribute definitions from $AttrDef. */ 11999663SMark.Logan@Sun.COM ntfs_log_debug("Loading $AttrDef... "); 12009663SMark.Logan@Sun.COM ni = ntfs_inode_open(vol, FILE_AttrDef); 12019663SMark.Logan@Sun.COM if (!ni) { 12029663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 12039663SMark.Logan@Sun.COM ntfs_log_perror("Failed to open inode"); 12049663SMark.Logan@Sun.COM goto error_exit; 12059663SMark.Logan@Sun.COM } 12069663SMark.Logan@Sun.COM /* Get an ntfs attribute for $AttrDef/$DATA. */ 12079663SMark.Logan@Sun.COM na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); 12089663SMark.Logan@Sun.COM if (!na) { 12099663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 12109663SMark.Logan@Sun.COM ntfs_log_perror("Failed to open ntfs attribute"); 12119663SMark.Logan@Sun.COM goto error_exit; 12129663SMark.Logan@Sun.COM } 12139663SMark.Logan@Sun.COM /* Check we don't overflow 32-bits. */ 12149663SMark.Logan@Sun.COM if (na->data_size > 0xffffffffLL) { 12159663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 12169663SMark.Logan@Sun.COM ntfs_log_debug("Error: Attribute definition table is too big " 12179663SMark.Logan@Sun.COM "(max 32-bit allowed).\n"); 12189663SMark.Logan@Sun.COM errno = EINVAL; 12199663SMark.Logan@Sun.COM goto error_exit; 12209663SMark.Logan@Sun.COM } 12219663SMark.Logan@Sun.COM vol->attrdef_len = na->data_size; 12229663SMark.Logan@Sun.COM vol->attrdef = (ATTR_DEF*)ntfs_malloc(na->data_size); 12239663SMark.Logan@Sun.COM if (!vol->attrdef) { 12249663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 12259663SMark.Logan@Sun.COM goto error_exit; 12269663SMark.Logan@Sun.COM } 12279663SMark.Logan@Sun.COM /* Read in the $DATA attribute value into the buffer. */ 12289663SMark.Logan@Sun.COM l = ntfs_attr_pread(na, 0, na->data_size, vol->attrdef); 12299663SMark.Logan@Sun.COM if (l != na->data_size) { 12309663SMark.Logan@Sun.COM ntfs_log_debug(FAILED); 12319663SMark.Logan@Sun.COM ntfs_log_debug("Amount of data read does not correspond to " 12329663SMark.Logan@Sun.COM "expected length!\n"); 12339663SMark.Logan@Sun.COM errno = EIO; 12349663SMark.Logan@Sun.COM goto error_exit; 12359663SMark.Logan@Sun.COM } 12369663SMark.Logan@Sun.COM /* Done with the $AttrDef mft record. */ 12379663SMark.Logan@Sun.COM ntfs_log_debug(OK); 12389663SMark.Logan@Sun.COM ntfs_attr_close(na); 12399663SMark.Logan@Sun.COM if (ntfs_inode_close(ni)) 12409663SMark.Logan@Sun.COM ntfs_log_perror("Failed to close inode, leaking memory"); 12419663SMark.Logan@Sun.COM /* Initialize number of free clusters and MFT records. */ 12429663SMark.Logan@Sun.COM if (ntfs_volume_get_nr_free_mft_records(vol)) { 12439663SMark.Logan@Sun.COM ntfs_log_perror("Failed to calculate number of free MFTs"); 12449663SMark.Logan@Sun.COM goto error_exit; 12459663SMark.Logan@Sun.COM } 12469663SMark.Logan@Sun.COM if (ntfs_volume_get_nr_free_clusters(vol)) { 12479663SMark.Logan@Sun.COM ntfs_log_perror("Failed to calculate number of free clusters"); 12489663SMark.Logan@Sun.COM goto error_exit; 12499663SMark.Logan@Sun.COM } 12509663SMark.Logan@Sun.COM /* 12519663SMark.Logan@Sun.COM * Check for dirty logfile and hibernated Windows. 12529663SMark.Logan@Sun.COM * We care only about read-write mounts. 12539663SMark.Logan@Sun.COM * 12549663SMark.Logan@Sun.COM * If all is ok, reset the logfile and set the dirty bit on the volume. 12559663SMark.Logan@Sun.COM * 12569663SMark.Logan@Sun.COM * But do not do that if this is a FORENSIC mount. 12579663SMark.Logan@Sun.COM */ 12589663SMark.Logan@Sun.COM if (!(flags & NTFS_MNT_RDONLY)) { 12599663SMark.Logan@Sun.COM if (ntfs_volume_check_hiberfile(vol) < 0) 12609663SMark.Logan@Sun.COM goto error_exit; 12619663SMark.Logan@Sun.COM if (ntfs_volume_check_logfile(vol) < 0) { 12629663SMark.Logan@Sun.COM if (errno != EOPNOTSUPP || !(flags & NTFS_MNT_FORCE)) 12639663SMark.Logan@Sun.COM goto error_exit; 12649663SMark.Logan@Sun.COM ntfs_log_warning("WARNING: $LogFile is not clean, " 12659663SMark.Logan@Sun.COM "forced to continue.\n"); 12669663SMark.Logan@Sun.COM NVolSetWasDirty(vol); /* Leave volume dirty since we 12679663SMark.Logan@Sun.COM empted logfile. */ 12689663SMark.Logan@Sun.COM } 12699663SMark.Logan@Sun.COM if (!NVolForensicMount(vol)) { 12709663SMark.Logan@Sun.COM if (ntfs_logfile_reset(vol) < 0) 12719663SMark.Logan@Sun.COM goto error_exit; 12729663SMark.Logan@Sun.COM if (!(vol->flags & VOLUME_IS_DIRTY)) { 12739663SMark.Logan@Sun.COM vol->flags |= VOLUME_IS_DIRTY; 12749663SMark.Logan@Sun.COM if (ntfs_volume_write_flags(vol, vol->flags) < 12759663SMark.Logan@Sun.COM 0) 12769663SMark.Logan@Sun.COM goto error_exit; 12779663SMark.Logan@Sun.COM } 12789663SMark.Logan@Sun.COM } 12799663SMark.Logan@Sun.COM } 12809663SMark.Logan@Sun.COM return vol; 12819663SMark.Logan@Sun.COM io_error_exit: 12829663SMark.Logan@Sun.COM errno = EIO; 12839663SMark.Logan@Sun.COM error_exit: 12849663SMark.Logan@Sun.COM eo = errno; 12859663SMark.Logan@Sun.COM if (ctx) 12869663SMark.Logan@Sun.COM ntfs_attr_put_search_ctx(ctx); 12879663SMark.Logan@Sun.COM free(m); 12889663SMark.Logan@Sun.COM free(m2); 12899663SMark.Logan@Sun.COM __ntfs_volume_release(vol); 12909663SMark.Logan@Sun.COM errno = eo; 12919663SMark.Logan@Sun.COM return NULL; 12929663SMark.Logan@Sun.COM } 12939663SMark.Logan@Sun.COM 12949663SMark.Logan@Sun.COM /** 12959663SMark.Logan@Sun.COM * ntfs_mount - open ntfs volume 12969663SMark.Logan@Sun.COM * @name: name of device/file to open 12979663SMark.Logan@Sun.COM * @flags: optional mount flags 12989663SMark.Logan@Sun.COM * 12999663SMark.Logan@Sun.COM * This function mounts an ntfs volume. @name should contain the name of the 13009663SMark.Logan@Sun.COM * device/file to mount as the ntfs volume. 13019663SMark.Logan@Sun.COM * 13029663SMark.Logan@Sun.COM * @flags is an optional second parameter. See ntfs_device_mount comment for 13039663SMark.Logan@Sun.COM * description. 13049663SMark.Logan@Sun.COM * 13059663SMark.Logan@Sun.COM * The function opens the device or file @name and verifies that it contains a 13069663SMark.Logan@Sun.COM * valid bootsector. Then, it allocates an ntfs_volume structure and initializes 13079663SMark.Logan@Sun.COM * some of the values inside the structure from the information stored in the 13089663SMark.Logan@Sun.COM * bootsector. It proceeds to load the necessary system files and completes 13099663SMark.Logan@Sun.COM * setting up the structure. 13109663SMark.Logan@Sun.COM * 13119663SMark.Logan@Sun.COM * Return the allocated volume structure on success and NULL on error with 13129663SMark.Logan@Sun.COM * errno set to the error code. 13139663SMark.Logan@Sun.COM * 13149663SMark.Logan@Sun.COM * Note, that a copy is made of @name, and hence it can be discarded as 13159663SMark.Logan@Sun.COM * soon as the function returns. 13169663SMark.Logan@Sun.COM */ 13179663SMark.Logan@Sun.COM ntfs_volume *ntfs_mount(const char *name __attribute__((unused)), 13189663SMark.Logan@Sun.COM ntfs_mount_flags flags __attribute__((unused))) 13199663SMark.Logan@Sun.COM { 13209663SMark.Logan@Sun.COM #ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS 13219663SMark.Logan@Sun.COM struct ntfs_device *dev; 13229663SMark.Logan@Sun.COM ntfs_volume *vol; 13239663SMark.Logan@Sun.COM 13249663SMark.Logan@Sun.COM /* Allocate an ntfs_device structure. */ 13259663SMark.Logan@Sun.COM dev = ntfs_device_alloc(name, 0, &ntfs_device_default_io_ops, NULL); 13269663SMark.Logan@Sun.COM if (!dev) 13279663SMark.Logan@Sun.COM return NULL; 13289663SMark.Logan@Sun.COM /* Call ntfs_device_mount() to do the actual mount. */ 13299663SMark.Logan@Sun.COM vol = ntfs_device_mount(dev, flags); 13309663SMark.Logan@Sun.COM if (!vol) { 13319663SMark.Logan@Sun.COM int eo = errno; 13329663SMark.Logan@Sun.COM ntfs_device_free(dev); 13339663SMark.Logan@Sun.COM errno = eo; 13349663SMark.Logan@Sun.COM } 13359663SMark.Logan@Sun.COM return vol; 13369663SMark.Logan@Sun.COM #else 13379663SMark.Logan@Sun.COM /* 13389663SMark.Logan@Sun.COM * ntfs_mount() makes no sense if NO_NTFS_DEVICE_DEFAULT_IO_OPS is 13399663SMark.Logan@Sun.COM * defined as there are no device operations available in libntfs in 13409663SMark.Logan@Sun.COM * this case. 13419663SMark.Logan@Sun.COM */ 13429663SMark.Logan@Sun.COM errno = EOPNOTSUPP; 13439663SMark.Logan@Sun.COM return NULL; 13449663SMark.Logan@Sun.COM #endif 13459663SMark.Logan@Sun.COM } 13469663SMark.Logan@Sun.COM 13479663SMark.Logan@Sun.COM /** 13489663SMark.Logan@Sun.COM * ntfs_device_umount - close ntfs volume 13499663SMark.Logan@Sun.COM * @vol: address of ntfs_volume structure of volume to close 13509663SMark.Logan@Sun.COM * @force: if true force close the volume even if it is busy 13519663SMark.Logan@Sun.COM * 13529663SMark.Logan@Sun.COM * Deallocate all structures (including @vol itself) associated with the ntfs 13539663SMark.Logan@Sun.COM * volume @vol. 13549663SMark.Logan@Sun.COM * 13559663SMark.Logan@Sun.COM * Note it is up to the caller to destroy the device associated with the volume 13569663SMark.Logan@Sun.COM * being unmounted after this function returns. 13579663SMark.Logan@Sun.COM * 13589663SMark.Logan@Sun.COM * Return 0 on success. On error return -1 with errno set appropriately 13599663SMark.Logan@Sun.COM * (most likely to one of EAGAIN, EBUSY or EINVAL). The EAGAIN error means that 13609663SMark.Logan@Sun.COM * an operation is in progress and if you try the close later the operation 13619663SMark.Logan@Sun.COM * might be completed and the close succeed. 13629663SMark.Logan@Sun.COM * 13639663SMark.Logan@Sun.COM * If @force is true (i.e. not zero) this function will close the volume even 13649663SMark.Logan@Sun.COM * if this means that data might be lost. 13659663SMark.Logan@Sun.COM * 13669663SMark.Logan@Sun.COM * @vol must have previously been returned by a call to ntfs_device_mount(). 13679663SMark.Logan@Sun.COM * 13689663SMark.Logan@Sun.COM * @vol itself is deallocated and should no longer be dereferenced after this 13699663SMark.Logan@Sun.COM * function returns success. If it returns an error then nothing has been done 13709663SMark.Logan@Sun.COM * so it is safe to continue using @vol. 13719663SMark.Logan@Sun.COM */ 13729663SMark.Logan@Sun.COM int ntfs_device_umount(ntfs_volume *vol, 13739663SMark.Logan@Sun.COM const BOOL force __attribute__((unused))) 13749663SMark.Logan@Sun.COM { 13759663SMark.Logan@Sun.COM if (!vol) { 13769663SMark.Logan@Sun.COM errno = EINVAL; 13779663SMark.Logan@Sun.COM return -1; 13789663SMark.Logan@Sun.COM } 13799663SMark.Logan@Sun.COM __ntfs_volume_release(vol); 13809663SMark.Logan@Sun.COM return 0; 13819663SMark.Logan@Sun.COM } 13829663SMark.Logan@Sun.COM 13839663SMark.Logan@Sun.COM /** 13849663SMark.Logan@Sun.COM * ntfs_umount - close ntfs volume 13859663SMark.Logan@Sun.COM * @vol: address of ntfs_volume structure of volume to close 13869663SMark.Logan@Sun.COM * @force: if true force close the volume even if it is busy 13879663SMark.Logan@Sun.COM * 13889663SMark.Logan@Sun.COM * Deallocate all structures (including @vol itself) associated with the ntfs 13899663SMark.Logan@Sun.COM * volume @vol. 13909663SMark.Logan@Sun.COM * 13919663SMark.Logan@Sun.COM * Return 0 on success. On error return -1 with errno set appropriately 13929663SMark.Logan@Sun.COM * (most likely to one of EAGAIN, EBUSY or EINVAL). The EAGAIN error means that 13939663SMark.Logan@Sun.COM * an operation is in progress and if you try the close later the operation 13949663SMark.Logan@Sun.COM * might be completed and the close succeed. 13959663SMark.Logan@Sun.COM * 13969663SMark.Logan@Sun.COM * If @force is true (i.e. not zero) this function will close the volume even 13979663SMark.Logan@Sun.COM * if this means that data might be lost. 13989663SMark.Logan@Sun.COM * 13999663SMark.Logan@Sun.COM * @vol must have previously been returned by a call to ntfs_mount(). 14009663SMark.Logan@Sun.COM * 14019663SMark.Logan@Sun.COM * @vol itself is deallocated and should no longer be dereferenced after this 14029663SMark.Logan@Sun.COM * function returns success. If it returns an error then nothing has been done 14039663SMark.Logan@Sun.COM * so it is safe to continue using @vol. 14049663SMark.Logan@Sun.COM */ 14059663SMark.Logan@Sun.COM int ntfs_umount(ntfs_volume *vol, 14069663SMark.Logan@Sun.COM const BOOL force __attribute__((unused))) 14079663SMark.Logan@Sun.COM { 14089663SMark.Logan@Sun.COM struct ntfs_device *dev; 14099663SMark.Logan@Sun.COM 14109663SMark.Logan@Sun.COM if (!vol) { 14119663SMark.Logan@Sun.COM errno = EINVAL; 14129663SMark.Logan@Sun.COM return -1; 14139663SMark.Logan@Sun.COM } 14149663SMark.Logan@Sun.COM dev = vol->u.dev; 14159663SMark.Logan@Sun.COM __ntfs_volume_release(vol); 14169663SMark.Logan@Sun.COM ntfs_device_free(dev); 14179663SMark.Logan@Sun.COM return 0; 14189663SMark.Logan@Sun.COM } 14199663SMark.Logan@Sun.COM 14209663SMark.Logan@Sun.COM #ifdef HAVE_MNTENT_H 14219663SMark.Logan@Sun.COM 14229663SMark.Logan@Sun.COM #ifndef HAVE_REALPATH 14239663SMark.Logan@Sun.COM /** 14249663SMark.Logan@Sun.COM * realpath - If there is no realpath on the system 14259663SMark.Logan@Sun.COM */ 14269663SMark.Logan@Sun.COM static char *realpath(const char *path, char *resolved_path) 14279663SMark.Logan@Sun.COM { 14289663SMark.Logan@Sun.COM strncpy(resolved_path, path, PATH_MAX); 14299663SMark.Logan@Sun.COM resolved_path[PATH_MAX] = '\0'; 14309663SMark.Logan@Sun.COM return resolved_path; 14319663SMark.Logan@Sun.COM } 14329663SMark.Logan@Sun.COM #endif 14339663SMark.Logan@Sun.COM 14349663SMark.Logan@Sun.COM /** 14359663SMark.Logan@Sun.COM * ntfs_mntent_check - desc 14369663SMark.Logan@Sun.COM * 14379663SMark.Logan@Sun.COM * If you are wanting to use this, you actually wanted to use 14389663SMark.Logan@Sun.COM * ntfs_check_if_mounted(), you just didn't realize. (-: 14399663SMark.Logan@Sun.COM * 14409663SMark.Logan@Sun.COM * See description of ntfs_check_if_mounted(), below. 14419663SMark.Logan@Sun.COM */ 14429663SMark.Logan@Sun.COM static int ntfs_mntent_check(const char *file, unsigned long *mnt_flags) 14439663SMark.Logan@Sun.COM { 14449663SMark.Logan@Sun.COM struct mntent *mnt; 14459663SMark.Logan@Sun.COM char *real_file = NULL, *real_fsname = NULL; 14469663SMark.Logan@Sun.COM FILE *f; 14479663SMark.Logan@Sun.COM int err = 0; 14489663SMark.Logan@Sun.COM 14499663SMark.Logan@Sun.COM real_file = ntfs_malloc(PATH_MAX + 1); 14509663SMark.Logan@Sun.COM if (!real_file) 14519663SMark.Logan@Sun.COM return -1; 14529663SMark.Logan@Sun.COM real_fsname = ntfs_malloc(PATH_MAX + 1); 14539663SMark.Logan@Sun.COM if (!real_fsname) { 14549663SMark.Logan@Sun.COM err = errno; 14559663SMark.Logan@Sun.COM goto exit; 14569663SMark.Logan@Sun.COM } 14579663SMark.Logan@Sun.COM if (!realpath(file, real_file)) { 14589663SMark.Logan@Sun.COM err = errno; 14599663SMark.Logan@Sun.COM goto exit; 14609663SMark.Logan@Sun.COM } 14619663SMark.Logan@Sun.COM if (!(f = setmntent(MOUNTED, "r"))) { 14629663SMark.Logan@Sun.COM err = errno; 14639663SMark.Logan@Sun.COM goto exit; 14649663SMark.Logan@Sun.COM } 14659663SMark.Logan@Sun.COM while ((mnt = getmntent(f))) { 14669663SMark.Logan@Sun.COM if (!realpath(mnt->mnt_fsname, real_fsname)) 14679663SMark.Logan@Sun.COM continue; 14689663SMark.Logan@Sun.COM if (!strcmp(real_file, real_fsname)) 14699663SMark.Logan@Sun.COM break; 14709663SMark.Logan@Sun.COM } 14719663SMark.Logan@Sun.COM endmntent(f); 14729663SMark.Logan@Sun.COM if (!mnt) 14739663SMark.Logan@Sun.COM goto exit; 14749663SMark.Logan@Sun.COM *mnt_flags = NTFS_MF_MOUNTED; 14759663SMark.Logan@Sun.COM if (!strcmp(mnt->mnt_dir, "/")) 14769663SMark.Logan@Sun.COM *mnt_flags |= NTFS_MF_ISROOT; 14779663SMark.Logan@Sun.COM #ifdef HAVE_HASMNTOPT 14789663SMark.Logan@Sun.COM if (hasmntopt(mnt, "ro") && !hasmntopt(mnt, "rw")) 14799663SMark.Logan@Sun.COM *mnt_flags |= NTFS_MF_READONLY; 14809663SMark.Logan@Sun.COM #endif 14819663SMark.Logan@Sun.COM exit: 14829663SMark.Logan@Sun.COM free(real_file); 14839663SMark.Logan@Sun.COM free(real_fsname); 14849663SMark.Logan@Sun.COM if (err) { 14859663SMark.Logan@Sun.COM errno = err; 14869663SMark.Logan@Sun.COM return -1; 14879663SMark.Logan@Sun.COM } 14889663SMark.Logan@Sun.COM return 0; 14899663SMark.Logan@Sun.COM } 14909663SMark.Logan@Sun.COM #endif /* HAVE_MNTENT_H */ 14919663SMark.Logan@Sun.COM 14929663SMark.Logan@Sun.COM /** 14939663SMark.Logan@Sun.COM * ntfs_check_if_mounted - check if an ntfs volume is currently mounted 14949663SMark.Logan@Sun.COM * @file: device file to check 14959663SMark.Logan@Sun.COM * @mnt_flags: pointer into which to return the ntfs mount flags (see volume.h) 14969663SMark.Logan@Sun.COM * 14979663SMark.Logan@Sun.COM * If the running system does not support the {set,get,end}mntent() calls, 14989663SMark.Logan@Sun.COM * just return 0 and set *@mnt_flags to zero. 14999663SMark.Logan@Sun.COM * 15009663SMark.Logan@Sun.COM * When the system does support the calls, ntfs_check_if_mounted() first tries 15019663SMark.Logan@Sun.COM * to find the device @file in /etc/mtab (or wherever this is kept on the 15029663SMark.Logan@Sun.COM * running system). If it is not found, assume the device is not mounted and 15039663SMark.Logan@Sun.COM * return 0 and set *@mnt_flags to zero. 15049663SMark.Logan@Sun.COM * 15059663SMark.Logan@Sun.COM * If the device @file is found, set the NTFS_MF_MOUNTED flags in *@mnt_flags. 15069663SMark.Logan@Sun.COM * 15079663SMark.Logan@Sun.COM * Further if @file is mounted as the file system root ("/"), set the flag 15089663SMark.Logan@Sun.COM * NTFS_MF_ISROOT in *@mnt_flags. 15099663SMark.Logan@Sun.COM * 15109663SMark.Logan@Sun.COM * Finally, check if the file system is mounted read-only, and if so set the 15119663SMark.Logan@Sun.COM * NTFS_MF_READONLY flag in *@mnt_flags. 15129663SMark.Logan@Sun.COM * 15139663SMark.Logan@Sun.COM * On success return 0 with *@mnt_flags set to the ntfs mount flags. 15149663SMark.Logan@Sun.COM * 15159663SMark.Logan@Sun.COM * On error return -1 with errno set to the error code. 15169663SMark.Logan@Sun.COM */ 15179663SMark.Logan@Sun.COM int ntfs_check_if_mounted(const char *file __attribute__((unused)), 15189663SMark.Logan@Sun.COM unsigned long *mnt_flags) 15199663SMark.Logan@Sun.COM { 15209663SMark.Logan@Sun.COM *mnt_flags = 0; 15219663SMark.Logan@Sun.COM #ifdef HAVE_MNTENT_H 15229663SMark.Logan@Sun.COM return ntfs_mntent_check(file, mnt_flags); 15239663SMark.Logan@Sun.COM #else 15249663SMark.Logan@Sun.COM return 0; 15259663SMark.Logan@Sun.COM #endif 15269663SMark.Logan@Sun.COM } 15279663SMark.Logan@Sun.COM 15289663SMark.Logan@Sun.COM /** 15299663SMark.Logan@Sun.COM * ntfs_version_is_supported - check if NTFS version is supported. 15309663SMark.Logan@Sun.COM * @vol: ntfs volume whose version we're interested in. 15319663SMark.Logan@Sun.COM * 15329663SMark.Logan@Sun.COM * The function checks if the NTFS volume version is known or not. 15339663SMark.Logan@Sun.COM * Version 1.1 and 1.2 are used by Windows NT3.x and NT4. 15349663SMark.Logan@Sun.COM * Version 2.x is used by Windows 2000 Betas. 15359663SMark.Logan@Sun.COM * Version 3.0 is used by Windows 2000. 15369663SMark.Logan@Sun.COM * Version 3.1 is used by Windows XP, Windows Server 2003 and Vista. 15379663SMark.Logan@Sun.COM * 15389663SMark.Logan@Sun.COM * Return 0 if NTFS version is supported otherwise -1 with errno set. 15399663SMark.Logan@Sun.COM * 15409663SMark.Logan@Sun.COM * The following error codes are defined: 15419663SMark.Logan@Sun.COM * EOPNOTSUPP - Unknown NTFS version 15429663SMark.Logan@Sun.COM * EINVAL - Invalid argument 15439663SMark.Logan@Sun.COM */ 15449663SMark.Logan@Sun.COM int ntfs_version_is_supported(ntfs_volume *vol) 15459663SMark.Logan@Sun.COM { 15469663SMark.Logan@Sun.COM u8 major, minor; 15479663SMark.Logan@Sun.COM 15489663SMark.Logan@Sun.COM if (!vol) { 15499663SMark.Logan@Sun.COM errno = EINVAL; 15509663SMark.Logan@Sun.COM return -1; 15519663SMark.Logan@Sun.COM } 15529663SMark.Logan@Sun.COM 15539663SMark.Logan@Sun.COM major = vol->major_ver; 15549663SMark.Logan@Sun.COM minor = vol->minor_ver; 15559663SMark.Logan@Sun.COM 15569663SMark.Logan@Sun.COM if (NTFS_V1_1(major, minor) || NTFS_V1_2(major, minor)) 15579663SMark.Logan@Sun.COM return 0; 15589663SMark.Logan@Sun.COM 15599663SMark.Logan@Sun.COM if (NTFS_V2_X(major, minor)) 15609663SMark.Logan@Sun.COM return 0; 15619663SMark.Logan@Sun.COM 15629663SMark.Logan@Sun.COM if (NTFS_V3_0(major, minor) || NTFS_V3_1(major, minor)) 15639663SMark.Logan@Sun.COM return 0; 15649663SMark.Logan@Sun.COM 15659663SMark.Logan@Sun.COM errno = EOPNOTSUPP; 15669663SMark.Logan@Sun.COM return -1; 15679663SMark.Logan@Sun.COM } 15689663SMark.Logan@Sun.COM 15699663SMark.Logan@Sun.COM /** 15709663SMark.Logan@Sun.COM * ntfs_logfile_reset - "empty" $LogFile data attribute value 15719663SMark.Logan@Sun.COM * @vol: ntfs volume whose $LogFile we intend to reset. 15729663SMark.Logan@Sun.COM * 15739663SMark.Logan@Sun.COM * Fill the value of the $LogFile data attribute, i.e. the contents of 15749663SMark.Logan@Sun.COM * the file, with 0xff's, thus marking the journal as empty. 15759663SMark.Logan@Sun.COM * 15769663SMark.Logan@Sun.COM * FIXME(?): We might need to zero the LSN field of every single mft 15779663SMark.Logan@Sun.COM * record as well. (But, first try without doing that and see what 15789663SMark.Logan@Sun.COM * happens, since chkdsk might pickup the pieces and do it for us...) 15799663SMark.Logan@Sun.COM * 15809663SMark.Logan@Sun.COM * On success return 0. 15819663SMark.Logan@Sun.COM * 15829663SMark.Logan@Sun.COM * On error return -1 with errno set to the error code. 15839663SMark.Logan@Sun.COM */ 15849663SMark.Logan@Sun.COM int ntfs_logfile_reset(ntfs_volume *vol) 15859663SMark.Logan@Sun.COM { 15869663SMark.Logan@Sun.COM ntfs_inode *ni; 15879663SMark.Logan@Sun.COM ntfs_attr *na; 15889663SMark.Logan@Sun.COM int eo; 15899663SMark.Logan@Sun.COM 15909663SMark.Logan@Sun.COM if (!vol) { 15919663SMark.Logan@Sun.COM errno = EINVAL; 15929663SMark.Logan@Sun.COM return -1; 15939663SMark.Logan@Sun.COM } 15949663SMark.Logan@Sun.COM 15959663SMark.Logan@Sun.COM if ((ni = ntfs_inode_open(vol, FILE_LogFile)) == NULL) { 15969663SMark.Logan@Sun.COM ntfs_log_perror("Failed to open inode FILE_LogFile."); 15979663SMark.Logan@Sun.COM return -1; 15989663SMark.Logan@Sun.COM } 15999663SMark.Logan@Sun.COM 16009663SMark.Logan@Sun.COM if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) { 16019663SMark.Logan@Sun.COM eo = errno; 16029663SMark.Logan@Sun.COM ntfs_log_perror("Failed to open $FILE_LogFile/$DATA"); 16039663SMark.Logan@Sun.COM goto error_exit; 16049663SMark.Logan@Sun.COM } 16059663SMark.Logan@Sun.COM 16069663SMark.Logan@Sun.COM if (ntfs_empty_logfile(na)) { 16079663SMark.Logan@Sun.COM eo = errno; 16089663SMark.Logan@Sun.COM ntfs_log_perror("Failed to empty $FILE_LogFile/$DATA"); 16099663SMark.Logan@Sun.COM ntfs_attr_close(na); 16109663SMark.Logan@Sun.COM goto error_exit; 16119663SMark.Logan@Sun.COM } 16129663SMark.Logan@Sun.COM ntfs_attr_close(na); 16139663SMark.Logan@Sun.COM return ntfs_inode_close(ni); 16149663SMark.Logan@Sun.COM 16159663SMark.Logan@Sun.COM error_exit: 16169663SMark.Logan@Sun.COM ntfs_inode_close(ni); 16179663SMark.Logan@Sun.COM errno = eo; 16189663SMark.Logan@Sun.COM return -1; 16199663SMark.Logan@Sun.COM } 16209663SMark.Logan@Sun.COM 16219663SMark.Logan@Sun.COM /** 16229663SMark.Logan@Sun.COM * ntfs_volume_write_flags - set the flags of an ntfs volume 16239663SMark.Logan@Sun.COM * @vol: ntfs volume where we set the volume flags 16249663SMark.Logan@Sun.COM * @flags: new flags 16259663SMark.Logan@Sun.COM * 16269663SMark.Logan@Sun.COM * Set the on-disk volume flags in the mft record of $Volume and 16279663SMark.Logan@Sun.COM * on volume @vol to @flags. 16289663SMark.Logan@Sun.COM * 16299663SMark.Logan@Sun.COM * Return 0 if successful and -1 if not with errno set to the error code. 16309663SMark.Logan@Sun.COM */ 16319663SMark.Logan@Sun.COM int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags) 16329663SMark.Logan@Sun.COM { 16339663SMark.Logan@Sun.COM ATTR_RECORD *a; 16349663SMark.Logan@Sun.COM VOLUME_INFORMATION *c; 16359663SMark.Logan@Sun.COM ntfs_attr_search_ctx *ctx; 16369663SMark.Logan@Sun.COM int ret = -1; /* failure */ 16379663SMark.Logan@Sun.COM 16389663SMark.Logan@Sun.COM if (!vol || !vol->vol_ni) { 16399663SMark.Logan@Sun.COM errno = EINVAL; 16409663SMark.Logan@Sun.COM return -1; 16419663SMark.Logan@Sun.COM } 16429663SMark.Logan@Sun.COM /* Get a pointer to the volume information attribute. */ 16439663SMark.Logan@Sun.COM ctx = ntfs_attr_get_search_ctx(vol->vol_ni, NULL); 16449663SMark.Logan@Sun.COM if (!ctx) { 16459663SMark.Logan@Sun.COM ntfs_log_perror("Failed to allocate attribute search context"); 16469663SMark.Logan@Sun.COM return -1; 16479663SMark.Logan@Sun.COM } 16489663SMark.Logan@Sun.COM if (ntfs_attr_lookup(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, 16499663SMark.Logan@Sun.COM 0, ctx)) { 16509663SMark.Logan@Sun.COM ntfs_log_error("Attribute $VOLUME_INFORMATION was not found " 16519663SMark.Logan@Sun.COM "in $Volume!\n"); 16529663SMark.Logan@Sun.COM goto err_out; 16539663SMark.Logan@Sun.COM } 16549663SMark.Logan@Sun.COM a = ctx->attr; 16559663SMark.Logan@Sun.COM /* Sanity check. */ 16569663SMark.Logan@Sun.COM if (a->non_resident) { 16579663SMark.Logan@Sun.COM ntfs_log_error("Attribute $VOLUME_INFORMATION must be " 16589663SMark.Logan@Sun.COM "resident (and it isn't)!\n"); 16599663SMark.Logan@Sun.COM errno = EIO; 16609663SMark.Logan@Sun.COM goto err_out; 16619663SMark.Logan@Sun.COM } 16629663SMark.Logan@Sun.COM /* Get a pointer to the value of the attribute. */ 16639663SMark.Logan@Sun.COM c = (VOLUME_INFORMATION*)(le16_to_cpu(a->u.res.value_offset) + (char*)a); 16649663SMark.Logan@Sun.COM /* Sanity checks. */ 16659663SMark.Logan@Sun.COM if ((char*)c + le32_to_cpu(a->u.res.value_length) > (char*)ctx->mrec + 16669663SMark.Logan@Sun.COM le32_to_cpu(ctx->mrec->bytes_in_use) || 16679663SMark.Logan@Sun.COM le16_to_cpu(a->u.res.value_offset) + 16689663SMark.Logan@Sun.COM le32_to_cpu(a->u.res.value_length) > le32_to_cpu(a->length)) { 16699663SMark.Logan@Sun.COM ntfs_log_error("Attribute $VOLUME_INFORMATION in $Volume is " 16709663SMark.Logan@Sun.COM "corrupt!\n"); 16719663SMark.Logan@Sun.COM errno = EIO; 16729663SMark.Logan@Sun.COM goto err_out; 16739663SMark.Logan@Sun.COM } 16749663SMark.Logan@Sun.COM /* Set the volume flags. */ 16759663SMark.Logan@Sun.COM vol->flags = c->flags = flags & VOLUME_FLAGS_MASK; 16769663SMark.Logan@Sun.COM /* Write them to disk. */ 16779663SMark.Logan@Sun.COM ntfs_inode_mark_dirty(vol->vol_ni); 16789663SMark.Logan@Sun.COM if (ntfs_inode_sync(vol->vol_ni)) { 16799663SMark.Logan@Sun.COM ntfs_log_perror("Error writing $Volume"); 16809663SMark.Logan@Sun.COM goto err_out; 16819663SMark.Logan@Sun.COM } 16829663SMark.Logan@Sun.COM ret = 0; /* success */ 16839663SMark.Logan@Sun.COM err_out: 16849663SMark.Logan@Sun.COM ntfs_attr_put_search_ctx(ctx); 16859663SMark.Logan@Sun.COM if (ret) 16869663SMark.Logan@Sun.COM ntfs_log_error("%s(): Failed.\n", "ntfs_volume_write_flags"); 16879663SMark.Logan@Sun.COM return ret; 16889663SMark.Logan@Sun.COM } 16899663SMark.Logan@Sun.COM 1690