Merge git://github.com/Paragon-Software-Group/linux-ntfs3

Merge NTFSv3 filesystem from Konstantin Komarov:
 "This patch adds NTFS Read-Write driver to fs/ntfs3.

  Having decades of expertise in commercial file systems development and
  huge test coverage, we at Paragon Software GmbH want to make our
  contribution to the Open Source Community by providing implementation
  of NTFS Read-Write driver for the Linux Kernel.

  This is fully functional NTFS Read-Write driver. Current version works
  with NTFS (including v3.1) and normal/compressed/sparse files and
  supports journal replaying.

  We plan to support this version after the codebase once merged, and
  add new features and fix bugs. For example, full journaling support
  over JBD will be added in later updates"

Link: https://lore.kernel.org/lkml/20210729134943.778917-1-almaz.alexandrovich@paragon-software.com/
Link: https://lore.kernel.org/lkml/aa4aa155-b9b2-9099-b7a2-349d8d9d8fbd@paragon-software.com/

* git://github.com/Paragon-Software-Group/linux-ntfs3: (35 commits)
  fs/ntfs3: Change how module init/info messages are displayed
  fs/ntfs3: Remove GPL boilerplates from decompress lib files
  fs/ntfs3: Remove unnecessary condition checking from ntfs_file_read_iter
  fs/ntfs3: Fix integer overflow in ni_fiemap with fiemap_prep()
  fs/ntfs3: Restyle comments to better align with kernel-doc
  fs/ntfs3: Rework file operations
  fs/ntfs3: Remove fat ioctl's from ntfs3 driver for now
  fs/ntfs3: Restyle comments to better align with kernel-doc
  fs/ntfs3: Fix error handling in indx_insert_into_root()
  fs/ntfs3: Potential NULL dereference in hdr_find_split()
  fs/ntfs3: Fix error code in indx_add_allocate()
  fs/ntfs3: fix an error code in ntfs_get_acl_ex()
  fs/ntfs3: add checks for allocation failure
  fs/ntfs3: Use kcalloc/kmalloc_array over kzalloc/kmalloc
  fs/ntfs3: Do not use driver own alloc wrappers
  fs/ntfs3: Use kernel ALIGN macros over driver specific
  fs/ntfs3: Restyle comment block in ni_parse_reparse()
  fs/ntfs3: Remove unused including <linux/version.h>
  fs/ntfs3: Fix fall-through warnings for Clang
  fs/ntfs3: Fix one none utf8 char in source file
  ...
This commit is contained in:
Linus Torvalds 2021-09-04 11:15:50 -07:00
Родитель 6abaa83c73 2e3a51b59e
Коммит f7464060f7
33 изменённых файлов: 31018 добавлений и 0 удалений

Просмотреть файл

@ -101,6 +101,7 @@ Documentation for filesystem implementations.
nilfs2
nfs/index
ntfs
ntfs3
ocfs2
ocfs2-online-filecheck
omfs

Просмотреть файл

@ -0,0 +1,106 @@
.. SPDX-License-Identifier: GPL-2.0
=====
NTFS3
=====
Summary and Features
====================
NTFS3 is fully functional NTFS Read-Write driver. The driver works with
NTFS versions up to 3.1, normal/compressed/sparse files
and journal replaying. File system type to use on mount is 'ntfs3'.
- This driver implements NTFS read/write support for normal, sparse and
compressed files.
- Supports native journal replaying;
- Supports extended attributes
Predefined extended attributes:
- 'system.ntfs_security' gets/sets security
descriptor (SECURITY_DESCRIPTOR_RELATIVE)
- 'system.ntfs_attrib' gets/sets ntfs file/dir attributes.
Note: applied to empty files, this allows to switch type between
sparse(0x200), compressed(0x800) and normal;
- Supports NFS export of mounted NTFS volumes.
Mount Options
=============
The list below describes mount options supported by NTFS3 driver in addition to
generic ones.
===============================================================================
nls=name This option informs the driver how to interpret path
strings and translate them to Unicode and back. If
this option is not set, the default codepage will be
used (CONFIG_NLS_DEFAULT).
Examples:
'nls=utf8'
uid=
gid=
umask= Controls the default permissions for files/directories created
after the NTFS volume is mounted.
fmask=
dmask= Instead of specifying umask which applies both to
files and directories, fmask applies only to files and
dmask only to directories.
nohidden Files with the Windows-specific HIDDEN (FILE_ATTRIBUTE_HIDDEN)
attribute will not be shown under Linux.
sys_immutable Files with the Windows-specific SYSTEM
(FILE_ATTRIBUTE_SYSTEM) attribute will be marked as system
immutable files.
discard Enable support of the TRIM command for improved performance
on delete operations, which is recommended for use with the
solid-state drives (SSD).
force Forces the driver to mount partitions even if 'dirty' flag
(volume dirty) is set. Not recommended for use.
sparse Create new files as "sparse".
showmeta Use this parameter to show all meta-files (System Files) on
a mounted NTFS partition.
By default, all meta-files are hidden.
prealloc Preallocate space for files excessively when file size is
increasing on writes. Decreases fragmentation in case of
parallel write operations to different files.
no_acs_rules "No access rules" mount option sets access rights for
files/folders to 777 and owner/group to root. This mount
option absorbs all other permissions:
- permissions change for files/folders will be reported
as successful, but they will remain 777;
- owner/group change will be reported as successful, but
they will stay as root
acl Support POSIX ACLs (Access Control Lists). Effective if
supported by Kernel. Not to be confused with NTFS ACLs.
The option specified as acl enables support for POSIX ACLs.
noatime All files and directories will not update their last access
time attribute if a partition is mounted with this parameter.
This option can speed up file system operation.
===============================================================================
ToDo list
=========
- Full journaling support (currently journal replaying is supported) over JBD.
References
==========
https://www.paragon-software.com/home/ntfs-linux-professional/
- Commercial version of the NTFS driver for Linux.
almaz.alexandrovich@paragon-software.com
- Direct e-mail address for feedback and requests on the NTFS3 implementation.

Просмотреть файл

@ -13340,6 +13340,15 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/aia21/ntfs.git
F: Documentation/filesystems/ntfs.rst
F: fs/ntfs/
NTFS3 FILESYSTEM
M: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
L: ntfs3@lists.linux.dev
S: Supported
W: http://www.paragon-software.com/
T: git https://github.com/Paragon-Software-Group/linux-ntfs3.git
F: Documentation/filesystems/ntfs3.rst
F: fs/ntfs3/
NUBUS SUBSYSTEM
M: Finn Thain <fthain@linux-m68k.org>
L: linux-m68k@lists.linux-m68k.org

Просмотреть файл

@ -136,6 +136,7 @@ menu "DOS/FAT/EXFAT/NT Filesystems"
source "fs/fat/Kconfig"
source "fs/exfat/Kconfig"
source "fs/ntfs/Kconfig"
source "fs/ntfs3/Kconfig"
endmenu
endif # BLOCK

Просмотреть файл

@ -101,6 +101,7 @@ obj-$(CONFIG_CIFS) += cifs/
obj-$(CONFIG_SMB_SERVER) += ksmbd/
obj-$(CONFIG_HPFS_FS) += hpfs/
obj-$(CONFIG_NTFS_FS) += ntfs/
obj-$(CONFIG_NTFS3_FS) += ntfs3/
obj-$(CONFIG_UFS_FS) += ufs/
obj-$(CONFIG_EFS_FS) += efs/
obj-$(CONFIG_JFFS2_FS) += jffs2/

46
fs/ntfs3/Kconfig Normal file
Просмотреть файл

@ -0,0 +1,46 @@
# SPDX-License-Identifier: GPL-2.0-only
config NTFS3_FS
tristate "NTFS Read-Write file system support"
select NLS
help
Windows OS native file system (NTFS) support up to NTFS version 3.1.
Y or M enables the NTFS3 driver with full features enabled (read,
write, journal replaying, sparse/compressed files support).
File system type to use on mount is "ntfs3". Module name (M option)
is also "ntfs3".
Documentation: <file:Documentation/filesystems/ntfs3.rst>
config NTFS3_64BIT_CLUSTER
bool "64 bits per NTFS clusters"
depends on NTFS3_FS && 64BIT
help
Windows implementation of ntfs.sys uses 32 bits per clusters.
If activated 64 bits per clusters you will be able to use 4k cluster
for 16T+ volumes. Windows will not be able to mount such volumes.
It is recommended to say N here.
config NTFS3_LZX_XPRESS
bool "activate support of external compressions lzx/xpress"
depends on NTFS3_FS
help
In Windows 10 one can use command "compact" to compress any files.
4 possible variants of compression are: xpress4k, xpress8k, xpress16k and lzx.
If activated you will be able to read such files correctly.
It is recommended to say Y here.
config NTFS3_FS_POSIX_ACL
bool "NTFS POSIX Access Control Lists"
depends on NTFS3_FS
select FS_POSIX_ACL
help
POSIX Access Control Lists (ACLs) support additional access rights
for users and groups beyond the standard owner/group/world scheme,
and this option selects support for ACLs specifically for ntfs
filesystems.
NOTE: this is linux only feature. Windows will ignore these ACLs.
If you don't know what Access Control Lists are, say N.

36
fs/ntfs3/Makefile Normal file
Просмотреть файл

@ -0,0 +1,36 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the ntfs3 filesystem support.
#
# to check robot warnings
ccflags-y += -Wint-to-pointer-cast \
$(call cc-option,-Wunused-but-set-variable,-Wunused-const-variable) \
$(call cc-option,-Wold-style-declaration,-Wout-of-line-declaration)
obj-$(CONFIG_NTFS3_FS) += ntfs3.o
ntfs3-y := attrib.o \
attrlist.o \
bitfunc.o \
bitmap.o \
dir.o \
fsntfs.o \
frecord.o \
file.o \
fslog.o \
inode.o \
index.o \
lznt.o \
namei.o \
record.o \
run.o \
super.o \
upcase.o \
xattr.o
ntfs3-$(CONFIG_NTFS3_LZX_XPRESS) += $(addprefix lib/,\
decompress_common.o \
lzx_decompress.o \
xpress_decompress.o \
)

2093
fs/ntfs3/attrib.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

460
fs/ntfs3/attrlist.c Normal file
Просмотреть файл

@ -0,0 +1,460 @@
// SPDX-License-Identifier: GPL-2.0
/*
*
* Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/nls.h>
#include "debug.h"
#include "ntfs.h"
#include "ntfs_fs.h"
/*
* al_is_valid_le
*
* Return: True if @le is valid.
*/
static inline bool al_is_valid_le(const struct ntfs_inode *ni,
struct ATTR_LIST_ENTRY *le)
{
if (!le || !ni->attr_list.le || !ni->attr_list.size)
return false;
return PtrOffset(ni->attr_list.le, le) + le16_to_cpu(le->size) <=
ni->attr_list.size;
}
void al_destroy(struct ntfs_inode *ni)
{
run_close(&ni->attr_list.run);
kfree(ni->attr_list.le);
ni->attr_list.le = NULL;
ni->attr_list.size = 0;
ni->attr_list.dirty = false;
}
/*
* ntfs_load_attr_list
*
* This method makes sure that the ATTRIB list, if present,
* has been properly set up.
*/
int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
{
int err;
size_t lsize;
void *le = NULL;
if (ni->attr_list.size)
return 0;
if (!attr->non_res) {
lsize = le32_to_cpu(attr->res.data_size);
le = kmalloc(al_aligned(lsize), GFP_NOFS);
if (!le) {
err = -ENOMEM;
goto out;
}
memcpy(le, resident_data(attr), lsize);
} else if (attr->nres.svcn) {
err = -EINVAL;
goto out;
} else {
u16 run_off = le16_to_cpu(attr->nres.run_off);
lsize = le64_to_cpu(attr->nres.data_size);
run_init(&ni->attr_list.run);
err = run_unpack_ex(&ni->attr_list.run, ni->mi.sbi, ni->mi.rno,
0, le64_to_cpu(attr->nres.evcn), 0,
Add2Ptr(attr, run_off),
le32_to_cpu(attr->size) - run_off);
if (err < 0)
goto out;
le = kmalloc(al_aligned(lsize), GFP_NOFS);
if (!le) {
err = -ENOMEM;
goto out;
}
err = ntfs_read_run_nb(ni->mi.sbi, &ni->attr_list.run, 0, le,
lsize, NULL);
if (err)
goto out;
}
ni->attr_list.size = lsize;
ni->attr_list.le = le;
return 0;
out:
ni->attr_list.le = le;
al_destroy(ni);
return err;
}
/*
* al_enumerate
*
* Return:
* * The next list le.
* * If @le is NULL then return the first le.
*/
struct ATTR_LIST_ENTRY *al_enumerate(struct ntfs_inode *ni,
struct ATTR_LIST_ENTRY *le)
{
size_t off;
u16 sz;
if (!le) {
le = ni->attr_list.le;
} else {
sz = le16_to_cpu(le->size);
if (sz < sizeof(struct ATTR_LIST_ENTRY)) {
/* Impossible 'cause we should not return such le. */
return NULL;
}
le = Add2Ptr(le, sz);
}
/* Check boundary. */
off = PtrOffset(ni->attr_list.le, le);
if (off + sizeof(struct ATTR_LIST_ENTRY) > ni->attr_list.size) {
/* The regular end of list. */
return NULL;
}
sz = le16_to_cpu(le->size);
/* Check le for errors. */
if (sz < sizeof(struct ATTR_LIST_ENTRY) ||
off + sz > ni->attr_list.size ||
sz < le->name_off + le->name_len * sizeof(short)) {
return NULL;
}
return le;
}
/*
* al_find_le
*
* Find the first le in the list which matches type, name and VCN.
*
* Return: NULL if not found.
*/
struct ATTR_LIST_ENTRY *al_find_le(struct ntfs_inode *ni,
struct ATTR_LIST_ENTRY *le,
const struct ATTRIB *attr)
{
CLST svcn = attr_svcn(attr);
return al_find_ex(ni, le, attr->type, attr_name(attr), attr->name_len,
&svcn);
}
/*
* al_find_ex
*
* Find the first le in the list which matches type, name and VCN.
*
* Return: NULL if not found.
*/
struct ATTR_LIST_ENTRY *al_find_ex(struct ntfs_inode *ni,
struct ATTR_LIST_ENTRY *le,
enum ATTR_TYPE type, const __le16 *name,
u8 name_len, const CLST *vcn)
{
struct ATTR_LIST_ENTRY *ret = NULL;
u32 type_in = le32_to_cpu(type);
while ((le = al_enumerate(ni, le))) {
u64 le_vcn;
int diff = le32_to_cpu(le->type) - type_in;
/* List entries are sorted by type, name and VCN. */
if (diff < 0)
continue;
if (diff > 0)
return ret;
if (le->name_len != name_len)
continue;
le_vcn = le64_to_cpu(le->vcn);
if (!le_vcn) {
/*
* Compare entry names only for entry with vcn == 0.
*/
diff = ntfs_cmp_names(le_name(le), name_len, name,
name_len, ni->mi.sbi->upcase,
true);
if (diff < 0)
continue;
if (diff > 0)
return ret;
}
if (!vcn)
return le;
if (*vcn == le_vcn)
return le;
if (*vcn < le_vcn)
return ret;
ret = le;
}
return ret;
}
/*
* al_find_le_to_insert
*
* Find the first list entry which matches type, name and VCN.
*/
static struct ATTR_LIST_ENTRY *al_find_le_to_insert(struct ntfs_inode *ni,
enum ATTR_TYPE type,
const __le16 *name,
u8 name_len, CLST vcn)
{
struct ATTR_LIST_ENTRY *le = NULL, *prev;
u32 type_in = le32_to_cpu(type);
/* List entries are sorted by type, name and VCN. */
while ((le = al_enumerate(ni, prev = le))) {
int diff = le32_to_cpu(le->type) - type_in;
if (diff < 0)
continue;
if (diff > 0)
return le;
if (!le->vcn) {
/*
* Compare entry names only for entry with vcn == 0.
*/
diff = ntfs_cmp_names(le_name(le), le->name_len, name,
name_len, ni->mi.sbi->upcase,
true);
if (diff < 0)
continue;
if (diff > 0)
return le;
}
if (le64_to_cpu(le->vcn) >= vcn)
return le;
}
return prev ? Add2Ptr(prev, le16_to_cpu(prev->size)) : ni->attr_list.le;
}
/*
* al_add_le
*
* Add an "attribute list entry" to the list.
*/
int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name,
u8 name_len, CLST svcn, __le16 id, const struct MFT_REF *ref,
struct ATTR_LIST_ENTRY **new_le)
{
int err;
struct ATTRIB *attr;
struct ATTR_LIST_ENTRY *le;
size_t off;
u16 sz;
size_t asize, new_asize, old_size;
u64 new_size;
typeof(ni->attr_list) *al = &ni->attr_list;
/*
* Compute the size of the new 'le'
*/
sz = le_size(name_len);
old_size = al->size;
new_size = old_size + sz;
asize = al_aligned(old_size);
new_asize = al_aligned(new_size);
/* Scan forward to the point at which the new 'le' should be inserted. */
le = al_find_le_to_insert(ni, type, name, name_len, svcn);
off = PtrOffset(al->le, le);
if (new_size > asize) {
void *ptr = kmalloc(new_asize, GFP_NOFS);
if (!ptr)
return -ENOMEM;
memcpy(ptr, al->le, off);
memcpy(Add2Ptr(ptr, off + sz), le, old_size - off);
le = Add2Ptr(ptr, off);
kfree(al->le);
al->le = ptr;
} else {
memmove(Add2Ptr(le, sz), le, old_size - off);
}
*new_le = le;
al->size = new_size;
le->type = type;
le->size = cpu_to_le16(sz);
le->name_len = name_len;
le->name_off = offsetof(struct ATTR_LIST_ENTRY, name);
le->vcn = cpu_to_le64(svcn);
le->ref = *ref;
le->id = id;
memcpy(le->name, name, sizeof(short) * name_len);
err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
&new_size, true, &attr);
if (err) {
/* Undo memmove above. */
memmove(le, Add2Ptr(le, sz), old_size - off);
al->size = old_size;
return err;
}
al->dirty = true;
if (attr && attr->non_res) {
err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le,
al->size);
if (err)
return err;
al->dirty = false;
}
return 0;
}
/*
* al_remove_le - Remove @le from attribute list.
*/
bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le)
{
u16 size;
size_t off;
typeof(ni->attr_list) *al = &ni->attr_list;
if (!al_is_valid_le(ni, le))
return false;
/* Save on stack the size of 'le' */
size = le16_to_cpu(le->size);
off = PtrOffset(al->le, le);
memmove(le, Add2Ptr(le, size), al->size - (off + size));
al->size -= size;
al->dirty = true;
return true;
}
/*
* al_delete_le - Delete first le from the list which matches its parameters.
*/
bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn,
const __le16 *name, size_t name_len,
const struct MFT_REF *ref)
{
u16 size;
struct ATTR_LIST_ENTRY *le;
size_t off;
typeof(ni->attr_list) *al = &ni->attr_list;
/* Scan forward to the first le that matches the input. */
le = al_find_ex(ni, NULL, type, name, name_len, &vcn);
if (!le)
return false;
off = PtrOffset(al->le, le);
next:
if (off >= al->size)
return false;
if (le->type != type)
return false;
if (le->name_len != name_len)
return false;
if (name_len && ntfs_cmp_names(le_name(le), name_len, name, name_len,
ni->mi.sbi->upcase, true))
return false;
if (le64_to_cpu(le->vcn) != vcn)
return false;
/*
* The caller specified a segment reference, so we have to
* scan through the matching entries until we find that segment
* reference or we run of matching entries.
*/
if (ref && memcmp(ref, &le->ref, sizeof(*ref))) {
off += le16_to_cpu(le->size);
le = Add2Ptr(al->le, off);
goto next;
}
/* Save on stack the size of 'le'. */
size = le16_to_cpu(le->size);
/* Delete the le. */
memmove(le, Add2Ptr(le, size), al->size - (off + size));
al->size -= size;
al->dirty = true;
return true;
}
int al_update(struct ntfs_inode *ni)
{
int err;
struct ATTRIB *attr;
typeof(ni->attr_list) *al = &ni->attr_list;
if (!al->dirty || !al->size)
return 0;
/*
* Attribute list increased on demand in al_add_le.
* Attribute list decreased here.
*/
err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL,
false, &attr);
if (err)
goto out;
if (!attr->non_res) {
memcpy(resident_data(attr), al->le, al->size);
} else {
err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le,
al->size);
if (err)
goto out;
attr->nres.valid_size = attr->nres.data_size;
}
ni->mi.dirty = true;
al->dirty = false;
out:
return err;
}

