Merge tag '2021.8.22' into edge.strict_endians
Conflicts: libntfs-3g/attrib.c libntfs-3g/bootsect.c ntfsprogs/ntfsfix.cedge.strict_endians
commit
11a3997a0d
|
@ -1,6 +1,3 @@
|
|||
ChangeLog can be found at :
|
||||
|
||||
Detailed ChangeLog can be found at
|
||||
http://www.tuxera.com/community/release-history/
|
||||
|
||||
The changes and history may also be found on the source repository :
|
||||
http://sourceforge.net/p/ntfs-3g/ntfs-3g/ci/edge/tree/
|
||||
https://github.com/tuxera/ntfs-3g/wiki
|
||||
|
|
6
NEWS
6
NEWS
|
@ -1,5 +1,3 @@
|
|||
Project information can be found at :
|
||||
|
||||
Project news are at http://tuxera.com/community/ntfs-3g-download/
|
||||
|
||||
Release notes are maintained at http://tuxera.com/community/release-history/
|
||||
|
||||
https://github.com/tuxera/ntfs-3g/
|
||||
|
|
43
README
43
README
|
@ -3,9 +3,10 @@ INTRODUCTION
|
|||
============
|
||||
|
||||
The NTFS-3G driver is an open source, freely available read/write NTFS driver
|
||||
for Linux, FreeBSD, Mac OS X, NetBSD, OpenSolaris, QNX and Haiku. It provides
|
||||
for Linux, FreeBSD, macOS, NetBSD, OpenIndiana, QNX and Haiku. It provides
|
||||
safe and fast handling of the Windows XP, Windows Server 2003, Windows 2000,
|
||||
Windows Vista, Windows Server 2008 and Windows 7 file systems.
|
||||
Windows Vista, Windows Server 2008, Windows 7, Windows 8, Windows Server 2012,
|
||||
Windows Server 2016, Windows 10 and Windows Server 2019 NTFS file systems.
|
||||
|
||||
The purpose of the project is to develop, quality assurance and support a
|
||||
trustable, featureful and high performance solution for hardware platforms
|
||||
|
@ -18,21 +19,22 @@ Besides the common file system features, NTFS-3G has support for file
|
|||
ownership and permissions, POSIX ACLs, junction points, extended attributes
|
||||
and creating internally compressed files (parameter files in the directory
|
||||
.NTFS-3G may be required to enable them). The new compressed file formats
|
||||
available in Windows 10 can also be read through a plugin. For using
|
||||
advanced features, please get the instructions from
|
||||
|
||||
http://www.tuxera.com/community/ntfs-3g-advanced/
|
||||
available in Windows 10 can also be read through a plugin.
|
||||
|
||||
News, support answers, problem submission instructions, support and discussion
|
||||
forums, performance numbers and other information are available on the project
|
||||
web site at
|
||||
forums, and other information are available on the project web site at
|
||||
|
||||
https://github.com/tuxera/ntfs-3g
|
||||
|
||||
The project has been funded, supported and maintained since 2008 by Tuxera:
|
||||
|
||||
https://tuxera.com
|
||||
|
||||
http://www.tuxera.com/community/
|
||||
|
||||
LICENSES
|
||||
========
|
||||
|
||||
All the NTFS related components : the file system drivers, the ntfsprogs
|
||||
All the NTFS related components: the file system drivers, the ntfsprogs
|
||||
utilities and the shared library libntfs-3g are distributed 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
|
||||
|
@ -41,6 +43,7 @@ version. See the included file COPYING.
|
|||
The fuse-lite library is distributed under the terms of the GNU LGPLv2.
|
||||
See the included file COPYING.LIB.
|
||||
|
||||
|
||||
QUICK INSTALLATION
|
||||
==================
|
||||
|
||||
|
@ -73,12 +76,6 @@ There are also a few make targets for building parts :
|
|||
make drivers : only build drivers and libraries, without ntfsprogs
|
||||
make ntfsprogs : only build ntfsprogs and libntfs-3g, without drivers
|
||||
|
||||
Non-Linux: Please see
|
||||
|
||||
http://www.tuxera.com/community/ntfs-3g-download/
|
||||
|
||||
for known OS specific installation and source packages, but generally
|
||||
the same procedures apply.
|
||||
|
||||
USAGE
|
||||
=====
|
||||
|
@ -106,23 +103,23 @@ TESTING WITHOUT INSTALLING
|
|||
Newer versions of ntfs-3g can be tested without installing anything and
|
||||
without disturbing an existing installation. Just configure and make as
|
||||
shown previously. This will create the scripts ntfs-3g and lowntfs-3g
|
||||
in the src directory, which you may activate for testing :
|
||||
in the src directory, which you may activate for testing:
|
||||
|
||||
./configure
|
||||
make
|
||||
|
||||
then, as root :
|
||||
then, as root:
|
||||
src/ntfs-3g [-o mount-options] /dev/sda1 /mnt/windows
|
||||
|
||||
And, to end the test, unmount the usual way :
|
||||
And, to end the test, unmount the usual way:
|
||||
umount /dev/sda1
|
||||
|
||||
|
||||
NTFS UTILITIES
|
||||
==============
|
||||
|
||||
The ntfsprogs includes utilities for doing all required tasks to NTFS
|
||||
partitions. In general, just run a utility without any command line
|
||||
The ntfsprogs directory includes utilities for doing all required tasks to
|
||||
NTFS partitions. In general, just run a utility without any command line
|
||||
options to display the version number and usage syntax.
|
||||
|
||||
The following utilities are so far implemented:
|
||||
|
@ -159,6 +156,6 @@ ntfscat - Concatenate files and print their contents on the standard output.
|
|||
|
||||
ntfscp - Overwrite files on an NTFS partition.
|
||||
|
||||
ntfssecaudit : audit the security metadata.
|
||||
ntfssecaudit - Audit the security metadata.
|
||||
|
||||
ntfsusermap : assistance for building a user mapping file.
|
||||
ntfsusermap - Assistance for building a user mapping file.
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
|
||||
# Autoconf
|
||||
AC_PREREQ(2.59)
|
||||
AC_INIT([ntfs-3g],[2017.3.23],[ntfs-3g-devel@lists.sf.net])
|
||||
LIBNTFS_3G_VERSION="88"
|
||||
AC_INIT([ntfs-3g],[2021.8.22],[ntfs-3g-devel@lists.sf.net])
|
||||
LIBNTFS_3G_VERSION="89"
|
||||
AC_CONFIG_SRCDIR([src/ntfs-3g.c])
|
||||
|
||||
# Environment
|
||||
|
|
|
@ -398,6 +398,7 @@ extern int ntfs_attr_data_write(ntfs_inode *ni,
|
|||
const char *buf, size_t size, off_t offset);
|
||||
extern int ntfs_attr_shrink_size(ntfs_inode *ni, ntfschar *stream_name,
|
||||
int stream_name_len, off_t offset);
|
||||
extern int ntfs_attr_inconsistent(const ATTR_RECORD *a, const MFT_REF mref);
|
||||
|
||||
#endif /* defined _NTFS_ATTRIB_H */
|
||||
|
||||
|
|
|
@ -139,6 +139,10 @@ extern ntfs_index_context *ntfs_index_ctx_get(ntfs_inode *ni,
|
|||
extern void ntfs_index_ctx_put(ntfs_index_context *ictx);
|
||||
extern void ntfs_index_ctx_reinit(ntfs_index_context *ictx);
|
||||
|
||||
extern int ntfs_index_block_inconsistent(const INDEX_BLOCK *ib, u32 block_size,
|
||||
u64 inum, VCN vcn);
|
||||
extern int ntfs_index_entry_inconsistent(const INDEX_ENTRY *ie,
|
||||
COLLATION_RULES collation_rule, u64 inum);
|
||||
extern int ntfs_index_lookup(const void *key, const int key_len,
|
||||
ntfs_index_context *ictx) __attribute_warn_unused_result__;
|
||||
|
||||
|
|
|
@ -117,6 +117,7 @@ typedef enum {
|
|||
NV_HideDotFiles, /* 1: Set hidden flag on dot files */
|
||||
NV_Compression, /* 1: allow compression */
|
||||
NV_NoFixupWarn, /* 1: Do not log fixup errors */
|
||||
NV_FreeSpaceKnown, /* 1: The free space is now known */
|
||||
} ntfs_volume_state_bits;
|
||||
|
||||
#define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state)
|
||||
|
@ -155,6 +156,10 @@ typedef enum {
|
|||
#define NVolSetNoFixupWarn(nv) set_nvol_flag(nv, NoFixupWarn)
|
||||
#define NVolClearNoFixupWarn(nv) clear_nvol_flag(nv, NoFixupWarn)
|
||||
|
||||
#define NVolFreeSpaceKnown(nv) test_nvol_flag(nv, FreeSpaceKnown)
|
||||
#define NVolSetFreeSpaceKnown(nv) set_nvol_flag(nv, FreeSpaceKnown)
|
||||
#define NVolClearFreeSpaceKnown(nv) clear_nvol_flag(nv, FreeSpaceKnown)
|
||||
|
||||
/*
|
||||
* NTFS version 1.1 and 1.2 are used by Windows NT4.
|
||||
* NTFS version 2.x is used by Windows 2000 Beta
|
||||
|
|
|
@ -1330,6 +1330,10 @@ struct POSIX_SECURITY *ntfs_build_basic_posix(
|
|||
pydesc->acccnt = 3;
|
||||
pydesc->defcnt = 0;
|
||||
pydesc->firstdef = 6;
|
||||
pydesc->filler = 0;
|
||||
pydesc->acl.version = POSIX_VERSION;
|
||||
pydesc->acl.flags = 0;
|
||||
pydesc->acl.filler = 0;
|
||||
} else
|
||||
errno = ENOMEM;
|
||||
return (pydesc);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Copyright (c) 2002-2005 Richard Russon
|
||||
* Copyright (c) 2002-2008 Szabolcs Szakacsits
|
||||
* Copyright (c) 2004-2007 Yura Pakhuchiy
|
||||
* Copyright (c) 2007-2020 Jean-Pierre Andre
|
||||
* Copyright (c) 2007-2021 Jean-Pierre Andre
|
||||
* Copyright (c) 2010 Erik Larsson
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
|
@ -489,6 +489,17 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
|
|||
}
|
||||
|
||||
if (a->non_resident) {
|
||||
if ((!le16_andz(a->flags, ATTR_COMPRESSION_MASK)
|
||||
|| a->compression_unit)
|
||||
&& (ni->vol->major_ver < 3)) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Compressed inode %lld not allowed"
|
||||
" on NTFS %d.%d",
|
||||
(unsigned long long)ni->mft_no,
|
||||
ni->vol->major_ver,
|
||||
ni->vol->major_ver);
|
||||
goto put_err_out;
|
||||
}
|
||||
if (!le16_andz(a->flags, ATTR_COMPRESSION_MASK)
|
||||
&& !a->compression_unit) {
|
||||
errno = EIO;
|
||||
|
@ -497,6 +508,17 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
|
|||
(unsigned long long)ni->mft_no, le32_to_cpu(type));
|
||||
goto put_err_out;
|
||||
}
|
||||
if (!le16_andz(a->flags, ATTR_COMPRESSION_MASK)
|
||||
&& (a->compression_unit
|
||||
!= STANDARD_COMPRESSION_UNIT)) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Compressed inode %lld attr 0x%lx has "
|
||||
"an unsupported compression unit %d",
|
||||
(unsigned long long)ni->mft_no,
|
||||
(long)le32_to_cpu(type),
|
||||
(int)a->compression_unit);
|
||||
goto put_err_out;
|
||||
}
|
||||
ntfs_attr_init(na, TRUE, a->flags,
|
||||
!le16_andz(a->flags, ATTR_IS_ENCRYPTED),
|
||||
!le16_andz(a->flags, ATTR_IS_SPARSE),
|
||||
|
@ -1256,6 +1278,17 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs,
|
|||
LCN lcn_seek_from = -1;
|
||||
VCN cur_vcn, from_vcn;
|
||||
|
||||
if (na->ni->mft_no == FILE_Bitmap) {
|
||||
/*
|
||||
* Filling a hole in the main bitmap implies allocating
|
||||
* clusters, which is likely to imply updating the
|
||||
* bitmap in a cluster being allocated.
|
||||
* Not supported now, could lead to endless recursions.
|
||||
*/
|
||||
ntfs_log_error("Corrupt $BitMap not fully allocated\n");
|
||||
errno = EIO;
|
||||
goto err_out;
|
||||
}
|
||||
to_write = min(count, ((*rl)->length << vol->cluster_size_bits) - *ofs);
|
||||
|
||||
cur_vcn = (*rl)->vcn;
|
||||
|
@ -2765,6 +2798,8 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name,
|
|||
ATTR_RECORD *a;
|
||||
ntfs_volume *vol;
|
||||
ntfschar *upcase;
|
||||
ptrdiff_t offs;
|
||||
ptrdiff_t space;
|
||||
u32 upcase_len;
|
||||
|
||||
ntfs_log_trace("attribute type 0x%x.\n", le32_to_cpu(type));
|
||||
|
@ -2794,8 +2829,17 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name,
|
|||
a = (ATTR_RECORD*)((char*)ctx->attr +
|
||||
le32_to_cpu(ctx->attr->length));
|
||||
for (;; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) {
|
||||
if (p2n(a) < p2n(ctx->mrec) || (char*)a > (char*)ctx->mrec +
|
||||
le32_to_cpu(ctx->mrec->bytes_allocated))
|
||||
/*
|
||||
* Make sure the attribute fully lies within the MFT record
|
||||
* and we can safely access its minimal fields.
|
||||
*/
|
||||
offs = p2n(a) - p2n(ctx->mrec);
|
||||
space = le32_to_cpu(ctx->mrec->bytes_in_use) - offs;
|
||||
if ((offs < 0)
|
||||
|| (((space < (ptrdiff_t)offsetof(ATTR_RECORD,
|
||||
resident_end))
|
||||
|| (space < (ptrdiff_t)le32_to_cpu(a->length)))
|
||||
&& ((space < 4) || !le32_eq(a->type, AT_END))))
|
||||
break;
|
||||
ctx->attr = a;
|
||||
if ((!le32_eq(type, AT_UNUSED) && (le32_to_cpu(a->type) >
|
||||
|
@ -2824,6 +2868,16 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name,
|
|||
}
|
||||
} else {
|
||||
register int rc;
|
||||
|
||||
if (a->name_length
|
||||
&& ((le16_to_cpu(a->name_offset)
|
||||
+ a->name_length * sizeof(ntfschar))
|
||||
> le32_to_cpu(a->length))) {
|
||||
ntfs_log_error("Corrupt attribute name"
|
||||
" in MFT record %lld\n",
|
||||
(long long)ctx->ntfs_ino->mft_no);
|
||||
break;
|
||||
}
|
||||
if (name && ((rc = ntfs_names_full_collate(name,
|
||||
name_len, (ntfschar*)((char*)a +
|
||||
le16_to_cpu(a->name_offset)),
|
||||
|
@ -2986,6 +3040,8 @@ static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name,
|
|||
u8 *al_start, *al_end;
|
||||
ATTR_RECORD *a;
|
||||
ntfschar *al_name;
|
||||
ptrdiff_t offs;
|
||||
ptrdiff_t space;
|
||||
u32 al_name_len;
|
||||
BOOL is_first_search = FALSE;
|
||||
|
||||
|
@ -3026,8 +3082,22 @@ static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name,
|
|||
le32_to_cpu(AT_ATTRIBUTE_LIST))
|
||||
goto find_attr_list_attr;
|
||||
} else {
|
||||
/* Check for small entry */
|
||||
if (((p2n(al_end) - p2n(ctx->al_entry))
|
||||
< (long)offsetof(ATTR_LIST_ENTRY, name))
|
||||
|| (le16_to_cpu(ctx->al_entry->length) & 7)
|
||||
|| (le16_to_cpu(ctx->al_entry->length)
|
||||
< offsetof(ATTR_LIST_ENTRY, name)))
|
||||
goto corrupt;
|
||||
|
||||
al_entry = (ATTR_LIST_ENTRY*)((char*)ctx->al_entry +
|
||||
le16_to_cpu(ctx->al_entry->length));
|
||||
if ((u8*)al_entry == al_end)
|
||||
goto not_found;
|
||||
/* Preliminary check for small entry */
|
||||
if ((p2n(al_end) - p2n(al_entry))
|
||||
< (long)offsetof(ATTR_LIST_ENTRY, name))
|
||||
goto corrupt;
|
||||
/*
|
||||
* If this is an enumeration and the attribute list attribute
|
||||
* is the next one in the enumeration sequence, just return the
|
||||
|
@ -3090,11 +3160,18 @@ find_attr_list_attr:
|
|||
/* Catch the end of the attribute list. */
|
||||
if ((u8*)al_entry == al_end)
|
||||
goto not_found;
|
||||
if (le16_cmpz(al_entry->length))
|
||||
break;
|
||||
if ((u8*)al_entry + 6 > al_end || (u8*)al_entry +
|
||||
le16_to_cpu(al_entry->length) > al_end)
|
||||
break;
|
||||
|
||||
if ((((u8*)al_entry + offsetof(ATTR_LIST_ENTRY, name)) > al_end)
|
||||
|| ((u8*)al_entry + le16_to_cpu(al_entry->length) > al_end)
|
||||
|| (le16_to_cpu(al_entry->length) & 7)
|
||||
|| (le16_to_cpu(al_entry->length)
|
||||
< offsetof(ATTR_LIST_ENTRY, name_length))
|
||||
|| (al_entry->name_length
|
||||
&& ((u8*)al_entry + al_entry->name_offset
|
||||
+ al_entry->name_length * sizeof(ntfschar))
|
||||
> al_end))
|
||||
break; /* corrupt */
|
||||
|
||||
next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry +
|
||||
le16_to_cpu(al_entry->length));
|
||||
if (!le32_eq(type, AT_UNUSED)) {
|
||||
|
@ -3174,6 +3251,12 @@ is_enumeration:
|
|||
ctx->mrec = ctx->base_mrec;
|
||||
} else {
|
||||
/* We want an extent record. */
|
||||
if (!vol->mft_na) {
|
||||
ntfs_log_perror("$MFT not ready for "
|
||||
"opening an extent to inode %lld\n",
|
||||
(long long)base_ni->mft_no);
|
||||
break;
|
||||
}
|
||||
ni = ntfs_extent_inode_open(base_ni,
|
||||
al_entry->mft_reference);
|
||||
if (!ni)
|
||||
|
@ -3202,12 +3285,18 @@ is_enumeration:
|
|||
* with the same meanings as above.
|
||||
*/
|
||||
do_next_attr_loop:
|
||||
if ((char*)a < (char*)ctx->mrec || (char*)a > (char*)ctx->mrec +
|
||||
le32_to_cpu(ctx->mrec->bytes_allocated))
|
||||
/*
|
||||
* Make sure the attribute fully lies within the MFT record
|
||||
* and we can safely access its minimal fields.
|
||||
*/
|
||||
offs = p2n(a) - p2n(ctx->mrec);
|
||||
space = le32_to_cpu(ctx->mrec->bytes_in_use) - offs;
|
||||
if (offs < 0)
|
||||
break;
|
||||
if (le32_eq(a->type, AT_END))
|
||||
if ((space >= 4) && le32_eq(a->type, AT_END))
|
||||
continue;
|
||||
if (le32_cmpz(a->length))
|
||||
if ((space < (ptrdiff_t)offsetof(ATTR_RECORD, resident_end))
|
||||
|| (space < (ptrdiff_t)le32_to_cpu(a->length)))
|
||||
break;
|
||||
if (!le16_eq(al_entry->instance, a->instance))
|
||||
goto do_next_attr;
|
||||
|
@ -3241,13 +3330,15 @@ do_next_attr:
|
|||
a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length));
|
||||
goto do_next_attr_loop;
|
||||
}
|
||||
corrupt :
|
||||
if (ni != base_ni) {
|
||||
ctx->ntfs_ino = base_ni;
|
||||
ctx->mrec = ctx->base_mrec;
|
||||
ctx->attr = ctx->base_attr;
|
||||
}
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Inode is corrupt (%lld)", (long long)base_ni->mft_no);
|
||||
ntfs_log_error("Corrupt attribute list entry in MFT record %lld\n",
|
||||
(long long)base_ni->mft_no);
|
||||
return -1;
|
||||
not_found:
|
||||
/*
|
||||
|
@ -3299,6 +3390,190 @@ not_found:
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the consistency of an attribute
|
||||
*
|
||||
* Do the general consistency checks of the selected attribute :
|
||||
* - the required fields can be accessed
|
||||
* - the variable fields do not overflow
|
||||
* - the attribute is [non-]resident if it must be
|
||||
* - miscelleaneous checks
|
||||
*
|
||||
* Returns 0 if the checks pass
|
||||
* -1 with errno = EIO otherwise
|
||||
*/
|
||||
|
||||
int ntfs_attr_inconsistent(const ATTR_RECORD *a, const MFT_REF mref)
|
||||
{
|
||||
const FILE_NAME_ATTR *fn;
|
||||
const INDEX_ROOT *ir;
|
||||
u64 inum;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The attribute was found to fully lie within the MFT
|
||||
* record, now make sure its relevant parts (name, runlist,
|
||||
* value) also lie within. The first step is to make sure
|
||||
* the attribute has the minimum length so that accesses to
|
||||
* the lengths and offsets of these parts are safe.
|
||||
*/
|
||||
ret = 0;
|
||||
inum = MREF(mref);
|
||||
if (a->non_resident) {
|
||||
if ((a->non_resident != 1)
|
||||
|| (le32_to_cpu(a->length)
|
||||
< offsetof(ATTR_RECORD, non_resident_end))
|
||||
|| (le16_to_cpu(a->mapping_pairs_offset)
|
||||
>= le32_to_cpu(a->length))
|
||||
|| (a->name_length
|
||||
&& (((u32)le16_to_cpu(a->name_offset)
|
||||
+ a->name_length * sizeof(ntfschar))
|
||||
> le32_to_cpu(a->length)))
|
||||
|| (le64_to_cpu(a->highest_vcn)
|
||||
< le64_to_cpu(a->lowest_vcn))) {
|
||||
ntfs_log_error("Corrupt non resident attribute"
|
||||
" 0x%x in MFT record %lld\n",
|
||||
(int)le32_to_cpu(a->type),
|
||||
(long long)inum);
|
||||
errno = EIO;
|
||||
ret = -1;
|
||||
}
|
||||
} else {
|
||||
if ((le32_to_cpu(a->length)
|
||||
< offsetof(ATTR_RECORD, resident_end))
|
||||
|| (le32_to_cpu(a->value_length) & 0xff000000)
|
||||
|| (!le32_cmpz(a->value_length)
|
||||
&& ((le16_to_cpu(a->value_offset)
|
||||
+ le32_to_cpu(a->value_length))
|
||||
> le32_to_cpu(a->length)))
|
||||
|| (a->name_length
|
||||
&& (((u32)le16_to_cpu(a->name_offset)
|
||||
+ a->name_length * sizeof(ntfschar))
|
||||
> le32_to_cpu(a->length)))) {
|
||||
ntfs_log_error("Corrupt resident attribute"
|
||||
" 0x%x in MFT record %lld\n",
|
||||
(int)le32_to_cpu(a->type),
|
||||
(long long)inum);
|
||||
errno = EIO;
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
if (!ret) {
|
||||
/*
|
||||
* Checking whether an attribute must be [non-]resident
|
||||
* is hard-coded for well-known ones. This should be
|
||||
* done through ntfs_attr_can_be_non_resident(), based on
|
||||
* $AttrDef, but this would give an easy way to bypass
|
||||
* the checks.
|
||||
* Attributes which are not well-known are not checked.
|
||||
*
|
||||
* Note : at this stage we know that a->length and
|
||||
* a->value_length cannot look like being negative.
|
||||
*/
|
||||
/* switch(a->type) { */
|
||||
if (le32_eq(a->type, AT_FILE_NAME)) {
|
||||
/* Check file names are resident and do not overflow */
|
||||
fn = (const FILE_NAME_ATTR*)((const u8*)a
|
||||
+ le16_to_cpu(a->value_offset));
|
||||
if (a->non_resident
|
||||
|| (le32_to_cpu(a->value_length)
|
||||
< offsetof(FILE_NAME_ATTR, file_name))
|
||||
|| !fn->file_name_length
|
||||
|| ((fn->file_name_length * sizeof(ntfschar)
|
||||
+ offsetof(FILE_NAME_ATTR, file_name))
|
||||
> le32_to_cpu(a->value_length))) {
|
||||
ntfs_log_error("Corrupt file name"
|
||||
" attribute in MFT record %lld.\n",
|
||||
(long long)inum);
|
||||
errno = EIO;
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
else if (le32_eq(a->type, AT_INDEX_ROOT)) {
|
||||
/* Check root index is resident and does not overflow */
|
||||
ir = (const INDEX_ROOT*)((const u8*)a +
|
||||
le16_to_cpu(a->value_offset));
|
||||
/* index.allocated_size may overflow while resizing */
|
||||
if (a->non_resident
|
||||
|| (le32_to_cpu(a->value_length)
|
||||
< offsetof(INDEX_ROOT, index.reserved))
|
||||
|| (le32_to_cpu(ir->index.entries_offset)
|
||||
< sizeof(INDEX_HEADER))
|
||||
|| (le32_to_cpu(ir->index.index_length)
|
||||
< le32_to_cpu(ir->index.entries_offset))
|
||||
|| (le32_to_cpu(ir->index.allocated_size)
|
||||
< le32_to_cpu(ir->index.index_length))
|
||||
|| (le32_to_cpu(a->value_length)
|
||||
< (le32_to_cpu(ir->index.allocated_size)
|
||||
+ offsetof(INDEX_ROOT, reserved)))) {
|
||||
ntfs_log_error("Corrupt index root"
|
||||
" in MFT record %lld.\n",
|
||||
(long long)inum);
|
||||
errno = EIO;
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
else if (le32_eq(a->type, AT_STANDARD_INFORMATION)) {
|
||||
if (a->non_resident
|
||||
|| (le32_to_cpu(a->value_length)
|
||||
< offsetof(STANDARD_INFORMATION,
|
||||
v1_end))) {
|
||||
ntfs_log_error("Corrupt standard information"
|
||||
" in MFT record %lld\n",
|
||||
(long long)inum);
|
||||
errno = EIO;
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
else if (le32_eq(a->type, AT_OBJECT_ID)) {
|
||||
if (a->non_resident
|
||||
|| (le32_to_cpu(a->value_length)
|
||||
< sizeof(GUID))) {
|
||||
ntfs_log_error("Corrupt object id"
|
||||
" in MFT record %lld\n",
|
||||
(long long)inum);
|
||||
errno = EIO;
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
else if (le32_eq(a->type, AT_VOLUME_NAME) ||
|
||||
le32_eq(a->type, AT_EA_INFORMATION)) {
|
||||
if (a->non_resident) {
|
||||
ntfs_log_error("Attribute 0x%x in MFT record"
|
||||
" %lld should be resident.\n",
|
||||
(int)le32_to_cpu(a->type),
|
||||
(long long)inum);
|
||||
errno = EIO;
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
else if (le32_eq(a->type, AT_VOLUME_INFORMATION)) {
|
||||
if (a->non_resident
|
||||
|| (le32_to_cpu(a->value_length)
|
||||
< sizeof(VOLUME_INFORMATION))) {
|
||||
ntfs_log_error("Corrupt volume information"
|
||||
" in MFT record %lld\n",
|
||||
(long long)inum);
|
||||
errno = EIO;
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
else if (le32_eq(a->type, AT_INDEX_ALLOCATION)) {
|
||||
if (!a->non_resident) {
|
||||
ntfs_log_error("Corrupt index allocation"
|
||||
" in MFT record %lld",
|
||||
(long long)inum);
|
||||
errno = EIO;
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
}
|
||||
/* } */
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_attr_lookup - find an attribute in an ntfs inode
|
||||
* @type: attribute type to find
|
||||
|
@ -3546,8 +3821,9 @@ ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol,
|
|||
ntfs_log_perror("%s: type=%d", __FUNCTION__, le32_to_cpu(type));
|
||||
return NULL;
|
||||
}
|
||||
for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef <
|
||||
vol->attrdef_len && !le32_cmpz(ad->type); ++ad) {
|
||||
for (ad = vol->attrdef; ((ptrdiff_t)((u8*)ad - (u8*)vol->attrdef
|
||||
+ sizeof(ATTR_DEF)) <= vol->attrdef_len)
|
||||
&& !le32_cmpz(ad->type); ++ad) {
|
||||
/* We haven't found it yet, carry on searching. */
|
||||
if (le32_to_cpu(ad->type) < le32_to_cpu(type))
|
||||
continue;
|
||||
|
@ -4533,6 +4809,13 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size)
|
|||
}
|
||||
|
||||
/* Move attributes following @a to their new location. */
|
||||
if (((u8 *)m + old_size) < ((u8 *)a + attr_size)) {
|
||||
ntfs_log_error("Attribute 0x%x overflows"
|
||||
" from MFT record\n",
|
||||
(int)le32_to_cpu(a->type));
|
||||
errno = EIO;
|
||||
return (-1);
|
||||
}
|
||||
memmove((u8 *)a + new_size, (u8 *)a + attr_size,
|
||||
old_size - ((u8 *)a - (u8 *)m) - attr_size);
|
||||
|
||||
|
@ -4567,6 +4850,13 @@ int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a,
|
|||
|
||||
ntfs_log_trace("Entering for new size %u.\n", (unsigned)new_size);
|
||||
|
||||
if (le32_cmpz(a->value_length)) {
|
||||
/* Offset is unsafe when no value */
|
||||
int offset = ((offsetof(ATTR_RECORD, resident_end)
|
||||
+ a->name_length*sizeof(ntfschar) - 1) | 7) + 1;
|
||||
a->value_offset = cpu_to_le16(offset);
|
||||
}
|
||||
|
||||
/* Resize the resident part of the attribute record. */
|
||||
if ((ret = ntfs_attr_record_resize(m, a, (le16_to_cpu(a->value_offset) +
|
||||
new_size + 7) & ~7)) < 0)
|
||||
|
@ -6663,6 +6953,20 @@ void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type,
|
|||
ntfs_log_perror("ntfs_attr_open failed, inode %lld attr 0x%lx",
|
||||
(long long)ni->mft_no,(long)le32_to_cpu(type));
|
||||
goto err_exit;
|
||||
}
|
||||
/*
|
||||
* Consistency check : restrict to 65536 bytes.
|
||||
* index bitmaps may need more, but still limited by
|
||||
* the number of clusters.
|
||||
*/
|
||||
if (((u64)na->data_size > 65536)
|
||||
&& (!le32_eq(type, AT_BITMAP)
|
||||
|| ((u64)na->data_size >
|
||||
(u64)((ni->vol->nr_clusters + 7) >> 3)))) {
|
||||
ntfs_log_error("Corrupt attribute 0x%lx in inode %lld\n",
|
||||
(long)le32_to_cpu(type),(long long)ni->mft_no);
|
||||
errno = EOVERFLOW;
|
||||
goto out;
|
||||
}
|
||||
data = ntfs_malloc(na->data_size);
|
||||
if (!data)
|
||||
|
|
|
@ -155,7 +155,9 @@ BOOL ntfs_boot_sector_is_ntfs(NTFS_BOOT_SECTOR *b)
|
|||
}
|
||||
|
||||
/* MFT and MFTMirr may not overlap the boot sector or be the same */
|
||||
if (sle64_cmpz(b->mft_lcn) || sle64_cmpz(b->mftmirr_lcn) || sle64_eq(b->mft_lcn, b->mftmirr_lcn)) {
|
||||
if (((s64)sle64_to_cpu(b->mft_lcn) <= 0)
|
||||
|| ((s64)sle64_to_cpu(b->mftmirr_lcn) <= 0)
|
||||
|| sle64_eq(b->mft_lcn, b->mftmirr_lcn)) {
|
||||
ntfs_log_error("Invalid location of MFT or MFTMirr.\n");
|
||||
goto not_ntfs;
|
||||
}
|
||||
|
|
|
@ -752,6 +752,12 @@ s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b)
|
|||
/* If it is a resident attribute, simply use ntfs_attr_pread(). */
|
||||
if (!NAttrNonResident(na))
|
||||
return ntfs_attr_pread(na, pos, count, b);
|
||||
if (na->compression_block_size < NTFS_SB_SIZE) {
|
||||
ntfs_log_error("Unsupported compression block size %ld\n",
|
||||
(long)na->compression_block_size);
|
||||
errno = EOVERFLOW;
|
||||
return (-1);
|
||||
}
|
||||
total = total2 = 0;
|
||||
/* Zero out reads beyond initialized size. */
|
||||
if (pos + count > na->initialized_size) {
|
||||
|
@ -1618,7 +1624,7 @@ static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl,
|
|||
*/
|
||||
|
||||
static s32 ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs,
|
||||
const char *outbuf, s32 count, BOOL compress,
|
||||
char *outbuf, s32 count, BOOL compress,
|
||||
BOOL appending, VCN *update_from)
|
||||
{
|
||||
s32 rounded;
|
||||
|
@ -1639,6 +1645,8 @@ static s32 ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs,
|
|||
if (!compress) {
|
||||
clsz = 1 << na->ni->vol->cluster_size_bits;
|
||||
rounded = ((count - 1) | (clsz - 1)) + 1;
|
||||
if (rounded > count)
|
||||
memset(&outbuf[count], 0, rounded - count);
|
||||
written = write_clusters(na->ni->vol, rl,
|
||||
offs, rounded, outbuf);
|
||||
if (written != rounded)
|
||||
|
@ -1700,6 +1708,12 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos,
|
|||
errno = EIO;
|
||||
return (-1);
|
||||
}
|
||||
if (na->compression_block_size < NTFS_SB_SIZE) {
|
||||
ntfs_log_error("Unsupported compression block size %ld\n",
|
||||
(long)na->compression_block_size);
|
||||
errno = EOVERFLOW;
|
||||
return (-1);
|
||||
}
|
||||
if (wrl->vcn < *update_from)
|
||||
*update_from = wrl->vcn;
|
||||
written = -1; /* default return */
|
||||
|
@ -1881,6 +1895,12 @@ int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs,
|
|||
errno = EIO;
|
||||
return (-1);
|
||||
}
|
||||
if (na->compression_block_size < NTFS_SB_SIZE) {
|
||||
ntfs_log_error("Unsupported compression block size %ld\n",
|
||||
(long)na->compression_block_size);
|
||||
errno = EOVERFLOW;
|
||||
return (-1);
|
||||
}
|
||||
if (wrl->vcn < *update_from)
|
||||
*update_from = wrl->vcn;
|
||||
vol = na->ni->vol;
|
||||
|
|
109
libntfs-3g/dir.c
109
libntfs-3g/dir.c
|
@ -293,6 +293,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
|
|||
(unsigned)index_block_size);
|
||||
goto put_err_out;
|
||||
}
|
||||
/* Consistency check of ir done while fetching attribute */
|
||||
index_end = (u8*)&ir->index + le32_to_cpu(ir->index.index_length);
|
||||
/* The first index entry. */
|
||||
ie = (INDEX_ENTRY*)((u8*)&ir->index +
|
||||
|
@ -305,10 +306,11 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
|
|||
/* Bounds checks. */
|
||||
if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie +
|
||||
sizeof(INDEX_ENTRY_HEADER) > index_end ||
|
||||
(u8*)ie + le16_to_cpu(ie->key_length) >
|
||||
(u8*)ie + le16_to_cpu(ie->length) >
|
||||
index_end) {
|
||||
ntfs_log_error("Index entry out of bounds in inode %lld"
|
||||
"\n", (unsigned long long)dir_ni->mft_no);
|
||||
ntfs_log_error("Index root entry out of bounds in"
|
||||
" inode %lld\n",
|
||||
(unsigned long long)dir_ni->mft_no);
|
||||
goto put_err_out;
|
||||
}
|
||||
/*
|
||||
|
@ -318,9 +320,10 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni,
|
|||
if (!le16_andz(ie->ie_flags, INDEX_ENTRY_END))
|
||||
break;
|
||||
|
||||
if (!le16_to_cpu(ie->length)) {
|
||||
ntfs_log_error("Zero length index entry in inode %lld"
|
||||
"\n", (unsigned long long)dir_ni->mft_no);
|
||||
/* The file name must not overflow from the entry */
|
||||
if (ntfs_index_entry_inconsistent(ie, COLLATION_FILE_NAME,
|
||||
dir_ni->mft_no)) {
|
||||
errno = EIO;
|
||||
goto put_err_out;
|
||||
}
|
||||
/*
|
||||
|
@ -398,37 +401,18 @@ descend_into_child_node:
|
|||
if (br != 1) {
|
||||
if (br != -1)
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Failed to read vcn 0x%llx",
|
||||
(unsigned long long)vcn);
|
||||
ntfs_log_perror("Failed to read vcn 0x%llx from inode %lld",
|
||||
(unsigned long long)vcn,
|
||||
(unsigned long long)ia_na->ni->mft_no);
|
||||
goto close_err_out;
|
||||
}
|
||||
|
||||
if (sle64_to_cpu(ia->index_block_vcn) != vcn) {
|
||||
ntfs_log_error("Actual VCN (0x%llx) of index buffer is different "
|
||||
"from expected VCN (0x%llx).\n",
|
||||
(long long)sle64_to_cpu(ia->index_block_vcn),
|
||||
(long long)vcn);
|
||||
errno = EIO;
|
||||
goto close_err_out;
|
||||
}
|
||||
if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) {
|
||||
ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode 0x%llx "
|
||||
"has a size (%u) differing from the directory "
|
||||
"specified size (%u).\n", (long long)vcn,
|
||||
(unsigned long long)dir_ni->mft_no,
|
||||
(unsigned) le32_to_cpu(ia->index.allocated_size) + 0x18,
|
||||
(unsigned)index_block_size);
|
||||
if (ntfs_index_block_inconsistent((INDEX_BLOCK*)ia, index_block_size,
|
||||
ia_na->ni->mft_no, vcn)) {
|
||||
errno = EIO;
|
||||
goto close_err_out;
|
||||
}
|
||||
index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
|
||||
if (index_end > (u8*)ia + index_block_size) {
|
||||
ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode "
|
||||
"0x%llx exceeds maximum size.\n",
|
||||
(long long)vcn, (unsigned long long)dir_ni->mft_no);
|
||||
errno = EIO;
|
||||
goto close_err_out;
|
||||
}
|
||||
|
||||
/* The first index entry. */
|
||||
ie = (INDEX_ENTRY*)((u8*)&ia->index +
|
||||
|
@ -442,7 +426,7 @@ descend_into_child_node:
|
|||
/* Bounds check. */
|
||||
if ((u8*)ie < (u8*)ia || (u8*)ie +
|
||||
sizeof(INDEX_ENTRY_HEADER) > index_end ||
|
||||
(u8*)ie + le16_to_cpu(ie->key_length) >
|
||||
(u8*)ie + le16_to_cpu(ie->length) >
|
||||
index_end) {
|
||||
ntfs_log_error("Index entry out of bounds in directory "
|
||||
"inode %lld.\n",
|
||||
|
@ -457,10 +441,10 @@ descend_into_child_node:
|
|||
if (!le16_andz(ie->ie_flags, INDEX_ENTRY_END))
|
||||
break;
|
||||
|
||||
if (!le16_to_cpu(ie->length)) {
|
||||
/* The file name must not overflow from the entry */
|
||||
if (ntfs_index_entry_inconsistent(ie, COLLATION_FILE_NAME,
|
||||
dir_ni->mft_no)) {
|
||||
errno = EIO;
|
||||
ntfs_log_error("Zero length index entry in inode %lld"
|
||||
"\n", (unsigned long long)dir_ni->mft_no);
|
||||
goto close_err_out;
|
||||
}
|
||||
/*
|
||||
|
@ -1086,12 +1070,6 @@ static MFT_REF ntfs_mft_get_parent_ref(ntfs_inode *ni)
|
|||
}
|
||||
fn = (FILE_NAME_ATTR*)((u8*)ctx->attr +
|
||||
le16_to_cpu(ctx->attr->value_offset));
|
||||
if ((u8*)fn + le32_to_cpu(ctx->attr->value_length) >
|
||||
(u8*)ctx->attr + le32_to_cpu(ctx->attr->length)) {
|
||||
ntfs_log_error("Corrupt file name attribute in inode %lld.\n",
|
||||
(unsigned long long)ni->mft_no);
|
||||
goto io_err_out;
|
||||
}
|
||||
mref = le64_to_cpu(fn->parent_directory);
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
return mref;
|
||||
|
@ -1250,9 +1228,13 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
|
|||
/* Bounds checks. */
|
||||
if ((u8*)ie < (u8*)ctx->mrec || (u8*)ie +
|
||||
sizeof(INDEX_ENTRY_HEADER) > index_end ||
|
||||
(u8*)ie + le16_to_cpu(ie->key_length) >
|
||||
index_end)
|
||||
(u8*)ie + le16_to_cpu(ie->length) >
|
||||
index_end) {
|
||||
ntfs_log_error("Index root entry out of bounds in"
|
||||
" inode %lld\n",
|
||||
(unsigned long long)dir_ni->mft_no);
|
||||
goto dir_err_out;
|
||||
}
|
||||
/* The last entry cannot contain a name. */
|
||||
if (!le16_andz(ie->ie_flags, INDEX_ENTRY_END))
|
||||
break;
|
||||
|
@ -1263,6 +1245,13 @@ int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos,
|
|||
/* Skip index root entry if continuing previous readdir. */
|
||||
if (ir_pos > (u8*)ie - (u8*)ir)
|
||||
continue;
|
||||
|
||||
/* The file name must not overflow from the entry */
|
||||
if (ntfs_index_entry_inconsistent(ie, COLLATION_FILE_NAME,
|
||||
dir_ni->mft_no)) {
|
||||
errno = EIO;
|
||||
goto dir_err_out;
|
||||
}
|
||||
/*
|
||||
* Submit the directory entry to ntfs_filldir(), which will
|
||||
* invoke the filldir() callback as appropriate.
|
||||
|
@ -1362,33 +1351,12 @@ find_next_index_buffer:
|
|||
}
|
||||
|
||||
ia_start = ia_pos & ~(s64)(index_block_size - 1);
|
||||
if (sle64_to_cpu(ia->index_block_vcn) != ia_start >>
|
||||
index_vcn_size_bits) {
|
||||
ntfs_log_error("Actual VCN (0x%llx) of index buffer is different "
|
||||
"from expected VCN (0x%llx) in inode 0x%llx.\n",
|
||||
(long long)sle64_to_cpu(ia->index_block_vcn),
|
||||
(long long)ia_start >> index_vcn_size_bits,
|
||||
(unsigned long long)dir_ni->mft_no);
|
||||
goto dir_err_out;
|
||||
}
|
||||
if (le32_to_cpu(ia->index.allocated_size) + 0x18 != index_block_size) {
|
||||
ntfs_log_error("Index buffer (VCN 0x%llx) of directory inode %lld "
|
||||
"has a size (%u) differing from the directory "
|
||||
"specified size (%u).\n", (long long)ia_start >>
|
||||
index_vcn_size_bits,
|
||||
(unsigned long long)dir_ni->mft_no,
|
||||
(unsigned) le32_to_cpu(ia->index.allocated_size)
|
||||
+ 0x18, (unsigned)index_block_size);
|
||||
if (ntfs_index_block_inconsistent((INDEX_BLOCK*)ia, index_block_size,
|
||||
ia_na->ni->mft_no, ia_start >> index_vcn_size_bits)) {
|
||||
goto dir_err_out;
|
||||
}
|
||||
index_end = (u8*)&ia->index + le32_to_cpu(ia->index.index_length);
|
||||
if (index_end > (u8*)ia + index_block_size) {
|
||||
ntfs_log_error("Size of index buffer (VCN 0x%llx) of directory inode "
|
||||
"%lld exceeds maximum size.\n",
|
||||
(long long)ia_start >> index_vcn_size_bits,
|
||||
(unsigned long long)dir_ni->mft_no);
|
||||
goto dir_err_out;
|
||||
}
|
||||
|
||||
/* The first index entry. */
|
||||
ie = (INDEX_ENTRY*)((u8*)&ia->index +
|
||||
le32_to_cpu(ia->index.entries_offset));
|
||||
|
@ -1403,7 +1371,7 @@ find_next_index_buffer:
|
|||
/* Bounds checks. */
|
||||
if ((u8*)ie < (u8*)ia || (u8*)ie +
|
||||
sizeof(INDEX_ENTRY_HEADER) > index_end ||
|
||||
(u8*)ie + le16_to_cpu(ie->key_length) >
|
||||
(u8*)ie + le16_to_cpu(ie->length) >
|
||||
index_end) {
|
||||
ntfs_log_error("Index entry out of bounds in directory inode "
|
||||
"%lld.\n", (unsigned long long)dir_ni->mft_no);
|
||||
|
@ -1419,6 +1387,13 @@ find_next_index_buffer:
|
|||
/* Skip index entry if continuing previous readdir. */
|
||||
if (ia_pos - ia_start > (u8*)ie - (u8*)ia)
|
||||
continue;
|
||||
|
||||
/* The file name must not overflow from the entry */
|
||||
if (ntfs_index_entry_inconsistent(ie, COLLATION_FILE_NAME,
|
||||
dir_ni->mft_no)) {
|
||||
errno = EIO;
|
||||
goto dir_err_out;
|
||||
}
|
||||
/*
|
||||
* Submit the directory entry to ntfs_filldir(), which will
|
||||
* invoke the filldir() callback as appropriate.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Copyright (c) 2004-2005 Richard Russon
|
||||
* Copyright (c) 2005-2006 Yura Pakhuchiy
|
||||
* Copyright (c) 2005-2008 Szabolcs Szakacsits
|
||||
* Copyright (c) 2007-2020 Jean-Pierre Andre
|
||||
* Copyright (c) 2007-2021 Jean-Pierre Andre
|
||||
*
|
||||
* This program/include file is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as published
|
||||
|
@ -388,42 +388,6 @@ static INDEX_ENTRY *ntfs_ie_dup_novcn(INDEX_ENTRY *ie)
|
|||
return dup;
|
||||
}
|
||||
|
||||
static int ntfs_ia_check(ntfs_index_context *icx, INDEX_BLOCK *ib, VCN vcn)
|
||||
{
|
||||
u32 ib_size = (unsigned)le32_to_cpu(ib->index.allocated_size) + 0x18;
|
||||
|
||||
ntfs_log_trace("Entering\n");
|
||||
|
||||
if (!ntfs_is_indx_record(ib->magic)) {
|
||||
|
||||
ntfs_log_error("Corrupt index block signature: vcn %lld inode "
|
||||
"%llu\n", (long long)vcn,
|
||||
(unsigned long long)icx->ni->mft_no);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sle64_to_cpu(ib->index_block_vcn) != vcn) {
|
||||
|
||||
ntfs_log_error("Corrupt index block: VCN (%lld) is different "
|
||||
"from expected VCN (%lld) in inode %llu\n",
|
||||
(long long)sle64_to_cpu(ib->index_block_vcn),
|
||||
(long long)vcn,
|
||||
(unsigned long long)icx->ni->mft_no);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ib_size != icx->block_size) {
|
||||
|
||||
ntfs_log_error("Corrupt index block : VCN (%lld) of inode %llu "
|
||||
"has a size (%u) differing from the index "
|
||||
"specified size (%u)\n", (long long)vcn,
|
||||
(unsigned long long)icx->ni->mft_no, ib_size,
|
||||
icx->block_size);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static INDEX_ROOT *ntfs_ir_lookup(ntfs_inode *ni, ntfschar *name,
|
||||
u32 name_len, ntfs_attr_search_ctx **ctx)
|
||||
{
|
||||
|
@ -469,6 +433,133 @@ static INDEX_ROOT *ntfs_ir_lookup2(ntfs_inode *ni, ntfschar *name, u32 len)
|
|||
return ir;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the consistency of an index block
|
||||
*
|
||||
* Make sure the index block does not overflow from the index record.
|
||||
* The size of block is assumed to have been checked to be what is
|
||||
* defined in the index root.
|
||||
*
|
||||
* Returns 0 if no error was found
|
||||
* -1 otherwise (with errno unchanged)
|
||||
*
|
||||
* |<--->| offsetof(INDEX_BLOCK, index)
|
||||
* | |<--->| sizeof(INDEX_HEADER)
|
||||
* | | |
|
||||
* | | | seq index entries unused
|
||||
* |=====|=====|=====|===========================|==============|
|
||||
* | | | | |
|
||||
* | |<--------->| entries_offset | |
|
||||
* | |<---------------- index_length ------->| |
|
||||
* | |<--------------------- allocated_size --------------->|
|
||||
* |<--------------------------- block_size ------------------->|
|
||||
*
|
||||
* size(INDEX_HEADER) <= ent_offset < ind_length <= alloc_size < bk_size
|
||||
*/
|
||||
|
||||
int ntfs_index_block_inconsistent(const INDEX_BLOCK *ib, u32 block_size,
|
||||
u64 inum, VCN vcn)
|
||||
{
|
||||
u32 ib_size = (unsigned)le32_to_cpu(ib->index.allocated_size)
|
||||
+ offsetof(INDEX_BLOCK, index);
|
||||
|
||||
if (!ntfs_is_indx_record(ib->magic)) {
|
||||
ntfs_log_error("Corrupt index block signature: vcn %lld inode "
|
||||
"%llu\n", (long long)vcn,
|
||||
(unsigned long long)inum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sle64_to_cpu(ib->index_block_vcn) != vcn) {
|
||||
ntfs_log_error("Corrupt index block: VCN (%lld) is different "
|
||||
"from expected VCN (%lld) in inode %llu\n",
|
||||
(long long)sle64_to_cpu(ib->index_block_vcn),
|
||||
(long long)vcn,
|
||||
(unsigned long long)inum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ib_size != block_size) {
|
||||
ntfs_log_error("Corrupt index block : VCN (%lld) of inode %llu "
|
||||
"has a size (%u) differing from the index "
|
||||
"specified size (%u)\n", (long long)vcn,
|
||||
(unsigned long long)inum, ib_size,
|
||||
(unsigned int)block_size);
|
||||
return -1;
|
||||
}
|
||||
if (le32_to_cpu(ib->index.entries_offset) < sizeof(INDEX_HEADER)) {
|
||||
ntfs_log_error("Invalid index entry offset in inode %lld\n",
|
||||
(unsigned long long)inum);
|
||||
return -1;
|
||||
}
|
||||
if (le32_to_cpu(ib->index.index_length)
|
||||
<= le32_to_cpu(ib->index.entries_offset)) {
|
||||
ntfs_log_error("No space for index entries in inode %lld\n",
|
||||
(unsigned long long)inum);
|
||||
return -1;
|
||||
}
|
||||
if (le32_to_cpu(ib->index.allocated_size)
|
||||
< le32_to_cpu(ib->index.index_length)) {
|
||||
ntfs_log_error("Index entries overflow in inode %lld\n",
|
||||
(unsigned long long)inum);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check the consistency of an index entry
|
||||
*
|
||||
* Make sure data and key do not overflow from entry.
|
||||
* As a side effect, an entry with zero length is rejected.
|
||||
* This entry must be a full one (no INDEX_ENTRY_END flag), and its
|
||||
* length must have been checked beforehand to not overflow from the
|
||||
* index record.
|
||||
*
|
||||
* Returns 0 if no error was found
|
||||
* -1 otherwise (with errno unchanged)
|
||||
*/
|
||||
|
||||
int ntfs_index_entry_inconsistent(const INDEX_ENTRY *ie,
|
||||
COLLATION_RULES collation_rule, u64 inum)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = 0;
|
||||
if (!le16_cmpz(ie->key_length)
|
||||
&& ((le16_to_cpu(ie->key_length) + offsetof(INDEX_ENTRY, key))
|
||||
> le16_to_cpu(ie->length))) {
|
||||
ntfs_log_error("Overflow from index entry in inode %lld\n",
|
||||
(long long)inum);
|
||||
ret = -1;
|
||||
|
||||
} else
|
||||
if (le32_eq(collation_rule, COLLATION_FILE_NAME)) {
|
||||
if ((offsetof(INDEX_ENTRY, key.file_name.file_name)
|
||||
+ ie->key.file_name.file_name_length
|
||||
* sizeof(ntfschar))
|
||||
> le16_to_cpu(ie->length)) {
|
||||
ntfs_log_error("File name overflow from index"
|
||||
" entry in inode %lld\n",
|
||||
(long long)inum);
|
||||
ret = -1;
|
||||
}
|
||||
} else {
|
||||
if (!le16_cmpz(ie->data_length)
|
||||
&& ((le16_to_cpu(ie->data_offset)
|
||||
+ le16_to_cpu(ie->data_length))
|
||||
> le16_to_cpu(ie->length))) {
|
||||
ntfs_log_error("Data overflow from index"
|
||||
" entry in inode %lld\n",
|
||||
(long long)inum);
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a key in the index block.
|
||||
*
|
||||
|
@ -521,6 +612,12 @@ static int ntfs_ie_lookup(const void *key, const int key_len,
|
|||
ntfs_log_error("Collation function not defined\n");
|
||||
errno = EOPNOTSUPP;
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
/* Make sure key and data do not overflow from entry */
|
||||
if (ntfs_index_entry_inconsistent(ie, icx->ir->collation_rule,
|
||||
icx->ni->mft_no)) {
|
||||
errno = EIO;
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
rc = icx->collate(icx->ni->vol, key, key_len,
|
||||
&ie->key, le16_to_cpu(ie->key_length));
|
||||
|
@ -606,8 +703,11 @@ static int ntfs_ib_read(ntfs_index_context *icx, VCN vcn, INDEX_BLOCK *dst)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (ntfs_ia_check(icx, dst, vcn))
|
||||
if (ntfs_index_block_inconsistent((INDEX_BLOCK*)dst, icx->block_size,
|
||||
icx->ia_na->ni->mft_no, vcn)) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -703,6 +803,7 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic
|
|||
else
|
||||
icx->vcn_size_bits = NTFS_BLOCK_SIZE_BITS;
|
||||
/* get the appropriate collation function */
|
||||
icx->ir = ir;
|
||||
icx->collate = ntfs_get_collate_function(ir->collation_rule);
|
||||
if (!icx->collate) {
|
||||
err = errno = EOPNOTSUPP;
|
||||
|
@ -712,10 +813,6 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic
|
|||
}
|
||||
|
||||
old_vcn = VCN_INDEX_ROOT_PARENT;
|
||||
/*
|
||||
* FIXME: check for both ir and ib that the first index entry is
|
||||
* within the index block.
|
||||
*/
|
||||
ret = ntfs_ie_lookup(key, key_len, icx, &ir->index, &vcn, &ie);
|
||||
if (ret == STATUS_ERROR) {
|
||||
err = errno;
|
||||
|
@ -782,6 +879,10 @@ descend_into_child_node:
|
|||
err_out:
|
||||
icx->bad_index = TRUE; /* Force icx->* to be freed */
|
||||
err_lookup:
|
||||
if (icx->actx) {
|
||||
ntfs_attr_put_search_ctx(icx->actx);
|
||||
icx->actx = NULL;
|
||||
}
|
||||
free(ib);
|
||||
if (!err)
|
||||
err = EIO;
|
||||
|
|
|
@ -189,6 +189,13 @@ static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref)
|
|||
" %lld", (long long)MREF(mref));
|
||||
goto put_err_out;
|
||||
}
|
||||
lthle = ctx->attr->value_length;
|
||||
if (le32_to_cpu(lthle) < offsetof(STANDARD_INFORMATION, owner_id)) {
|
||||
ntfs_log_error("Corrupt STANDARD_INFORMATION in base"
|
||||
" record %lld\n",
|
||||
(long long)MREF(mref));
|
||||
goto put_err_out;
|
||||
}
|
||||
std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr +
|
||||
le16_to_cpu(ctx->attr->value_offset));
|
||||
ni->flags = std_info->file_attributes;
|
||||
|
@ -196,10 +203,9 @@ static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref)
|
|||
ni->last_data_change_time = std_info->last_data_change_time;
|
||||
ni->last_mft_change_time = std_info->last_mft_change_time;
|
||||
ni->last_access_time = std_info->last_access_time;
|
||||
/* JPA insert v3 extensions if present */
|
||||
/* length may be seen as 72 (v1.x) or 96 (v3.x) */
|
||||
lthle = ctx->attr->length;
|
||||
if (le32_to_cpu(lthle) > sizeof(STANDARD_INFORMATION)) {
|
||||
/* Insert v3 extensions if present */
|
||||
/* length may be seen as 48 (v1.x) or 72 (v3.x) */
|
||||
if (le32_to_cpu(lthle) >= offsetof(STANDARD_INFORMATION, v3_end)) {
|
||||
set_nino_flag(ni, v3_Extensions);
|
||||
ni->owner_id = std_info->owner_id;
|
||||
ni->security_id = std_info->security_id;
|
||||
|
@ -225,9 +231,9 @@ static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref)
|
|||
l = ntfs_get_attribute_value_length(ctx->attr);
|
||||
if (!l)
|
||||
goto put_err_out;
|
||||
if (l > 0x40000) {
|
||||
if ((u64)l > 0x40000) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Too large attrlist attribute (%lld), inode "
|
||||
ntfs_log_perror("Too large attrlist attribute (%llu), inode "
|
||||
"%lld", (long long)l, (long long)MREF(mref));
|
||||
goto put_err_out;
|
||||
}
|
||||
|
@ -760,13 +766,13 @@ static int ntfs_inode_sync_standard_information(ntfs_inode *ni)
|
|||
|
||||
/* JPA update v3.x extensions, ensuring consistency */
|
||||
|
||||
lthle = ctx->attr->length;
|
||||
lthle = ctx->attr->value_length;
|
||||
lth = le32_to_cpu(lthle);
|
||||
if (test_nino_flag(ni, v3_Extensions)
|
||||
&& (lth <= sizeof(STANDARD_INFORMATION)))
|
||||
&& (lth < offsetof(STANDARD_INFORMATION, v3_end)))
|
||||
ntfs_log_error("bad sync of standard information\n");
|
||||
|
||||
if (lth > sizeof(STANDARD_INFORMATION)) {
|
||||
if (lth >= offsetof(STANDARD_INFORMATION, v3_end)) {
|
||||
std_info->owner_id = ni->owner_id;
|
||||
std_info->security_id = ni->security_id;
|
||||
std_info->quota_charged = ni->quota_charged;
|
||||
|
|
|
@ -368,12 +368,14 @@ runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count,
|
|||
/* Allocate the bitmap bit. */
|
||||
*byte |= bit;
|
||||
writeback = 1;
|
||||
if (vol->free_clusters <= 0)
|
||||
ntfs_log_error("Non-positive free clusters "
|
||||
"(%lld)!\n",
|
||||
if (NVolFreeSpaceKnown(vol)) {
|
||||
if (vol->free_clusters <= 0)
|
||||
ntfs_log_error("Non-positive free"
|
||||
" clusters (%lld)!\n",
|
||||
(long long)vol->free_clusters);
|
||||
else
|
||||
vol->free_clusters--;
|
||||
else
|
||||
vol->free_clusters--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Coalesce with previous run if adjacent LCNs.
|
||||
|
@ -602,7 +604,8 @@ int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl)
|
|||
ret = 0;
|
||||
out:
|
||||
vol->free_clusters += nr_freed;
|
||||
if (vol->free_clusters > vol->nr_clusters)
|
||||
if (NVolFreeSpaceKnown(vol)
|
||||
&& (vol->free_clusters > vol->nr_clusters))
|
||||
ntfs_log_error("Too many free clusters (%lld > %lld)!",
|
||||
(long long)vol->free_clusters,
|
||||
(long long)vol->nr_clusters);
|
||||
|
|
|
@ -172,6 +172,15 @@ int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref,
|
|||
cnt = vol->mftmirr_size - m;
|
||||
if (cnt > count)
|
||||
cnt = count;
|
||||
if ((m + cnt) > vol->mftmirr_na->initialized_size >>
|
||||
vol->mft_record_size_bits) {
|
||||
errno = ESPIPE;
|
||||
ntfs_log_perror("Trying to write non-allocated mftmirr"
|
||||
" records (%lld > %lld)", (long long)m + cnt,
|
||||
(long long)vol->mftmirr_na->initialized_size >>
|
||||
vol->mft_record_size_bits);
|
||||
return -1;
|
||||
}
|
||||
bmirr = ntfs_malloc(cnt * vol->mft_record_size);
|
||||
if (!bmirr)
|
||||
return -1;
|
||||
|
@ -210,11 +219,26 @@ int ntfs_mft_records_write(const ntfs_volume *vol, const MFT_REF mref,
|
|||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the consistency of an MFT record
|
||||
*
|
||||
* Make sure its general fields are safe, then examine all its
|
||||
* attributes and apply generic checks to them.
|
||||
* The attribute checks are skipped when a record is being read in
|
||||
* order to collect its sequence number for creating a new record.
|
||||
*
|
||||
* Returns 0 if the checks are successful
|
||||
* -1 with errno = EIO otherwise
|
||||
*/
|
||||
|
||||
int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref,
|
||||
MFT_RECORD *m)
|
||||
{
|
||||
ATTR_RECORD *a;
|
||||
ATTR_TYPES previous_type;
|
||||
int ret = -1;
|
||||
u32 offset;
|
||||
s32 space;
|
||||
|
||||
if (!ntfs_is_file_record(m->magic)) {
|
||||
if (!NVolNoFixupWarn(vol))
|
||||
|
@ -231,13 +255,57 @@ int ntfs_mft_record_check(const ntfs_volume *vol, const MFT_REF mref,
|
|||
le32_to_cpu(m->bytes_allocated));
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (!NVolNoFixupWarn(vol)
|
||||
&& (le32_to_cpu(m->bytes_in_use) > vol->mft_record_size)) {
|
||||
ntfs_log_error("Record %llu has corrupt in-use size "
|
||||
"(%u > %u)\n", (unsigned long long)MREF(mref),
|
||||
(int)le32_to_cpu(m->bytes_in_use),
|
||||
(int)vol->mft_record_size);
|
||||
goto err_out;
|
||||
}
|
||||
if (le16_to_cpu(m->attrs_offset) & 7) {
|
||||
ntfs_log_error("Attributes badly aligned in record %llu\n",
|
||||
(unsigned long long)MREF(mref));
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
a = (ATTR_RECORD *)((char *)m + le16_to_cpu(m->attrs_offset));
|
||||
if (p2n(a) < p2n(m) || (char *)a > (char *)m + vol->mft_record_size) {
|
||||
ntfs_log_error("Record %llu is corrupt\n",
|
||||
(unsigned long long)MREF(mref));
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
if (!NVolNoFixupWarn(vol)) {
|
||||
offset = le16_to_cpu(m->attrs_offset);
|
||||
space = le32_to_cpu(m->bytes_in_use) - offset;
|
||||
a = (ATTR_RECORD*)((char*)m + offset);
|
||||
previous_type = AT_STANDARD_INFORMATION;
|
||||
while ((space >= (s32)offsetof(ATTR_RECORD, resident_end))
|
||||
&& !le32_eq(a->type, AT_END)
|
||||
&& (le32_to_cpu(a->type) >= le32_to_cpu(previous_type))) {
|
||||
if ((le32_to_cpu(a->length) <= (u32)space)
|
||||
&& !(le32_to_cpu(a->length) & 7)) {
|
||||
if (!ntfs_attr_inconsistent(a, mref)) {
|
||||
previous_type = a->type;
|
||||
offset += le32_to_cpu(a->length);
|
||||
space -= le32_to_cpu(a->length);
|
||||
a = (ATTR_RECORD*)((char*)m + offset);
|
||||
} else
|
||||
goto err_out;
|
||||
} else {
|
||||
ntfs_log_error("Corrupted MFT record %llu\n",
|
||||
(unsigned long long)MREF(mref));
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
/* We are supposed to reach an AT_END */
|
||||
if ((space < 4) || !le32_eq(a->type, AT_END)) {
|
||||
ntfs_log_error("Bad end of MFT record %llu\n",
|
||||
(unsigned long long)MREF(mref));
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
err_out:
|
||||
|
|
|
@ -221,15 +221,24 @@ static int __ntfs_volume_release(ntfs_volume *v)
|
|||
return errno ? -1 : 0;
|
||||
}
|
||||
|
||||
static void ntfs_attr_setup_flag(ntfs_inode *ni)
|
||||
static int ntfs_attr_setup_flag(ntfs_inode *ni)
|
||||
{
|
||||
STANDARD_INFORMATION *si;
|
||||
s64 lth;
|
||||
int r;
|
||||
|
||||
si = ntfs_attr_readall(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0, NULL);
|
||||
si = (STANDARD_INFORMATION*)ntfs_attr_readall(ni,
|
||||
AT_STANDARD_INFORMATION, AT_UNNAMED, 0, <h);
|
||||
if (si) {
|
||||
ni->flags = si->file_attributes;
|
||||
if ((u64)lth >= offsetof(STANDARD_INFORMATION, owner_id))
|
||||
ni->flags = si->file_attributes;
|
||||
free(si);
|
||||
r = 0;
|
||||
} else {
|
||||
ntfs_log_error("Failed to get standard information of $MFT\n");
|
||||
r = -1;
|
||||
}
|
||||
return (r);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -303,16 +312,19 @@ static int ntfs_mft_load(ntfs_volume *vol)
|
|||
ntfs_log_error("Failed to get value of $MFT/$ATTR_LIST.\n");
|
||||
goto io_error_exit;
|
||||
}
|
||||
if (l != vol->mft_ni->attr_list_size) {
|
||||
if ((l != vol->mft_ni->attr_list_size)
|
||||
|| (l < (s64)offsetof(ATTR_LIST_ENTRY, name))) {
|
||||
ntfs_log_error("Partial read of $MFT/$ATTR_LIST (%lld != "
|
||||
"%u).\n", (long long)l,
|
||||
vol->mft_ni->attr_list_size);
|
||||
"%u or < %d).\n", (long long)l,
|
||||
vol->mft_ni->attr_list_size,
|
||||
(int)offsetof(ATTR_LIST_ENTRY, name));
|
||||
goto io_error_exit;
|
||||
}
|
||||
|
||||
mft_has_no_attr_list:
|
||||
|
||||
ntfs_attr_setup_flag(vol->mft_ni);
|
||||
if (ntfs_attr_setup_flag(vol->mft_ni))
|
||||
goto error_exit;
|
||||
|
||||
/* We now have a fully setup ntfs inode for $MFT in vol->mft_ni. */
|
||||
|
||||
|
@ -355,6 +367,11 @@ mft_has_no_attr_list:
|
|||
ntfs_log_perror("ntfs_mapping_pairs_decompress() failed");
|
||||
goto error_exit;
|
||||
}
|
||||
/* Make sure $DATA is the MFT itself */
|
||||
if (nrl->lcn != vol->mft_lcn) {
|
||||
ntfs_log_perror("The MFT is not self-contained");
|
||||
goto error_exit;
|
||||
}
|
||||
vol->mft_na->rl = nrl;
|
||||
|
||||
/* Get the lowest vcn for the next extent. */
|
||||
|
@ -448,6 +465,12 @@ static int ntfs_mftmirr_load(ntfs_volume *vol)
|
|||
ntfs_log_perror("Failed to map runlist of $MFTMirr/$DATA");
|
||||
goto error_exit;
|
||||
}
|
||||
if (vol->mftmirr_na->rl->lcn != vol->mftmirr_lcn) {
|
||||
ntfs_log_error("Bad $MFTMirr lcn 0x%llx, want 0x%llx\n",
|
||||
(long long)vol->mftmirr_na->rl->lcn,
|
||||
(long long)vol->mftmirr_lcn);
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -601,6 +624,10 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev,
|
|||
vol->mft_zone_end = vol->mft_lcn + mft_zone_size;
|
||||
while (vol->mft_zone_end >= vol->nr_clusters) {
|
||||
mft_zone_size >>= 1;
|
||||
if (!mft_zone_size) {
|
||||
errno = EINVAL;
|
||||
goto error_exit;
|
||||
}
|
||||
vol->mft_zone_end = vol->mft_lcn + mft_zone_size;
|
||||
}
|
||||
ntfs_log_debug("mft_zone_end = 0x%llx\n", (long long)vol->mft_zone_end);
|
||||
|
@ -949,6 +976,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
|
|||
}
|
||||
goto error_exit;
|
||||
}
|
||||
for (i = 0; (i < l) && (i < FILE_first_user); ++i)
|
||||
if (ntfs_mft_record_check(vol, FILE_MFT + i,
|
||||
(MFT_RECORD*)(m + i*vol->mft_record_size)))
|
||||
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) {
|
||||
|
@ -958,6 +989,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
|
|||
}
|
||||
vol->mftmirr_size = l;
|
||||
}
|
||||
for (i = 0; (i < l) && (i < FILE_first_user); ++i)
|
||||
if (ntfs_mft_record_check(vol, FILE_MFT + i,
|
||||
(MFT_RECORD*)(m2 + i*vol->mft_record_size)))
|
||||
goto error_exit;
|
||||
ntfs_log_debug("Comparing $MFTMirr to $MFT...\n");
|
||||
/* Windows 10 does not update the full $MFTMirr any more */
|
||||
for (i = 0; (i < vol->mftmirr_size) && (i < FILE_first_user); ++i) {
|
||||
|
@ -1048,19 +1083,19 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
|
|||
na = ntfs_attr_open(ni, AT_DATA, AT_UNNAMED, 0);
|
||||
if (!na) {
|
||||
ntfs_log_perror("Failed to open ntfs attribute");
|
||||
ntfs_inode_close(ni);
|
||||
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.
|
||||
* 2-byte Unicode characters. Anyway we currently can only process
|
||||
* such characters.
|
||||
*/
|
||||
if (na->data_size & ~0x1ffffffffULL) {
|
||||
ntfs_log_error("Error: Upcase table is too big (max 32-bit "
|
||||
"allowed).\n");
|
||||
if ((na->data_size - 2) & ~0x1fffeULL) {
|
||||
ntfs_log_error("Error: Upcase table is invalid (want size even "
|
||||
"<= 131072).\n");
|
||||
errno = EINVAL;
|
||||
goto error_exit;
|
||||
goto bad_upcase;
|
||||
}
|
||||
if (vol->upcase_len != na->data_size >> 1) {
|
||||
vol->upcase_len = na->data_size >> 1;
|
||||
|
@ -1068,7 +1103,7 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
|
|||
free(vol->upcase);
|
||||
vol->upcase = ntfs_malloc(na->data_size);
|
||||
if (!vol->upcase)
|
||||
goto error_exit;
|
||||
goto bad_upcase;
|
||||
}
|
||||
/* Read in the $DATA attribute value into the buffer. */
|
||||
l = ntfs_attr_pread(na, 0, na->data_size, vol->upcase);
|
||||
|
@ -1077,7 +1112,7 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
|
|||
"(%lld != %lld).\n", (long long)l,
|
||||
(long long)na->data_size);
|
||||
errno = EIO;
|
||||
goto error_exit;
|
||||
goto bad_upcase;
|
||||
}
|
||||
/* Done with the $UpCase mft record. */
|
||||
ntfs_attr_close(na);
|
||||
|
@ -1213,10 +1248,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
|
|||
ntfs_log_perror("Failed to open ntfs attribute");
|
||||
goto error_exit;
|
||||
}
|
||||
/* Check we don't overflow 32-bits. */
|
||||
if (na->data_size > 0xffffffffLL) {
|
||||
/* Check we don't overflow 24-bits. */
|
||||
if ((u64)na->data_size > 0xffffffLL) {
|
||||
ntfs_log_error("Attribute definition table is too big (max "
|
||||
"32-bit allowed).\n");
|
||||
"24-bit allowed).\n");
|
||||
errno = EINVAL;
|
||||
goto error_exit;
|
||||
}
|
||||
|
@ -1282,6 +1317,10 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, ntfs_mount_flags flags)
|
|||
}
|
||||
|
||||
return vol;
|
||||
bad_upcase :
|
||||
ntfs_attr_close(na);
|
||||
ntfs_inode_close(ni);
|
||||
goto error_exit;
|
||||
io_error_exit:
|
||||
errno = EIO;
|
||||
error_exit:
|
||||
|
@ -1855,8 +1894,10 @@ int ntfs_volume_get_free_space(ntfs_volume *vol)
|
|||
|
||||
if (vol->free_mft_records < 0)
|
||||
ntfs_log_perror("Failed to calculate free MFT records");
|
||||
else
|
||||
else {
|
||||
NVolSetFreeSpaceKnown(vol);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
|
|
@ -1155,7 +1155,7 @@ close_attr:
|
|||
if (!fstat(fileno(in),&st)) {
|
||||
s64 change_time = st.st_mtime*10000000LL
|
||||
+ NTFS_TIME_OFFSET;
|
||||
out->last_data_change_time = cpu_to_le64(change_time);
|
||||
out->last_data_change_time = cpu_to_sle64(change_time);
|
||||
ntfs_inode_update_times(out, 0);
|
||||
} else {
|
||||
ntfs_log_error("Failed to get the time stamp.\n");
|
||||
|
|
|
@ -780,14 +780,19 @@ static ATTR_RECORD *find_unnamed_attr(MFT_RECORD *mrec, ATTR_TYPES type)
|
|||
{
|
||||
ATTR_RECORD *a;
|
||||
u32 offset;
|
||||
s32 space;
|
||||
|
||||
/* fetch the requested attribute */
|
||||
offset = le16_to_cpu(mrec->attrs_offset);
|
||||
space = le32_to_cpu(mrec->bytes_in_use) - offset;
|
||||
a = (ATTR_RECORD*)((char*)mrec + offset);
|
||||
while ((offset < le32_to_cpu(mrec->bytes_in_use))
|
||||
while ((space >= (s32)offsetof(ATTR_RECORD, resident_end))
|
||||
&& !le32_eq(a->type, AT_END)
|
||||
&& (le32_to_cpu(a->length) <= (u32)space)
|
||||
&& !(le32_to_cpu(a->length) & 7)
|
||||
&& (!le32_eq(a->type, type) || a->name_length)) {
|
||||
offset += le32_to_cpu(a->length);
|
||||
space -= le32_to_cpu(a->length);
|
||||
a = (ATTR_RECORD*)((char*)mrec + offset);
|
||||
}
|
||||
if ((offset >= le32_to_cpu(mrec->bytes_in_use))
|
||||
|
@ -823,7 +828,8 @@ static BOOL short_mft_selfloc_condition(struct MFT_SELF_LOCATED *selfloc)
|
|||
vol->mft_record_size, mft0)
|
||||
== vol->mft_record_size)
|
||||
&& !ntfs_mst_post_read_fixup((NTFS_RECORD*)mft0,
|
||||
vol->mft_record_size)) {
|
||||
vol->mft_record_size)
|
||||
&& !ntfs_mft_record_check(vol, 0, mft0)) {
|
||||
a = find_unnamed_attr(mft0,AT_DATA);
|
||||
if (a
|
||||
&& a->non_resident
|
||||
|
@ -961,7 +967,9 @@ static BOOL self_mapped_selfloc_condition(struct MFT_SELF_LOCATED *selfloc)
|
|||
if ((ntfs_pread(vol->dev, offs, vol->mft_record_size,
|
||||
mft1) == vol->mft_record_size)
|
||||
&& !ntfs_mst_post_read_fixup((NTFS_RECORD*)mft1,
|
||||
vol->mft_record_size)) {
|
||||
vol->mft_record_size)
|
||||
&& !ntfs_mft_record_check(vol, inum, mft1)) {
|
||||
|
||||
lowest_vcn = (SELFLOC_LIMIT*vol->mft_record_size)
|
||||
>> vol->cluster_size_bits;
|
||||
a = find_unnamed_attr(mft1,AT_DATA);
|
||||
|
@ -1017,7 +1025,8 @@ static BOOL spare_record_selfloc_condition(struct MFT_SELF_LOCATED *selfloc)
|
|||
if ((ntfs_pread(vol->dev, offs, vol->mft_record_size,
|
||||
mft2) == vol->mft_record_size)
|
||||
&& !ntfs_mst_post_read_fixup((NTFS_RECORD*)mft2,
|
||||
vol->mft_record_size)) {
|
||||
vol->mft_record_size)
|
||||
&& !ntfs_mft_record_check(vol, inum, mft2)) {
|
||||
if (le64_cmpz(mft2->base_mft_record)
|
||||
&& !le16_andz(mft2->flags, MFT_RECORD_IN_USE)
|
||||
&& !find_unnamed_attr(mft2,AT_ATTRIBUTE_LIST)
|
||||
|
|
|
@ -263,7 +263,7 @@ static const char *usage_msg =
|
|||
"Copyright (C) 2005-2007 Yura Pakhuchiy\n"
|
||||
"Copyright (C) 2006-2009 Szabolcs Szakacsits\n"
|
||||
"Copyright (C) 2007-2021 Jean-Pierre Andre\n"
|
||||
"Copyright (C) 2009 Erik Larsson\n"
|
||||
"Copyright (C) 2009-2020 Erik Larsson\n"
|
||||
"\n"
|
||||
"Usage: %s [-o option[,...]] <device|image_file> <mount_point>\n"
|
||||
"\n"
|
||||
|
@ -4347,8 +4347,7 @@ static int ntfs_open(const char *device)
|
|||
if (ctx->ignore_case && ntfs_set_ignore_case(vol))
|
||||
goto err_out;
|
||||
|
||||
vol->free_clusters = ntfs_attr_get_free_bits(vol->lcnbmp_na);
|
||||
if (vol->free_clusters < 0) {
|
||||
if (ntfs_volume_get_free_space(ctx->vol)) {
|
||||
ntfs_log_perror("Failed to read NTFS $Bitmap");
|
||||
goto err_out;
|
||||
}
|
||||
|
@ -4368,9 +4367,12 @@ static int ntfs_open(const char *device)
|
|||
}
|
||||
|
||||
errno = 0;
|
||||
goto out;
|
||||
err_out:
|
||||
if (!errno) /* Make sure to return an error */
|
||||
errno = EIO;
|
||||
out :
|
||||
return ntfs_volume_error(errno);
|
||||
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
|
|
|
@ -198,7 +198,7 @@ static const char *usage_msg =
|
|||
"Copyright (C) 2005-2007 Yura Pakhuchiy\n"
|
||||
"Copyright (C) 2006-2009 Szabolcs Szakacsits\n"
|
||||
"Copyright (C) 2007-2021 Jean-Pierre Andre\n"
|
||||
"Copyright (C) 2009 Erik Larsson\n"
|
||||
"Copyright (C) 2009-2020 Erik Larsson\n"
|
||||
"\n"
|
||||
"Usage: %s [-o option[,...]] <device|image_file> <mount_point>\n"
|
||||
"\n"
|
||||
|
@ -3965,6 +3965,9 @@ static struct fuse_operations ntfs_3g_ops = {
|
|||
.rmdir = ntfs_fuse_rmdir,
|
||||
#ifdef HAVE_UTIMENSAT
|
||||
.utimens = ntfs_fuse_utimens,
|
||||
#if defined(linux) & !defined(FUSE_INTERNAL) & (FUSE_VERSION < 30)
|
||||
.flag_utime_omit_ok = 1,
|
||||
#endif /* defined(linux) & !defined(FUSE_INTERNAL) */
|
||||
#else
|
||||
.utime = ntfs_fuse_utime,
|
||||
#endif
|
||||
|
@ -4053,8 +4056,7 @@ static int ntfs_open(const char *device)
|
|||
!ctx->hide_hid_files, ctx->hide_dot_files))
|
||||
goto err_out;
|
||||
|
||||
ctx->vol->free_clusters = ntfs_attr_get_free_bits(ctx->vol->lcnbmp_na);
|
||||
if (ctx->vol->free_clusters < 0) {
|
||||
if (ntfs_volume_get_free_space(ctx->vol)) {
|
||||
ntfs_log_perror("Failed to read NTFS $Bitmap");
|
||||
goto err_out;
|
||||
}
|
||||
|
@ -4073,9 +4075,12 @@ static int ntfs_open(const char *device)
|
|||
}
|
||||
|
||||
errno = 0;
|
||||
goto out;
|
||||
err_out:
|
||||
if (!errno)
|
||||
errno = EIO;
|
||||
out :
|
||||
return ntfs_volume_error(errno);
|
||||
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
|
|
Loading…
Reference in New Issue