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 */
ntfs_volume_alloc(void)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 */
__ntfs_volume_release(ntfs_volume * v)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 */
ntfs_mft_load(ntfs_volume * vol)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 */
ntfs_mftmirr_load(ntfs_volume * vol)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 */
ntfs_volume_startup(struct ntfs_device * dev,ntfs_mount_flags flags)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 */
ntfs_volume_check_logfile(ntfs_volume * vol)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 */
ntfs_hiberfile_open(ntfs_volume * vol)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 */
ntfs_volume_check_hiberfile(ntfs_volume * vol)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 */
ntfs_volume_get_nr_free_mft_records(ntfs_volume * vol)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 */
ntfs_volume_get_nr_free_clusters(ntfs_volume * vol)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 */
ntfs_device_mount(struct ntfs_device * dev,ntfs_mount_flags flags)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 */
ntfs_mount(const char * name,ntfs_mount_flags flags)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 */
ntfs_device_umount(ntfs_volume * vol,const BOOL force)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 */
ntfs_umount(ntfs_volume * vol,const BOOL force)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 */
realpath(const char * path,char * resolved_path)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 */
ntfs_mntent_check(const char * file,unsigned long * mnt_flags)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 */
ntfs_check_if_mounted(const char * file,unsigned long * mnt_flags)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 */
ntfs_version_is_supported(ntfs_volume * vol)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 */
ntfs_logfile_reset(ntfs_volume * vol)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 */
ntfs_volume_write_flags(ntfs_volume * vol,const le16 flags)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