134
fs/ntfs3/bitfunc.c Normal file
Просмотреть файл

@ -0,0 +1,134 @@
// SPDX-License-Identifier: GPL-2.0
/*
*
* Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/nls.h>
#include "debug.h"
#include "ntfs.h"
#include "ntfs_fs.h"
#define BITS_IN_SIZE_T (sizeof(size_t) * 8)
/*
* fill_mask[i] - first i bits are '1' , i = 0,1,2,3,4,5,6,7,8
* fill_mask[i] = 0xFF >> (8-i)
*/
static const u8 fill_mask[] = { 0x00, 0x01, 0x03, 0x07, 0x0F,
0x1F, 0x3F, 0x7F, 0xFF };
/*
* zero_mask[i] - first i bits are '0' , i = 0,1,2,3,4,5,6,7,8
* zero_mask[i] = 0xFF << i
*/
static const u8 zero_mask[] = { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0,
0xE0, 0xC0, 0x80, 0x00 };
/*
* are_bits_clear
*
* Return: True if all bits [bit, bit+nbits) are zeros "0".
*/
bool are_bits_clear(const ulong *lmap, size_t bit, size_t nbits)
{
size_t pos = bit & 7;
const u8 *map = (u8 *)lmap + (bit >> 3);
if (pos) {
if (8 - pos >= nbits)
return !nbits || !(*map & fill_mask[pos + nbits] &
zero_mask[pos]);
if (*map++ & zero_mask[pos])
return false;
nbits -= 8 - pos;
}
pos = ((size_t)map) & (sizeof(size_t) - 1);
if (pos) {
pos = sizeof(size_t) - pos;
if (nbits >= pos * 8) {
for (nbits -= pos * 8; pos; pos--, map++) {
if (*map)
return false;
}
}
}
for (pos = nbits / BITS_IN_SIZE_T; pos; pos--, map += sizeof(size_t)) {
if (*((size_t *)map))
return false;
}
for (pos = (nbits % BITS_IN_SIZE_T) >> 3; pos; pos--, map++) {
if (*map)
return false;
}
pos = nbits & 7;
if (pos && (*map & fill_mask[pos]))
return false;
return true;
}
/*
* are_bits_set
*
* Return: True if all bits [bit, bit+nbits) are ones "1".
*/
bool are_bits_set(const ulong *lmap, size_t bit, size_t nbits)
{
u8 mask;
size_t pos = bit & 7;
const u8 *map = (u8 *)lmap + (bit >> 3);
if (pos) {
if (8 - pos >= nbits) {
mask = fill_mask[pos + nbits] & zero_mask[pos];
return !nbits || (*map & mask) == mask;
}
mask = zero_mask[pos];
if ((*map++ & mask) != mask)
return false;
nbits -= 8 - pos;
}
pos = ((size_t)map) & (sizeof(size_t) - 1);
if (pos) {
pos = sizeof(size_t) - pos;
if (nbits >= pos * 8) {
for (nbits -= pos * 8; pos; pos--, map++) {
if (*map != 0xFF)
return false;
}
}
}
for (pos = nbits / BITS_IN_SIZE_T; pos; pos--, map += sizeof(size_t)) {
if (*((size_t *)map) != MINUS_ONE_T)
return false;
}
for (pos = (nbits % BITS_IN_SIZE_T) >> 3; pos; pos--, map++) {
if (*map != 0xFF)
return false;
}
pos = nbits & 7;
if (pos) {
u8 mask = fill_mask[pos];
if ((*map & mask) != mask)
return false;
}
return true;
}

1493
fs/ntfs3/bitmap.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

52
fs/ntfs3/debug.h Normal file
Просмотреть файл

@ -0,0 +1,52 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
*
* Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
*
* Useful functions for debugging.
*
*/
// clang-format off
#ifndef _LINUX_NTFS3_DEBUG_H
#define _LINUX_NTFS3_DEBUG_H
#ifndef Add2Ptr
#define Add2Ptr(P, I) ((void *)((u8 *)(P) + (I)))
#define PtrOffset(B, O) ((size_t)((size_t)(O) - (size_t)(B)))
#endif
#ifdef CONFIG_PRINTK
__printf(2, 3)
void ntfs_printk(const struct super_block *sb, const char *fmt, ...);
__printf(2, 3)
void ntfs_inode_printk(struct inode *inode, const char *fmt, ...);
#else
static inline __printf(2, 3)
void ntfs_printk(const struct super_block *sb, const char *fmt, ...)
{
}
static inline __printf(2, 3)
void ntfs_inode_printk(struct inode *inode, const char *fmt, ...)
{
}
#endif
/*
* Logging macros. Thanks Joe Perches <joe@perches.com> for implementation.
*/
#define ntfs_err(sb, fmt, ...) ntfs_printk(sb, KERN_ERR fmt, ##__VA_ARGS__)
#define ntfs_warn(sb, fmt, ...) ntfs_printk(sb, KERN_WARNING fmt, ##__VA_ARGS__)
#define ntfs_info(sb, fmt, ...) ntfs_printk(sb, KERN_INFO fmt, ##__VA_ARGS__)
#define ntfs_notice(sb, fmt, ...) \
ntfs_printk(sb, KERN_NOTICE fmt, ##__VA_ARGS__)
#define ntfs_inode_err(inode, fmt, ...) \
ntfs_inode_printk(inode, KERN_ERR fmt, ##__VA_ARGS__)
#define ntfs_inode_warn(inode, fmt, ...) \
ntfs_inode_printk(inode, KERN_WARNING fmt, ##__VA_ARGS__)
#endif /* _LINUX_NTFS3_DEBUG_H */
// clang-format on

599
fs/ntfs3/dir.c Normal file
Просмотреть файл

@ -0,0 +1,599 @@
// SPDX-License-Identifier: GPL-2.0
/*
*
* Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
*
* Directory handling functions for NTFS-based filesystems.
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/iversion.h>
#include <linux/nls.h>
#include "debug.h"
#include "ntfs.h"
#include "ntfs_fs.h"
/* Convert little endian UTF-16 to NLS string. */
int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const struct le_str *uni,
u8 *buf, int buf_len)
{
int ret, uni_len, warn;
const __le16 *ip;
u8 *op;
struct nls_table *nls = sbi->options.nls;
static_assert(sizeof(wchar_t) == sizeof(__le16));
if (!nls) {
/* UTF-16 -> UTF-8 */
ret = utf16s_to_utf8s((wchar_t *)uni->name, uni->len,
UTF16_LITTLE_ENDIAN, buf, buf_len);
buf[ret] = '\0';
return ret;
}
ip = uni->name;
op = buf;
uni_len = uni->len;
warn = 0;
while (uni_len--) {
u16 ec;
int charlen;
char dump[5];
if (buf_len < NLS_MAX_CHARSET_SIZE) {
ntfs_warn(sbi->sb,
"filename was truncated while converting.");
break;
}
ec = le16_to_cpu(*ip++);
charlen = nls->uni2char(ec, op, buf_len);
if (charlen > 0) {
op += charlen;
buf_len -= charlen;
continue;
}
*op++ = '_';
buf_len -= 1;
if (warn)
continue;
warn = 1;
hex_byte_pack(&dump[0], ec >> 8);
hex_byte_pack(&dump[2], ec);
dump[4] = 0;
ntfs_err(sbi->sb, "failed to convert \"%s\" to %s", dump,
nls->charset);
}
*op = '\0';
return op - buf;
}
// clang-format off
#define PLANE_SIZE 0x00010000
#define SURROGATE_PAIR 0x0000d800
#define SURROGATE_LOW 0x00000400
#define SURROGATE_BITS 0x000003ff
// clang-format on
/*
* put_utf16 - Modified version of put_utf16 from fs/nls/nls_base.c
*
* Function is sparse warnings free.
*/
static inline void put_utf16(wchar_t *s, unsigned int c,
enum utf16_endian endian)
{
static_assert(sizeof(wchar_t) == sizeof(__le16));
static_assert(sizeof(wchar_t) == sizeof(__be16));
switch (endian) {
default:
*s = (wchar_t)c;
break;
case UTF16_LITTLE_ENDIAN:
*(__le16 *)s = __cpu_to_le16(c);
break;
case UTF16_BIG_ENDIAN:
*(__be16 *)s = __cpu_to_be16(c);
break;
}
}
/*
* _utf8s_to_utf16s
*
* Modified version of 'utf8s_to_utf16s' allows to
* detect -ENAMETOOLONG without writing out of expected maximum.
*/
static int _utf8s_to_utf16s(const u8 *s, int inlen, enum utf16_endian endian,
wchar_t *pwcs, int maxout)
{
u16 *op;
int size;
unicode_t u;
op = pwcs;
while (inlen > 0 && *s) {
if (*s & 0x80) {
size = utf8_to_utf32(s, inlen, &u);
if (size < 0)
return -EINVAL;
s += size;
inlen -= size;
if (u >= PLANE_SIZE) {
if (maxout < 2)
return -ENAMETOOLONG;
u -= PLANE_SIZE;
put_utf16(op++,
SURROGATE_PAIR |
((u >> 10) & SURROGATE_BITS),
endian);
put_utf16(op++,
SURROGATE_PAIR | SURROGATE_LOW |
(u & SURROGATE_BITS),
endian);
maxout -= 2;
} else {
if (maxout < 1)
return -ENAMETOOLONG;
put_utf16(op++, u, endian);
maxout--;
}
} else {
if (maxout < 1)
return -ENAMETOOLONG;
put_utf16(op++, *s++, endian);
inlen--;
maxout--;
}
}
return op - pwcs;
}
/*
* ntfs_nls_to_utf16 - Convert input string to UTF-16.
* @name: Input name.
* @name_len: Input name length.
* @uni: Destination memory.
* @max_ulen: Destination memory.
* @endian: Endian of target UTF-16 string.
*
* This function is called:
* - to create NTFS name
* - to create symlink
*
* Return: UTF-16 string length or error (if negative).
*/
int ntfs_nls_to_utf16(struct ntfs_sb_info *sbi, const u8 *name, u32 name_len,
struct cpu_str *uni, u32 max_ulen,
enum utf16_endian endian)
{
int ret, slen;
const u8 *end;
struct nls_table *nls = sbi->options.nls;
u16 *uname = uni->name;
static_assert(sizeof(wchar_t) == sizeof(u16));
if (!nls) {
/* utf8 -> utf16 */
ret = _utf8s_to_utf16s(name, name_len, endian, uname, max_ulen);
uni->len = ret;
return ret;
}
for (ret = 0, end = name + name_len; name < end; ret++, name += slen) {
if (ret >= max_ulen)
return -ENAMETOOLONG;
slen = nls->char2uni(name, end - name, uname + ret);
if (!slen)
return -EINVAL;
if (slen < 0)
return slen;
}
#ifdef __BIG_ENDIAN
if (endian == UTF16_LITTLE_ENDIAN) {
int i = ret;
while (i--) {
__cpu_to_le16s(uname);
uname++;
}
}
#else
if (endian == UTF16_BIG_ENDIAN) {
int i = ret;
while (i--) {
__cpu_to_be16s(uname);
uname++;
}
}
#endif
uni->len = ret;
return ret;
}
/*
* dir_search_u - Helper function.
*/
struct inode *dir_search_u(struct inode *dir, const struct cpu_str *uni,
struct ntfs_fnd *fnd)
{
int err = 0;
struct super_block *sb = dir->i_sb;
struct ntfs_sb_info *sbi = sb->s_fs_info;
struct ntfs_inode *ni = ntfs_i(dir);
struct NTFS_DE *e;
int diff;
struct inode *inode = NULL;
struct ntfs_fnd *fnd_a = NULL;
if (!fnd) {
fnd_a = fnd_get();
if (!fnd_a) {
err = -ENOMEM;
goto out;
}
fnd = fnd_a;
}
err = indx_find(&ni->dir, ni, NULL, uni, 0, sbi, &diff, &e, fnd);
if (err)
goto out;
if (diff) {
err = -ENOENT;
goto out;
}
inode = ntfs_iget5(sb, &e->ref, uni);
if (!IS_ERR(inode) && is_bad_inode(inode)) {
iput(inode);
err = -EINVAL;
}
out:
fnd_put(fnd_a);
return err == -ENOENT ? NULL : err ? ERR_PTR(err) : inode;
}
static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
const struct NTFS_DE *e, u8 *name,
struct dir_context *ctx)
{
const struct ATTR_FILE_NAME *fname;
unsigned long ino;
int name_len;
u32 dt_type;
fname = Add2Ptr(e, sizeof(struct NTFS_DE));
if (fname->type == FILE_NAME_DOS)
return 0;
if (!mi_is_ref(&ni->mi, &fname->home))
return 0;
ino = ino_get(&e->ref);
if (ino == MFT_REC_ROOT)
return 0;
/* Skip meta files. Unless option to show metafiles is set. */
if (!sbi->options.showmeta && ntfs_is_meta_file(sbi, ino))
return 0;
if (sbi->options.nohidden && (fname->dup.fa & FILE_ATTRIBUTE_HIDDEN))
return 0;
name_len = ntfs_utf16_to_nls(sbi, (struct le_str *)&fname->name_len,
name, PATH_MAX);
if (name_len <= 0) {
ntfs_warn(sbi->sb, "failed to convert name for inode %lx.",
ino);
return 0;
}
dt_type = (fname->dup.fa & FILE_ATTRIBUTE_DIRECTORY) ? DT_DIR : DT_REG;
return !dir_emit(ctx, (s8 *)name, name_len, ino, dt_type);
}
/*
* ntfs_read_hdr - Helper function for ntfs_readdir().
*/
static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
const struct INDEX_HDR *hdr, u64 vbo, u64 pos,
u8 *name, struct dir_context *ctx)
{
int err;
const struct NTFS_DE *e;
u32 e_size;
u32 end = le32_to_cpu(hdr->used);
u32 off = le32_to_cpu(hdr->de_off);
for (;; off += e_size) {
if (off + sizeof(struct NTFS_DE) > end)
return -1;
e = Add2Ptr(hdr, off);
e_size = le16_to_cpu(e->size);
if (e_size < sizeof(struct NTFS_DE) || off + e_size > end)
return -1;
if (de_is_last(e))
return 0;
/* Skip already enumerated. */
if (vbo + off < pos)
continue;
if (le16_to_cpu(e->key_size) < SIZEOF_ATTRIBUTE_FILENAME)
return -1;
ctx->pos = vbo + off;
/* Submit the name to the filldir callback. */
err = ntfs_filldir(sbi, ni, e, name, ctx);
if (err)
return err;
}
}
/*
* ntfs_readdir - file_operations::iterate_shared
*
* Use non sorted enumeration.
* We have an example of broken volume where sorted enumeration
* counts each name twice.
*/
static int ntfs_readdir(struct file *file, struct dir_context *ctx)
{
const struct INDEX_ROOT *root;
u64 vbo;
size_t bit;
loff_t eod;
int err = 0;
struct inode *dir = file_inode(file);
struct ntfs_inode *ni = ntfs_i(dir);
struct super_block *sb = dir->i_sb;
struct ntfs_sb_info *sbi = sb->s_fs_info;
loff_t i_size = i_size_read(dir);
u32 pos = ctx->pos;
u8 *name = NULL;
struct indx_node *node = NULL;
u8 index_bits = ni->dir.index_bits;
/* Name is a buffer of PATH_MAX length. */
static_assert(NTFS_NAME_LEN * 4 < PATH_MAX);
eod = i_size + sbi->record_size;
if (pos >= eod)
return 0;
if (!dir_emit_dots(file, ctx))
return 0;
/* Allocate PATH_MAX bytes. */
name = __getname();
if (!name)
return -ENOMEM;
if (!ni->mi_loaded && ni->attr_list.size) {
/*
* Directory inode is locked for read.
* Load all subrecords to avoid 'write' access to 'ni' during
* directory reading.
*/
ni_lock(ni);
if (!ni->mi_loaded && ni->attr_list.size) {
err = ni_load_all_mi(ni);
if (!err)
ni->mi_loaded = true;
}
ni_unlock(ni);
if (err)
goto out;
}
root = indx_get_root(&ni->dir, ni, NULL, NULL);
if (!root) {
err = -EINVAL;
goto out;
}
if (pos >= sbi->record_size) {
bit = (pos - sbi->record_size) >> index_bits;
} else {
err = ntfs_read_hdr(sbi, ni, &root->ihdr, 0, pos, name, ctx);
if (err)
goto out;
bit = 0;
}
if (!i_size) {
ctx->pos = eod;
goto out;
}
for (;;) {
vbo = (u64)bit << index_bits;
if (vbo >= i_size) {
ctx->pos = eod;
goto out;
}
err = indx_used_bit(&ni->dir, ni, &bit);
if (err)
goto out;
if (bit == MINUS_ONE_T) {
ctx->pos = eod;
goto out;
}
vbo = (u64)bit << index_bits;
if (vbo >= i_size) {
ntfs_inode_err(dir, "Looks like your dir is corrupt");
err = -EINVAL;
goto out;
}
err = indx_read(&ni->dir, ni, bit << ni->dir.idx2vbn_bits,
&node);
if (err)
goto out;
err = ntfs_read_hdr(sbi, ni, &node->index->ihdr,
vbo + sbi->record_size, pos, name, ctx);
if (err)
goto out;
bit += 1;
}
out:
__putname(name);
put_indx_node(node);
if (err == -ENOENT) {
err = 0;
ctx->pos = pos;
}
return err;
}
static int ntfs_dir_count(struct inode *dir, bool *is_empty, size_t *dirs,
size_t *files)
{
int err = 0;
struct ntfs_inode *ni = ntfs_i(dir);
struct NTFS_DE *e = NULL;
struct INDEX_ROOT *root;
struct INDEX_HDR *hdr;
const struct ATTR_FILE_NAME *fname;
u32 e_size, off, end;
u64 vbo = 0;
size_t drs = 0, fles = 0, bit = 0;
loff_t i_size = ni->vfs_inode.i_size;
struct indx_node *node = NULL;
u8 index_bits = ni->dir.index_bits;
if (is_empty)
*is_empty = true;
root = indx_get_root(&ni->dir, ni, NULL, NULL);
if (!root)
return -EINVAL;
hdr = &root->ihdr;
for (;;) {
end = le32_to_cpu(hdr->used);
off = le32_to_cpu(hdr->de_off);
for (; off + sizeof(struct NTFS_DE) <= end; off += e_size) {
e = Add2Ptr(hdr, off);
e_size = le16_to_cpu(e->size);
if (e_size < sizeof(struct NTFS_DE) ||
off + e_size > end)
break;
if (de_is_last(e))
break;
fname = de_get_fname(e);
if (!fname)
continue;
if (fname->type == FILE_NAME_DOS)
continue;
if (is_empty) {
*is_empty = false;
if (!dirs && !files)
goto out;
}
if (fname->dup.fa & FILE_ATTRIBUTE_DIRECTORY)
drs += 1;
else
fles += 1;
}
if (vbo >= i_size)
goto out;
err = indx_used_bit(&ni->dir, ni, &bit);
if (err)
goto out;
if (bit == MINUS_ONE_T)
goto out;
vbo = (u64)bit << index_bits;
if (vbo >= i_size)
goto out;
err = indx_read(&ni->dir, ni, bit << ni->dir.idx2vbn_bits,
&node);
if (err)
goto out;
hdr = &node->index->ihdr;
bit += 1;
vbo = (u64)bit << ni->dir.idx2vbn_bits;
}
out:
put_indx_node(node);
if (dirs)
*dirs = drs;
if (files)
*files = fles;
return err;
}
bool dir_is_empty(struct inode *dir)
{
bool is_empty = false;
ntfs_dir_count(dir, &is_empty, NULL, NULL);
return is_empty;
}
// clang-format off
const struct file_operations ntfs_dir_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.iterate_shared = ntfs_readdir,
.fsync = generic_file_fsync,
.open = ntfs_file_open,
};
// clang-format on

