From b47fe4d7adfe05ef006283669dc6f4111bd917c5 Mon Sep 17 00:00:00 2001 From: !flatcap Date: Thu, 22 Aug 2002 18:09:47 +0000 Subject: [PATCH] AT_NONAME -> AT_UNNAMED 2002/07/14 17:21:32-00:00 !szaka Move ntfs_set_volume_flags() from attrib[ch] to volume.[ch] 2002/07/14 13:18:01-00:00 !szaka ntfs_reset_logfile(): delete a NOOP 2002/07/14 12:34:44-00:00 !szaka ntfs_reset_logfile(): free resources in error, handle partial I/O 2002/07/12 12:38:54-00:00 !szaka Rewrote $LogFile reset using new inode API and moved it from ntfsfix to libntfs as ntfs_reset_logfile(). ntfsresize also resets log file. 2002/07/12 08:18:15-00:00 !antona sorry flatcap. - remove a lot of reorganization which rips the code into incomprehensible order. - all functions were and are again now placed together by logicnot by sheer randomness. - functions which call each other are placed together for example... 2002/07/12 07:10:44-00:00 !szaka NTFS_V* version macros and ntfs_is_version_supported() added. Modified ntfsfix and ntfsresize to use them. 2002/07/11 23:44:13-00:00 !flatcap new function headers and a few function moves 2002/07/11 16:20:34-00:00 !flatcap whitespace and include guards 2002/07/09 19:17:49-00:00 !flatcap move the runlist functions from attrib.c to runlist.c 2002/07/09 01:01:40-00:00 !flatcap more noname changes 2002/07/08 23:27:16-00:00 !flatcap added AT_NONAME so we can search for a (un)named attribute or just iterate through all attributes 2002/07/02 23:47:10-00:00 !antona Global replacement of __[su]{8,16,32,64} with [su]{8,16,32,64} and layout.h define it. 2002/06/07 01:16:20-00:00 !antona Updates 2002/06/02 23:02:20-00:00 !antona More fixes and updates. 2002/06/02 13:57:59-00:00 !antona Fix detection of read-only mounts in volume.c::ntfs_check_mnteent(). 2002/06/01 00:41:45-00:00 !antona huge update! 2002/04/29 01:53:55-00:00 !antona Loads of stuff. Improvements, start on attr pread and attr mst_pread. Write to follow. ntfslabel cleanup and extensions. libntfs cleanups, fixes, etc. 2002/04/27 19:49:09-00:00 !antona Update library, new APIs ntfs_attr_find_vcn(), misc fixes and cleanups, make all the utilities compile, fix bugs I noticed in ntfslabel and it now works properly. 2002/04/25 08:22:14-00:00 !antona Can't type.. 2002/04/25 08:19:32-00:00 !antona Improve function description. 2002/04/23 23:27:33-00:00 !antona Fixup the force option in mkntfs.c. Change the ntfs_check_if_mounted so it works on system not implementing {set,get,end}mntent, too. Also make it more powerful in telling us not only if something is mounted but also if it is the fs root and if it is read-only. 2002/04/23 08:37:07-00:00 !antona Fix minor buglet in volume.c and tidy up white space. 2002/04/23 08:29:13-00:00 !mattjf Minor change to ntfs_check_if_mounted() description. 2002/04/23 08:25:41-00:00 !mattjf Code cleanup. Removed check_mount() and added a generic ntfs_check_if_mounted() function to the library Better error checking 2002/04/22 10:34:31-00:00 !antona Attribute list support (merging done, part 2, some stuff still incomplete). mkntfs ntfs volume creation. See the changelog... 2002/04/20 23:09:43-00:00 !antona Port attribute lookup functions with attribute list support from ntfs tng driver. Port/reimplement extent mft record handling code as well. Rename out all dollar signs from type names and constants. Adapt all callers to new API. Note mkntfs is currently broken due to some needed work. 2002/04/20 01:53:02-00:00 !antona Rename mft code adding ntfs_ prefix. Change all return values to zero on success. Thanks to mattjf for pointing out the inconsistencies. 2002/04/19 21:09:55-00:00 !antona Finished provisional inode.c::ntfs_{open,close}_inode() functions. Also, started defining API provided by attrib.[ch], so far only done search context related stuff. 2002/04/16 20:56:01-00:00 !antona Add new API unistr.[ch]: ntfs_ucstombs() and make ntfslabel use it 2002/04/16 15:34:32-00:00 !antona Fix the library... 2002/04/16 12:13:53-00:00 !antona New API function mft.[ch]::read_file_record(). Also some cleanups. 2002/04/15 20:04:28-00:00 !antona Fix all compiler warnings that came up with -Wall. Enabled -Wall for ./configure --enable-debug everywhere. Fix a few bugs in mkntfs that came up in the warnings (just error code paths, nothing major). 2002/04/15 19:02:41-00:00 !antona Really fix the library and mkntfs while at it. 2002/04/15 18:54:07-00:00 !antona Update library for the new API. 2002/04/15 00:42:08-00:00 !antona Big rewrite of disk_io.c. Now should have stable API for low level disk access. Move all mft record related stuff from disk_io.c to mft.c. 2002/04/14 14:08:30-00:00 !antona Cleanup library code. Throw away unused stuff. 2002/04/12 15:23:47-00:00 !antona Cleanup ntfslabel, write a man page for it, integrate it all in the distribution properly, silence output from ntfs_mount() (conditional on running configure with --enable-debug), update all docs accordingly. Add Rich and Matt to AUTHORS. 2002/04/11 23:10:59-00:00 !flatcap Matt Fanto's changes to retrieve the volume name and a utility to print it 2001/12/06 01:14:52-00:00 !antona Added mount flags to ntfs_mount and adapted utilities to new mount syntax. 2001/06/10 15:54:20-00:00 !antona Linux-NTFS 0.1.0-pre1 ===================== -fixed up ntfsfix and ntfsdump_logfile -corrected stuff -several bug fixes -fixed (hopefully) final bug with mkntfs (sd generator was wrong due to brain'o) -mkntfs now completed, only need to add a few command line options before first public release. -rpm generation file updated and autostrip modified to make use of install-stip make target instead of stripping manually -made bootsector check verbosity during mount dependent on --enable-debug configure option. 2001/06/01 02:07:26-00:00 !antona It has been a long time since last commit. At moment have done a lot of work on mkntfs but also at the moment ntfsfix and ntfsdump_logfile and libntfs are broken. Basically only mkntfs works and that is not complete either. 2001/04/11 14:29:12-00:00 !antona Almost fixed compilaton. Remember to declare a struct type with struct NAME { declaration }; rather than the bogus struct { declaration NAME }; then can use typedef NAME othername; on a forward declaration of NAME (struct NAME;) and all is fine... 2001/04/11 11:49:16-00:00 !antona Header file reorganisation so that it compiles. 2001/04/10 23:37:19-00:00 !antona Ok, ntfsd was a mistake for userspace. It increases complexity no end while not giving us much functionality. Lets get it working and then worry about the kernel. - As it was the idea originally anyway, so this is just a return on the right track. (-8 We keep the timer and signal handler but the only thing we do is to set a bool flag (ntfs_need_sync) and we will just check this in appropriate places and if it is true we call ntfs_sync_volumes() which sets it back to false. This means no more locking at all of any description and no need to worry about the signal handler interrupting things in bad ways and/or at bad times in the main code. 2001/04/09 00:05:37-00:00 !antona More or less finished file handling. (Probably some useful functions are still missing but they will be implemented as need arises.) One thing that is stupid at the moment is we don't limit the amount of cached mft_records so if you were to load loads the machine would eventually run out of memory... Can't happen with files as they are limited to 1000, unless you are short of memory. (Hard limit at the moment, set in ntfs_mount(). Maybe ntfsd should be monitoring memory usage and be throwing out unused cache entries and closed_files? That would mean to have locking everywhere, though.) Still missing: - Convert old code to use new stuff. - Add non-resident attributes somewhere. Either into the mft_entry structure or into the ntfs_file structure, but which? At the moment I tend to mft_entry so they can be synced together with the entries by ntfsd. 2001/04/08 03:02:55-00:00 !antona Added cvs Id header. 2001/04/08 01:58:29-00:00 !antona User space conversion of locking complete. I settled for using simple spinlocks and atomic variables and instead of deadlocking/livelocking when using spin_lock(), use spin_trylock() in a while letting go of the cpu between each call and making a maximum of 100 iterations (or we return EDEADLK error code). This is not the most efficient way, especially as can't have multiple readers but it is the simplest way to go about things. Should now have (almost) all required helper functions for dealing with mft entries implemented. Now need the file handling and then convert the whole project to use the new code and then can finally get back to work on attribute searching... 2001/04/05 20:14:45-00:00 !antona Commit of current state of development including locking a la kernel. This doesn't work on user space (semaphores don't work). Just want to have it committed. Will take out locking / modify it where necessary to use pthreads ASAP. 2001/04/02 02:04:37-00:00 !antona Everything compiles again! Yey! (Don't know about working though, haven't tried it... So be careful...) The definitely final find_{first_}attr() functions are in place. Currently still no support for attribute lists. The two new _RE files contain the C-fied and more or less (more less than more actually) cleaned up functions from the ntfs driver. Once they are cleaned up (find_attr() is already completed but I left it in the _RE files for future reference/educational value) and modified to suit my ideas of how they should work, which are not quite the same as the driver way, they will make it into attrib.[ch]. If anyone gives the new code a try, I would be interested in whether it worked or not... (-; 2001/03/31 15:27:34-00:00 !antona Added new name comparison function. 2001/03/06 02:10:55-00:00 !antona Allow variable length upcase table. Progressing on find_next_attr(). 2001/03/05 03:04:40-00:00 !antona Corresponding changes to the library. 2001/03/02 15:05:54-00:00 !antona Commit latest library state. !!!NOTE!!! This breaks everything!!! I'm in the middle of rewritting the find_attribute stuff in attrib.c at the moment. 2001/01/30 12:55:21-00:00 !antona Fixed the compilation issues. 2001/01/25 22:25:43-00:00 !antona More files added to ntfs lib. Fixed some consistency problems. 2001/01/25 14:00:43-00:00 !antona Added volume.c code and fixed some typos and inconsistencies. (Logical change 1.5) --- libntfs/volume.c | 1102 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1102 insertions(+) diff --git a/libntfs/volume.c b/libntfs/volume.c index e69de29b..07f68f4e 100644 --- a/libntfs/volume.c +++ b/libntfs/volume.c @@ -0,0 +1,1102 @@ +/* + * $Id$ + * + * volume.c - NTFS volume handling code. Part of the Linux-NTFS project. + * + * Copyright (c) 2000-2002 Anton Altaparmakov. + * + * This program/include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the Linux-NTFS + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "volume.h" +#include "attrib.h" +#include "mft.h" +#include "bootsect.h" +#include "disk_io.h" +#include "debug.h" +#include "inode.h" +#include "runlist.h" + +/** + * Internal: + * + * __allocate_ntfs_volume - + * + */ +static ntfs_volume *__allocate_ntfs_volume(void) +{ + return (ntfs_volume*)calloc(1, sizeof(ntfs_volume)); +} + +/** + * Internal: + * + * __release_ntfs_volume - + * + */ +static void __release_ntfs_volume(ntfs_volume *v) +{ + if (v->fd) + close(v->fd); + if (v->dev_name) + free(v->dev_name); + if (v->vol_name) + free(v->vol_name); + if (v->lcnbmp_na) + ntfs_attr_close(v->lcnbmp_na); + if (v->lcnbmp_ni) + ntfs_close_inode(v->lcnbmp_ni); + if (v->mftbmp_na) + ntfs_attr_close(v->mftbmp_na); + if (v->mft_na) + ntfs_attr_close(v->mft_na); + if (v->mft_ni) + ntfs_close_inode(v->mft_ni); + if (v->mftmirr_na) + ntfs_attr_close(v->mftmirr_na); + if (v->mftmirr_ni) + ntfs_close_inode(v->mftmirr_ni); + if (v->upcase) + free(v->upcase); + free(v); +} + +/* External declaration for internal function. */ +extern ntfs_inode *allocate_ntfs_inode(ntfs_volume *); + +/** + * Internal: + * + * ntfs_load_mft - load the $MFT and setup the ntfs volume with it + * @vol: ntfs volume whose $MFT to load + * + * Load $MFT from @vol and setup @vol with it. After calling this function the + * volume @vol is ready for use by all read access functions provided by the + * ntfs library. + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_load_mft(ntfs_volume *vol) +{ + VCN next_vcn, last_vcn, highest_vcn; + s64 l; + MFT_RECORD *mb = NULL; + ntfs_attr_search_ctx *ctx = NULL; + ATTR_RECORD *a; + int eo; + + /* Manually setup an ntfs_inode. */ + vol->mft_ni = allocate_ntfs_inode(vol); + mb = (MFT_RECORD*)malloc(vol->mft_record_size); + if (!vol->mft_ni || !mb) { + Dperror("Error allocating memory for $MFT"); + goto error_exit; + } + vol->mft_ni->mft_no = 0; + vol->mft_ni->mrec = mb; + /* Can't use any of the higher level functions yet! */ + l = ntfs_mst_pread(vol->fd, vol->mft_lcn << vol->cluster_size_bits, 1, + vol->mft_record_size, mb); + if (l != 1) { + if (l != -1) + errno = EIO; + Dperror("Error reading $MFT"); + goto error_exit; + } + if (is_baad_record(mb->magic)) { + Dputs("Error: Incomplete multi sector transfer detected in " + "$MFT."); + goto io_error_exit; + } + if (!is_mft_record(mb->magic)) { + Dputs("Error: $MFT has invalid magic."); + goto io_error_exit; + } + ctx = ntfs_get_attr_search_ctx(vol->mft_ni, mb); + if (!ctx) { + Dperror("Failed to allocate attribute search context"); + goto error_exit; + } + if (p2n(ctx->attr) < p2n(mb) || + (char*)ctx->attr > (char*)mb + vol->mft_record_size) { + Dputs("Error: $MFT is corrupt."); + goto io_error_exit; + } + /* Find the $ATTRIBUTE_LIST attribute in $MFT if present. */ + if (ntfs_lookup_attr(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { + if (errno != ENOENT) { + Dputs("Error: $MFT has corrupt attribute list."); + goto io_error_exit; + } + goto mft_has_no_attr_list; + } + NInoSetAttrList(vol->mft_ni); + l = get_attribute_value_length(ctx->attr); + if (l <= 0 || l > 0x40000) { + Dputs("Error: $MFT/$ATTRIBUTE_LIST has invalid length."); + goto io_error_exit; + } + vol->mft_ni->attr_list_size = l; + vol->mft_ni->attr_list = malloc(l); + if (!vol->mft_ni->attr_list) { + Dputs("Error: failed to allocate buffer for attribute list."); + goto error_exit; + } + l = get_attribute_value(vol, vol->mft_ni->mrec, ctx->attr, + vol->mft_ni->attr_list); + if (!l) { + Dputs("Error: failed to get value of $MFT/$ATTRIBUTE_LIST."); + goto io_error_exit; + } + if (l != vol->mft_ni->attr_list_size) { + Dputs("Error: got unexepected amount of data when reading " + "$MFT/$ATTRIBUTE_LIST."); + goto io_error_exit; + } + if (ctx->attr->non_resident) { + NInoSetAttrListNonResident(vol->mft_ni); + // FIXME: We are duplicating work here! (AIA) + vol->mft_ni->attr_list_rl = ntfs_decompress_mapping_pairs(vol, + ctx->attr, NULL); + if (!vol->mft_ni->attr_list_rl) { + Dperror("Error: failed to get run list for " + "$MFT/$ATTRIBUTE_LIST"); + goto error_exit; + } + } +mft_has_no_attr_list: + /* We now have a fully setup ntfs inode for $MFT in vol->mft_ni. */ + + /* Get an ntfs attribute for $MFT/$DATA and set it up, too. */ + vol->mft_na = ntfs_attr_open(vol->mft_ni, AT_DATA, AT_UNNAMED, 0); + if (!vol->mft_na) { + Dperror("Failed to open ntfs attribute"); + goto error_exit; + } + /* Set the number of mft records. */ + vol->nr_mft_records = vol->mft_na->data_size >> + vol->mft_record_size_bits; + /* Read all extents from the $DATA attribute in $MFT. */ + ntfs_reinit_attr_search_ctx(ctx); + last_vcn = vol->mft_na->allocated_size >> vol->cluster_size_bits; + highest_vcn = next_vcn = 0; + a = NULL; + while (!ntfs_lookup_attr(AT_DATA, AT_UNNAMED, 0, 0, next_vcn, NULL, 0, ctx)) { + run_list_element *nrl; + + a = ctx->attr; + /* $MFT must be non-resident. */ + if (!a->non_resident) { + Dputs("$MFT must be non-resident but a resident " + "extent was found. $MFT is corrupt. " + "Run chkdsk."); + goto io_error_exit; + } + /* $MFT must be uncompressed and unencrypted. */ + if (a->flags & ATTR_COMPRESSION_MASK || + a->flags & ATTR_IS_ENCRYPTED) { + Dputs("$MFT must be uncompressed and unencrypted but " + "a compressed/encrypted extent was " + "found. $MFT is corrupt. Run chkdsk."); + goto io_error_exit; + } + /* + * Decompress the mapping pairs array of this extent and merge + * the result into the existing run list. No need for locking + * as we have exclusive access to the inode at this time and we + * are a mount in progress task, too. + */ + nrl = ntfs_decompress_mapping_pairs(vol, a, vol->mft_na->rl); + if (!nrl) { + Dperror("decompress_mapping_pairs() failed"); + goto error_exit; + } + vol->mft_na->rl = nrl; + + /* Get the lowest vcn for the next extent. */ + highest_vcn = sle64_to_cpu(a->highest_vcn); + next_vcn = highest_vcn + 1; + + /* Only one extent or error, which we catch below. */ + if (next_vcn <= 0) + break; + + /* Avoid endless loops due to corruption. */ + if (next_vcn < sle64_to_cpu(a->lowest_vcn)) { + Dputs("$MFT has corrupt attribute list attribute. " + "Run chkdsk."); + goto io_error_exit; + } + } + if (!a) { + Dputs("$MFT/$DATA attribute not found. $MFT is corrupt. " + "Run chkdsk."); + goto io_error_exit; + } + if (highest_vcn && highest_vcn != last_vcn - 1) { + Dputs("Failed to load the complete run list for $MFT/$DATA. " + "Bug or corrupt $MFT. Run chkdsk."); + Dprintf("highest_vcn = 0x%Lx, last_vcn - 1 = 0x%Lx\n", + (long long)highest_vcn, + (long long)last_vcn - 1); + goto io_error_exit; + } + /* Done with the $Mft mft record. */ + ntfs_put_attr_search_ctx(ctx); + ctx = NULL; + /* + * The volume is now setup so we can use all read access functions. + */ + vol->mftbmp_na = ntfs_attr_open(vol->mft_ni, AT_BITMAP, AT_UNNAMED, 0); + if (!vol->mftbmp_na) { + Dperror("Failed to open $MFT/$BITMAP"); + goto error_exit; + } + return 0; +io_error_exit: + errno = EIO; +error_exit: + eo = errno; + if (ctx) + ntfs_put_attr_search_ctx(ctx); + if (vol->mft_na) { + ntfs_attr_close(vol->mft_na); + vol->mft_na = NULL; + } + if (vol->mft_ni) { + ntfs_close_inode(vol->mft_ni); + vol->mft_ni = NULL; + } + errno = eo; + return -1; +} + +/** + * Internal: + * + * ntfs_load_mftmirr - load the $MFTMirr and setup the ntfs volume with it + * @vol: ntfs volume whose $MFTMirr to load + * + * Load $MFTMirr from @vol and setup @vol with it. After calling this function + * the volume @vol is ready for use by all write access functions provided by + * the ntfs library (assuming ntfs_load_mft() has been called successfully + * beforehand). + * + * Return 0 on success and -1 on error with errno set to the error code. + */ +static int ntfs_load_mftmirr(ntfs_volume *vol) +{ + int i; + run_list_element rl[2]; + + vol->mftmirr_ni = ntfs_open_inode(vol, FILE_MFTMirr); + if (!vol->mftmirr_ni) { + Dperror("Failed to open inode $MFTMirr"); + return -1; + } + /* Get an ntfs attribute for $MFTMirr/$DATA, too. */ + vol->mftmirr_na = ntfs_attr_open(vol->mftmirr_ni, AT_DATA, AT_UNNAMED, 0); + if (!vol->mftmirr_na) { + Dperror("Failed to open $MFTMirr/$DATA"); + goto error_exit; + } + if (ntfs_attr_map_run_list(vol->mftmirr_na, 0) < 0) { + Dperror("Failed to map run list of $MFTMirr/$DATA"); + goto error_exit; + } + /* Construct the mft mirror run list. */ + rl[0].vcn = 0; + rl[0].lcn = vol->mftmirr_lcn; + rl[0].length = (vol->mftmirr_size * vol->mft_record_size + + vol->cluster_size - 1) / vol->cluster_size; + rl[1].vcn = rl[0].length; + rl[1].lcn = LCN_ENOENT; + rl[1].length = 0; + /* Compare the two run lists. They must be identical. */ + i = 0; + do { + if (rl[i].vcn != vol->mftmirr_na->rl[i].vcn || + rl[i].lcn != vol->mftmirr_na->rl[i].lcn || + rl[i].length != vol->mftmirr_na->rl[i].length) { + Dputs("Error: $MFTMirr location mismatch! Run chkdsk."); + errno = EIO; + goto error_exit; + } + } while (rl[i++].length); + return 0; +error_exit: + i = errno; + if (vol->mftmirr_na) { + ntfs_attr_close(vol->mftmirr_na); + vol->mftmirr_na = NULL; + } + ntfs_close_inode(vol->mftmirr_ni); + vol->mftmirr_ni = NULL; + errno = i; + return -1; +} + +/** + * ntfs_startup_volume - allocate and setup an ntfs volume + * @name: name of device/file to open + * @rwflag: optional mount flags + * + * Load, verify and parse bootsector; load and setup $MFT and $MFTMirr. After + * calling this function, the volume is setup sufficiently to call all read + * and write access functions provided by the library. + * + * Return the allocated volume structure on success and NULL on error with + * errno set to the error code. + */ +ntfs_volume *ntfs_startup_volume(const char *name, unsigned long rwflag) +{ + s64 br; + const char *OK = "OK"; + const char *FAILED = "FAILED"; + ntfs_volume *vol; + NTFS_BOOT_SECTOR *bs = NULL; + int eo; +#ifdef DEBUG + BOOL debug = 1; +#else + BOOL debug = 0; +#endif + + /* Allocate the volume structure. */ + vol = __allocate_ntfs_volume(); + if (!vol) + return NULL; + /* Make a copy of the partition name. */ + if (!(vol->dev_name = strdup(name))) + goto error_exit; + /* Allocate the boot sector structure. */ + if (!(bs = (NTFS_BOOT_SECTOR *)malloc(sizeof(NTFS_BOOT_SECTOR)))) + goto error_exit; + if (rwflag & MS_RDONLY) + NVolSetReadOnly(vol); + Dprintf("Reading bootsector... "); + if ((vol->fd = open(name, NVolReadOnly(vol) ? O_RDONLY: O_RDWR)) < 0) { + Dputs(FAILED); + Dperror("Error opening partition file"); + goto error_exit; + } + /* Now read the bootsector. */ + br = ntfs_pread(vol->fd, 0, sizeof(NTFS_BOOT_SECTOR), bs); + if (br != sizeof(NTFS_BOOT_SECTOR)) { + Dputs(FAILED); + if (br != -1) + errno = EINVAL; + if (!br) + Dputs("Error: partition is smaller than bootsector " + "size. Weird!"); + else + Dperror("Error reading bootsector"); + goto error_exit; + } + Dputs(OK); + if (!is_boot_sector_ntfs(bs, !debug)) { + Dprintf("Error: %s is not a valid NTFS partition!\n", name); + errno = EINVAL; + goto error_exit; + } + if (parse_ntfs_boot_sector(vol, bs) < 0) { + Dperror("Failed to parse ntfs bootsector"); + goto error_exit; + } + free(bs); + bs = NULL; + + /* Need to setup $MFT so we can use the library read functions. */ + Dprintf("Loading $MFT... "); + if (ntfs_load_mft(vol) < 0) { + Dputs(FAILED); + Dperror("Failed to load $MFT"); + goto error_exit; + } + Dputs(OK); + + /* Need to setup $MFTMirr so we can use the write functions, too. */ + Dprintf("Loading $MFTMirr... "); + if (ntfs_load_mftmirr(vol) < 0) { + Dputs(FAILED); + Dperror("Failed to load $MFTMirr"); + goto error_exit; + } + Dputs(OK); + return vol; +error_exit: + eo = errno; + if (bs) + free(bs); + if (vol) + __release_ntfs_volume(vol); + errno = eo; + return NULL; +} + +/** + * ntfs_mount - open ntfs volume + * @name: name of device/file to open + * @rwflag: optional mount flags + * + * This function mounts an ntfs volume. @name should contain the name of the + * device/file to mount as the ntfs volume. + * + * @rwflags is an optional second parameter. The same flags are used as for + * the mount system call (man 2 mount). Currently only the following flag + * is implemented: + * MS_RDONLY - mount volume read-only + * + * The function opens the device or file @name and verifies that it contains a + * valid bootsector. Then, it allocates an ntfs_volume structure and initializes + * some of the values inside the structure from the information stored in the + * bootsector. It proceeds to load the necessary system files and completes + * setting up the structure. + * + * Return the allocated volume structure on success and NULL on error with + * errno set to the error code. + * + * Note, that a copy is made of @name, and hence it can be discarded as + * soon as the function returns. + */ +ntfs_volume *ntfs_mount(const char *name, unsigned long rwflag) +{ + s64 l; + const char *OK = "OK"; + const char *FAILED = "FAILED"; + ntfs_volume *vol; + u8 *m = NULL, *m2 = NULL; + ntfs_attr_search_ctx *ctx = NULL; + ntfs_inode *ni; + ntfs_attr *na; + ATTR_RECORD *a; + VOLUME_INFORMATION *vinf; + uchar_t *vname; + int i, j, eo; + u32 u; + + if (!name) { + errno = EINVAL; + return NULL; + } + + vol = ntfs_startup_volume(name, rwflag); + if (!vol) { + Dperror("Failed to startup volume"); + return NULL; + } + + /* Load data from $MFT and $MFTMirr and compare the contents. */ + m = (u8*)malloc(vol->mftmirr_size << vol->mft_record_size_bits); + m2 = (u8*)malloc(vol->mftmirr_size << vol->mft_record_size_bits); + if (!m || !m2) { + Dperror("Failed to allocate memory"); + goto error_exit; + } + + l = ntfs_attr_mst_pread(vol->mft_na, 0, vol->mftmirr_size, + vol->mft_record_size, m); + if (l != vol->mftmirr_size) { + if (l == -1) + Dperror("Failed to read $MFT"); + else { + Dputs("Length of data not equal expected length."); + errno = EIO; + } + goto error_exit; + } + l = ntfs_attr_mst_pread(vol->mftmirr_na, 0, vol->mftmirr_size, + vol->mft_record_size, m2); + if (l != vol->mftmirr_size) { + if (l == -1) + Dperror("Failed to read $MFTMirr"); + else { + Dputs("Length of data not equal expected length."); + errno = EIO; + } + goto error_exit; + } + Dprintf("Comparing $MFTMirr to $MFT... "); + for (i = 0; i < vol->mftmirr_size; ++i) { + const char *ESTR[12] = { "$MFT", "$MFTMirr", "$LogFile", + "$Volume", "$AttrDef", "root directory", "$Bitmap", + "$Boot", "$BadClus", "$Secure", "$UpCase", "$Extend" }; + const char *s; + + if (i < 12) + s = ESTR[i]; + else if (i < 16) + s = "system file"; + else + s = "mft record"; + + if (is_baad_recordp(m + i * vol->mft_record_size)) { + Dputs("FAILED"); + Dprintf("$MFT error: Incomplete multi sector transfer " + "detected in %s.\n", s); + goto io_error_exit; + } + if (!is_mft_recordp(m + i * vol->mft_record_size)) { + Dputs("FAILED"); + Dprintf("$MFT error: Invalid mft record for %s.\n", s); + goto io_error_exit; + } + if (is_baad_recordp(m2 + i * vol->mft_record_size)) { + Dputs("FAILED"); + Dprintf("$MFTMirr error: Incomplete multi sector " + "transfer detected in %s.\n", s); + goto io_error_exit; + } + if (!is_mft_recordp(m2 + i * vol->mft_record_size)) { + Dputs("FAILED"); + Dprintf("$MFTMirr error: Invalid mft record for %s.\n", + s); + goto io_error_exit; + } + if (memcmp((u8*)m + i * vol->mft_record_size, (u8*)m2 + + i * vol->mft_record_size, + ntfs_get_mft_record_data_size((MFT_RECORD*)( + (u8*)m + i * vol->mft_record_size)))) { + Dputs(FAILED); + Dputs("$MFTMirr does not match $MFT. Run chkdsk."); + goto io_error_exit; + } + } + Dputs(OK); + + free(m2); + free(m); + m = m2 = NULL; + + /* Now load the bitmap from $Bitmap. */ + Dprintf("Loading $Bitmap... "); + vol->lcnbmp_ni = ntfs_open_inode(vol, FILE_Bitmap); + if (!vol->lcnbmp_ni) { + Dputs(FAILED); + Dperror("Failed to open inode"); + goto error_exit; + } + /* Get an ntfs attribute for $Bitmap/$DATA. */ + vol->lcnbmp_na = ntfs_attr_open(vol->lcnbmp_ni, AT_DATA, AT_UNNAMED, 0); + if (!vol->lcnbmp_na) { + Dputs(FAILED); + Dperror("Failed to open ntfs attribute"); + goto error_exit; + } + /* Done with the $BitMap mft record. */ + Dputs(OK); + + /* Now load the upcase table from $UpCase. */ + Dprintf("Loading $UpCase... "); + ni = ntfs_open_inode(vol, FILE_UpCase); + if (!ni) { + Dputs(FAILED); + Dperror("Failed to open inode"); + goto error_exit; + } + /* Get an ntfs attribute for $UpCase/$DATA. */ + na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0); + if (!na) { + Dputs(FAILED); + Dperror("Failed to open ntfs attribute"); + goto error_exit; + } + /* + * Note: Normally, the upcase table has a length equal to 65536 + * 2-byte Unicode characters but allow for different cases, so no + * checks done. Just check we don't overflow 32-bits worth of Unicode + * characters. + */ + if (na->data_size & ~0x1ffffffffULL) { + Dputs(FAILED); + Dputs("Error: Upcase table is too big (max 32-bit allowed)."); + errno = EINVAL; + goto error_exit; + } + vol->upcase_len = na->data_size >> 1; + vol->upcase = (uchar_t*)malloc(na->data_size); + if (!vol->upcase) { + Dputs(FAILED); + Dputs("Not enough memory to load $UpCase."); + goto error_exit; + } + /* Read in the $DATA attribute value into the buffer. */ + l = ntfs_attr_pread(na, 0, na->data_size, vol->upcase); + if (l != na->data_size) { + Dputs(FAILED); + Dputs("Amount of data read does not correspond to expected " + "length!"); + errno = EIO; + goto error_exit; + } + /* Done with the $UpCase mft record. */ + Dputs(OK); + ntfs_attr_close(na); + if (ntfs_close_inode(ni)) + Dperror("Failed to close inode, leaking memory"); + + /* + * Now load $Volume and set the version information and flags in the + * vol structure accordingly. + */ + Dprintf("Loading $Volume... "); + ni = ntfs_open_inode(vol, FILE_Volume); + if (!ni) { + Dputs(FAILED); + Dperror("Failed to open inode"); + goto error_exit; + } + /* Get an ntfs attribute for $UpCase/$DATA. */ + ctx = ntfs_get_attr_search_ctx(ni, NULL); + if (!ctx) { + Dputs(FAILED); + Dperror("Failed to allocate attribute search context"); + goto error_exit; + } + /* Find the $VOLUME_INFORMATION attribute. */ + if (ntfs_lookup_attr(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, 0, + ctx)) { + Dputs(FAILED); + Dputs("$VOLUME_INFORMATION attribute not found in " + "$Volume?!?"); + goto error_exit; + } + a = ctx->attr; + /* Has to be resident. */ + if (a->non_resident) { + Dputs(FAILED); + Dputs("Error: Attribute $VOLUME_INFORMATION must be resident " + "(and it isn't)!"); + errno = EIO; + goto error_exit; + } + /* Get a pointer to the value of the attribute. */ + vinf = (VOLUME_INFORMATION*)(le16_to_cpu(a->value_offset) + (char*)a); + /* Sanity checks. */ + if ((char*)vinf + le32_to_cpu(a->value_length) > (char*)ctx->mrec + + le16_to_cpu(ctx->mrec->bytes_in_use) || + le16_to_cpu(a->value_offset) + le32_to_cpu( + a->value_length) > le32_to_cpu(a->length)) { + Dputs(FAILED); + Dputs("Error: Attribute $VOLUME_INFORMATION in $Volume is " + "corrupt!"); + errno = EIO; + goto error_exit; + } + /* Setup vol from the volume information attribute value. */ + vol->major_ver = vinf->major_ver; + vol->minor_ver = vinf->minor_ver; + /* Do not use le16_to_cpu() macro here as our VOLUME_FLAGS are + defined using cpu_to_le16() macro and hence are consistent. */ + vol->flags = vinf->flags; + /* Find the $VOLUME_NAME attribute. */ + ntfs_reinit_attr_search_ctx(ctx); + if (ntfs_lookup_attr(AT_VOLUME_NAME, AT_UNNAMED, 0, 0, 0, NULL, 0, ctx)) { + Dputs(FAILED); + Dputs("$VOLUME_NAME attribute not found in $Volume?!?"); + goto error_exit; + } + a = ctx->attr; + /* Has to be resident. */ + if (a->non_resident) { + Dputs(FAILED); + Dputs("Error: Attribute $VOLUME_NAME must be resident!"); + errno = EIO; + goto error_exit; + } + /* Get a pointer to the value of the attribute. */ + vname = (uchar_t*)(le16_to_cpu(a->value_offset) + (char*)a); + u = le32_to_cpu(a->value_length) / 2; + /* Convert Unicode volume name to current locale multibyte format. */ + vol->vol_name = NULL; + if (ntfs_ucstombs(vname, u, &vol->vol_name, 0) == -1) { + Dperror("Error: Volume name could not be converted to " + "current locale"); + Dputs("Forcing name into ASCII by replacing non-ASCII " + "characters with underscores."); + vol->vol_name = malloc(u + 1); + if (!vol->vol_name) { + Dputs(FAILED); + Dputs("Error: Unable to allocate memory for volume " + "name!"); + goto error_exit; + } + for (j = 0; j < u; j++) { + uchar_t uc = le16_to_cpu(vname[j]); + if (uc > 0xff) + uc = (uchar_t)'_'; + vol->vol_name[j] = (char)uc; + } + vol->vol_name[u] = '\0'; + } + Dputs(OK); + ntfs_put_attr_search_ctx(ctx); + ctx = NULL; + if (ntfs_close_inode(ni)) + Dperror("Failed to close inode, leaking memory"); + + /* FIXME: Need to deal with FILE_AttrDef. (AIA) */ + + return vol; +io_error_exit: + errno = EIO; +error_exit: + eo = errno; + if (ctx) + ntfs_put_attr_search_ctx(ctx); + if (m) + free(m); + if (m2) + free(m2); + if (vol) + __release_ntfs_volume(vol); + errno = eo; + return NULL; +} + +/** + * ntfs_umount - close ntfs volume + * @vol: address of ntfs_volume structure of volume to close + * @force: if true force close the volume even if it is busy + * + * Deallocate all structures (including @vol itself) associated with the ntfs + * volume @vol. + * + * Return 0 on success. On error return -1 with errno set appropriately + * (most likely to one of EAGAIN, EBUSY or EINVAL). The EAGAIN error means that + * an operation is in progress and if you try the close later the operation + * might be completed and the close succeed. + * + * If @force is true (i.e. not zero) this function will close the volume even + * if this means that data might be lost. + * + * @vol must have previously been returned by a call to ntfs_mount(). + * + * @vol itself is deallocated and should no longer be dereferenced after this + * function returns success. If it returns an error then nothing has been done + * so it is safe to continue using @vol. + */ +int ntfs_umount(ntfs_volume *vol, const BOOL force) +{ + if (!vol) { + errno = EINVAL; + return -1; + } + __release_ntfs_volume(vol); + return 0; +} + +#ifdef HAVE_MNTENT_H +/** + * Internal: + * + * ntfs_check_mntent - desc + * + * If you are wanting to use this, you actually wanted to use + * ntfs_check_if_mounted(), you just didn't realize. (-: + * + * See description of ntfs_check_if_mounted(), below. + */ +static int ntfs_check_mntent(const char *file, unsigned long *mnt_flags) +{ + struct mntent *mnt; + FILE *f; + + if (!(f = setmntent(MOUNTED, "r"))) + return -1; + while ((mnt = getmntent(f))) + if (!strcmp(file, mnt->mnt_fsname)) + break; + endmntent(f); + if (!mnt) + return 0; + *mnt_flags = NTFS_MF_MOUNTED; + if (!strcmp(mnt->mnt_dir, "/")) + *mnt_flags |= NTFS_MF_ISROOT; + if (hasmntopt(mnt, "ro") && !hasmntopt(mnt, "rw")) + *mnt_flags |= NTFS_MF_READONLY; + return 0; +} + +#endif + +/** + * ntfs_check_if_mounted - check if an ntfs volume is currently mounted + * @file: device file to check + * @mnt_flags: pointer into which to return the ntfs mount flags (see volume.h) + * + * If the running system does not support the {set,get,end}mntent() calls, + * just return 0 and set *@mnt_flags to zero. + * + * When the system does support the calls, ntfs_check_if_mounted() first tries + * to find the device @file in /etc/mtab (or wherever this is kept on the + * running system). If it is not found, assume the device is not mounted and + * return 0 and set *@mnt_flags to zero. + * + * If the device @file is found, set the NTFS_MF_MOUNTED flags in *@mnt_flags. + * + * Further if @file is mounted as the file system root ("/"), set the flag + * NTFS_MF_ISROOT in *@mnt_flags. + * + * Finally, check if the file system is mounted read-only, and if so set the + * NTFS_MF_READONLY flag in *@mnt_flags. + * + * On sucess, return 0 with *@mnt_flags set to the ntfs mount flags. + * + * On error, return -1 with errno set to the error code. + */ +int ntfs_check_if_mounted(const char *file, unsigned long *mnt_flags) +{ + *mnt_flags = 0; +#ifdef HAVE_MNTENT_H + return ntfs_check_mntent(file, mnt_flags); +#else + return 0; +#endif +} + +/** + * ntfs_version_supported - check if NTFS version is supported. + * @vol: ntfs volume whose version we're interested in. + * + * The function checks if the NTFS volume version is known or not. + * Version 1.1 and 1.2 are used by Windows NT4. + * Version 2.x is used by Windows 2000 Beta's + * Version 3.0 is used by Windows 2000. + * Version 3.1 is used by Windows XP and .NET. + * + * Return 0 if NTFS version is supported otherwise -1 with errno set. + * + * The following error codes are defined: + * ENOTSUP Unknown NTFS versions + * EINVAL Invalid argument + */ +int ntfs_is_version_supported(ntfs_volume *vol) +{ + u8 major, minor; + + if (!vol) { + errno = EINVAL; + return -1; + } + + major = vol->major_ver; + minor = vol->minor_ver; + + if (NTFS_V1_1(major, minor) || NTFS_V1_2(major, minor)) + return 0; + + if (NTFS_V2_X(major, minor)) + return 0; + + if (NTFS_V3_0(major, minor) || NTFS_V3_1(major, minor)) + return 0; + + errno = ENOTSUP; + return -1; +} + +/** + * ntfs_reset_logfile - "empty" $LogFile data attribute value + * @vol: ntfs volume whose $LogFile we intend to reset. + * + * Fill the value of the $LogFile data attribute, i.e. the contents of + * the file, with 0xff's, thus marking the journal as empty. + * FIXME(?): We might need to zero the LSN field of every single mft + * record as well. (But, first try without doing that and see what + * happens, since chkdsk might pickup the pieces and do it for us...) + * + * On success return 0. + * + * On error, return -1 with errno set to the error code. + */ +int ntfs_reset_logfile(ntfs_volume *vol) +{ + ntfs_inode *ni; + ntfs_attr *na; + s64 len, pos, count; + char buf[NTFS_BUF_SIZE]; + int eo; + + if (!vol) { + errno = EINVAL; + return -1; + } + + if ((ni = ntfs_open_inode(vol, FILE_LogFile)) == NULL) { + Dperror("Failed to open inode FILE_LogFile.\n"); + return -1; + } + + if ((na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0)) == NULL) { + Dperror("Failed to open $FILE_LogFile/$DATA\n"); + goto error_exit; + } + + /* The $DATA attribute of the $LogFile has to be non-resident. */ + if (!NAttrNonResident(na)) { + Dprintf("$LogFile $DATA attribute is resident!?!\n"); + errno = EIO; + goto io_error_exit; + } + + /* Get length of $LogFile contents. */ + len = na->data_size; + if (!len) { + Dprintf("$LogFile has zero length, no disk write needed.\n"); + return 0; + } + + /* Read $LogFile until its end. We do this as a check for correct + length thus making sure we are decompressing the mapping pairs + array correctly and hence writing below is safe as well. */ + pos = 0; + while ((count = ntfs_attr_pread(na, pos, NTFS_BUF_SIZE, buf)) > 0) + pos += count; + + if (count == -1 || pos != len) { + Dprintf("Amount of $LogFile data read does not " + "correspond to expected length!"); + if (count != -1) + errno = EIO; + goto io_error_exit; + } + + /* Fill the buffer with 0xff's. */ + memset(buf, -1, NTFS_BUF_SIZE); + + /* Set the $DATA attribute. */ + pos = 0; + while ((count = len - pos) > 0) { + if (count > NTFS_BUF_SIZE) + count = NTFS_BUF_SIZE; + + if ((count = ntfs_attr_pwrite(na, pos, count, buf)) <= 0) { + Dprintf("Failed to set the $LogFile attribute value."); + if (count != -1) + errno = EIO; + goto io_error_exit; + } + pos += count; + } + + ntfs_attr_close(na); + return ntfs_close_inode(ni); + +io_error_exit: + eo = errno; + ntfs_attr_close(na); + errno = eo; +error_exit: + eo = errno; + ntfs_close_inode(ni); + errno = eo; + return -1; +} + +/** + * ntfs_set_volume_flags - set the flags of an ntfs volume + * @vol: ntfs volume where we set the volume flags + * @flags: new flags + * + * Set the on-disk volume flags in the mft record of $Volume and + * on volume @vol to @flags. + * + * Return 0 on successful and -1 if not, with errno set to the error code. + */ +int ntfs_set_volume_flags(ntfs_volume *vol, const u16 flags) +{ + MFT_RECORD *m = NULL; + ATTR_RECORD *r; + VOLUME_INFORMATION *c; + ntfs_attr_search_ctx *ctx; + int ret = -1; /* failure */ + + if (!vol) { + errno = EINVAL; + return -1; + } + + if (ntfs_read_file_record(vol, FILE_Volume, &m, NULL)) { + Dperror("Failed to read $Volume"); + return -1; + } + + /* Sanity check */ + if (!(m->flags & MFT_RECORD_IN_USE)) { + Dprintf("Error: $Volume has been deleted. Cannot " + "handle this yet. Run chkdsk to fix this.\n"); + errno = EIO; + goto err_exit; + } + + /* Get a pointer to the volume information attribute. */ + ctx = ntfs_get_attr_search_ctx(NULL, m); + if (!ctx) { + Dperror("Failed to allocate attribute search context"); + goto err_exit; + } + if (ntfs_lookup_attr(AT_VOLUME_INFORMATION, AT_UNNAMED, 0, 0, 0, NULL, 0, + ctx)) { + Dputs("Error: Attribute $VOLUME_INFORMATION was not found in " + "$Volume!"); + goto err_out; + } + r = ctx->attr; + /* Sanity check. */ + if (r->non_resident) { + Dputs("Error: Attribute $VOLUME_INFORMATION must be resident " + "(and it isn't)!"); + errno = EIO; + goto err_out; + } + /* Get a pointer to the value of the attribute. */ + c = (VOLUME_INFORMATION*)(le16_to_cpu(r->value_offset) + (char*)r); + /* Sanity checks. */ + if ((char*)c + le32_to_cpu(r->value_length) > + le16_to_cpu(m->bytes_in_use) + (char*)m || + le16_to_cpu(r->value_offset) + + le32_to_cpu(r->value_length) > le32_to_cpu(r->length)) { + Dputs("Error: Attribute $VOLUME_INFORMATION in $Volume is " + "corrupt!"); + errno = EIO; + goto err_out; + } + /* Set the volume flags. */ + vol->flags = c->flags = cpu_to_le16(flags); + + if (ntfs_write_mft_record(vol, FILE_Volume, m)) { + Dperror("Error writing $Volume"); + goto err_out; + } + + ret = 0; /* success */ +err_out: + ntfs_put_attr_search_ctx(ctx); +err_exit: + if (m) + free(m); + return ret; +} +