Merge tag '2021.8.22' into edge.strict_endians

Conflicts:
	libntfs-3g/attrib.c
	libntfs-3g/bootsect.c
	ntfsprogs/ntfsfix.c
edge.strict_endians
Erik Larsson 2021-09-06 15:12:08 +02:00
commit 11a3997a0d
21 changed files with 751 additions and 209 deletions

View File

@ -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
View File

@ -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
View File

@ -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.

View 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

View File

@ -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 */

View File

@ -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__;

View File

@ -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

View File

@ -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);

View File

@ -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)

View File

@ -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;
}

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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:

View File

@ -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, &lth);
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);
}

View File

@ -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");

View File

@ -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)

View File

@ -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)

View File

@ -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)