1251
fs/ntfs3/file.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

3257
fs/ntfs3/frecord.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

5217
fs/ntfs3/fslog.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

2509
fs/ntfs3/fsntfs.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

2650
fs/ntfs3/index.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

1957
fs/ntfs3/inode.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,319 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* decompress_common.c - Code shared by the XPRESS and LZX decompressors
*
* Copyright (C) 2015 Eric Biggers
*/
#include "decompress_common.h"
/*
* make_huffman_decode_table() -
*
* Build a decoding table for a canonical prefix code, or "Huffman code".
*
* This is an internal function, not part of the library API!
*
* This takes as input the length of the codeword for each symbol in the
* alphabet and produces as output a table that can be used for fast
* decoding of prefix-encoded symbols using read_huffsym().
*
* Strictly speaking, a canonical prefix code might not be a Huffman
* code. But this algorithm will work either way; and in fact, since
* Huffman codes are defined in terms of symbol frequencies, there is no
* way for the decompressor to know whether the code is a true Huffman
* code or not until all symbols have been decoded.
*
* Because the prefix code is assumed to be "canonical", it can be
* reconstructed directly from the codeword lengths. A prefix code is
* canonical if and only if a longer codeword never lexicographically
* precedes a shorter codeword, and the lexicographic ordering of
* codewords of the same length is the same as the lexicographic ordering
* of the corresponding symbols. Consequently, we can sort the symbols
* primarily by codeword length and secondarily by symbol value, then
* reconstruct the prefix code by generating codewords lexicographically
* in that order.
*
* This function does not, however, generate the prefix code explicitly.
* Instead, it directly builds a table for decoding symbols using the
* code. The basic idea is this: given the next 'max_codeword_len' bits
* in the input, we can look up the decoded symbol by indexing a table
* containing 2**max_codeword_len entries. A codeword with length
* 'max_codeword_len' will have exactly one entry in this table, whereas
* a codeword shorter than 'max_codeword_len' will have multiple entries
* in this table. Precisely, a codeword of length n will be represented
* by 2**(max_codeword_len - n) entries in this table. The 0-based index
* of each such entry will contain the corresponding codeword as a prefix
* when zero-padded on the left to 'max_codeword_len' binary digits.
*
* That's the basic idea, but we implement two optimizations regarding
* the format of the decode table itself:
*
* - For many compression formats, the maximum codeword length is too
* long for it to be efficient to build the full decoding table
* whenever a new prefix code is used. Instead, we can build the table
* using only 2**table_bits entries, where 'table_bits' is some number
* less than or equal to 'max_codeword_len'. Then, only codewords of
* length 'table_bits' and shorter can be directly looked up. For
* longer codewords, the direct lookup instead produces the root of a
* binary tree. Using this tree, the decoder can do traditional
* bit-by-bit decoding of the remainder of the codeword. Child nodes
* are allocated in extra entries at the end of the table; leaf nodes
* contain symbols. Note that the long-codeword case is, in general,
* not performance critical, since in Huffman codes the most frequently
* used symbols are assigned the shortest codeword lengths.
*
* - When we decode a symbol using a direct lookup of the table, we still
* need to know its length so that the bitstream can be advanced by the
* appropriate number of bits. The simple solution is to simply retain
* the 'lens' array and use the decoded symbol as an index into it.
* However, this requires two separate array accesses in the fast path.
* The optimization is to store the length directly in the decode
* table. We use the bottom 11 bits for the symbol and the top 5 bits
* for the length. In addition, to combine this optimization with the
* previous one, we introduce a special case where the top 2 bits of
* the length are both set if the entry is actually the root of a
* binary tree.
*
* @decode_table:
* The array in which to create the decoding table. This must have
* a length of at least ((2**table_bits) + 2 * num_syms) entries.
*
* @num_syms:
* The number of symbols in the alphabet; also, the length of the
* 'lens' array. Must be less than or equal to 2048.
*
* @table_bits:
* The order of the decode table size, as explained above. Must be
* less than or equal to 13.
*
* @lens:
* An array of length @num_syms, indexable by symbol, that gives the
* length of the codeword, in bits, for that symbol. The length can
* be 0, which means that the symbol does not have a codeword
* assigned.
*
* @max_codeword_len:
* The longest codeword length allowed in the compression format.
* All entries in 'lens' must be less than or equal to this value.
* This must be less than or equal to 23.
*
* @working_space
* A temporary array of length '2 * (max_codeword_len + 1) +
* num_syms'.
*
* Returns 0 on success, or -1 if the lengths do not form a valid prefix
* code.
*/
int make_huffman_decode_table(u16 decode_table[], const u32 num_syms,
const u32 table_bits, const u8 lens[],
const u32 max_codeword_len,
u16 working_space[])
{
const u32 table_num_entries = 1 << table_bits;
u16 * const len_counts = &working_space[0];
u16 * const offsets = &working_space[1 * (max_codeword_len + 1)];
u16 * const sorted_syms = &working_space[2 * (max_codeword_len + 1)];
int left;
void *decode_table_ptr;
u32 sym_idx;
u32 codeword_len;
u32 stores_per_loop;
u32 decode_table_pos;
u32 len;
u32 sym;
/* Count how many symbols have each possible codeword length.
* Note that a length of 0 indicates the corresponding symbol is not
* used in the code and therefore does not have a codeword.
*/
for (len = 0; len <= max_codeword_len; len++)
len_counts[len] = 0;
for (sym = 0; sym < num_syms; sym++)
len_counts[lens[sym]]++;
/* We can assume all lengths are <= max_codeword_len, but we
* cannot assume they form a valid prefix code. A codeword of
* length n should require a proportion of the codespace equaling
* (1/2)^n. The code is valid if and only if the codespace is
* exactly filled by the lengths, by this measure.
*/
left = 1;
for (len = 1; len <= max_codeword_len; len++) {
left <<= 1;
left -= len_counts[len];
if (left < 0) {
/* The lengths overflow the codespace; that is, the code
* is over-subscribed.
*/
return -1;
}
}
if (left) {
/* The lengths do not fill the codespace; that is, they form an
* incomplete set.
*/
if (left == (1 << max_codeword_len)) {
/* The code is completely empty. This is arguably
* invalid, but in fact it is valid in LZX and XPRESS,
* so we must allow it. By definition, no symbols can
* be decoded with an empty code. Consequently, we
* technically don't even need to fill in the decode
* table. However, to avoid accessing uninitialized
* memory if the algorithm nevertheless attempts to
* decode symbols using such a code, we zero out the
* decode table.
*/
memset(decode_table, 0,
table_num_entries * sizeof(decode_table[0]));
return 0;
}
return -1;
}
/* Sort the symbols primarily by length and secondarily by symbol order.
*/
/* Initialize 'offsets' so that offsets[len] for 1 <= len <=
* max_codeword_len is the number of codewords shorter than 'len' bits.
*/
offsets[1] = 0;
for (len = 1; len < max_codeword_len; len++)
offsets[len + 1] = offsets[len] + len_counts[len];
/* Use the 'offsets' array to sort the symbols. Note that we do not
* include symbols that are not used in the code. Consequently, fewer
* than 'num_syms' entries in 'sorted_syms' may be filled.
*/
for (sym = 0; sym < num_syms; sym++)
if (lens[sym])
sorted_syms[offsets[lens[sym]]++] = sym;
/* Fill entries for codewords with length <= table_bits
* --- that is, those short enough for a direct mapping.
*
* The table will start with entries for the shortest codeword(s), which
* have the most entries. From there, the number of entries per
* codeword will decrease.
*/
decode_table_ptr = decode_table;
sym_idx = 0;
codeword_len = 1;
stores_per_loop = (1 << (table_bits - codeword_len));
for (; stores_per_loop != 0; codeword_len++, stores_per_loop >>= 1) {
u32 end_sym_idx = sym_idx + len_counts[codeword_len];
for (; sym_idx < end_sym_idx; sym_idx++) {
u16 entry;
u16 *p;
u32 n;
entry = ((u32)codeword_len << 11) | sorted_syms[sym_idx];
p = (u16 *)decode_table_ptr;
n = stores_per_loop;
do {
*p++ = entry;
} while (--n);
decode_table_ptr = p;
}
}
/* If we've filled in the entire table, we are done. Otherwise,
* there are codewords longer than table_bits for which we must
* generate binary trees.
*/
decode_table_pos = (u16 *)decode_table_ptr - decode_table;
if (decode_table_pos != table_num_entries) {
u32 j;
u32 next_free_tree_slot;
u32 cur_codeword;
/* First, zero out the remaining entries. This is
* necessary so that these entries appear as
* "unallocated" in the next part. Each of these entries
* will eventually be filled with the representation of
* the root node of a binary tree.
*/
j = decode_table_pos;
do {
decode_table[j] = 0;
} while (++j != table_num_entries);
/* We allocate child nodes starting at the end of the
* direct lookup table. Note that there should be
* 2*num_syms extra entries for this purpose, although
* fewer than this may actually be needed.
*/
next_free_tree_slot = table_num_entries;
/* Iterate through each codeword with length greater than
* 'table_bits', primarily in order of codeword length
* and secondarily in order of symbol.
*/
for (cur_codeword = decode_table_pos << 1;
codeword_len <= max_codeword_len;
codeword_len++, cur_codeword <<= 1) {
u32 end_sym_idx = sym_idx + len_counts[codeword_len];
for (; sym_idx < end_sym_idx; sym_idx++, cur_codeword++) {
/* 'sorted_sym' is the symbol represented by the
* codeword.
*/
u32 sorted_sym = sorted_syms[sym_idx];
u32 extra_bits = codeword_len - table_bits;
u32 node_idx = cur_codeword >> extra_bits;
/* Go through each bit of the current codeword
* beyond the prefix of length @table_bits and
* walk the appropriate binary tree, allocating
* any slots that have not yet been allocated.
*
* Note that the 'pointer' entry to the binary
* tree, which is stored in the direct lookup
* portion of the table, is represented
* identically to other internal (non-leaf)
* nodes of the binary tree; it can be thought
* of as simply the root of the tree. The
* representation of these internal nodes is
* simply the index of the left child combined
* with the special bits 0xC000 to distinguish
* the entry from direct mapping and leaf node
* entries.
*/
do {
/* At least one bit remains in the
* codeword, but the current node is an
* unallocated leaf. Change it to an
* internal node.
*/
if (decode_table[node_idx] == 0) {
decode_table[node_idx] =
next_free_tree_slot | 0xC000;
decode_table[next_free_tree_slot++] = 0;
decode_table[next_free_tree_slot++] = 0;
}
/* Go to the left child if the next bit
* in the codeword is 0; otherwise go to
* the right child.
*/
node_idx = decode_table[node_idx] & 0x3FFF;
--extra_bits;
node_idx += (cur_codeword >> extra_bits) & 1;
} while (extra_bits != 0);
/* We've traversed the tree using the entire
* codeword, and we're now at the entry where
* the actual symbol will be stored. This is
* distinguished from internal nodes by not
* having its high two bits set.
*/
decode_table[node_idx] = sorted_sym;
}
}
}
return 0;
}

Просмотреть файл

@ -0,0 +1,338 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* decompress_common.h - Code shared by the XPRESS and LZX decompressors
*
* Copyright (C) 2015 Eric Biggers
*/
#include <linux/string.h>
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
/* "Force inline" macro (not required, but helpful for performance) */
#define forceinline __always_inline
/* Enable whole-word match copying on selected architectures */
#if defined(__i386__) || defined(__x86_64__) || defined(__ARM_FEATURE_UNALIGNED)
# define FAST_UNALIGNED_ACCESS
#endif
/* Size of a machine word */
#define WORDBYTES (sizeof(size_t))
static forceinline void
copy_unaligned_word(const void *src, void *dst)
{
put_unaligned(get_unaligned((const size_t *)src), (size_t *)dst);
}
/* Generate a "word" with platform-dependent size whose bytes all contain the
* value 'b'.
*/
static forceinline size_t repeat_byte(u8 b)
{
size_t v;
v = b;
v |= v << 8;
v |= v << 16;
v |= v << ((WORDBYTES == 8) ? 32 : 0);
return v;
}
/* Structure that encapsulates a block of in-memory data being interpreted as a
* stream of bits, optionally with interwoven literal bytes. Bits are assumed
* to be stored in little endian 16-bit coding units, with the bits ordered high
* to low.
*/
struct input_bitstream {
/* Bits that have been read from the input buffer. The bits are
* left-justified; the next bit is always bit 31.
*/
u32 bitbuf;
/* Number of bits currently held in @bitbuf. */
u32 bitsleft;
/* Pointer to the next byte to be retrieved from the input buffer. */
const u8 *next;
/* Pointer to just past the end of the input buffer. */
const u8 *end;
};
/* Initialize a bitstream to read from the specified input buffer. */
static forceinline void init_input_bitstream(struct input_bitstream *is,
const void *buffer, u32 size)
{
is->bitbuf = 0;
is->bitsleft = 0;
is->next = buffer;
is->end = is->next + size;
}
/* Ensure the bit buffer variable for the bitstream contains at least @num_bits
* bits. Following this, bitstream_peek_bits() and/or bitstream_remove_bits()
* may be called on the bitstream to peek or remove up to @num_bits bits. Note
* that @num_bits must be <= 16.
*/
static forceinline void bitstream_ensure_bits(struct input_bitstream *is,
u32 num_bits)
{
if (is->bitsleft < num_bits) {
if (is->end - is->next >= 2) {
is->bitbuf |= (u32)get_unaligned_le16(is->next)
<< (16 - is->bitsleft);
is->next += 2;
}
is->bitsleft += 16;
}
}
/* Return the next @num_bits bits from the bitstream, without removing them.
* There must be at least @num_bits remaining in the buffer variable, from a
* previous call to bitstream_ensure_bits().
*/
static forceinline u32
bitstream_peek_bits(const struct input_bitstream *is, const u32 num_bits)
{
return (is->bitbuf >> 1) >> (sizeof(is->bitbuf) * 8 - num_bits - 1);
}
/* Remove @num_bits from the bitstream. There must be at least @num_bits
* remaining in the buffer variable, from a previous call to
* bitstream_ensure_bits().
*/
static forceinline void
bitstream_remove_bits(struct input_bitstream *is, u32 num_bits)
{
is->bitbuf <<= num_bits;
is->bitsleft -= num_bits;
}
/* Remove and return @num_bits bits from the bitstream. There must be at least
* @num_bits remaining in the buffer variable, from a previous call to
* bitstream_ensure_bits().
*/
static forceinline u32
bitstream_pop_bits(struct input_bitstream *is, u32 num_bits)
{
u32 bits = bitstream_peek_bits(is, num_bits);
bitstream_remove_bits(is, num_bits);
return bits;
}
/* Read and return the next @num_bits bits from the bitstream. */
static forceinline u32
bitstream_read_bits(struct input_bitstream *is, u32 num_bits)
{
bitstream_ensure_bits(is, num_bits);
return bitstream_pop_bits(is, num_bits);
}
/* Read and return the next literal byte embedded in the bitstream. */
static forceinline u8
bitstream_read_byte(struct input_bitstream *is)
{
if (unlikely(is->end == is->next))
return 0;
return *is->next++;
}
/* Read and return the next 16-bit integer embedded in the bitstream. */
static forceinline u16
bitstream_read_u16(struct input_bitstream *is)
{
u16 v;
if (unlikely(is->end - is->next < 2))
return 0;
v = get_unaligned_le16(is->next);
is->next += 2;
return v;
}
/* Read and return the next 32-bit integer embedded in the bitstream. */
static forceinline u32
bitstream_read_u32(struct input_bitstream *is)
{
u32 v;
if (unlikely(is->end - is->next < 4))
return 0;
v = get_unaligned_le32(is->next);
is->next += 4;
return v;
}
/* Read into @dst_buffer an array of literal bytes embedded in the bitstream.
* Return either a pointer to the byte past the last written, or NULL if the
* read overflows the input buffer.
*/
static forceinline void *bitstream_read_bytes(struct input_bitstream *is,
void *dst_buffer, size_t count)
{
if ((size_t)(is->end - is->next) < count)
return NULL;
memcpy(dst_buffer, is->next, count);
is->next += count;
return (u8 *)dst_buffer + count;
}
/* Align the input bitstream on a coding-unit boundary. */
static forceinline void bitstream_align(struct input_bitstream *is)
{
is->bitsleft = 0;
is->bitbuf = 0;
}
extern int make_huffman_decode_table(u16 decode_table[], const u32 num_syms,
const u32 num_bits, const u8 lens[],
const u32 max_codeword_len,
u16 working_space[]);
/* Reads and returns the next Huffman-encoded symbol from a bitstream. If the
* input data is exhausted, the Huffman symbol is decoded as if the missing bits
* are all zeroes.
*/
static forceinline u32 read_huffsym(struct input_bitstream *istream,
const u16 decode_table[],
u32 table_bits,
u32 max_codeword_len)
{
u32 entry;
u32 key_bits;
bitstream_ensure_bits(istream, max_codeword_len);
/* Index the decode table by the next table_bits bits of the input. */
key_bits = bitstream_peek_bits(istream, table_bits);
entry = decode_table[key_bits];
if (entry < 0xC000) {
/* Fast case: The decode table directly provided the
* symbol and codeword length. The low 11 bits are the
* symbol, and the high 5 bits are the codeword length.
*/
bitstream_remove_bits(istream, entry >> 11);
return entry & 0x7FF;
}
/* Slow case: The codeword for the symbol is longer than
* table_bits, so the symbol does not have an entry
* directly in the first (1 << table_bits) entries of the
* decode table. Traverse the appropriate binary tree
* bit-by-bit to decode the symbol.
*/
bitstream_remove_bits(istream, table_bits);
do {
key_bits = (entry & 0x3FFF) + bitstream_pop_bits(istream, 1);
} while ((entry = decode_table[key_bits]) >= 0xC000);
return entry;
}
/*
* Copy an LZ77 match at (dst - offset) to dst.
*
* The length and offset must be already validated --- that is, (dst - offset)
* can't underrun the output buffer, and (dst + length) can't overrun the output
* buffer. Also, the length cannot be 0.
*
* @bufend points to the byte past the end of the output buffer. This function
* won't write any data beyond this position.
*
* Returns dst + length.
*/
static forceinline u8 *lz_copy(u8 *dst, u32 length, u32 offset, const u8 *bufend,
u32 min_length)
{
const u8 *src = dst - offset;
/*
* Try to copy one machine word at a time. On i386 and x86_64 this is
* faster than copying one byte at a time, unless the data is
* near-random and all the matches have very short lengths. Note that
* since this requires unaligned memory accesses, it won't necessarily
* be faster on every architecture.
*
* Also note that we might copy more than the length of the match. For
* example, if a word is 8 bytes and the match is of length 5, then
* we'll simply copy 8 bytes. This is okay as long as we don't write
* beyond the end of the output buffer, hence the check for (bufend -
* end >= WORDBYTES - 1).
*/
#ifdef FAST_UNALIGNED_ACCESS
u8 * const end = dst + length;
if (bufend - end >= (ptrdiff_t)(WORDBYTES - 1)) {
if (offset >= WORDBYTES) {
/* The source and destination words don't overlap. */
/* To improve branch prediction, one iteration of this
* loop is unrolled. Most matches are short and will
* fail the first check. But if that check passes, then
* it becomes increasing likely that the match is long
* and we'll need to continue copying.
*/
copy_unaligned_word(src, dst);
src += WORDBYTES;
dst += WORDBYTES;
if (dst < end) {
do {
copy_unaligned_word(src, dst);
src += WORDBYTES;
dst += WORDBYTES;
} while (dst < end);
}
return end;
} else if (offset == 1) {
/* Offset 1 matches are equivalent to run-length
* encoding of the previous byte. This case is common
* if the data contains many repeated bytes.
*/
size_t v = repeat_byte(*(dst - 1));
do {
put_unaligned(v, (size_t *)dst);
src += WORDBYTES;
dst += WORDBYTES;
} while (dst < end);
return end;
}
/*
* We don't bother with special cases for other 'offset <
* WORDBYTES', which are usually rarer than 'offset == 1'. Extra
* checks will just slow things down. Actually, it's possible
* to handle all the 'offset < WORDBYTES' cases using the same
* code, but it still becomes more complicated doesn't seem any
* faster overall; it definitely slows down the more common
* 'offset == 1' case.
*/
}
#endif /* FAST_UNALIGNED_ACCESS */
/* Fall back to a bytewise copy. */
if (min_length >= 2) {
*dst++ = *src++;
length--;
}
if (min_length >= 3) {
*dst++ = *src++;
length--;
}
do {
*dst++ = *src++;
} while (--length);
return dst;
}

26
fs/ntfs3/lib/lib.h Normal file
Просмотреть файл

@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Adapted for linux kernel by Alexander Mamaev:
* - remove implementations of get_unaligned_
* - assume GCC is always defined
* - ISO C90
* - linux kernel code style
*/
/* globals from xpress_decompress.c */
struct xpress_decompressor *xpress_allocate_decompressor(void);
void xpress_free_decompressor(struct xpress_decompressor *d);
int xpress_decompress(struct xpress_decompressor *__restrict d,
const void *__restrict compressed_data,
size_t compressed_size,
void *__restrict uncompressed_data,
size_t uncompressed_size);
/* globals from lzx_decompress.c */
struct lzx_decompressor *lzx_allocate_decompressor(void);
void lzx_free_decompressor(struct lzx_decompressor *d);
int lzx_decompress(struct lzx_decompressor *__restrict d,
const void *__restrict compressed_data,
size_t compressed_size, void *__restrict uncompressed_data,
size_t uncompressed_size);

Просмотреть файл

@ -0,0 +1,670 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* lzx_decompress.c - A decompressor for the LZX compression format, which can
* be used in "System Compressed" files. This is based on the code from wimlib.
* This code only supports a window size (dictionary size) of 32768 bytes, since
* this is the only size used in System Compression.
*
* Copyright (C) 2015 Eric Biggers
*/
#include "decompress_common.h"
#include "lib.h"
/* Number of literal byte values */
#define LZX_NUM_CHARS 256
/* The smallest and largest allowed match lengths */
#define LZX_MIN_MATCH_LEN 2
#define LZX_MAX_MATCH_LEN 257
/* Number of distinct match lengths that can be represented */
#define LZX_NUM_LENS (LZX_MAX_MATCH_LEN - LZX_MIN_MATCH_LEN + 1)
/* Number of match lengths for which no length symbol is required */
#define LZX_NUM_PRIMARY_LENS 7
#define LZX_NUM_LEN_HEADERS (LZX_NUM_PRIMARY_LENS + 1)
/* Valid values of the 3-bit block type field */
#define LZX_BLOCKTYPE_VERBATIM 1
#define LZX_BLOCKTYPE_ALIGNED 2
#define LZX_BLOCKTYPE_UNCOMPRESSED 3
/* Number of offset slots for a window size of 32768 */
#define LZX_NUM_OFFSET_SLOTS 30
/* Number of symbols in the main code for a window size of 32768 */
#define LZX_MAINCODE_NUM_SYMBOLS \
(LZX_NUM_CHARS + (LZX_NUM_OFFSET_SLOTS * LZX_NUM_LEN_HEADERS))
/* Number of symbols in the length code */
#define LZX_LENCODE_NUM_SYMBOLS (LZX_NUM_LENS - LZX_NUM_PRIMARY_LENS)
/* Number of symbols in the precode */
#define LZX_PRECODE_NUM_SYMBOLS 20
/* Number of bits in which each precode codeword length is represented */
#define LZX_PRECODE_ELEMENT_SIZE 4
/* Number of low-order bits of each match offset that are entropy-encoded in
* aligned offset blocks
*/
#define LZX_NUM_ALIGNED_OFFSET_BITS 3
/* Number of symbols in the aligned offset code */
#define LZX_ALIGNEDCODE_NUM_SYMBOLS (1 << LZX_NUM_ALIGNED_OFFSET_BITS)
/* Mask for the match offset bits that are entropy-encoded in aligned offset
* blocks
*/
#define LZX_ALIGNED_OFFSET_BITMASK ((1 << LZX_NUM_ALIGNED_OFFSET_BITS) - 1)
/* Number of bits in which each aligned offset codeword length is represented */
#define LZX_ALIGNEDCODE_ELEMENT_SIZE 3
/* Maximum lengths (in bits) of the codewords in each Huffman code */
#define LZX_MAX_MAIN_CODEWORD_LEN 16
#define LZX_MAX_LEN_CODEWORD_LEN 16
#define LZX_MAX_PRE_CODEWORD_LEN ((1 << LZX_PRECODE_ELEMENT_SIZE) - 1)
#define LZX_MAX_ALIGNED_CODEWORD_LEN ((1 << LZX_ALIGNEDCODE_ELEMENT_SIZE) - 1)
/* The default "filesize" value used in pre/post-processing. In the LZX format
* used in cabinet files this value must be given to the decompressor, whereas
* in the LZX format used in WIM files and system-compressed files this value is
* fixed at 12000000.
*/
#define LZX_DEFAULT_FILESIZE 12000000
/* Assumed block size when the encoded block size begins with a 0 bit. */
#define LZX_DEFAULT_BLOCK_SIZE 32768
/* Number of offsets in the recent (or "repeat") offsets queue. */
#define LZX_NUM_RECENT_OFFSETS 3
/* These values are chosen for fast decompression. */
#define LZX_MAINCODE_TABLEBITS 11
#define LZX_LENCODE_TABLEBITS 10
#define LZX_PRECODE_TABLEBITS 6
#define LZX_ALIGNEDCODE_TABLEBITS 7
#define LZX_READ_LENS_MAX_OVERRUN 50
/* Mapping: offset slot => first match offset that uses that offset slot.
*/
static const u32 lzx_offset_slot_base[LZX_NUM_OFFSET_SLOTS + 1] = {
0, 1, 2, 3, 4, /* 0 --- 4 */
6, 8, 12, 16, 24, /* 5 --- 9 */
32, 48, 64, 96, 128, /* 10 --- 14 */
192, 256, 384, 512, 768, /* 15 --- 19 */
1024, 1536, 2048, 3072, 4096, /* 20 --- 24 */
6144, 8192, 12288, 16384, 24576, /* 25 --- 29 */
32768, /* extra */
};
/* Mapping: offset slot => how many extra bits must be read and added to the
* corresponding offset slot base to decode the match offset.
*/
static const u8 lzx_extra_offset_bits[LZX_NUM_OFFSET_SLOTS] = {
0, 0, 0, 0, 1,
1, 2, 2, 3, 3,
4, 4, 5, 5, 6,
6, 7, 7, 8, 8,
9, 9, 10, 10, 11,
11, 12, 12, 13, 13,
};
/* Reusable heap-allocated memory for LZX decompression */
struct lzx_decompressor {
/* Huffman decoding tables, and arrays that map symbols to codeword
* lengths
*/
u16 maincode_decode_table[(1 << LZX_MAINCODE_TABLEBITS) +
(LZX_MAINCODE_NUM_SYMBOLS * 2)];
u8 maincode_lens[LZX_MAINCODE_NUM_SYMBOLS + LZX_READ_LENS_MAX_OVERRUN];
u16 lencode_decode_table[(1 << LZX_LENCODE_TABLEBITS) +
(LZX_LENCODE_NUM_SYMBOLS * 2)];
u8 lencode_lens[LZX_LENCODE_NUM_SYMBOLS + LZX_READ_LENS_MAX_OVERRUN];
u16 alignedcode_decode_table[(1 << LZX_ALIGNEDCODE_TABLEBITS) +
(LZX_ALIGNEDCODE_NUM_SYMBOLS * 2)];
u8 alignedcode_lens[LZX_ALIGNEDCODE_NUM_SYMBOLS];
u16 precode_decode_table[(1 << LZX_PRECODE_TABLEBITS) +
(LZX_PRECODE_NUM_SYMBOLS * 2)];
u8 precode_lens[LZX_PRECODE_NUM_SYMBOLS];
/* Temporary space for make_huffman_decode_table() */
u16 working_space[2 * (1 + LZX_MAX_MAIN_CODEWORD_LEN) +
LZX_MAINCODE_NUM_SYMBOLS];
};
static void undo_e8_translation(void *target, s32 input_pos)
{
s32 abs_offset, rel_offset;
abs_offset = get_unaligned_le32(target);
if (abs_offset >= 0) {
if (abs_offset < LZX_DEFAULT_FILESIZE) {
/* "good translation" */
rel_offset = abs_offset - input_pos;
put_unaligned_le32(rel_offset, target);
}
} else {
if (abs_offset >= -input_pos) {
/* "compensating translation" */
rel_offset = abs_offset + LZX_DEFAULT_FILESIZE;
put_unaligned_le32(rel_offset, target);
}
}
}
/*
* Undo the 'E8' preprocessing used in LZX. Before compression, the
* uncompressed data was preprocessed by changing the targets of suspected x86
* CALL instructions from relative offsets to absolute offsets. After
* match/literal decoding, the decompressor must undo the translation.
*/
static void lzx_postprocess(u8 *data, u32 size)
{
/*
* A worthwhile optimization is to push the end-of-buffer check into the
* relatively rare E8 case. This is possible if we replace the last six
* bytes of data with E8 bytes; then we are guaranteed to hit an E8 byte
* before reaching end-of-buffer. In addition, this scheme guarantees
* that no translation can begin following an E8 byte in the last 10
* bytes because a 4-byte offset containing E8 as its high byte is a
* large negative number that is not valid for translation. That is
* exactly what we need.
*/
u8 *tail;
u8 saved_bytes[6];
u8 *p;
if (size <= 10)
return;
tail = &data[size - 6];
memcpy(saved_bytes, tail, 6);
memset(tail, 0xE8, 6);
p = data;
for (;;) {
while (*p != 0xE8)
p++;
if (p >= tail)
break;
undo_e8_translation(p + 1, p - data);
p += 5;
}
memcpy(tail, saved_bytes, 6);
}
/* Read a Huffman-encoded symbol using the precode. */
static forceinline u32 read_presym(const struct lzx_decompressor *d,
struct input_bitstream *is)
{
return read_huffsym(is, d->precode_decode_table,
LZX_PRECODE_TABLEBITS, LZX_MAX_PRE_CODEWORD_LEN);
}
/* Read a Huffman-encoded symbol using the main code. */
static forceinline u32 read_mainsym(const struct lzx_decompressor *d,
struct input_bitstream *is)
{
return read_huffsym(is, d->maincode_decode_table,
LZX_MAINCODE_TABLEBITS, LZX_MAX_MAIN_CODEWORD_LEN);
}
/* Read a Huffman-encoded symbol using the length code. */
static forceinline u32 read_lensym(const struct lzx_decompressor *d,
struct input_bitstream *is)
{
return read_huffsym(is, d->lencode_decode_table,
LZX_LENCODE_TABLEBITS, LZX_MAX_LEN_CODEWORD_LEN);
}
/* Read a Huffman-encoded symbol using the aligned offset code. */
static forceinline u32 read_alignedsym(const struct lzx_decompressor *d,
struct input_bitstream *is)
{
return read_huffsym(is, d->alignedcode_decode_table,
LZX_ALIGNEDCODE_TABLEBITS,
LZX_MAX_ALIGNED_CODEWORD_LEN);
}
/*
* Read the precode from the compressed input bitstream, then use it to decode
* @num_lens codeword length values.
*
* @is: The input bitstream.
*
* @lens: An array that contains the length values from the previous time
* the codeword lengths for this Huffman code were read, or all 0's
* if this is the first time. This array must have at least
* (@num_lens + LZX_READ_LENS_MAX_OVERRUN) entries.
*
* @num_lens: Number of length values to decode.
*
* Returns 0 on success, or -1 if the data was invalid.
*/
static int lzx_read_codeword_lens(struct lzx_decompressor *d,
struct input_bitstream *is,
u8 *lens, u32 num_lens)
{
u8 *len_ptr = lens;
u8 *lens_end = lens + num_lens;
int i;
/* Read the lengths of the precode codewords. These are given
* explicitly.
*/
for (i = 0; i < LZX_PRECODE_NUM_SYMBOLS; i++) {
d->precode_lens[i] =
bitstream_read_bits(is, LZX_PRECODE_ELEMENT_SIZE);
}
/* Make the decoding table for the precode. */
if (make_huffman_decode_table(d->precode_decode_table,
LZX_PRECODE_NUM_SYMBOLS,
LZX_PRECODE_TABLEBITS,
d->precode_lens,
LZX_MAX_PRE_CODEWORD_LEN,
d->working_space))
return -1;
/* Decode the codeword lengths. */
do {
u32 presym;
u8 len;
/* Read the next precode symbol. */
presym = read_presym(d, is);
if (presym < 17) {
/* Difference from old length */
len = *len_ptr - presym;
if ((s8)len < 0)
len += 17;
*len_ptr++ = len;
} else {
/* Special RLE values */
u32 run_len;
if (presym == 17) {
/* Run of 0's */
run_len = 4 + bitstream_read_bits(is, 4);
len = 0;
} else if (presym == 18) {
/* Longer run of 0's */
run_len = 20 + bitstream_read_bits(is, 5);
len = 0;
} else {
/* Run of identical lengths */
run_len = 4 + bitstream_read_bits(is, 1);
presym = read_presym(d, is);
if (presym > 17)
return -1;
len = *len_ptr - presym;
if ((s8)len < 0)
len += 17;
}
do {
*len_ptr++ = len;
} while (--run_len);
/* Worst case overrun is when presym == 18,
* run_len == 20 + 31, and only 1 length was remaining.
* So LZX_READ_LENS_MAX_OVERRUN == 50.
*
* Overrun while reading the first half of maincode_lens
* can corrupt the previous values in the second half.
* This doesn't really matter because the resulting
* lengths will still be in range, and data that
* generates overruns is invalid anyway.
*/
}
} while (len_ptr < lens_end);
return 0;
}
/*
* Read the header of an LZX block and save the block type and (uncompressed)
* size in *block_type_ret and *block_size_ret, respectively.
*
* If the block is compressed, also update the Huffman decode @tables with the
* new Huffman codes. If the block is uncompressed, also update the match
* offset @queue with the new match offsets.
*
* Return 0 on success, or -1 if the data was invalid.
*/
static int lzx_read_block_header(struct lzx_decompressor *d,
struct input_bitstream *is,
int *block_type_ret,
u32 *block_size_ret,
u32 recent_offsets[])
{
int block_type;
u32 block_size;
int i;
bitstream_ensure_bits(is, 4);
/* The first three bits tell us what kind of block it is, and should be
* one of the LZX_BLOCKTYPE_* values.
*/
block_type = bitstream_pop_bits(is, 3);
/* Read the block size. */
if (bitstream_pop_bits(is, 1)) {
block_size = LZX_DEFAULT_BLOCK_SIZE;
} else {
block_size = 0;
block_size |= bitstream_read_bits(is, 8);
block_size <<= 8;
block_size |= bitstream_read_bits(is, 8);
}
switch (block_type) {
case LZX_BLOCKTYPE_ALIGNED:
/* Read the aligned offset code and prepare its decode table.
*/
for (i = 0; i < LZX_ALIGNEDCODE_NUM_SYMBOLS; i++) {
d->alignedcode_lens[i] =
bitstream_read_bits(is,
LZX_ALIGNEDCODE_ELEMENT_SIZE);
}
if (make_huffman_decode_table(d->alignedcode_decode_table,
LZX_ALIGNEDCODE_NUM_SYMBOLS,
LZX_ALIGNEDCODE_TABLEBITS,
d->alignedcode_lens,
LZX_MAX_ALIGNED_CODEWORD_LEN,
d->working_space))
return -1;
/* Fall though, since the rest of the header for aligned offset
* blocks is the same as that for verbatim blocks.
*/
fallthrough;
case LZX_BLOCKTYPE_VERBATIM:
/* Read the main code and prepare its decode table.
*
* Note that the codeword lengths in the main code are encoded
* in two parts: one part for literal symbols, and one part for
* match symbols.
*/
if (lzx_read_codeword_lens(d, is, d->maincode_lens,
LZX_NUM_CHARS))
return -1;
if (lzx_read_codeword_lens(d, is,
d->maincode_lens + LZX_NUM_CHARS,
LZX_MAINCODE_NUM_SYMBOLS - LZX_NUM_CHARS))
return -1;
if (make_huffman_decode_table(d->maincode_decode_table,
LZX_MAINCODE_NUM_SYMBOLS,
LZX_MAINCODE_TABLEBITS,
d->maincode_lens,
LZX_MAX_MAIN_CODEWORD_LEN,
d->working_space))
return -1;
/* Read the length code and prepare its decode table. */
if (lzx_read_codeword_lens(d, is, d->lencode_lens,
LZX_LENCODE_NUM_SYMBOLS))
return -1;
if (make_huffman_decode_table(d->lencode_decode_table,
LZX_LENCODE_NUM_SYMBOLS,
LZX_LENCODE_TABLEBITS,
d->lencode_lens,
LZX_MAX_LEN_CODEWORD_LEN,
d->working_space))
return -1;
break;
case LZX_BLOCKTYPE_UNCOMPRESSED:
/* Before reading the three recent offsets from the uncompressed
* block header, the stream must be aligned on a 16-bit
* boundary. But if the stream is *already* aligned, then the
* next 16 bits must be discarded.
*/
bitstream_ensure_bits(is, 1);
bitstream_align(is);
recent_offsets[0] = bitstream_read_u32(is);
recent_offsets[1] = bitstream_read_u32(is);
recent_offsets[2] = bitstream_read_u32(is);
/* Offsets of 0 are invalid. */
if (recent_offsets[0] == 0 || recent_offsets[1] == 0 ||
recent_offsets[2] == 0)
return -1;
break;
default:
/* Unrecognized block type. */
return -1;
}
*block_type_ret = block_type;
*block_size_ret = block_size;
return 0;
}
/* Decompress a block of LZX-compressed data. */
static int lzx_decompress_block(const struct lzx_decompressor *d,
struct input_bitstream *is,
int block_type, u32 block_size,
u8 * const out_begin, u8 *out_next,
u32 recent_offsets[])
{
u8 * const block_end = out_next + block_size;
u32 ones_if_aligned = 0U - (block_type == LZX_BLOCKTYPE_ALIGNED);
do {
u32 mainsym;
u32 match_len;
u32 match_offset;
u32 offset_slot;
u32 num_extra_bits;
mainsym = read_mainsym(d, is);
if (mainsym < LZX_NUM_CHARS) {
/* Literal */
*out_next++ = mainsym;
continue;
}
/* Match */
/* Decode the length header and offset slot. */
mainsym -= LZX_NUM_CHARS;
match_len = mainsym % LZX_NUM_LEN_HEADERS;
offset_slot = mainsym / LZX_NUM_LEN_HEADERS;
/* If needed, read a length symbol to decode the full length. */
if (match_len == LZX_NUM_PRIMARY_LENS)
match_len += read_lensym(d, is);
match_len += LZX_MIN_MATCH_LEN;
if (offset_slot < LZX_NUM_RECENT_OFFSETS) {
/* Repeat offset */
/* Note: This isn't a real LRU queue, since using the R2
* offset doesn't bump the R1 offset down to R2. This
* quirk allows all 3 recent offsets to be handled by
* the same code. (For R0, the swap is a no-op.)
*/
match_offset = recent_offsets[offset_slot];
recent_offsets[offset_slot] = recent_offsets[0];
recent_offsets[0] = match_offset;
} else {
/* Explicit offset */
/* Look up the number of extra bits that need to be read
* to decode offsets with this offset slot.
*/
num_extra_bits = lzx_extra_offset_bits[offset_slot];
/* Start with the offset slot base value. */
match_offset = lzx_offset_slot_base[offset_slot];
/* In aligned offset blocks, the low-order 3 bits of
* each offset are encoded using the aligned offset
* code. Otherwise, all the extra bits are literal.
*/
if ((num_extra_bits & ones_if_aligned) >= LZX_NUM_ALIGNED_OFFSET_BITS) {
match_offset +=
bitstream_read_bits(is, num_extra_bits -
LZX_NUM_ALIGNED_OFFSET_BITS)
<< LZX_NUM_ALIGNED_OFFSET_BITS;
match_offset += read_alignedsym(d, is);
} else {
match_offset += bitstream_read_bits(is, num_extra_bits);
}
/* Adjust the offset. */
match_offset -= (LZX_NUM_RECENT_OFFSETS - 1);
/* Update the recent offsets. */
recent_offsets[2] = recent_offsets[1];
recent_offsets[1] = recent_offsets[0];
recent_offsets[0] = match_offset;
}
/* Validate the match, then copy it to the current position. */
if (match_len > (size_t)(block_end - out_next))
return -1;
if (match_offset > (size_t)(out_next - out_begin))
return -1;
out_next = lz_copy(out_next, match_len, match_offset,
block_end, LZX_MIN_MATCH_LEN);
} while (out_next != block_end);
return 0;
}
/*
* lzx_allocate_decompressor - Allocate an LZX decompressor
*
* Return the pointer to the decompressor on success, or return NULL and set
* errno on failure.
*/
struct lzx_decompressor *lzx_allocate_decompressor(void)
{
return kmalloc(sizeof(struct lzx_decompressor), GFP_NOFS);
}
/*
* lzx_decompress - Decompress a buffer of LZX-compressed data
*
* @decompressor: A decompressor allocated with lzx_allocate_decompressor()
* @compressed_data: The buffer of data to decompress
* @compressed_size: Number of bytes of compressed data
* @uncompressed_data: The buffer in which to store the decompressed data
* @uncompressed_size: The number of bytes the data decompresses into
*
* Return 0 on success, or return -1 and set errno on failure.
*/
int lzx_decompress(struct lzx_decompressor *decompressor,
const void *compressed_data, size_t compressed_size,
void *uncompressed_data, size_t uncompressed_size)
{
struct lzx_decompressor *d = decompressor;
u8 * const out_begin = uncompressed_data;
u8 *out_next = out_begin;
u8 * const out_end = out_begin + uncompressed_size;
struct input_bitstream is;
u32 recent_offsets[LZX_NUM_RECENT_OFFSETS] = {1, 1, 1};
int e8_status = 0;
init_input_bitstream(&is, compressed_data, compressed_size);
/* Codeword lengths begin as all 0's for delta encoding purposes. */
memset(d->maincode_lens, 0, LZX_MAINCODE_NUM_SYMBOLS);
memset(d->lencode_lens, 0, LZX_LENCODE_NUM_SYMBOLS);
/* Decompress blocks until we have all the uncompressed data. */
while (out_next != out_end) {
int block_type;
u32 block_size;
if (lzx_read_block_header(d, &is, &block_type, &block_size,
recent_offsets))
goto invalid;
if (block_size < 1 || block_size > (size_t)(out_end - out_next))
goto invalid;
if (block_type != LZX_BLOCKTYPE_UNCOMPRESSED) {
/* Compressed block */
if (lzx_decompress_block(d,
&is,
block_type,
block_size,
out_begin,
out_next,
recent_offsets))
goto invalid;
e8_status |= d->maincode_lens[0xe8];
out_next += block_size;
} else {
/* Uncompressed block */
out_next = bitstream_read_bytes(&is, out_next,
block_size);
if (!out_next)
goto invalid;
if (block_size & 1)
bitstream_read_byte(&is);
e8_status = 1;
}
}
/* Postprocess the data unless it cannot possibly contain 0xe8 bytes. */
if (e8_status)
lzx_postprocess(uncompressed_data, uncompressed_size);
return 0;
invalid:
return -1;
}
/*
* lzx_free_decompressor - Free an LZX decompressor
*
* @decompressor: A decompressor that was allocated with
* lzx_allocate_decompressor(), or NULL.
*/
void lzx_free_decompressor(struct lzx_decompressor *decompressor)
{
kfree(decompressor);
}

Просмотреть файл

@ -0,0 +1,142 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* xpress_decompress.c - A decompressor for the XPRESS compression format
* (Huffman variant), which can be used in "System Compressed" files. This is
* based on the code from wimlib.
*
* Copyright (C) 2015 Eric Biggers
*/
#include "decompress_common.h"
#include "lib.h"
#define XPRESS_NUM_SYMBOLS 512
#define XPRESS_MAX_CODEWORD_LEN 15
#define XPRESS_MIN_MATCH_LEN 3
/* This value is chosen for fast decompression. */
#define XPRESS_TABLEBITS 12
/* Reusable heap-allocated memory for XPRESS decompression */
struct xpress_decompressor {
/* The Huffman decoding table */
u16 decode_table[(1 << XPRESS_TABLEBITS) + 2 * XPRESS_NUM_SYMBOLS];
/* An array that maps symbols to codeword lengths */
u8 lens[XPRESS_NUM_SYMBOLS];
/* Temporary space for make_huffman_decode_table() */
u16 working_space[2 * (1 + XPRESS_MAX_CODEWORD_LEN) +
XPRESS_NUM_SYMBOLS];
};
/*
* xpress_allocate_decompressor - Allocate an XPRESS decompressor
*
* Return the pointer to the decompressor on success, or return NULL and set
* errno on failure.
*/
struct xpress_decompressor *xpress_allocate_decompressor(void)
{
return kmalloc(sizeof(struct xpress_decompressor), GFP_NOFS);
}
/*
* xpress_decompress - Decompress a buffer of XPRESS-compressed data
*
* @decompressor: A decompressor that was allocated with
* xpress_allocate_decompressor()
* @compressed_data: The buffer of data to decompress
* @compressed_size: Number of bytes of compressed data
* @uncompressed_data: The buffer in which to store the decompressed data
* @uncompressed_size: The number of bytes the data decompresses into
*
* Return 0 on success, or return -1 and set errno on failure.
*/
int xpress_decompress(struct xpress_decompressor *decompressor,
const void *compressed_data, size_t compressed_size,
void *uncompressed_data, size_t uncompressed_size)
{
struct xpress_decompressor *d = decompressor;
const u8 * const in_begin = compressed_data;
u8 * const out_begin = uncompressed_data;
u8 *out_next = out_begin;
u8 * const out_end = out_begin + uncompressed_size;
struct input_bitstream is;
u32 i;
/* Read the Huffman codeword lengths. */
if (compressed_size < XPRESS_NUM_SYMBOLS / 2)
goto invalid;
for (i = 0; i < XPRESS_NUM_SYMBOLS / 2; i++) {
d->lens[i*2 + 0] = in_begin[i] & 0xF;
d->lens[i*2 + 1] = in_begin[i] >> 4;
}
/* Build a decoding table for the Huffman code. */
if (make_huffman_decode_table(d->decode_table, XPRESS_NUM_SYMBOLS,
XPRESS_TABLEBITS, d->lens,
XPRESS_MAX_CODEWORD_LEN,
d->working_space))
goto invalid;
/* Decode the matches and literals. */
init_input_bitstream(&is, in_begin + XPRESS_NUM_SYMBOLS / 2,
compressed_size - XPRESS_NUM_SYMBOLS / 2);
while (out_next != out_end) {
u32 sym;
u32 log2_offset;
u32 length;
u32 offset;
sym = read_huffsym(&is, d->decode_table,
XPRESS_TABLEBITS, XPRESS_MAX_CODEWORD_LEN);
if (sym < 256) {
/* Literal */
*out_next++ = sym;
} else {
/* Match */
length = sym & 0xf;
log2_offset = (sym >> 4) & 0xf;
bitstream_ensure_bits(&is, 16);
offset = ((u32)1 << log2_offset) |
bitstream_pop_bits(&is, log2_offset);
if (length == 0xf) {
length += bitstream_read_byte(&is);
if (length == 0xf + 0xff)
length = bitstream_read_u16(&is);
}
length += XPRESS_MIN_MATCH_LEN;
if (offset > (size_t)(out_next - out_begin))
goto invalid;
if (length > (size_t)(out_end - out_next))
goto invalid;
out_next = lz_copy(out_next, length, offset, out_end,
XPRESS_MIN_MATCH_LEN);
}
}
return 0;
invalid:
return -1;
}
/*
* xpress_free_decompressor - Free an XPRESS decompressor
*
* @decompressor: A decompressor that was allocated with
* xpress_allocate_decompressor(), or NULL.
*/
void xpress_free_decompressor(struct xpress_decompressor *decompressor)
{
kfree(decompressor);
}

453
fs/ntfs3/lznt.c Normal file
Просмотреть файл

@ -0,0 +1,453 @@
// SPDX-License-Identifier: GPL-2.0
/*
*
* Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/nls.h>
#include "debug.h"
#include "ntfs.h"
#include "ntfs_fs.h"
// clang-format off
/* Src buffer is zero. */
#define LZNT_ERROR_ALL_ZEROS 1
#define LZNT_CHUNK_SIZE 0x1000
// clang-format on
struct lznt_hash {
const u8 *p1;
const u8 *p2;
};
struct lznt {
const u8 *unc;
const u8 *unc_end;
const u8 *best_match;
size_t max_len;
bool std;
struct lznt_hash hash[LZNT_CHUNK_SIZE];
};
static inline size_t get_match_len(const u8 *ptr, const u8 *end, const u8 *prev,
size_t max_len)
{
size_t len = 0;
while (ptr + len < end && ptr[len] == prev[len] && ++len < max_len)
;
return len;
}
static size_t longest_match_std(const u8 *src, struct lznt *ctx)
{
size_t hash_index;
size_t len1 = 0, len2 = 0;
const u8 **hash;
hash_index =
((40543U * ((((src[0] << 4) ^ src[1]) << 4) ^ src[2])) >> 4) &
(LZNT_CHUNK_SIZE - 1);
hash = &(ctx->hash[hash_index].p1);
if (hash[0] >= ctx->unc && hash[0] < src && hash[0][0] == src[0] &&
hash[0][1] == src[1] && hash[0][2] == src[2]) {
len1 = 3;
if (ctx->max_len > 3)
len1 += get_match_len(src + 3, ctx->unc_end,
hash[0] + 3, ctx->max_len - 3);
}
if (hash[1] >= ctx->unc && hash[1] < src && hash[1][0] == src[0] &&
hash[1][1] == src[1] && hash[1][2] == src[2]) {
len2 = 3;
if (ctx->max_len > 3)
len2 += get_match_len(src + 3, ctx->unc_end,
hash[1] + 3, ctx->max_len - 3);
}
/* Compare two matches and select the best one. */
if (len1 < len2) {
ctx->best_match = hash[1];
len1 = len2;
} else {
ctx->best_match = hash[0];
}
hash[1] = hash[0];
hash[0] = src;
return len1;
}
static size_t longest_match_best(const u8 *src, struct lznt *ctx)
{
size_t max_len;
const u8 *ptr;
if (ctx->unc >= src || !ctx->max_len)
return 0;
max_len = 0;
for (ptr = ctx->unc; ptr < src; ++ptr) {
size_t len =
get_match_len(src, ctx->unc_end, ptr, ctx->max_len);
if (len >= max_len) {
max_len = len;
ctx->best_match = ptr;
}
}
return max_len >= 3 ? max_len : 0;
}
static const size_t s_max_len[] = {
0x1002, 0x802, 0x402, 0x202, 0x102, 0x82, 0x42, 0x22, 0x12,
};
static const size_t s_max_off[] = {
0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000,
};
static inline u16 make_pair(size_t offset, size_t len, size_t index)
{
return ((offset - 1) << (12 - index)) |
((len - 3) & (((1 << (12 - index)) - 1)));
}
static inline size_t parse_pair(u16 pair, size_t *offset, size_t index)
{
*offset = 1 + (pair >> (12 - index));
return 3 + (pair & ((1 << (12 - index)) - 1));
}
/*
* compress_chunk
*
* Return:
* * 0 - Ok, @cmpr contains @cmpr_chunk_size bytes of compressed data.
* * 1 - Input buffer is full zero.
* * -2 - The compressed buffer is too small to hold the compressed data.
*/
static inline int compress_chunk(size_t (*match)(const u8 *, struct lznt *),
const u8 *unc, const u8 *unc_end, u8 *cmpr,
u8 *cmpr_end, size_t *cmpr_chunk_size,
struct lznt *ctx)
{
size_t cnt = 0;
size_t idx = 0;
const u8 *up = unc;
u8 *cp = cmpr + 3;
u8 *cp2 = cmpr + 2;
u8 not_zero = 0;
/* Control byte of 8-bit values: ( 0 - means byte as is, 1 - short pair ). */
u8 ohdr = 0;
u8 *last;
u16 t16;
if (unc + LZNT_CHUNK_SIZE < unc_end)
unc_end = unc + LZNT_CHUNK_SIZE;
last = min(cmpr + LZNT_CHUNK_SIZE + sizeof(short), cmpr_end);
ctx->unc = unc;
ctx->unc_end = unc_end;
ctx->max_len = s_max_len[0];
while (up < unc_end) {
size_t max_len;
while (unc + s_max_off[idx] < up)
ctx->max_len = s_max_len[++idx];
/* Find match. */
max_len = up + 3 <= unc_end ? (*match)(up, ctx) : 0;
if (!max_len) {
if (cp >= last)
goto NotCompressed;
not_zero |= *cp++ = *up++;
} else if (cp + 1 >= last) {
goto NotCompressed;
} else {
t16 = make_pair(up - ctx->best_match, max_len, idx);
*cp++ = t16;
*cp++ = t16 >> 8;
ohdr |= 1 << cnt;
up += max_len;
}
cnt = (cnt + 1) & 7;
if (!cnt) {
*cp2 = ohdr;
ohdr = 0;
cp2 = cp;
cp += 1;
}
}
if (cp2 < last)
*cp2 = ohdr;
else
cp -= 1;
*cmpr_chunk_size = cp - cmpr;
t16 = (*cmpr_chunk_size - 3) | 0xB000;
cmpr[0] = t16;
cmpr[1] = t16 >> 8;
return not_zero ? 0 : LZNT_ERROR_ALL_ZEROS;
NotCompressed:
if ((cmpr + LZNT_CHUNK_SIZE + sizeof(short)) > last)
return -2;
/*
* Copy non cmpr data.
* 0x3FFF == ((LZNT_CHUNK_SIZE + 2 - 3) | 0x3000)
*/
cmpr[0] = 0xff;
cmpr[1] = 0x3f;
memcpy(cmpr + sizeof(short), unc, LZNT_CHUNK_SIZE);
*cmpr_chunk_size = LZNT_CHUNK_SIZE + sizeof(short);
return 0;
}
static inline ssize_t decompress_chunk(u8 *unc, u8 *unc_end, const u8 *cmpr,
const u8 *cmpr_end)
{
u8 *up = unc;
u8 ch = *cmpr++;
size_t bit = 0;
size_t index = 0;
u16 pair;
size_t offset, length;
/* Do decompression until pointers are inside range. */
while (up < unc_end && cmpr < cmpr_end) {
/* Correct index */
while (unc + s_max_off[index] < up)
index += 1;
/* Check the current flag for zero. */
if (!(ch & (1 << bit))) {
/* Just copy byte. */
*up++ = *cmpr++;
goto next;
}
/* Check for boundary. */
if (cmpr + 1 >= cmpr_end)
return -EINVAL;
/* Read a short from little endian stream. */
pair = cmpr[1];
pair <<= 8;
pair |= cmpr[0];
cmpr += 2;
/* Translate packed information into offset and length. */
length = parse_pair(pair, &offset, index);
/* Check offset for boundary. */
if (unc + offset > up)
return -EINVAL;
/* Truncate the length if necessary. */
if (up + length >= unc_end)
length = unc_end - up;
/* Now we copy bytes. This is the heart of LZ algorithm. */
for (; length > 0; length--, up++)
*up = *(up - offset);
next:
/* Advance flag bit value. */
bit = (bit + 1) & 7;
if (!bit) {
if (cmpr >= cmpr_end)
break;
ch = *cmpr++;
}
}
/* Return the size of uncompressed data. */
return up - unc;
}
/*
* get_lznt_ctx
* @level: 0 - Standard compression.
* !0 - Best compression, requires a lot of cpu.
*/
struct lznt *get_lznt_ctx(int level)
{
struct lznt *r = kzalloc(level ? offsetof(struct lznt, hash)
: sizeof(struct lznt),
GFP_NOFS);
if (r)
r->std = !level;
return r;
}
/*
* compress_lznt - Compresses @unc into @cmpr
*
* Return:
* * +x - Ok, @cmpr contains 'final_compressed_size' bytes of compressed data.
* * 0 - Input buffer is full zero.
*/
size_t compress_lznt(const void *unc, size_t unc_size, void *cmpr,
size_t cmpr_size, struct lznt *ctx)
{
int err;
size_t (*match)(const u8 *src, struct lznt *ctx);
u8 *p = cmpr;
u8 *end = p + cmpr_size;
const u8 *unc_chunk = unc;
const u8 *unc_end = unc_chunk + unc_size;
bool is_zero = true;
if (ctx->std) {
match = &longest_match_std;
memset(ctx->hash, 0, sizeof(ctx->hash));
} else {
match = &longest_match_best;
}
/* Compression cycle. */
for (; unc_chunk < unc_end; unc_chunk += LZNT_CHUNK_SIZE) {
cmpr_size = 0;
err = compress_chunk(match, unc_chunk, unc_end, p, end,
&cmpr_size, ctx);
if (err < 0)
return unc_size;
if (is_zero && err != LZNT_ERROR_ALL_ZEROS)
is_zero = false;
p += cmpr_size;
}
if (p <= end - 2)
p[0] = p[1] = 0;
return is_zero ? 0 : PtrOffset(cmpr, p);
}
/*
* decompress_lznt - Decompress @cmpr into @unc.
*/
ssize_t decompress_lznt(const void *cmpr, size_t cmpr_size, void *unc,
size_t unc_size)
{
const u8 *cmpr_chunk = cmpr;
const u8 *cmpr_end = cmpr_chunk + cmpr_size;
u8 *unc_chunk = unc;
u8 *unc_end = unc_chunk + unc_size;
u16 chunk_hdr;
if (cmpr_size < sizeof(short))
return -EINVAL;
/* Read chunk header. */
chunk_hdr = cmpr_chunk[1];
chunk_hdr <<= 8;
chunk_hdr |= cmpr_chunk[0];
/* Loop through decompressing chunks. */
for (;;) {
size_t chunk_size_saved;
size_t unc_use;
size_t cmpr_use = 3 + (chunk_hdr & (LZNT_CHUNK_SIZE - 1));
/* Check that the chunk actually fits the supplied buffer. */
if (cmpr_chunk + cmpr_use > cmpr_end)
return -EINVAL;
/* First make sure the chunk contains compressed data. */
if (chunk_hdr & 0x8000) {
/* Decompress a chunk and return if we get an error. */
ssize_t err =
decompress_chunk(unc_chunk, unc_end,
cmpr_chunk + sizeof(chunk_hdr),
cmpr_chunk + cmpr_use);
if (err < 0)
return err;
unc_use = err;
} else {
/* This chunk does not contain compressed data. */
unc_use = unc_chunk + LZNT_CHUNK_SIZE > unc_end
? unc_end - unc_chunk
: LZNT_CHUNK_SIZE;
if (cmpr_chunk + sizeof(chunk_hdr) + unc_use >
cmpr_end) {
return -EINVAL;
}
memcpy(unc_chunk, cmpr_chunk + sizeof(chunk_hdr),
unc_use);
}
/* Advance pointers. */
cmpr_chunk += cmpr_use;
unc_chunk += unc_use;
/* Check for the end of unc buffer. */
if (unc_chunk >= unc_end)
break;
/* Proceed the next chunk. */
if (cmpr_chunk > cmpr_end - 2)
break;
chunk_size_saved = LZNT_CHUNK_SIZE;
/* Read chunk header. */
chunk_hdr = cmpr_chunk[1];
chunk_hdr <<= 8;
chunk_hdr |= cmpr_chunk[0];
if (!chunk_hdr)
break;
/* Check the size of unc buffer. */
if (unc_use < chunk_size_saved) {
size_t t1 = chunk_size_saved - unc_use;
u8 *t2 = unc_chunk + t1;
/* 'Zero' memory. */
if (t2 >= unc_end)
break;
memset(unc_chunk, 0, t1);
unc_chunk = t2;
}
}
/* Check compression boundary. */
if (cmpr_chunk > cmpr_end)
return -EINVAL;
/*
* The unc size is just a difference between current
* pointer and original one.
*/
return PtrOffset(unc, unc_chunk);
}

411
fs/ntfs3/namei.c Normal file
Просмотреть файл

@ -0,0 +1,411 @@
// SPDX-License-Identifier: GPL-2.0
/*
*
* Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/iversion.h>
#include <linux/namei.h>
#include <linux/nls.h>
#include "debug.h"
#include "ntfs.h"
#include "ntfs_fs.h"
/*
* fill_name_de - Format NTFS_DE in @buf.
*/
int fill_name_de(struct ntfs_sb_info *sbi, void *buf, const struct qstr *name,
const struct cpu_str *uni)
{
int err;
struct NTFS_DE *e = buf;
u16 data_size;
struct ATTR_FILE_NAME *fname = (struct ATTR_FILE_NAME *)(e + 1);
#ifndef CONFIG_NTFS3_64BIT_CLUSTER
e->ref.high = fname->home.high = 0;
#endif
if (uni) {
#ifdef __BIG_ENDIAN
int ulen = uni->len;
__le16 *uname = fname->name;
const u16 *name_cpu = uni->name;
while (ulen--)
*uname++ = cpu_to_le16(*name_cpu++);
#else
memcpy(fname->name, uni->name, uni->len * sizeof(u16));
#endif
fname->name_len = uni->len;
} else {
/* Convert input string to unicode. */
err = ntfs_nls_to_utf16(sbi, name->name, name->len,
(struct cpu_str *)&fname->name_len,
NTFS_NAME_LEN, UTF16_LITTLE_ENDIAN);
if (err < 0)
return err;
}
fname->type = FILE_NAME_POSIX;
data_size = fname_full_size(fname);
e->size = cpu_to_le16(ALIGN(data_size, 8) + sizeof(struct NTFS_DE));
e->key_size = cpu_to_le16(data_size);
e->flags = 0;
e->res = 0;
return 0;
}
/*
* ntfs_lookup - inode_operations::lookup
*/
static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry,
u32 flags)
{
struct ntfs_inode *ni = ntfs_i(dir);
struct cpu_str *uni = __getname();
struct inode *inode;
int err;
if (!uni)
inode = ERR_PTR(-ENOMEM);
else {
err = ntfs_nls_to_utf16(ni->mi.sbi, dentry->d_name.name,
dentry->d_name.len, uni, NTFS_NAME_LEN,
UTF16_HOST_ENDIAN);
if (err < 0)
inode = ERR_PTR(err);
else {
ni_lock(ni);
inode = dir_search_u(dir, uni, NULL);
ni_unlock(ni);
}
__putname(uni);
}
return d_splice_alias(inode, dentry);
}
/*
* ntfs_create - inode_operations::create
*/
static int ntfs_create(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode, bool excl)
{
struct ntfs_inode *ni = ntfs_i(dir);
struct inode *inode;
ni_lock_dir(ni);
inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFREG | mode,
0, NULL, 0, NULL);
ni_unlock(ni);
return IS_ERR(inode) ? PTR_ERR(inode) : 0;
}
/*
* ntfs_mknod
*
* inode_operations::mknod
*/
static int ntfs_mknod(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode, dev_t rdev)
{
struct ntfs_inode *ni = ntfs_i(dir);
struct inode *inode;
ni_lock_dir(ni);
inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, mode, rdev,
NULL, 0, NULL);
ni_unlock(ni);
return IS_ERR(inode) ? PTR_ERR(inode) : 0;
}
/*
* ntfs_link - inode_operations::link
*/
static int ntfs_link(struct dentry *ode, struct inode *dir, struct dentry *de)
{
int err;
struct inode *inode = d_inode(ode);
struct ntfs_inode *ni = ntfs_i(inode);
if (S_ISDIR(inode->i_mode))
return -EPERM;
if (inode->i_nlink >= NTFS_LINK_MAX)
return -EMLINK;
ni_lock_dir(ntfs_i(dir));
if (inode != dir)
ni_lock(ni);
inc_nlink(inode);
ihold(inode);
err = ntfs_link_inode(inode, de);
if (!err) {
dir->i_ctime = dir->i_mtime = inode->i_ctime =
current_time(dir);
mark_inode_dirty(inode);
mark_inode_dirty(dir);
d_instantiate(de, inode);
} else {
drop_nlink(inode);
iput(inode);
}
if (inode != dir)
ni_unlock(ni);
ni_unlock(ntfs_i(dir));
return err;
}
/*
* ntfs_unlink - inode_operations::unlink
*/
static int ntfs_unlink(struct inode *dir, struct dentry *dentry)
{
struct ntfs_inode *ni = ntfs_i(dir);
int err;
ni_lock_dir(ni);
err = ntfs_unlink_inode(dir, dentry);
ni_unlock(ni);
return err;
}
/*
* ntfs_symlink - inode_operations::symlink
*/
static int ntfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, const char *symname)
{
u32 size = strlen(symname);
struct inode *inode;
struct ntfs_inode *ni = ntfs_i(dir);
ni_lock_dir(ni);
inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFLNK | 0777,
0, symname, size, NULL);
ni_unlock(ni);
return IS_ERR(inode) ? PTR_ERR(inode) : 0;
}
/*
* ntfs_mkdir- inode_operations::mkdir
*/
static int ntfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode)
{
struct inode *inode;
struct ntfs_inode *ni = ntfs_i(dir);
ni_lock_dir(ni);
inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFDIR | mode,
0, NULL, 0, NULL);
ni_unlock(ni);
return IS_ERR(inode) ? PTR_ERR(inode) : 0;
}
/*
* ntfs_rmdir - inode_operations::rm_dir
*/
static int ntfs_rmdir(struct inode *dir, struct dentry *dentry)
{
struct ntfs_inode *ni = ntfs_i(dir);
int err;
ni_lock_dir(ni);
err = ntfs_unlink_inode(dir, dentry);
ni_unlock(ni);
return err;
}
/*
* ntfs_rename - inode_operations::rename
*/
static int ntfs_rename(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, struct inode *new_dir,
struct dentry *new_dentry, u32 flags)
{
int err;
struct super_block *sb = dir->i_sb;
struct ntfs_sb_info *sbi = sb->s_fs_info;
struct ntfs_inode *dir_ni = ntfs_i(dir);
struct ntfs_inode *new_dir_ni = ntfs_i(new_dir);
struct inode *inode = d_inode(dentry);
struct ntfs_inode *ni = ntfs_i(inode);
struct inode *new_inode = d_inode(new_dentry);
struct NTFS_DE *de, *new_de;
bool is_same, is_bad;
/*
* de - memory of PATH_MAX bytes:
* [0-1024) - original name (dentry->d_name)
* [1024-2048) - paired to original name, usually DOS variant of dentry->d_name
* [2048-3072) - new name (new_dentry->d_name)
*/
static_assert(SIZEOF_ATTRIBUTE_FILENAME_MAX + SIZEOF_RESIDENT < 1024);
static_assert(SIZEOF_ATTRIBUTE_FILENAME_MAX + sizeof(struct NTFS_DE) <
1024);
static_assert(PATH_MAX >= 4 * 1024);
if (flags & ~RENAME_NOREPLACE)
return -EINVAL;
is_same = dentry->d_name.len == new_dentry->d_name.len &&
!memcmp(dentry->d_name.name, new_dentry->d_name.name,
dentry->d_name.len);
if (is_same && dir == new_dir) {
/* Nothing to do. */
return 0;
}
if (ntfs_is_meta_file(sbi, inode->i_ino)) {
/* Should we print an error? */
return -EINVAL;
}
if (new_inode) {
/* Target name exists. Unlink it. */
dget(new_dentry);
ni_lock_dir(new_dir_ni);
err = ntfs_unlink_inode(new_dir, new_dentry);
ni_unlock(new_dir_ni);
dput(new_dentry);
if (err)
return err;
}
/* Allocate PATH_MAX bytes. */
de = __getname();
if (!de)
return -ENOMEM;
/* Translate dentry->d_name into unicode form. */
err = fill_name_de(sbi, de, &dentry->d_name, NULL);
if (err < 0)
goto out;
if (is_same) {
/* Reuse 'de'. */
new_de = de;
} else {
/* Translate new_dentry->d_name into unicode form. */
new_de = Add2Ptr(de, 2048);
err = fill_name_de(sbi, new_de, &new_dentry->d_name, NULL);
if (err < 0)
goto out;
}
ni_lock_dir(dir_ni);
ni_lock(ni);
is_bad = false;
err = ni_rename(dir_ni, new_dir_ni, ni, de, new_de, &is_bad);
if (is_bad) {
/* Restore after failed rename failed too. */
make_bad_inode(inode);
ntfs_inode_err(inode, "failed to undo rename");
ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
} else if (!err) {
inode->i_ctime = dir->i_ctime = dir->i_mtime =
current_time(dir);
mark_inode_dirty(inode);
mark_inode_dirty(dir);
if (dir != new_dir) {
new_dir->i_mtime = new_dir->i_ctime = dir->i_ctime;
mark_inode_dirty(new_dir);
}
if (IS_DIRSYNC(dir))
ntfs_sync_inode(dir);
if (IS_DIRSYNC(new_dir))
ntfs_sync_inode(inode);
}
ni_unlock(ni);
ni_unlock(dir_ni);
out:
__putname(de);
return err;
}
struct dentry *ntfs3_get_parent(struct dentry *child)
{
struct inode *inode = d_inode(child);
struct ntfs_inode *ni = ntfs_i(inode);
struct ATTR_LIST_ENTRY *le = NULL;
struct ATTRIB *attr = NULL;
struct ATTR_FILE_NAME *fname;
while ((attr = ni_find_attr(ni, attr, &le, ATTR_NAME, NULL, 0, NULL,
NULL))) {
fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME);
if (!fname)
continue;
return d_obtain_alias(
ntfs_iget5(inode->i_sb, &fname->home, NULL));
}
return ERR_PTR(-ENOENT);
}
// clang-format off
const struct inode_operations ntfs_dir_inode_operations = {
.lookup = ntfs_lookup,
.create = ntfs_create,
.link = ntfs_link,
.unlink = ntfs_unlink,
.symlink = ntfs_symlink,
.mkdir = ntfs_mkdir,
.rmdir = ntfs_rmdir,
.mknod = ntfs_mknod,
.rename = ntfs_rename,
.permission = ntfs_permission,
.get_acl = ntfs_get_acl,
.set_acl = ntfs_set_acl,
.setattr = ntfs3_setattr,
.getattr = ntfs_getattr,
.listxattr = ntfs_listxattr,
.fiemap = ntfs_fiemap,
};
const struct inode_operations ntfs_special_inode_operations = {
.setattr = ntfs3_setattr,
.getattr = ntfs_getattr,
.listxattr = ntfs_listxattr,
.get_acl = ntfs_get_acl,
.set_acl = ntfs_set_acl,
};
// clang-format on

1216
fs/ntfs3/ntfs.h Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

1111
fs/ntfs3/ntfs_fs.h Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

605
fs/ntfs3/record.c Normal file
Просмотреть файл

@ -0,0 +1,605 @@
// SPDX-License-Identifier: GPL-2.0
/*
*
* Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/nls.h>
#include "debug.h"
#include "ntfs.h"
#include "ntfs_fs.h"
static inline int compare_attr(const struct ATTRIB *left, enum ATTR_TYPE type,
const __le16 *name, u8 name_len,
const u16 *upcase)
{
/* First, compare the type codes. */
int diff = le32_to_cpu(left->type) - le32_to_cpu(type);
if (diff)
return diff;
/* They have the same type code, so we have to compare the names. */
return ntfs_cmp_names(attr_name(left), left->name_len, name, name_len,
upcase, true);
}
/*
* mi_new_attt_id
*
* Return: Unused attribute id that is less than mrec->next_attr_id.
*/
static __le16 mi_new_attt_id(struct mft_inode *mi)
{
u16 free_id, max_id, t16;
struct MFT_REC *rec = mi->mrec;
struct ATTRIB *attr;
__le16 id;
id = rec->next_attr_id;
free_id = le16_to_cpu(id);
if (free_id < 0x7FFF) {
rec->next_attr_id = cpu_to_le16(free_id + 1);
return id;
}
/* One record can store up to 1024/24 ~= 42 attributes. */
free_id = 0;
max_id = 0;
attr = NULL;
for (;;) {
attr = mi_enum_attr(mi, attr);
if (!attr) {
rec->next_attr_id = cpu_to_le16(max_id + 1);
mi->dirty = true;
return cpu_to_le16(free_id);
}
t16 = le16_to_cpu(attr->id);
if (t16 == free_id) {
free_id += 1;
attr = NULL;
} else if (max_id < t16)
max_id = t16;
}
}
int mi_get(struct ntfs_sb_info *sbi, CLST rno, struct mft_inode **mi)
{
int err;
struct mft_inode *m = kzalloc(sizeof(struct mft_inode), GFP_NOFS);
if (!m)
return -ENOMEM;
err = mi_init(m, sbi, rno);
if (err) {
kfree(m);
return err;
}
err = mi_read(m, false);
if (err) {
mi_put(m);
return err;
}
*mi = m;
return 0;
}
void mi_put(struct mft_inode *mi)
{
mi_clear(mi);
kfree(mi);
}
int mi_init(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno)
{
mi->sbi = sbi;
mi->rno = rno;
mi->mrec = kmalloc(sbi->record_size, GFP_NOFS);
if (!mi->mrec)
return -ENOMEM;
return 0;
}
/*
* mi_read - Read MFT data.
*/
int mi_read(struct mft_inode *mi, bool is_mft)
{
int err;
struct MFT_REC *rec = mi->mrec;
struct ntfs_sb_info *sbi = mi->sbi;
u32 bpr = sbi->record_size;
u64 vbo = (u64)mi->rno << sbi->record_bits;
struct ntfs_inode *mft_ni = sbi->mft.ni;
struct runs_tree *run = mft_ni ? &mft_ni->file.run : NULL;
struct rw_semaphore *rw_lock = NULL;
if (is_mounted(sbi)) {
if (!is_mft) {
rw_lock = &mft_ni->file.run_lock;
down_read(rw_lock);
}
}
err = ntfs_read_bh(sbi, run, vbo, &rec->rhdr, bpr, &mi->nb);
if (rw_lock)
up_read(rw_lock);
if (!err)
goto ok;
if (err == -E_NTFS_FIXUP) {
mi->dirty = true;
goto ok;
}
if (err != -ENOENT)
goto out;
if (rw_lock) {
ni_lock(mft_ni);
down_write(rw_lock);
}
err = attr_load_runs_vcn(mft_ni, ATTR_DATA, NULL, 0, &mft_ni->file.run,
vbo >> sbi->cluster_bits);
if (rw_lock) {
up_write(rw_lock);
ni_unlock(mft_ni);
}
if (err)
goto out;
if (rw_lock)
down_read(rw_lock);
err = ntfs_read_bh(sbi, run, vbo, &rec->rhdr, bpr, &mi->nb);
if (rw_lock)
up_read(rw_lock);
if (err == -E_NTFS_FIXUP) {
mi->dirty = true;
goto ok;
}
if (err)
goto out;
ok:
/* Check field 'total' only here. */
if (le32_to_cpu(rec->total) != bpr) {
err = -EINVAL;
goto out;
}
return 0;
out:
return err;
}
struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
{
const struct MFT_REC *rec = mi->mrec;
u32 used = le32_to_cpu(rec->used);
u32 t32, off, asize;
u16 t16;
if (!attr) {
u32 total = le32_to_cpu(rec->total);
off = le16_to_cpu(rec->attr_off);
if (used > total)
return NULL;
if (off >= used || off < MFTRECORD_FIXUP_OFFSET_1 ||
!IS_ALIGNED(off, 4)) {
return NULL;
}
/* Skip non-resident records. */
if (!is_rec_inuse(rec))
return NULL;
attr = Add2Ptr(rec, off);
} else {
/* Check if input attr inside record. */
off = PtrOffset(rec, attr);
if (off >= used)
return NULL;
asize = le32_to_cpu(attr->size);
if (asize < SIZEOF_RESIDENT) {
/* Impossible 'cause we should not return such attribute. */
return NULL;
}
attr = Add2Ptr(attr, asize);
off += asize;
}
asize = le32_to_cpu(attr->size);
/* Can we use the first field (attr->type). */
if (off + 8 > used) {
static_assert(ALIGN(sizeof(enum ATTR_TYPE), 8) == 8);
return NULL;
}
if (attr->type == ATTR_END) {
/* End of enumeration. */
return NULL;
}
/* 0x100 is last known attribute for now. */
t32 = le32_to_cpu(attr->type);
if ((t32 & 0xf) || (t32 > 0x100))
return NULL;
/* Check boundary. */
if (off + asize > used)
return NULL;
/* Check size of attribute. */
if (!attr->non_res) {
if (asize < SIZEOF_RESIDENT)
return NULL;
t16 = le16_to_cpu(attr->res.data_off);
if (t16 > asize)
return NULL;
t32 = le32_to_cpu(attr->res.data_size);
if (t16 + t32 > asize)
return NULL;
return attr;
}
/* Check some nonresident fields. */
if (attr->name_len &&
le16_to_cpu(attr->name_off) + sizeof(short) * attr->name_len >
le16_to_cpu(attr->nres.run_off)) {
return NULL;
}
if (attr->nres.svcn || !is_attr_ext(attr)) {
if (asize + 8 < SIZEOF_NONRESIDENT)
return NULL;
if (attr->nres.c_unit)
return NULL;
} else if (asize + 8 < SIZEOF_NONRESIDENT_EX)
return NULL;
return attr;
}
/*
* mi_find_attr - Find the attribute by type and name and id.
*/
struct ATTRIB *mi_find_attr(struct mft_inode *mi, struct ATTRIB *attr,
enum ATTR_TYPE type, const __le16 *name,
size_t name_len, const __le16 *id)
{
u32 type_in = le32_to_cpu(type);
u32 atype;
next_attr:
attr = mi_enum_attr(mi, attr);
if (!attr)
return NULL;
atype = le32_to_cpu(attr->type);
if (atype > type_in)
return NULL;
if (atype < type_in)
goto next_attr;
if (attr->name_len != name_len)
goto next_attr;
if (name_len && memcmp(attr_name(attr), name, name_len * sizeof(short)))
goto next_attr;
if (id && *id != attr->id)
goto next_attr;
return attr;
}
int mi_write(struct mft_inode *mi, int wait)
{
struct MFT_REC *rec;
int err;
struct ntfs_sb_info *sbi;
if (!mi->dirty)
return 0;
sbi = mi->sbi;
rec = mi->mrec;
err = ntfs_write_bh(sbi, &rec->rhdr, &mi->nb, wait);
if (err)
return err;
if (mi->rno < sbi->mft.recs_mirr)
sbi->flags |= NTFS_FLAGS_MFTMIRR;
mi->dirty = false;
return 0;
}
int mi_format_new(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno,
__le16 flags, bool is_mft)
{
int err;
u16 seq = 1;
struct MFT_REC *rec;
u64 vbo = (u64)rno << sbi->record_bits;
err = mi_init(mi, sbi, rno);
if (err)
return err;
rec = mi->mrec;
if (rno == MFT_REC_MFT) {
;
} else if (rno < MFT_REC_FREE) {
seq = rno;
} else if (rno >= sbi->mft.used) {
;
} else if (mi_read(mi, is_mft)) {
;
} else if (rec->rhdr.sign == NTFS_FILE_SIGNATURE) {
/* Record is reused. Update its sequence number. */
seq = le16_to_cpu(rec->seq) + 1;
if (!seq)
seq = 1;
}
memcpy(rec, sbi->new_rec, sbi->record_size);
rec->seq = cpu_to_le16(seq);
rec->flags = RECORD_FLAG_IN_USE | flags;
mi->dirty = true;
if (!mi->nb.nbufs) {
struct ntfs_inode *ni = sbi->mft.ni;
bool lock = false;
if (is_mounted(sbi) && !is_mft) {
down_read(&ni->file.run_lock);
lock = true;
}
err = ntfs_get_bh(sbi, &ni->file.run, vbo, sbi->record_size,
&mi->nb);
if (lock)
up_read(&ni->file.run_lock);
}
return err;
}
/*
* mi_mark_free - Mark record as unused and marks it as free in bitmap.
*/
void mi_mark_free(struct mft_inode *mi)
{
CLST rno = mi->rno;
struct ntfs_sb_info *sbi = mi->sbi;
if (rno >= MFT_REC_RESERVED && rno < MFT_REC_FREE) {
ntfs_clear_mft_tail(sbi, rno, rno + 1);
mi->dirty = false;
return;
}
if (mi->mrec) {
clear_rec_inuse(mi->mrec);
mi->dirty = true;
mi_write(mi, 0);
}
ntfs_mark_rec_free(sbi, rno);
}
/*
* mi_insert_attr - Reserve space for new attribute.
*
* Return: Not full constructed attribute or NULL if not possible to create.
*/
struct ATTRIB *mi_insert_attr(struct mft_inode *mi, enum ATTR_TYPE type,
const __le16 *name, u8 name_len, u32 asize,
u16 name_off)
{
size_t tail;
struct ATTRIB *attr;
__le16 id;
struct MFT_REC *rec = mi->mrec;
struct ntfs_sb_info *sbi = mi->sbi;
u32 used = le32_to_cpu(rec->used);
const u16 *upcase = sbi->upcase;
int diff;
/* Can we insert mi attribute? */
if (used + asize > mi->sbi->record_size)
return NULL;
/*
* Scan through the list of attributes to find the point
* at which we should insert it.
*/
attr = NULL;
while ((attr = mi_enum_attr(mi, attr))) {
diff = compare_attr(attr, type, name, name_len, upcase);
if (diff > 0)
break;
if (diff < 0)
continue;
if (!is_attr_indexed(attr))
return NULL;
break;
}
if (!attr) {
tail = 8; /* Not used, just to suppress warning. */
attr = Add2Ptr(rec, used - 8);
} else {
tail = used - PtrOffset(rec, attr);
}
id = mi_new_attt_id(mi);
memmove(Add2Ptr(attr, asize), attr, tail);
memset(attr, 0, asize);
attr->type = type;
attr->size = cpu_to_le32(asize);
attr->name_len = name_len;
attr->name_off = cpu_to_le16(name_off);
attr->id = id;
memmove(Add2Ptr(attr, name_off), name, name_len * sizeof(short));
rec->used = cpu_to_le32(used + asize);
mi->dirty = true;
return attr;
}
/*
* mi_remove_attr - Remove the attribute from record.
*
* NOTE: The source attr will point to next attribute.
*/
bool mi_remove_attr(struct ntfs_inode *ni, struct mft_inode *mi,
struct ATTRIB *attr)
{
struct MFT_REC *rec = mi->mrec;
u32 aoff = PtrOffset(rec, attr);
u32 used = le32_to_cpu(rec->used);
u32 asize = le32_to_cpu(attr->size);
if (aoff + asize > used)
return false;
if (ni && is_attr_indexed(attr)) {
le16_add_cpu(&ni->mi.mrec->hard_links, -1);
ni->mi.dirty = true;
}
used -= asize;
memmove(attr, Add2Ptr(attr, asize), used - aoff);
rec->used = cpu_to_le32(used);
mi->dirty = true;
return true;
}
/* bytes = "new attribute size" - "old attribute size" */
bool mi_resize_attr(struct mft_inode *mi, struct ATTRIB *attr, int bytes)
{
struct MFT_REC *rec = mi->mrec;
u32 aoff = PtrOffset(rec, attr);
u32 total, used = le32_to_cpu(rec->used);
u32 nsize, asize = le32_to_cpu(attr->size);
u32 rsize = le32_to_cpu(attr->res.data_size);
int tail = (int)(used - aoff - asize);
int dsize;
char *next;
if (tail < 0 || aoff >= used)
return false;
if (!bytes)
return true;
total = le32_to_cpu(rec->total);
next = Add2Ptr(attr, asize);
if (bytes > 0) {
dsize = ALIGN(bytes, 8);
if (used + dsize > total)
return false;
nsize = asize + dsize;
/* Move tail */
memmove(next + dsize, next, tail);
memset(next, 0, dsize);
used += dsize;
rsize += dsize;
} else {
dsize = ALIGN(-bytes, 8);
if (dsize > asize)
return false;
nsize = asize - dsize;
memmove(next - dsize, next, tail);
used -= dsize;
rsize -= dsize;
}
rec->used = cpu_to_le32(used);
attr->size = cpu_to_le32(nsize);
if (!attr->non_res)
attr->res.data_size = cpu_to_le32(rsize);
mi->dirty = true;
return true;
}
int mi_pack_runs(struct mft_inode *mi, struct ATTRIB *attr,
struct runs_tree *run, CLST len)
{
int err = 0;
struct ntfs_sb_info *sbi = mi->sbi;
u32 new_run_size;
CLST plen;
struct MFT_REC *rec = mi->mrec;
CLST svcn = le64_to_cpu(attr->nres.svcn);
u32 used = le32_to_cpu(rec->used);
u32 aoff = PtrOffset(rec, attr);
u32 asize = le32_to_cpu(attr->size);
char *next = Add2Ptr(attr, asize);
u16 run_off = le16_to_cpu(attr->nres.run_off);
u32 run_size = asize - run_off;
u32 tail = used - aoff - asize;
u32 dsize = sbi->record_size - used;
/* Make a maximum gap in current record. */
memmove(next + dsize, next, tail);
/* Pack as much as possible. */
err = run_pack(run, svcn, len, Add2Ptr(attr, run_off), run_size + dsize,
&plen);
if (err < 0) {
memmove(next, next + dsize, tail);
return err;
}
new_run_size = ALIGN(err, 8);
memmove(next + new_run_size - run_size, next + dsize, tail);
attr->size = cpu_to_le32(asize + new_run_size - run_size);
attr->nres.evcn = cpu_to_le64(svcn + plen - 1);
rec->used = cpu_to_le32(used + new_run_size - run_size);
mi->dirty = true;
return 0;
}

1113
fs/ntfs3/run.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

1512
fs/ntfs3/super.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

108
fs/ntfs3/upcase.c Normal file
Просмотреть файл

@ -0,0 +1,108 @@
// SPDX-License-Identifier: GPL-2.0
/*
*
* Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/module.h>
#include <linux/nls.h>
#include "debug.h"
#include "ntfs.h"
#include "ntfs_fs.h"
static inline u16 upcase_unicode_char(const u16 *upcase, u16 chr)
{
if (chr < 'a')
return chr;
if (chr <= 'z')
return chr - ('a' - 'A');
return upcase[chr];
}
/*
* ntfs_cmp_names
*
* Thanks Kari Argillander <kari.argillander@gmail.com> for idea and implementation 'bothcase'
*
* Straight way to compare names:
* - Case insensitive
* - If name equals and 'bothcases' then
* - Case sensitive
* 'Straight way' code scans input names twice in worst case.
* Optimized code scans input names only once.
*/
int ntfs_cmp_names(const __le16 *s1, size_t l1, const __le16 *s2, size_t l2,
const u16 *upcase, bool bothcase)
{
int diff1 = 0;
int diff2;
size_t len = min(l1, l2);
if (!bothcase && upcase)
goto case_insentive;
for (; len; s1++, s2++, len--) {
diff1 = le16_to_cpu(*s1) - le16_to_cpu(*s2);
if (diff1) {
if (bothcase && upcase)
goto case_insentive;
return diff1;
}
}
return l1 - l2;
case_insentive:
for (; len; s1++, s2++, len--) {
diff2 = upcase_unicode_char(upcase, le16_to_cpu(*s1)) -
upcase_unicode_char(upcase, le16_to_cpu(*s2));
if (diff2)
return diff2;
}
diff2 = l1 - l2;
return diff2 ? diff2 : diff1;
}
int ntfs_cmp_names_cpu(const struct cpu_str *uni1, const struct le_str *uni2,
const u16 *upcase, bool bothcase)
{
const u16 *s1 = uni1->name;
const __le16 *s2 = uni2->name;
size_t l1 = uni1->len;
size_t l2 = uni2->len;
size_t len = min(l1, l2);
int diff1 = 0;
int diff2;
if (!bothcase && upcase)
goto case_insentive;
for (; len; s1++, s2++, len--) {
diff1 = *s1 - le16_to_cpu(*s2);
if (diff1) {
if (bothcase && upcase)
goto case_insentive;
return diff1;
}
}
return l1 - l2;
case_insentive:
for (; len; s1++, s2++, len--) {
diff2 = upcase_unicode_char(upcase, *s1) -
upcase_unicode_char(upcase, le16_to_cpu(*s2));
if (diff2)
return diff2;
}
diff2 = l1 - l2;
return diff2 ? diff2 : diff1;
}

1122
fs/ntfs3/xattr.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу