add gcc11 and ntfs3
This commit is contained in:
@@ -530,7 +530,7 @@ $(eval $(call KernelPackage,fs-ntfs))
|
||||
define KernelPackage/fs-ntfs3
|
||||
SUBMENU:=$(FS_MENU)
|
||||
TITLE:=NTFS3 Read-Write file system support
|
||||
DEPENDS:=@LINUX_5_15 +kmod-nls-base
|
||||
DEPENDS:=@LINUX_5_4 +kmod-nls-base
|
||||
KCONFIG:= \
|
||||
CONFIG_NTFS3_FS \
|
||||
CONFIG_NTFS3_64BIT_CLUSTER=y \
|
||||
|
||||
@@ -10,7 +10,7 @@ define Package/ntfs3-mount
|
||||
CATEGORY:=Utilities
|
||||
SUBMENU:=Filesystem
|
||||
TITLE:=NTFS mount script for Paragon NTFS3 driver
|
||||
DEPENDS:=+!LINUX_5_15:kmod-fs-ntfs3-oot +LINUX_5_15:kmod-fs-ntfs3
|
||||
DEPENDS:=+kmod-fs-ntfs3
|
||||
PKGARCH:=all
|
||||
endef
|
||||
|
||||
|
||||
46
target/linux/generic/files-5.4/fs/ntfs3/Kconfig
Normal file
46
target/linux/generic/files-5.4/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.
|
||||
45
target/linux/generic/files-5.4/fs/ntfs3/Makefile
Normal file
45
target/linux/generic/files-5.4/fs/ntfs3/Makefile
Normal file
@@ -0,0 +1,45 @@
|
||||
# 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 \
|
||||
)
|
||||
|
||||
ccflags-$(CONFIG_NTFS3_LZX_XPRESS) += -DCONFIG_NTFS3_LZX_XPRESS
|
||||
ccflags-$(CONFIG_NTFS3_FS_POSIX_ACL) += -DCONFIG_NTFS3_FS_POSIX_ACL
|
||||
|
||||
all:
|
||||
make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules
|
||||
|
||||
clean:
|
||||
make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean
|
||||
2083
target/linux/generic/files-5.4/fs/ntfs3/attrib.c
Normal file
2083
target/linux/generic/files-5.4/fs/ntfs3/attrib.c
Normal file
File diff suppressed because it is too large
Load Diff
457
target/linux/generic/files-5.4/fs/ntfs3/attrlist.c
Normal file
457
target/linux/generic/files-5.4/fs/ntfs3/attrlist.c
Normal file
@@ -0,0 +1,457 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/fs.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, 0);
|
||||
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 sync)
|
||||
{
|
||||
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, sync);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
attr->nres.valid_size = attr->nres.data_size;
|
||||
}
|
||||
|
||||
ni->mi.dirty = true;
|
||||
al->dirty = false;
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
128
target/linux/generic/files-5.4/fs/ntfs3/bitfunc.c
Normal file
128
target/linux/generic/files-5.4/fs/ntfs3/bitfunc.c
Normal file
@@ -0,0 +1,128 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.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) {
|
||||
mask = fill_mask[pos];
|
||||
if ((*map & mask) != mask)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
1491
target/linux/generic/files-5.4/fs/ntfs3/bitmap.c
Normal file
1491
target/linux/generic/files-5.4/fs/ntfs3/bitmap.c
Normal file
File diff suppressed because it is too large
Load Diff
55
target/linux/generic/files-5.4/fs/ntfs3/debug.h
Normal file
55
target/linux/generic/files-5.4/fs/ntfs3/debug.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* 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
|
||||
|
||||
struct super_block;
|
||||
struct inode;
|
||||
|
||||
#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
|
||||
593
target/linux/generic/files-5.4/fs/ntfs3/dir.c
Normal file
593
target/linux/generic/files-5.4/fs/ntfs3/dir.c
Normal file
@@ -0,0 +1,593 @@
|
||||
// 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/fs.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 __le16 *name, u32 len,
|
||||
u8 *buf, int buf_len)
|
||||
{
|
||||
int ret, warn;
|
||||
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(name, len, UTF16_LITTLE_ENDIAN, buf,
|
||||
buf_len);
|
||||
buf[ret] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
op = buf;
|
||||
warn = 0;
|
||||
|
||||
while (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(*name++);
|
||||
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, fname->name, 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
|
||||
1256
target/linux/generic/files-5.4/fs/ntfs3/file.c
Normal file
1256
target/linux/generic/files-5.4/fs/ntfs3/file.c
Normal file
File diff suppressed because it is too large
Load Diff
3284
target/linux/generic/files-5.4/fs/ntfs3/frecord.c
Normal file
3284
target/linux/generic/files-5.4/fs/ntfs3/frecord.c
Normal file
File diff suppressed because it is too large
Load Diff
5213
target/linux/generic/files-5.4/fs/ntfs3/fslog.c
Normal file
5213
target/linux/generic/files-5.4/fs/ntfs3/fslog.c
Normal file
File diff suppressed because it is too large
Load Diff
2506
target/linux/generic/files-5.4/fs/ntfs3/fsntfs.c
Normal file
2506
target/linux/generic/files-5.4/fs/ntfs3/fsntfs.c
Normal file
File diff suppressed because it is too large
Load Diff
2584
target/linux/generic/files-5.4/fs/ntfs3/index.c
Normal file
2584
target/linux/generic/files-5.4/fs/ntfs3/index.c
Normal file
File diff suppressed because it is too large
Load Diff
1945
target/linux/generic/files-5.4/fs/ntfs3/inode.c
Normal file
1945
target/linux/generic/files-5.4/fs/ntfs3/inode.c
Normal file
File diff suppressed because it is too large
Load Diff
319
target/linux/generic/files-5.4/fs/ntfs3/lib/decompress_common.c
Normal file
319
target/linux/generic/files-5.4/fs/ntfs3/lib/decompress_common.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;
|
||||
}
|
||||
343
target/linux/generic/files-5.4/fs/ntfs3/lib/decompress_common.h
Normal file
343
target/linux/generic/files-5.4/fs/ntfs3/lib/decompress_common.h
Normal file
@@ -0,0 +1,343 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* decompress_common.h - Code shared by the XPRESS and LZX decompressors
|
||||
*
|
||||
* Copyright (C) 2015 Eric Biggers
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_NTFS3_LIB_DECOMPRESS_COMMON_H
|
||||
#define _LINUX_NTFS3_LIB_DECOMPRESS_COMMON_H
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
#endif /* _LINUX_NTFS3_LIB_DECOMPRESS_COMMON_H */
|
||||
32
target/linux/generic/files-5.4/fs/ntfs3/lib/lib.h
Normal file
32
target/linux/generic/files-5.4/fs/ntfs3/lib/lib.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/* 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
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_NTFS3_LIB_LIB_H
|
||||
#define _LINUX_NTFS3_LIB_LIB_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* 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);
|
||||
|
||||
#endif /* _LINUX_NTFS3_LIB_LIB_H */
|
||||
670
target/linux/generic/files-5.4/fs/ntfs3/lib/lzx_decompress.c
Normal file
670
target/linux/generic/files-5.4/fs/ntfs3/lib/lzx_decompress.c
Normal file
@@ -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);
|
||||
}
|
||||
142
target/linux/generic/files-5.4/fs/ntfs3/lib/xpress_decompress.c
Normal file
142
target/linux/generic/files-5.4/fs/ntfs3/lib/xpress_decompress.c
Normal file
@@ -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
target/linux/generic/files-5.4/fs/ntfs3/lznt.c
Normal file
453
target/linux/generic/files-5.4/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/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "debug.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);
|
||||
}
|
||||
387
target/linux/generic/files-5.4/fs/ntfs3/namei.c
Normal file
387
target/linux/generic/files-5.4/fs/ntfs3/namei.c
Normal file
@@ -0,0 +1,387 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/fs.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 inode *dir,
|
||||
struct dentry *dentry, umode_t mode, bool excl)
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
inode = ntfs_create_inode(dir, dentry, NULL, S_IFREG | mode,
|
||||
0, NULL, 0, NULL);
|
||||
|
||||
return IS_ERR(inode) ? PTR_ERR(inode) : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_mknod
|
||||
*
|
||||
* inode_operations::mknod
|
||||
*/
|
||||
static int ntfs_mknod(struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode, dev_t rdev)
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
inode = ntfs_create_inode(dir, dentry, NULL, mode, rdev,
|
||||
NULL, 0, NULL);
|
||||
|
||||
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 inode *dir,
|
||||
struct dentry *dentry, const char *symname)
|
||||
{
|
||||
u32 size = strlen(symname);
|
||||
struct inode *inode;
|
||||
|
||||
inode = ntfs_create_inode(dir, dentry, NULL, S_IFLNK | 0777,
|
||||
0, symname, size, NULL);
|
||||
|
||||
return IS_ERR(inode) ? PTR_ERR(inode) : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_mkdir- inode_operations::mkdir
|
||||
*/
|
||||
static int ntfs_mkdir(struct inode *dir,
|
||||
struct dentry *dentry, umode_t mode)
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
inode = ntfs_create_inode(dir, dentry, NULL, S_IFDIR | mode,
|
||||
0, NULL, 0, NULL);
|
||||
|
||||
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 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
|
||||
1224
target/linux/generic/files-5.4/fs/ntfs3/ntfs.h
Normal file
1224
target/linux/generic/files-5.4/fs/ntfs3/ntfs.h
Normal file
File diff suppressed because it is too large
Load Diff
1137
target/linux/generic/files-5.4/fs/ntfs3/ntfs_fs.h
Normal file
1137
target/linux/generic/files-5.4/fs/ntfs3/ntfs_fs.h
Normal file
File diff suppressed because it is too large
Load Diff
602
target/linux/generic/files-5.4/fs/ntfs3/record.c
Normal file
602
target/linux/generic/files-5.4/fs/ntfs3/record.c
Normal file
@@ -0,0 +1,602 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/fs.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;
|
||||
}
|
||||
1111
target/linux/generic/files-5.4/fs/ntfs3/run.c
Normal file
1111
target/linux/generic/files-5.4/fs/ntfs3/run.c
Normal file
File diff suppressed because it is too large
Load Diff
1511
target/linux/generic/files-5.4/fs/ntfs3/super.c
Normal file
1511
target/linux/generic/files-5.4/fs/ntfs3/super.c
Normal file
File diff suppressed because it is too large
Load Diff
104
target/linux/generic/files-5.4/fs/ntfs3/upcase.c
Normal file
104
target/linux/generic/files-5.4/fs/ntfs3/upcase.c
Normal file
@@ -0,0 +1,104 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.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;
|
||||
}
|
||||
992
target/linux/generic/files-5.4/fs/ntfs3/xattr.c
Normal file
992
target/linux/generic/files-5.4/fs/ntfs3/xattr.c
Normal file
@@ -0,0 +1,992 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
*
|
||||
* Copyright (C) 2019-2021 Paragon Software GmbH, All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/posix_acl.h>
|
||||
#include <linux/posix_acl_xattr.h>
|
||||
#include <linux/xattr.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "ntfs.h"
|
||||
#include "ntfs_fs.h"
|
||||
|
||||
// clang-format off
|
||||
#define SYSTEM_DOS_ATTRIB "system.dos_attrib"
|
||||
#define SYSTEM_NTFS_ATTRIB "system.ntfs_attrib"
|
||||
#define SYSTEM_NTFS_SECURITY "system.ntfs_security"
|
||||
// clang-format on
|
||||
|
||||
static inline size_t unpacked_ea_size(const struct EA_FULL *ea)
|
||||
{
|
||||
return ea->size ? le32_to_cpu(ea->size)
|
||||
: ALIGN(struct_size(ea, name,
|
||||
1 + ea->name_len +
|
||||
le16_to_cpu(ea->elength)),
|
||||
4);
|
||||
}
|
||||
|
||||
static inline size_t packed_ea_size(const struct EA_FULL *ea)
|
||||
{
|
||||
return struct_size(ea, name,
|
||||
1 + ea->name_len + le16_to_cpu(ea->elength)) -
|
||||
offsetof(struct EA_FULL, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* find_ea
|
||||
*
|
||||
* Assume there is at least one xattr in the list.
|
||||
*/
|
||||
static inline bool find_ea(const struct EA_FULL *ea_all, u32 bytes,
|
||||
const char *name, u8 name_len, u32 *off)
|
||||
{
|
||||
*off = 0;
|
||||
|
||||
if (!ea_all || !bytes)
|
||||
return false;
|
||||
|
||||
for (;;) {
|
||||
const struct EA_FULL *ea = Add2Ptr(ea_all, *off);
|
||||
u32 next_off = *off + unpacked_ea_size(ea);
|
||||
|
||||
if (next_off > bytes)
|
||||
return false;
|
||||
|
||||
if (ea->name_len == name_len &&
|
||||
!memcmp(ea->name, name, name_len))
|
||||
return true;
|
||||
|
||||
*off = next_off;
|
||||
if (next_off >= bytes)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_read_ea - Read all extended attributes.
|
||||
* @ea: New allocated memory.
|
||||
* @info: Pointer into resident data.
|
||||
*/
|
||||
static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea,
|
||||
size_t add_bytes, const struct EA_INFO **info)
|
||||
{
|
||||
int err;
|
||||
struct ntfs_sb_info *sbi = ni->mi.sbi;
|
||||
struct ATTR_LIST_ENTRY *le = NULL;
|
||||
struct ATTRIB *attr_info, *attr_ea;
|
||||
void *ea_p;
|
||||
u32 size;
|
||||
|
||||
static_assert(le32_to_cpu(ATTR_EA_INFO) < le32_to_cpu(ATTR_EA));
|
||||
|
||||
*ea = NULL;
|
||||
*info = NULL;
|
||||
|
||||
attr_info =
|
||||
ni_find_attr(ni, NULL, &le, ATTR_EA_INFO, NULL, 0, NULL, NULL);
|
||||
attr_ea =
|
||||
ni_find_attr(ni, attr_info, &le, ATTR_EA, NULL, 0, NULL, NULL);
|
||||
|
||||
if (!attr_ea || !attr_info)
|
||||
return 0;
|
||||
|
||||
*info = resident_data_ex(attr_info, sizeof(struct EA_INFO));
|
||||
if (!*info)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check Ea limit. */
|
||||
size = le32_to_cpu((*info)->size);
|
||||
if (size > sbi->ea_max_size)
|
||||
return -EFBIG;
|
||||
|
||||
if (attr_size(attr_ea) > sbi->ea_max_size)
|
||||
return -EFBIG;
|
||||
|
||||
/* Allocate memory for packed Ea. */
|
||||
ea_p = kmalloc(size + add_bytes, GFP_NOFS);
|
||||
if (!ea_p)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!size) {
|
||||
;
|
||||
} else if (attr_ea->non_res) {
|
||||
struct runs_tree run;
|
||||
|
||||
run_init(&run);
|
||||
|
||||
err = attr_load_runs(attr_ea, ni, &run, NULL);
|
||||
if (!err)
|
||||
err = ntfs_read_run_nb(sbi, &run, 0, ea_p, size, NULL);
|
||||
run_close(&run);
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
} else {
|
||||
void *p = resident_data_ex(attr_ea, size);
|
||||
|
||||
if (!p) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
memcpy(ea_p, p, size);
|
||||
}
|
||||
|
||||
memset(Add2Ptr(ea_p, size), 0, add_bytes);
|
||||
*ea = ea_p;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
kfree(ea_p);
|
||||
*ea = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_list_ea
|
||||
*
|
||||
* Copy a list of xattrs names into the buffer
|
||||
* provided, or compute the buffer size required.
|
||||
*
|
||||
* Return:
|
||||
* * Number of bytes used / required on
|
||||
* * -ERRNO - on failure
|
||||
*/
|
||||
static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer,
|
||||
size_t bytes_per_buffer)
|
||||
{
|
||||
const struct EA_INFO *info;
|
||||
struct EA_FULL *ea_all = NULL;
|
||||
const struct EA_FULL *ea;
|
||||
u32 off, size;
|
||||
int err;
|
||||
size_t ret;
|
||||
|
||||
err = ntfs_read_ea(ni, &ea_all, 0, &info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!info || !ea_all)
|
||||
return 0;
|
||||
|
||||
size = le32_to_cpu(info->size);
|
||||
|
||||
/* Enumerate all xattrs. */
|
||||
for (ret = 0, off = 0; off < size; off += unpacked_ea_size(ea)) {
|
||||
ea = Add2Ptr(ea_all, off);
|
||||
|
||||
if (buffer) {
|
||||
if (ret + ea->name_len + 1 > bytes_per_buffer) {
|
||||
err = -ERANGE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(buffer + ret, ea->name, ea->name_len);
|
||||
buffer[ret + ea->name_len] = 0;
|
||||
}
|
||||
|
||||
ret += ea->name_len + 1;
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(ea_all);
|
||||
return err ? err : ret;
|
||||
}
|
||||
|
||||
static int ntfs_get_ea(struct inode *inode, const char *name, size_t name_len,
|
||||
void *buffer, size_t size, size_t *required)
|
||||
{
|
||||
struct ntfs_inode *ni = ntfs_i(inode);
|
||||
const struct EA_INFO *info;
|
||||
struct EA_FULL *ea_all = NULL;
|
||||
const struct EA_FULL *ea;
|
||||
u32 off, len;
|
||||
int err;
|
||||
|
||||
if (!(ni->ni_flags & NI_FLAG_EA))
|
||||
return -ENODATA;
|
||||
|
||||
if (!required)
|
||||
ni_lock(ni);
|
||||
|
||||
len = 0;
|
||||
|
||||
if (name_len > 255) {
|
||||
err = -ENAMETOOLONG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ntfs_read_ea(ni, &ea_all, 0, &info);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (!info)
|
||||
goto out;
|
||||
|
||||
/* Enumerate all xattrs. */
|
||||
if (!find_ea(ea_all, le32_to_cpu(info->size), name, name_len, &off)) {
|
||||
err = -ENODATA;
|
||||
goto out;
|
||||
}
|
||||
ea = Add2Ptr(ea_all, off);
|
||||
|
||||
len = le16_to_cpu(ea->elength);
|
||||
if (!buffer) {
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (len > size) {
|
||||
err = -ERANGE;
|
||||
if (required)
|
||||
*required = len;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(buffer, ea->name + ea->name_len + 1, len);
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
kfree(ea_all);
|
||||
if (!required)
|
||||
ni_unlock(ni);
|
||||
|
||||
return err ? err : len;
|
||||
}
|
||||
|
||||
static noinline int ntfs_set_ea(struct inode *inode, const char *name,
|
||||
size_t name_len, const void *value,
|
||||
size_t val_size, int flags)
|
||||
{
|
||||
struct ntfs_inode *ni = ntfs_i(inode);
|
||||
struct ntfs_sb_info *sbi = ni->mi.sbi;
|
||||
int err;
|
||||
struct EA_INFO ea_info;
|
||||
const struct EA_INFO *info;
|
||||
struct EA_FULL *new_ea;
|
||||
struct EA_FULL *ea_all = NULL;
|
||||
size_t add, new_pack;
|
||||
u32 off, size;
|
||||
__le16 size_pack;
|
||||
struct ATTRIB *attr;
|
||||
struct ATTR_LIST_ENTRY *le;
|
||||
struct mft_inode *mi;
|
||||
struct runs_tree ea_run;
|
||||
u64 new_sz;
|
||||
void *p;
|
||||
|
||||
ni_lock(ni);
|
||||
|
||||
run_init(&ea_run);
|
||||
|
||||
if (name_len > 255) {
|
||||
err = -ENAMETOOLONG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
add = ALIGN(struct_size(ea_all, name, 1 + name_len + val_size), 4);
|
||||
|
||||
err = ntfs_read_ea(ni, &ea_all, add, &info);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (!info) {
|
||||
memset(&ea_info, 0, sizeof(ea_info));
|
||||
size = 0;
|
||||
size_pack = 0;
|
||||
} else {
|
||||
memcpy(&ea_info, info, sizeof(ea_info));
|
||||
size = le32_to_cpu(ea_info.size);
|
||||
size_pack = ea_info.size_pack;
|
||||
}
|
||||
|
||||
if (info && find_ea(ea_all, size, name, name_len, &off)) {
|
||||
struct EA_FULL *ea;
|
||||
size_t ea_sz;
|
||||
|
||||
if (flags & XATTR_CREATE) {
|
||||
err = -EEXIST;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ea = Add2Ptr(ea_all, off);
|
||||
|
||||
/*
|
||||
* Check simple case when we try to insert xattr with the same value
|
||||
* e.g. ntfs_save_wsl_perm
|
||||
*/
|
||||
if (val_size && le16_to_cpu(ea->elength) == val_size &&
|
||||
!memcmp(ea->name + ea->name_len + 1, value, val_size)) {
|
||||
/* xattr already contains the required value. */
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Remove current xattr. */
|
||||
if (ea->flags & FILE_NEED_EA)
|
||||
le16_add_cpu(&ea_info.count, -1);
|
||||
|
||||
ea_sz = unpacked_ea_size(ea);
|
||||
|
||||
le16_add_cpu(&ea_info.size_pack, 0 - packed_ea_size(ea));
|
||||
|
||||
memmove(ea, Add2Ptr(ea, ea_sz), size - off - ea_sz);
|
||||
|
||||
size -= ea_sz;
|
||||
memset(Add2Ptr(ea_all, size), 0, ea_sz);
|
||||
|
||||
ea_info.size = cpu_to_le32(size);
|
||||
|
||||
if ((flags & XATTR_REPLACE) && !val_size) {
|
||||
/* Remove xattr. */
|
||||
goto update_ea;
|
||||
}
|
||||
} else {
|
||||
if (flags & XATTR_REPLACE) {
|
||||
err = -ENODATA;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ea_all) {
|
||||
ea_all = kzalloc(add, GFP_NOFS);
|
||||
if (!ea_all) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Append new xattr. */
|
||||
new_ea = Add2Ptr(ea_all, size);
|
||||
new_ea->size = cpu_to_le32(add);
|
||||
new_ea->flags = 0;
|
||||
new_ea->name_len = name_len;
|
||||
new_ea->elength = cpu_to_le16(val_size);
|
||||
memcpy(new_ea->name, name, name_len);
|
||||
new_ea->name[name_len] = 0;
|
||||
memcpy(new_ea->name + name_len + 1, value, val_size);
|
||||
new_pack = le16_to_cpu(ea_info.size_pack) + packed_ea_size(new_ea);
|
||||
ea_info.size_pack = cpu_to_le16(new_pack);
|
||||
/* New size of ATTR_EA. */
|
||||
size += add;
|
||||
ea_info.size = cpu_to_le32(size);
|
||||
|
||||
/*
|
||||
* 1. Check ea_info.size_pack for overflow.
|
||||
* 2. New attibute size must fit value from $AttrDef
|
||||
*/
|
||||
if (new_pack > 0xffff || size > sbi->ea_max_size) {
|
||||
ntfs_inode_warn(
|
||||
inode,
|
||||
"The size of extended attributes must not exceed 64KiB");
|
||||
err = -EFBIG; // -EINVAL?
|
||||
goto out;
|
||||
}
|
||||
|
||||
update_ea:
|
||||
|
||||
if (!info) {
|
||||
/* Create xattr. */
|
||||
if (!size) {
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ni_insert_resident(ni, sizeof(struct EA_INFO),
|
||||
ATTR_EA_INFO, NULL, 0, NULL, NULL,
|
||||
NULL);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = ni_insert_resident(ni, 0, ATTR_EA, NULL, 0, NULL, NULL,
|
||||
NULL);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
new_sz = size;
|
||||
err = attr_set_size(ni, ATTR_EA, NULL, 0, &ea_run, new_sz, &new_sz,
|
||||
false, NULL);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
le = NULL;
|
||||
attr = ni_find_attr(ni, NULL, &le, ATTR_EA_INFO, NULL, 0, NULL, &mi);
|
||||
if (!attr) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!size) {
|
||||
/* Delete xattr, ATTR_EA_INFO */
|
||||
ni_remove_attr_le(ni, attr, mi, le);
|
||||
} else {
|
||||
p = resident_data_ex(attr, sizeof(struct EA_INFO));
|
||||
if (!p) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
memcpy(p, &ea_info, sizeof(struct EA_INFO));
|
||||
mi->dirty = true;
|
||||
}
|
||||
|
||||
le = NULL;
|
||||
attr = ni_find_attr(ni, NULL, &le, ATTR_EA, NULL, 0, NULL, &mi);
|
||||
if (!attr) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!size) {
|
||||
/* Delete xattr, ATTR_EA */
|
||||
ni_remove_attr_le(ni, attr, mi, le);
|
||||
} else if (attr->non_res) {
|
||||
err = ntfs_sb_write_run(sbi, &ea_run, 0, ea_all, size, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
} else {
|
||||
p = resident_data_ex(attr, size);
|
||||
if (!p) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
memcpy(p, ea_all, size);
|
||||
mi->dirty = true;
|
||||
}
|
||||
|
||||
/* Check if we delete the last xattr. */
|
||||
if (size)
|
||||
ni->ni_flags |= NI_FLAG_EA;
|
||||
else
|
||||
ni->ni_flags &= ~NI_FLAG_EA;
|
||||
|
||||
if (ea_info.size_pack != size_pack)
|
||||
ni->ni_flags |= NI_FLAG_UPDATE_PARENT;
|
||||
mark_inode_dirty(&ni->vfs_inode);
|
||||
|
||||
out:
|
||||
ni_unlock(ni);
|
||||
|
||||
run_close(&ea_run);
|
||||
kfree(ea_all);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NTFS3_FS_POSIX_ACL
|
||||
static struct posix_acl *ntfs_get_acl_ex(
|
||||
struct inode *inode, int type,
|
||||
int locked)
|
||||
{
|
||||
struct ntfs_inode *ni = ntfs_i(inode);
|
||||
const char *name;
|
||||
size_t name_len;
|
||||
struct posix_acl *acl;
|
||||
size_t req;
|
||||
int err;
|
||||
void *buf;
|
||||
|
||||
/* Allocate PATH_MAX bytes. */
|
||||
buf = __getname();
|
||||
if (!buf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* Possible values of 'type' was already checked above. */
|
||||
if (type == ACL_TYPE_ACCESS) {
|
||||
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||||
name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1;
|
||||
} else {
|
||||
name = XATTR_NAME_POSIX_ACL_DEFAULT;
|
||||
name_len = sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1;
|
||||
}
|
||||
|
||||
if (!locked)
|
||||
ni_lock(ni);
|
||||
|
||||
err = ntfs_get_ea(inode, name, name_len, buf, PATH_MAX, &req);
|
||||
|
||||
if (!locked)
|
||||
ni_unlock(ni);
|
||||
|
||||
/* Translate extended attribute to acl. */
|
||||
if (err >= 0) {
|
||||
acl = posix_acl_from_xattr(&init_user_ns, buf, err);
|
||||
} else if (err == -ENODATA) {
|
||||
acl = NULL;
|
||||
} else {
|
||||
acl = ERR_PTR(err);
|
||||
}
|
||||
|
||||
if (!IS_ERR(acl))
|
||||
set_cached_acl(inode, type, acl);
|
||||
|
||||
__putname(buf);
|
||||
|
||||
return acl;
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_get_acl - inode_operations::get_acl
|
||||
*/
|
||||
struct posix_acl *ntfs_get_acl(struct inode *inode, int type)
|
||||
{
|
||||
/* TODO: init_user_ns? */
|
||||
return ntfs_get_acl_ex(inode, type, 0);
|
||||
}
|
||||
|
||||
static noinline int ntfs_set_acl_ex(
|
||||
struct inode *inode, struct posix_acl *acl,
|
||||
int type)
|
||||
{
|
||||
const char *name;
|
||||
size_t size, name_len;
|
||||
void *value = NULL;
|
||||
int err = 0;
|
||||
int flags;
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
if (acl) {
|
||||
umode_t mode = inode->i_mode;
|
||||
|
||||
err = posix_acl_update_mode(inode, &mode,
|
||||
&acl);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (inode->i_mode != mode) {
|
||||
inode->i_mode = mode;
|
||||
mark_inode_dirty(inode);
|
||||
}
|
||||
}
|
||||
name = XATTR_NAME_POSIX_ACL_ACCESS;
|
||||
name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1;
|
||||
break;
|
||||
|
||||
case ACL_TYPE_DEFAULT:
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
return acl ? -EACCES : 0;
|
||||
name = XATTR_NAME_POSIX_ACL_DEFAULT;
|
||||
name_len = sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!acl) {
|
||||
/* Remove xattr if it can be presented via mode. */
|
||||
size = 0;
|
||||
value = NULL;
|
||||
flags = XATTR_REPLACE;
|
||||
} else {
|
||||
size = posix_acl_xattr_size(acl->a_count);
|
||||
value = kmalloc(size, GFP_NOFS);
|
||||
if (!value)
|
||||
return -ENOMEM;
|
||||
err = posix_acl_to_xattr(&init_user_ns, acl, value, size);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
err = ntfs_set_ea(inode, name, name_len, value, size, flags);
|
||||
if (err == -ENODATA && !size)
|
||||
err = 0; /* Removing non existed xattr. */
|
||||
if (!err)
|
||||
set_cached_acl(inode, type, acl);
|
||||
|
||||
out:
|
||||
kfree(value);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_set_acl - inode_operations::set_acl
|
||||
*/
|
||||
int ntfs_set_acl(struct inode *inode,
|
||||
struct posix_acl *acl, int type)
|
||||
{
|
||||
return ntfs_set_acl_ex(inode, acl, type);
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_init_acl - Initialize the ACLs of a new inode.
|
||||
*
|
||||
* Called from ntfs_create_inode().
|
||||
*/
|
||||
int ntfs_init_acl(struct inode *inode,
|
||||
struct inode *dir)
|
||||
{
|
||||
struct posix_acl *default_acl, *acl;
|
||||
int err;
|
||||
|
||||
err = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (default_acl) {
|
||||
err = ntfs_set_acl_ex(inode, default_acl,
|
||||
ACL_TYPE_DEFAULT);
|
||||
posix_acl_release(default_acl);
|
||||
} else {
|
||||
inode->i_default_acl = NULL;
|
||||
}
|
||||
|
||||
if (!acl)
|
||||
inode->i_acl = NULL;
|
||||
else {
|
||||
if (!err)
|
||||
err = ntfs_set_acl_ex(inode, acl,
|
||||
ACL_TYPE_ACCESS);
|
||||
posix_acl_release(acl);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ntfs_acl_chmod - Helper for ntfs3_setattr().
|
||||
*/
|
||||
int ntfs_acl_chmod(struct inode *inode)
|
||||
{
|
||||
struct super_block *sb = inode->i_sb;
|
||||
|
||||
if (!(sb->s_flags & SB_POSIXACL))
|
||||
return 0;
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return posix_acl_chmod(inode, inode->i_mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_permission - inode_operations::permission
|
||||
*/
|
||||
int ntfs_permission(struct inode *inode,
|
||||
int mask)
|
||||
{
|
||||
if (ntfs_sb(inode->i_sb)->options->noacsrules) {
|
||||
/* "No access rules" mode - Allow all changes. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return generic_permission(inode, mask);
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_listxattr - inode_operations::listxattr
|
||||
*/
|
||||
ssize_t ntfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct ntfs_inode *ni = ntfs_i(inode);
|
||||
ssize_t ret;
|
||||
|
||||
if (!(ni->ni_flags & NI_FLAG_EA)) {
|
||||
/* no xattr in file */
|
||||
return 0;
|
||||
}
|
||||
|
||||
ni_lock(ni);
|
||||
|
||||
ret = ntfs_list_ea(ni, buffer, size);
|
||||
|
||||
ni_unlock(ni);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de,
|
||||
struct inode *inode, const char *name, void *buffer,
|
||||
size_t size)
|
||||
{
|
||||
int err;
|
||||
struct ntfs_inode *ni = ntfs_i(inode);
|
||||
size_t name_len = strlen(name);
|
||||
|
||||
/* Dispatch request. */
|
||||
if (name_len == sizeof(SYSTEM_DOS_ATTRIB) - 1 &&
|
||||
!memcmp(name, SYSTEM_DOS_ATTRIB, sizeof(SYSTEM_DOS_ATTRIB))) {
|
||||
/* system.dos_attrib */
|
||||
if (!buffer) {
|
||||
err = sizeof(u8);
|
||||
} else if (size < sizeof(u8)) {
|
||||
err = -ENODATA;
|
||||
} else {
|
||||
err = sizeof(u8);
|
||||
*(u8 *)buffer = le32_to_cpu(ni->std_fa);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (name_len == sizeof(SYSTEM_NTFS_ATTRIB) - 1 &&
|
||||
!memcmp(name, SYSTEM_NTFS_ATTRIB, sizeof(SYSTEM_NTFS_ATTRIB))) {
|
||||
/* system.ntfs_attrib */
|
||||
if (!buffer) {
|
||||
err = sizeof(u32);
|
||||
} else if (size < sizeof(u32)) {
|
||||
err = -ENODATA;
|
||||
} else {
|
||||
err = sizeof(u32);
|
||||
*(u32 *)buffer = le32_to_cpu(ni->std_fa);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (name_len == sizeof(SYSTEM_NTFS_SECURITY) - 1 &&
|
||||
!memcmp(name, SYSTEM_NTFS_SECURITY, sizeof(SYSTEM_NTFS_SECURITY))) {
|
||||
/* system.ntfs_security*/
|
||||
struct SECURITY_DESCRIPTOR_RELATIVE *sd = NULL;
|
||||
size_t sd_size = 0;
|
||||
|
||||
if (!is_ntfs3(ni->mi.sbi)) {
|
||||
/* We should get nt4 security. */
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
} else if (le32_to_cpu(ni->std_security_id) <
|
||||
SECURITY_ID_FIRST) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ntfs_get_security_by_id(ni->mi.sbi, ni->std_security_id,
|
||||
&sd, &sd_size);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (!is_sd_valid(sd, sd_size)) {
|
||||
ntfs_inode_warn(
|
||||
inode,
|
||||
"looks like you get incorrect security descriptor id=%u",
|
||||
ni->std_security_id);
|
||||
}
|
||||
|
||||
if (!buffer) {
|
||||
err = sd_size;
|
||||
} else if (size < sd_size) {
|
||||
err = -ENODATA;
|
||||
} else {
|
||||
err = sd_size;
|
||||
memcpy(buffer, sd, sd_size);
|
||||
}
|
||||
kfree(sd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Deal with NTFS extended attribute. */
|
||||
err = ntfs_get_ea(inode, name, name_len, buffer, size, NULL);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_setxattr - inode_operations::setxattr
|
||||
*/
|
||||
static noinline int ntfs_setxattr(const struct xattr_handler *handler,
|
||||
|
||||
struct dentry *de, struct inode *inode,
|
||||
const char *name, const void *value,
|
||||
size_t size, int flags)
|
||||
{
|
||||
int err = -EINVAL;
|
||||
struct ntfs_inode *ni = ntfs_i(inode);
|
||||
size_t name_len = strlen(name);
|
||||
enum FILE_ATTRIBUTE new_fa;
|
||||
|
||||
/* Dispatch request. */
|
||||
if (name_len == sizeof(SYSTEM_DOS_ATTRIB) - 1 &&
|
||||
!memcmp(name, SYSTEM_DOS_ATTRIB, sizeof(SYSTEM_DOS_ATTRIB))) {
|
||||
if (sizeof(u8) != size)
|
||||
goto out;
|
||||
new_fa = cpu_to_le32(*(u8 *)value);
|
||||
goto set_new_fa;
|
||||
}
|
||||
|
||||
if (name_len == sizeof(SYSTEM_NTFS_ATTRIB) - 1 &&
|
||||
!memcmp(name, SYSTEM_NTFS_ATTRIB, sizeof(SYSTEM_NTFS_ATTRIB))) {
|
||||
if (size != sizeof(u32))
|
||||
goto out;
|
||||
new_fa = cpu_to_le32(*(u32 *)value);
|
||||
|
||||
if (S_ISREG(inode->i_mode)) {
|
||||
/* Process compressed/sparsed in special way. */
|
||||
ni_lock(ni);
|
||||
err = ni_new_attr_flags(ni, new_fa);
|
||||
ni_unlock(ni);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
set_new_fa:
|
||||
/*
|
||||
* Thanks Mark Harmstone:
|
||||
* Keep directory bit consistency.
|
||||
*/
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
new_fa |= FILE_ATTRIBUTE_DIRECTORY;
|
||||
else
|
||||
new_fa &= ~FILE_ATTRIBUTE_DIRECTORY;
|
||||
|
||||
if (ni->std_fa != new_fa) {
|
||||
ni->std_fa = new_fa;
|
||||
if (new_fa & FILE_ATTRIBUTE_READONLY)
|
||||
inode->i_mode &= ~0222;
|
||||
else
|
||||
inode->i_mode |= 0222;
|
||||
/* Std attribute always in primary record. */
|
||||
ni->mi.dirty = true;
|
||||
mark_inode_dirty(inode);
|
||||
}
|
||||
err = 0;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (name_len == sizeof(SYSTEM_NTFS_SECURITY) - 1 &&
|
||||
!memcmp(name, SYSTEM_NTFS_SECURITY, sizeof(SYSTEM_NTFS_SECURITY))) {
|
||||
/* system.ntfs_security*/
|
||||
__le32 security_id;
|
||||
bool inserted;
|
||||
struct ATTR_STD_INFO5 *std;
|
||||
|
||||
if (!is_ntfs3(ni->mi.sbi)) {
|
||||
/*
|
||||
* We should replace ATTR_SECURE.
|
||||
* Skip this way cause it is nt4 feature.
|
||||
*/
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!is_sd_valid(value, size)) {
|
||||
err = -EINVAL;
|
||||
ntfs_inode_warn(
|
||||
inode,
|
||||
"you try to set invalid security descriptor");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ntfs_insert_security(ni->mi.sbi, value, size,
|
||||
&security_id, &inserted);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
ni_lock(ni);
|
||||
std = ni_std5(ni);
|
||||
if (!std) {
|
||||
err = -EINVAL;
|
||||
} else if (std->security_id != security_id) {
|
||||
std->security_id = ni->std_security_id = security_id;
|
||||
/* Std attribute always in primary record. */
|
||||
ni->mi.dirty = true;
|
||||
mark_inode_dirty(&ni->vfs_inode);
|
||||
}
|
||||
ni_unlock(ni);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Deal with NTFS extended attribute. */
|
||||
err = ntfs_set_ea(inode, name, name_len, value, size, flags);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_save_wsl_perm
|
||||
*
|
||||
* save uid/gid/mode in xattr
|
||||
*/
|
||||
int ntfs_save_wsl_perm(struct inode *inode)
|
||||
{
|
||||
int err;
|
||||
__le32 value;
|
||||
|
||||
/* TODO: refactor this, so we don't lock 4 times in ntfs_set_ea */
|
||||
value = cpu_to_le32(i_uid_read(inode));
|
||||
err = ntfs_set_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &value,
|
||||
sizeof(value), 0);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
value = cpu_to_le32(i_gid_read(inode));
|
||||
err = ntfs_set_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &value,
|
||||
sizeof(value), 0);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
value = cpu_to_le32(inode->i_mode);
|
||||
err = ntfs_set_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &value,
|
||||
sizeof(value), 0);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
|
||||
value = cpu_to_le32(inode->i_rdev);
|
||||
err = ntfs_set_ea(inode, "$LXDEV", sizeof("$LXDEV") - 1, &value,
|
||||
sizeof(value), 0);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
/* In case of error should we delete all WSL xattr? */
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_get_wsl_perm
|
||||
*
|
||||
* get uid/gid/mode from xattr
|
||||
* it is called from ntfs_iget5->ntfs_read_mft
|
||||
*/
|
||||
void ntfs_get_wsl_perm(struct inode *inode)
|
||||
{
|
||||
size_t sz;
|
||||
__le32 value[3];
|
||||
|
||||
if (ntfs_get_ea(inode, "$LXUID", sizeof("$LXUID") - 1, &value[0],
|
||||
sizeof(value[0]), &sz) == sizeof(value[0]) &&
|
||||
ntfs_get_ea(inode, "$LXGID", sizeof("$LXGID") - 1, &value[1],
|
||||
sizeof(value[1]), &sz) == sizeof(value[1]) &&
|
||||
ntfs_get_ea(inode, "$LXMOD", sizeof("$LXMOD") - 1, &value[2],
|
||||
sizeof(value[2]), &sz) == sizeof(value[2])) {
|
||||
i_uid_write(inode, (uid_t)le32_to_cpu(value[0]));
|
||||
i_gid_write(inode, (gid_t)le32_to_cpu(value[1]));
|
||||
inode->i_mode = le32_to_cpu(value[2]);
|
||||
|
||||
if (ntfs_get_ea(inode, "$LXDEV", sizeof("$$LXDEV") - 1,
|
||||
&value[0], sizeof(value),
|
||||
&sz) == sizeof(value[0])) {
|
||||
inode->i_rdev = le32_to_cpu(value[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool ntfs_xattr_user_list(struct dentry *dentry)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
static const struct xattr_handler ntfs_xattr_handler = {
|
||||
.prefix = "",
|
||||
.get = ntfs_getxattr,
|
||||
.set = ntfs_setxattr,
|
||||
.list = ntfs_xattr_user_list,
|
||||
};
|
||||
|
||||
const struct xattr_handler *ntfs_xattr_handlers[] = {
|
||||
&ntfs_xattr_handler,
|
||||
NULL,
|
||||
};
|
||||
// clang-format on
|
||||
@@ -0,0 +1,40 @@
|
||||
From e64a5b846cd19f86eae8d8f2708f2e483dc37feb Mon Sep 17 00:00:00 2021
|
||||
From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
|
||||
Date: Thu, 28 Jan 2021 12:04:54 +0300
|
||||
Subject: [PATCH 09/11] fs/ntfs3: Add NTFS3 in fs/Kconfig and fs/Makefile
|
||||
|
||||
This adds NTFS3 in fs/Kconfig and fs/Makefile
|
||||
|
||||
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
|
||||
---
|
||||
fs/Kconfig | 1 +
|
||||
fs/Makefile | 1 +
|
||||
2 files changed, 2 insertions(+)
|
||||
|
||||
diff --git a/fs/Kconfig b/fs/Kconfig
|
||||
index da524c4d7b7e..0bbad356ab57 100644
|
||||
--- a/fs/Kconfig
|
||||
+++ b/fs/Kconfig
|
||||
@@ -145,6 +145,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
|
||||
diff --git a/fs/Makefile b/fs/Makefile
|
||||
index 999d1a23f036..4f5242cdaee2 100644
|
||||
--- a/fs/Makefile
|
||||
+++ b/fs/Makefile
|
||||
@@ -100,6 +100,7 @@ obj-$(CONFIG_SYSV_FS) += sysv/
|
||||
obj-$(CONFIG_CIFS) += cifs/
|
||||
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/
|
||||
--
|
||||
2.30.1
|
||||
|
||||
@@ -37,6 +37,39 @@ menuconfig TARGET_OPTIONS
|
||||
|
||||
Most people will answer N.
|
||||
|
||||
choice BPF_TOOLCHAIN
|
||||
prompt "BPF toolchain" if DEVEL
|
||||
default BPF_TOOLCHAIN_BUILD_LLVM if BUILDBOT
|
||||
default BPF_TOOLCHAIN_PREBUILT if HAS_PREBUILT_LLVM_TOOLCHAIN
|
||||
default BPF_TOOLCHAIN_NONE
|
||||
|
||||
config BPF_TOOLCHAIN_NONE
|
||||
bool "None"
|
||||
|
||||
config BPF_TOOLCHAIN_PREBUILT
|
||||
bool "Use prebuilt LLVM toolchain"
|
||||
depends on HAS_PREBUILT_LLVM_TOOLCHAIN
|
||||
select USE_LLVM_PREBUILT
|
||||
|
||||
config BPF_TOOLCHAIN_HOST
|
||||
select USE_LLVM_HOST
|
||||
bool "Use host LLVM toolchain"
|
||||
|
||||
config BPF_TOOLCHAIN_BUILD_LLVM
|
||||
select USE_LLVM_BUILD
|
||||
bool "Build LLVM toolchain for eBPF"
|
||||
help
|
||||
If enabled, a LLVM toolchain for building eBPF binaries will be built.
|
||||
If this is not enabled, eBPF packages can only be built if the host
|
||||
has a suitable toolchain
|
||||
endchoice
|
||||
|
||||
config BPF_TOOLCHAIN_HOST_PATH
|
||||
string
|
||||
depends on BPF_TOOLCHAIN_HOST
|
||||
prompt "Host LLVM toolchain path (prefix)" if DEVEL
|
||||
default "/usr/local/opt/llvm" if HOST_OS_MACOS
|
||||
default ""
|
||||
|
||||
menuconfig EXTERNAL_TOOLCHAIN
|
||||
bool
|
||||
@@ -213,6 +246,14 @@ comment "Binary tools"
|
||||
|
||||
source "toolchain/binutils/Config.in"
|
||||
|
||||
config DWARVES
|
||||
bool
|
||||
prompt "Build pahole" if TOOLCHAINOPTS
|
||||
depends on !HOST_OS_MACOS
|
||||
default n
|
||||
help
|
||||
Enable if you want to build pahole and the dwarves tools.
|
||||
|
||||
comment "Compiler"
|
||||
depends on TOOLCHAINOPTS
|
||||
|
||||
@@ -266,6 +307,26 @@ config GDB_PYTHON
|
||||
|
||||
help
|
||||
Enable the python bindings for GDB to allow using python in the gdb shell.
|
||||
|
||||
config HAS_BPF_TOOLCHAIN
|
||||
bool
|
||||
|
||||
config HAS_PREBUILT_LLVM_TOOLCHAIN
|
||||
def_bool $(shell, [ -f llvm-bpf/.llvm-version ] && echo y || echo n)
|
||||
|
||||
config USE_LLVM_HOST
|
||||
select HAS_BPF_TOOLCHAIN
|
||||
bool
|
||||
|
||||
config USE_LLVM_PREBUILT
|
||||
select HAS_BPF_TOOLCHAIN
|
||||
default y if !DEVEL && !BUILDBOT && HAS_PREBUILT_LLVM_TOOLCHAIN
|
||||
bool
|
||||
|
||||
config USE_LLVM_BUILD
|
||||
default y if !DEVEL && BUILDBOT
|
||||
select HAS_BPF_TOOLCHAIN
|
||||
bool
|
||||
|
||||
config USE_GLIBC
|
||||
default y if !TOOLCHAINOPTS && !EXTERNAL_TOOLCHAIN && !NATIVE_TOOLCHAIN && (arc)
|
||||
|
||||
@@ -29,6 +29,10 @@ choice
|
||||
config BINUTILS_USE_VERSION_2_38
|
||||
bool "Binutils 2.38"
|
||||
select BINUTILS_VERSION_2_38
|
||||
|
||||
config BINUTILS_USE_VERSION_2_39
|
||||
bool "Binutils 2.39"
|
||||
select BINUTILS_VERSION_2_39
|
||||
endchoice
|
||||
|
||||
config EXTRA_BINUTILS_CONFIG_OPTIONS
|
||||
|
||||
@@ -16,6 +16,9 @@ config BINUTILS_VERSION_2_37
|
||||
|
||||
config BINUTILS_VERSION_2_38
|
||||
bool
|
||||
|
||||
config BINUTILS_VERSION_2_39
|
||||
bool
|
||||
|
||||
config BINUTILS_VERSION
|
||||
string
|
||||
@@ -25,3 +28,4 @@ config BINUTILS_VERSION
|
||||
default "2.36.1" if BINUTILS_VERSION_2_36_1
|
||||
default "2.37" if BINUTILS_VERSION_2_37
|
||||
default "2.38" if BINUTILS_VERSION_2_38
|
||||
default "2.39" if BINUTILS_VERSION_2_39
|
||||
|
||||
@@ -39,6 +39,10 @@ ifeq ($(PKG_VERSION),2.38)
|
||||
PKG_HASH:=e316477a914f567eccc34d5d29785b8b0f5a10208d36bbacedcc39048ecfe024
|
||||
endif
|
||||
|
||||
ifeq ($(PKG_VERSION),2.39)
|
||||
PKG_HASH:=645c25f563b8adc0a81dbd6a41cffbf4d37083a382e02d5d3df4f65c09516d00
|
||||
endif
|
||||
|
||||
HOST_BUILD_PARALLEL:=1
|
||||
|
||||
PATCH_DIR:=./patches/$(PKG_VERSION)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
--- a/ld/Makefile.am
|
||||
+++ b/ld/Makefile.am
|
||||
@@ -50,7 +50,7 @@ AM_CFLAGS = $(WARN_CFLAGS) $(ELF_CLFAGS)
|
||||
# We put the scripts in the directory $(scriptdir)/ldscripts.
|
||||
# We can't put the scripts in $(datadir) because the SEARCH_DIR
|
||||
# directives need to be different for native and cross linkers.
|
||||
-scriptdir = $(tooldir)/lib
|
||||
+scriptdir = $(libdir)
|
||||
|
||||
EMUL = @EMUL@
|
||||
EMULATION_OFILES = @EMULATION_OFILES@
|
||||
--- a/ld/Makefile.in
|
||||
+++ b/ld/Makefile.in
|
||||
@@ -569,7 +569,7 @@ AM_CFLAGS = $(WARN_CFLAGS) $(ELF_CLFAGS)
|
||||
# We put the scripts in the directory $(scriptdir)/ldscripts.
|
||||
# We can't put the scripts in $(datadir) because the SEARCH_DIR
|
||||
# directives need to be different for native and cross linkers.
|
||||
-scriptdir = $(tooldir)/lib
|
||||
+scriptdir = $(libdir)
|
||||
BASEDIR = $(srcdir)/..
|
||||
BFDDIR = $(BASEDIR)/bfd
|
||||
INCDIR = $(BASEDIR)/include
|
||||
@@ -0,0 +1,18 @@
|
||||
--- a/bfd/elfxx-mips.c
|
||||
+++ b/bfd/elfxx-mips.c
|
||||
@@ -8066,6 +8066,7 @@ _bfd_mips_elf_create_dynamic_sections (b
|
||||
|
||||
name = SGI_COMPAT (abfd) ? "_DYNAMIC_LINK" : "_DYNAMIC_LINKING";
|
||||
bh = NULL;
|
||||
+ if (0) {
|
||||
if (!(_bfd_generic_link_add_one_symbol
|
||||
(info, abfd, name, BSF_GLOBAL, bfd_abs_section_ptr, 0,
|
||||
NULL, false, get_elf_backend_data (abfd)->collect, &bh)))
|
||||
@@ -8078,6 +8079,7 @@ _bfd_mips_elf_create_dynamic_sections (b
|
||||
|
||||
if (! bfd_elf_link_record_dynamic_symbol (info, h))
|
||||
return false;
|
||||
+ }
|
||||
|
||||
if (! mips_elf_hash_table (info)->use_rld_obj_head)
|
||||
{
|
||||
@@ -0,0 +1,38 @@
|
||||
--- a/bfd/config.bfd
|
||||
+++ b/bfd/config.bfd
|
||||
@@ -928,12 +928,12 @@ case "${targ}" in
|
||||
targ_selvecs="mips_elf32_le_vec mips_elf64_be_vec mips_elf64_le_vec mips_ecoff_be_vec mips_ecoff_le_vec"
|
||||
;;
|
||||
mips64*el-*-linux*)
|
||||
- targ_defvec=mips_elf32_ntrad_le_vec
|
||||
- targ_selvecs="mips_elf32_ntrad_be_vec mips_elf32_trad_le_vec mips_elf32_trad_be_vec mips_elf64_trad_le_vec mips_elf64_trad_be_vec"
|
||||
+ targ_defvec=mips_elf64_trad_le_vec
|
||||
+ targ_selvecs="mips_elf32_ntrad_le_vec mips_elf32_ntrad_be_vec mips_elf32_trad_le_vec mips_elf32_trad_be_vec mips_elf64_trad_be_vec"
|
||||
;;
|
||||
mips64*-*-linux*)
|
||||
- targ_defvec=mips_elf32_ntrad_be_vec
|
||||
- targ_selvecs="mips_elf32_ntrad_le_vec mips_elf32_trad_be_vec mips_elf32_trad_le_vec mips_elf64_trad_be_vec mips_elf64_trad_le_vec"
|
||||
+ targ_defvec=mips_elf64_trad_be_vec
|
||||
+ targ_selvecs="mips_elf32_ntrad_be_vec mips_elf32_ntrad_le_vec mips_elf32_trad_be_vec mips_elf32_trad_le_vec mips_elf64_trad_le_vec"
|
||||
;;
|
||||
mips*el-*-linux*)
|
||||
targ_defvec=mips_elf32_trad_le_vec
|
||||
--- a/ld/configure.tgt
|
||||
+++ b/ld/configure.tgt
|
||||
@@ -580,12 +580,12 @@ mips*-*-vxworks*) targ_emul=elf32ebmipvx
|
||||
;;
|
||||
mips*-*-windiss) targ_emul=elf32mipswindiss
|
||||
;;
|
||||
-mips64*el-*-linux-*) targ_emul=elf32ltsmipn32
|
||||
- targ_extra_emuls="elf32btsmipn32 elf32ltsmip elf32btsmip elf64ltsmip elf64btsmip"
|
||||
+mips64*el-*-linux-*) targ_emul=elf64ltsmip
|
||||
+ targ_extra_emuls="elf32btsmipn32 elf32ltsmipn32 elf32ltsmip elf32btsmip elf64btsmip"
|
||||
targ_extra_libpath=$targ_extra_emuls
|
||||
;;
|
||||
-mips64*-*-linux-*) targ_emul=elf32btsmipn32
|
||||
- targ_extra_emuls="elf32ltsmipn32 elf32btsmip elf32ltsmip elf64btsmip elf64ltsmip"
|
||||
+mips64*-*-linux-*) targ_emul=elf64btsmip
|
||||
+ targ_extra_emuls="elf32btsmipn32 elf32ltsmipn32 elf32btsmip elf32ltsmip elf64ltsmip"
|
||||
targ_extra_libpath=$targ_extra_emuls
|
||||
;;
|
||||
mips*el-*-linux-*) targ_emul=elf32ltsmip
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
choice
|
||||
prompt "GCC compiler Version" if TOOLCHAINOPTS
|
||||
default GCC_USE_VERSION_8
|
||||
default GCC_USE_VERSION_8 if mips || mipsel || mips64 || mips64el
|
||||
default GCC_USE_VERSION_11
|
||||
help
|
||||
Select the version of gcc you wish to use.
|
||||
|
||||
@@ -21,6 +22,9 @@ choice
|
||||
|
||||
config GCC_USE_VERSION_11
|
||||
bool "gcc 11.x"
|
||||
|
||||
config GCC_USE_VERSION_12
|
||||
bool "gcc 12.x"
|
||||
endchoice
|
||||
|
||||
config GCC_USE_GRAPHITE
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
config GCC_VERSION_7
|
||||
default y if GCC_USE_VERSION_7
|
||||
bool
|
||||
|
||||
config GCC_VERSION_8
|
||||
default y if GCC_USE_VERSION_8
|
||||
default y if mips || mipsel || mips64 || mips64el
|
||||
bool
|
||||
|
||||
config GCC_VERSION_9
|
||||
default y if GCC_USE_VERSION_9
|
||||
@@ -10,17 +15,18 @@ config GCC_VERSION_10
|
||||
default y if GCC_USE_VERSION_10
|
||||
bool
|
||||
|
||||
config GCC_VERSION_11
|
||||
default y if GCC_USE_VERSION_11
|
||||
config GCC_VERSION_12
|
||||
default y if GCC_USE_VERSION_12
|
||||
bool
|
||||
|
||||
config GCC_VERSION
|
||||
string
|
||||
default "7.5.0" if GCC_VERSION_7
|
||||
default "8.4.0" if GCC_VERSION_8
|
||||
default "9.3.0" if GCC_VERSION_9
|
||||
default "10.3.0" if GCC_VERSION_10
|
||||
default "11.2.0" if GCC_VERSION_11
|
||||
default "8.4.0"
|
||||
default "12.2.0" if GCC_VERSION_12
|
||||
default "11.3.0"
|
||||
|
||||
config GCC_USE_IREMAP
|
||||
bool
|
||||
|
||||
@@ -44,8 +44,12 @@ ifeq ($(PKG_VERSION),10.3.0)
|
||||
PKG_HASH:=64f404c1a650f27fc33da242e1f2df54952e3963a49e06e73f6940f3223ac344
|
||||
endif
|
||||
|
||||
ifeq ($(PKG_VERSION),11.2.0)
|
||||
PKG_HASH:=d08edc536b54c372a1010ff6619dd274c0f1603aa49212ba20f7aa2cda36fa8b
|
||||
ifeq ($(PKG_VERSION),11.3.0)
|
||||
PKG_HASH:=b47cf2818691f5b1e21df2bb38c795fac2cfbd640ede2d0a5e1c89e338a3ac39
|
||||
endif
|
||||
|
||||
ifeq ($(PKG_VERSION),12.2.0)
|
||||
PKG_HASH:=e549cf9cf3594a00e27b6589d4322d70e0720cdd213f39beb4181e06926230ff
|
||||
endif
|
||||
|
||||
PATCH_DIR=../patches/$(GCC_VERSION)
|
||||
@@ -122,7 +126,7 @@ GCC_CONFIGURE:= \
|
||||
--disable-decimal-float \
|
||||
--with-diagnostics-color=auto-if-env \
|
||||
--enable-__cxa_atexit \
|
||||
--disable-libstdcxx-dual-abi \
|
||||
--enable-libstdcxx-dual-abi \
|
||||
--with-default-libstdcxx-abi=new
|
||||
ifneq ($(CONFIG_mips)$(CONFIG_mipsel),)
|
||||
GCC_CONFIGURE += --with-mips-plt
|
||||
@@ -187,6 +191,12 @@ define Host/SetToolchainInfo
|
||||
$(SED) 's,GCC_VERSION=.*,GCC_VERSION=$(GCC_VERSION),' $(TOOLCHAIN_DIR)/info.mk
|
||||
endef
|
||||
|
||||
ifdef CONFIG_GCC_USE_VERSION_12
|
||||
GCC_VERSION_FILE:=gcc/genversion.cc
|
||||
else
|
||||
GCC_VERSION_FILE:=gcc/version.c
|
||||
endif
|
||||
|
||||
ifneq ($(GCC_PREPARE),)
|
||||
define Host/Prepare
|
||||
$(call Host/SetToolchainInfo)
|
||||
@@ -195,8 +205,7 @@ ifneq ($(GCC_PREPARE),)
|
||||
$(CP) $(SCRIPT_DIR)/config.{guess,sub} $(HOST_SOURCE_DIR)/
|
||||
$(SED) 's,^MULTILIB_OSDIRNAMES,# MULTILIB_OSDIRNAMES,' $(HOST_SOURCE_DIR)/gcc/config/*/t-*
|
||||
$(SED) 'd' $(HOST_SOURCE_DIR)/gcc/DEV-PHASE
|
||||
$(SED) 's, DATESTAMP,,' $(HOST_SOURCE_DIR)/gcc/version.c
|
||||
#(cd $(HOST_SOURCE_DIR)/libstdc++-v3; autoconf;);
|
||||
$(SED) 's, DATESTAMP,,' $(HOST_SOURCE_DIR)/$(GCC_VERSION_FILE)
|
||||
$(SED) 's,gcc_no_link=yes,gcc_no_link=no,' $(HOST_SOURCE_DIR)/libstdc++-v3/configure
|
||||
mkdir -p $(GCC_BUILD_DIR)
|
||||
endef
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
From da45b3fde60095756f5f6030f6012c23a3d34429 Mon Sep 17 00:00:00 2001
|
||||
From: Andrew McDonnell <bugs@andrewmcdonnell.net>
|
||||
Date: Fri, 3 Oct 2014 19:09:00 +0930
|
||||
Subject: Add .note.GNU-stack section
|
||||
|
||||
See http://lists.busybox.net/pipermail/uclibc/2014-October/048671.html
|
||||
Below copied from https://gcc.gnu.org/ml/gcc-patches/2014-09/msg02430.html
|
||||
|
||||
Re: [Patch, MIPS] Add .note.GNU-stack section
|
||||
|
||||
From: Steve Ellcey <sellcey at mips dot com>
|
||||
|
||||
On Wed, 2014-09-10 at 10:15 -0700, Eric Christopher wrote:
|
||||
>
|
||||
>
|
||||
> On Wed, Sep 10, 2014 at 9:27 AM, <pinskia@gmail.com> wrote:
|
||||
|
||||
> This works except you did not update the assembly files in
|
||||
> libgcc or glibc. We (Cavium) have the same patch in our tree
|
||||
> for a few released versions.
|
||||
|
||||
> Mind just checking yours in then Andrew?
|
||||
|
||||
> Thanks!
|
||||
> -eric
|
||||
|
||||
I talked to Andrew about what files he changed in GCC and created and
|
||||
tested this new patch. Andrew also mentioned changing some assembly
|
||||
files in glibc but I don't see any use of '.section .note.GNU-stack' in
|
||||
any assembly files in glibc (for any platform) so I wasn't planning on
|
||||
creating a glibc to add them to mips glibc assembly language files.
|
||||
|
||||
OK to check in this patch?
|
||||
|
||||
Steve Ellcey
|
||||
sellcey@mips.com
|
||||
|
||||
|
||||
|
||||
2014-09-26 Steve Ellcey <sellcey@mips.com>
|
||||
---
|
||||
gcc/config/mips/mips.c | 3 +++
|
||||
libgcc/config/mips/crti.S | 4 ++++
|
||||
libgcc/config/mips/crtn.S | 3 +++
|
||||
libgcc/config/mips/mips16.S | 4 ++++
|
||||
libgcc/config/mips/vr4120-div.S | 4 ++++
|
||||
5 files changed, 18 insertions(+)
|
||||
|
||||
--- a/gcc/config/mips/mips.c
|
||||
+++ b/gcc/config/mips/mips.c
|
||||
@@ -22890,6 +22890,9 @@ mips_asm_file_end (void)
|
||||
#define TARGET_ASM_FILE_END mips_asm_file_end
|
||||
|
||||
|
||||
+#undef TARGET_ASM_FILE_END
|
||||
+#define TARGET_ASM_FILE_END file_end_indicate_exec_stack
|
||||
+
|
||||
struct gcc_target targetm = TARGET_INITIALIZER;
|
||||
|
||||
#include "gt-mips.h"
|
||||
--- a/libgcc/config/mips/crti.S
|
||||
+++ b/libgcc/config/mips/crti.S
|
||||
@@ -24,6 +24,10 @@ see the files COPYING3 and COPYING.RUNTI
|
||||
/* An executable stack is *not* required for these functions. */
|
||||
#include "gnustack.h"
|
||||
|
||||
+
|
||||
+/* An executable stack is *not* required for these functions. */
|
||||
+ .section .note.GNU-stack,"",%progbits
|
||||
+
|
||||
/* 4 slots for argument spill area. 1 for cpreturn, 1 for stack.
|
||||
Return spill offset of 40 and 20. Aligned to 16 bytes for n32. */
|
||||
|
||||
--- a/libgcc/config/mips/crtn.S
|
||||
+++ b/libgcc/config/mips/crtn.S
|
||||
@@ -24,6 +24,9 @@ see the files COPYING3 and COPYING.RUNTI
|
||||
/* An executable stack is *not* required for these functions. */
|
||||
#include "gnustack.h"
|
||||
|
||||
+/* An executable stack is *not* required for these functions. */
|
||||
+ .section .note.GNU-stack,"",%progbits
|
||||
+
|
||||
/* 4 slots for argument spill area. 1 for cpreturn, 1 for stack.
|
||||
Return spill offset of 40 and 20. Aligned to 16 bytes for n32. */
|
||||
|
||||
--- a/libgcc/config/mips/mips16.S
|
||||
+++ b/libgcc/config/mips/mips16.S
|
||||
@@ -51,6 +51,10 @@ see the files COPYING3 and COPYING.RUNTI
|
||||
values using the soft-float calling convention, but do the actual
|
||||
operation using the hard floating point instructions. */
|
||||
|
||||
+/* An executable stack is *not* required for these functions. */
|
||||
+ .section .note.GNU-stack,"",%progbits
|
||||
+ .previous
|
||||
+
|
||||
#if defined _MIPS_SIM && (_MIPS_SIM == _ABIO32 || _MIPS_SIM == _ABIO64)
|
||||
|
||||
/* This file contains 32-bit assembly code. */
|
||||
--- a/libgcc/config/mips/vr4120-div.S
|
||||
+++ b/libgcc/config/mips/vr4120-div.S
|
||||
@@ -29,6 +29,10 @@ see the files COPYING3 and COPYING.RUNTI
|
||||
-mfix-vr4120. div and ddiv do not give the correct result when one
|
||||
of the operands is negative. */
|
||||
|
||||
+/* An executable stack is *not* required for these functions. */
|
||||
+ .section .note.GNU-stack,"",%progbits
|
||||
+ .previous
|
||||
+
|
||||
.set nomips16
|
||||
|
||||
#define DIV \
|
||||
@@ -12,7 +12,7 @@ Date: Tue Feb 26 16:16:33 2013 +0000
|
||||
|
||||
--- a/gcc/Makefile.in
|
||||
+++ b/gcc/Makefile.in
|
||||
@@ -3355,18 +3355,10 @@ doc/gcc.info: $(TEXI_GCC_FILES)
|
||||
@@ -3348,18 +3348,10 @@ doc/gcc.info: $(TEXI_GCC_FILES)
|
||||
doc/gccint.info: $(TEXI_GCCINT_FILES)
|
||||
doc/cppinternals.info: $(TEXI_CPPINT_FILES)
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
From ea650cae26da4a8fc04f0c4666f4dd776d0b5fc0 Mon Sep 17 00:00:00 2001
|
||||
From: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
|
||||
Date: Sun, 14 Nov 2021 21:54:25 -0800
|
||||
Subject: [PATCH] configure: define TARGET_LIBC_GNUSTACK on musl
|
||||
|
||||
musl only uses PT_GNU_STACK to set default thread stack size and has no
|
||||
executable stack support[0], so there is no reason not to emit the
|
||||
.note.GNU-stack section on musl builds.
|
||||
|
||||
[0]: https://lore.kernel.org/all/20190423192534.GN23599@brightrain.aerifal.cx/T/#u
|
||||
|
||||
gcc/ChangeLog:
|
||||
|
||||
* configure: Regenerate.
|
||||
* configure.ac: define TARGET_LIBC_GNUSTACK on musl
|
||||
|
||||
Signed-off-by: Ilya Lipnitskiy <ilya.lipnitskiy@gmail.com>
|
||||
---
|
||||
gcc/configure | 3 +++
|
||||
gcc/configure.ac | 3 +++
|
||||
2 files changed, 6 insertions(+)
|
||||
|
||||
--- a/gcc/configure
|
||||
+++ b/gcc/configure
|
||||
@@ -31533,6 +31533,9 @@ fi
|
||||
# Check if the target LIBC handles PT_GNU_STACK.
|
||||
gcc_cv_libc_gnustack=unknown
|
||||
case "$target" in
|
||||
+ mips*-*-linux-musl*)
|
||||
+ gcc_cv_libc_gnustack=yes
|
||||
+ ;;
|
||||
mips*-*-linux*)
|
||||
|
||||
if test $glibc_version_major -gt 2 \
|
||||
--- a/gcc/configure.ac
|
||||
+++ b/gcc/configure.ac
|
||||
@@ -7023,6 +7023,9 @@ fi
|
||||
# Check if the target LIBC handles PT_GNU_STACK.
|
||||
gcc_cv_libc_gnustack=unknown
|
||||
case "$target" in
|
||||
+ mips*-*-linux-musl*)
|
||||
+ gcc_cv_libc_gnustack=yes
|
||||
+ ;;
|
||||
mips*-*-linux*)
|
||||
GCC_GLIBC_VERSION_GTE_IFELSE([2], [31], [gcc_cv_libc_gnustack=yes], )
|
||||
;;
|
||||
@@ -40,7 +40,7 @@ Date: Tue Jul 31 00:52:27 2007 +0000
|
||||
case OPT_fconstant_string_class_:
|
||||
constant_string_class_name = arg;
|
||||
break;
|
||||
@@ -1196,6 +1205,47 @@ c_common_init (void)
|
||||
@@ -1198,6 +1207,47 @@ c_common_init (void)
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ Date: Tue Jul 31 00:52:27 2007 +0000
|
||||
; On SVR4 targets, it also controls whether or not to emit a
|
||||
--- a/gcc/doc/invoke.texi
|
||||
+++ b/gcc/doc/invoke.texi
|
||||
@@ -9055,6 +9055,17 @@ This option is only supported for C and
|
||||
@@ -9058,6 +9058,17 @@ This option is only supported for C and
|
||||
@option{-Wall} and by @option{-Wpedantic}, which can be disabled with
|
||||
@option{-Wno-pointer-sign}.
|
||||
|
||||
@@ -7,7 +7,7 @@ Date: Sat Apr 21 03:02:39 2012 +0000
|
||||
|
||||
--- a/gcc/gcc.c
|
||||
+++ b/gcc/gcc.c
|
||||
@@ -10100,8 +10100,10 @@ getenv_spec_function (int argc, const ch
|
||||
@@ -10106,8 +10106,10 @@ getenv_spec_function (int argc, const ch
|
||||
}
|
||||
|
||||
if (!value)
|
||||
@@ -0,0 +1,45 @@
|
||||
commit 9c6e71079b46ad5433165feaa2001450f2017b56
|
||||
Author: Przemysław Buczkowski <prem@prem.moe>
|
||||
Date: Mon Aug 16 13:16:21 2021 +0100
|
||||
|
||||
GCC: Patch for Apple Silicon compatibility
|
||||
|
||||
This patch fixes a linker error occuring when compiling
|
||||
the cross-compiler on macOS and ARM64 architecture.
|
||||
|
||||
Adapted from:
|
||||
https://github.com/richfelker/musl-cross-make/issues/116#issuecomment-823612404
|
||||
|
||||
Change-Id: Ia3ee98a163bbb62689f42e2da83a5ef36beb0913
|
||||
Reviewed-on: https://review.haiku-os.org/c/buildtools/+/4329
|
||||
Reviewed-by: John Scipione <jscipione@gmail.com>
|
||||
Reviewed-by: Adrien Destugues <pulkomandy@gmail.com>
|
||||
|
||||
--- a/gcc/config/aarch64/aarch64.h
|
||||
+++ b/gcc/config/aarch64/aarch64.h
|
||||
@@ -1236,7 +1236,7 @@ extern const char *aarch64_rewrite_mcpu
|
||||
#define MCPU_TO_MARCH_SPEC_FUNCTIONS \
|
||||
{ "rewrite_mcpu", aarch64_rewrite_mcpu },
|
||||
|
||||
-#if defined(__aarch64__)
|
||||
+#if defined(__aarch64__) && ! defined(__APPLE__)
|
||||
extern const char *host_detect_local_cpu (int argc, const char **argv);
|
||||
#define HAVE_LOCAL_CPU_DETECT
|
||||
# define EXTRA_SPEC_FUNCTIONS \
|
||||
--- a/gcc/config/host-darwin.c
|
||||
+++ b/gcc/config/host-darwin.c
|
||||
@@ -22,6 +22,8 @@
|
||||
#include "coretypes.h"
|
||||
#include "diagnostic-core.h"
|
||||
#include "config/host-darwin.h"
|
||||
+#include "hosthooks.h"
|
||||
+#include "hosthooks-def.h"
|
||||
|
||||
/* Yes, this is really supposed to work. */
|
||||
/* This allows for a pagesize of 16384, which we have on Darwin20, but should
|
||||
@@ -79,3 +81,5 @@ darwin_gt_pch_use_address (void *addr, s
|
||||
|
||||
return ret;
|
||||
}
|
||||
+
|
||||
+const struct host_hooks host_hooks = HOST_HOOKS_INITIALIZER;
|
||||
24
toolchain/gcc/patches/12.2.0/002-case_insensitive.patch
Normal file
24
toolchain/gcc/patches/12.2.0/002-case_insensitive.patch
Normal file
@@ -0,0 +1,24 @@
|
||||
commit 81cc26c706b2bc8c8c1eb1a322e5c5157900836e
|
||||
Author: Felix Fietkau <nbd@openwrt.org>
|
||||
Date: Sun Oct 19 21:45:51 2014 +0000
|
||||
|
||||
gcc: do not assume that the Mac OS X filesystem is case insensitive
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
|
||||
|
||||
SVN-Revision: 42973
|
||||
|
||||
--- a/include/filenames.h
|
||||
+++ b/include/filenames.h
|
||||
@@ -44,11 +44,6 @@ extern "C" {
|
||||
# define IS_DIR_SEPARATOR(c) IS_DOS_DIR_SEPARATOR (c)
|
||||
# define IS_ABSOLUTE_PATH(f) IS_DOS_ABSOLUTE_PATH (f)
|
||||
#else /* not DOSish */
|
||||
-# if defined(__APPLE__)
|
||||
-# ifndef HAVE_CASE_INSENSITIVE_FILE_SYSTEM
|
||||
-# define HAVE_CASE_INSENSITIVE_FILE_SYSTEM 1
|
||||
-# endif
|
||||
-# endif /* __APPLE__ */
|
||||
# define HAS_DRIVE_SPEC(f) (0)
|
||||
# define IS_DIR_SEPARATOR(c) IS_UNIX_DIR_SEPARATOR (c)
|
||||
# define IS_ABSOLUTE_PATH(f) IS_UNIX_ABSOLUTE_PATH (f)
|
||||
35
toolchain/gcc/patches/12.2.0/010-documentation.patch
Normal file
35
toolchain/gcc/patches/12.2.0/010-documentation.patch
Normal file
@@ -0,0 +1,35 @@
|
||||
commit 098bd91f5eae625c7d2ee621e10930fc4434e5e2
|
||||
Author: Luka Perkov <luka@openwrt.org>
|
||||
Date: Tue Feb 26 16:16:33 2013 +0000
|
||||
|
||||
gcc: don't build documentation
|
||||
|
||||
This closes #13039.
|
||||
|
||||
Signed-off-by: Luka Perkov <luka@openwrt.org>
|
||||
|
||||
SVN-Revision: 35807
|
||||
|
||||
--- a/gcc/Makefile.in
|
||||
+++ b/gcc/Makefile.in
|
||||
@@ -3366,18 +3366,10 @@ doc/gcc.info: $(TEXI_GCC_FILES)
|
||||
doc/gccint.info: $(TEXI_GCCINT_FILES)
|
||||
doc/cppinternals.info: $(TEXI_CPPINT_FILES)
|
||||
|
||||
-doc/%.info: %.texi
|
||||
- if [ x$(BUILD_INFO) = xinfo ]; then \
|
||||
- $(MAKEINFO) $(MAKEINFOFLAGS) -I . -I $(gcc_docdir) \
|
||||
- -I $(gcc_docdir)/include -o $@ $<; \
|
||||
- fi
|
||||
+doc/%.info:
|
||||
|
||||
# Duplicate entry to handle renaming of gccinstall.info
|
||||
-doc/gccinstall.info: $(TEXI_GCCINSTALL_FILES)
|
||||
- if [ x$(BUILD_INFO) = xinfo ]; then \
|
||||
- $(MAKEINFO) $(MAKEINFOFLAGS) -I $(gcc_docdir) \
|
||||
- -I $(gcc_docdir)/include -o $@ $<; \
|
||||
- fi
|
||||
+doc/gccinstall.info:
|
||||
|
||||
doc/cpp.dvi: $(TEXI_CPP_FILES)
|
||||
doc/gcc.dvi: $(TEXI_GCC_FILES)
|
||||
20
toolchain/gcc/patches/12.2.0/110-Fix-MIPS-PR-84790.patch
Normal file
20
toolchain/gcc/patches/12.2.0/110-Fix-MIPS-PR-84790.patch
Normal file
@@ -0,0 +1,20 @@
|
||||
Fix https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84790.
|
||||
MIPS16 functions have a static assembler prologue which clobbers
|
||||
registers v0 and v1. Add these register clobbers to function call
|
||||
instructions.
|
||||
|
||||
--- a/gcc/config/mips/mips.cc
|
||||
+++ b/gcc/config/mips/mips.cc
|
||||
@@ -3134,6 +3134,12 @@ mips_emit_call_insn (rtx pattern, rtx or
|
||||
emit_insn (gen_update_got_version ());
|
||||
}
|
||||
|
||||
+ if (TARGET_MIPS16 && TARGET_USE_GOT)
|
||||
+ {
|
||||
+ clobber_reg (&CALL_INSN_FUNCTION_USAGE (insn), MIPS16_PIC_TEMP);
|
||||
+ clobber_reg (&CALL_INSN_FUNCTION_USAGE (insn), MIPS_PROLOGUE_TEMP (word_mode));
|
||||
+ }
|
||||
+
|
||||
if (TARGET_MIPS16
|
||||
&& TARGET_EXPLICIT_RELOCS
|
||||
&& TARGET_CALL_CLOBBERED_GP)
|
||||
13
toolchain/gcc/patches/12.2.0/230-musl_libssp.patch
Normal file
13
toolchain/gcc/patches/12.2.0/230-musl_libssp.patch
Normal file
@@ -0,0 +1,13 @@
|
||||
--- a/gcc/gcc.cc
|
||||
+++ b/gcc/gcc.cc
|
||||
@@ -985,7 +985,9 @@ proper position among the other output f
|
||||
#endif
|
||||
|
||||
#ifndef LINK_SSP_SPEC
|
||||
-#ifdef TARGET_LIBC_PROVIDES_SSP
|
||||
+#if DEFAULT_LIBC == LIBC_MUSL
|
||||
+#define LINK_SSP_SPEC "-lssp_nonshared"
|
||||
+#elif defined(TARGET_LIBC_PROVIDES_SSP)
|
||||
#define LINK_SSP_SPEC "%{fstack-protector|fstack-protector-all" \
|
||||
"|fstack-protector-strong|fstack-protector-explicit:}"
|
||||
#else
|
||||
@@ -0,0 +1,21 @@
|
||||
commit ecf7671b769fe96f7b5134be442089f8bdba55d2
|
||||
Author: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Thu Aug 4 20:29:45 2016 +0200
|
||||
|
||||
gcc: add a patch to generate better code with Os on mips
|
||||
|
||||
Also happens to reduce compressed code size a bit
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
|
||||
--- a/gcc/config/mips/mips.cc
|
||||
+++ b/gcc/config/mips/mips.cc
|
||||
@@ -20216,7 +20216,7 @@ mips_option_override (void)
|
||||
flag_pcc_struct_return = 0;
|
||||
|
||||
/* Decide which rtx_costs structure to use. */
|
||||
- if (optimize_size)
|
||||
+ if (0 && optimize_size)
|
||||
mips_cost = &mips_rtx_cost_optimize_size;
|
||||
else
|
||||
mips_cost = &mips_rtx_cost_data[mips_tune];
|
||||
33
toolchain/gcc/patches/12.2.0/810-arm-softfloat-libgcc.patch
Normal file
33
toolchain/gcc/patches/12.2.0/810-arm-softfloat-libgcc.patch
Normal file
@@ -0,0 +1,33 @@
|
||||
commit 8570c4be394cff7282f332f97da2ff569a927ddb
|
||||
Author: Imre Kaloz <kaloz@openwrt.org>
|
||||
Date: Wed Feb 2 20:06:12 2011 +0000
|
||||
|
||||
fixup arm soft-float symbols
|
||||
|
||||
SVN-Revision: 25325
|
||||
|
||||
--- a/libgcc/config/arm/t-linux
|
||||
+++ b/libgcc/config/arm/t-linux
|
||||
@@ -1,6 +1,10 @@
|
||||
LIB1ASMSRC = arm/lib1funcs.S
|
||||
LIB1ASMFUNCS = _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_lnx _clzsi2 _clzdi2 \
|
||||
- _ctzsi2 _arm_addsubdf3 _arm_addsubsf3
|
||||
+ _ctzsi2 _arm_addsubdf3 _arm_addsubsf3 \
|
||||
+ _arm_negdf2 _arm_muldivdf3 _arm_cmpdf2 _arm_unorddf2 \
|
||||
+ _arm_fixdfsi _arm_fixunsdfsi _arm_truncdfsf2 \
|
||||
+ _arm_negsf2 _arm_muldivsf3 _arm_cmpsf2 _arm_unordsf2 \
|
||||
+ _arm_fixsfsi _arm_fixunssfsi
|
||||
|
||||
# Just for these, we omit the frame pointer since it makes such a big
|
||||
# difference.
|
||||
--- a/gcc/config/arm/linux-elf.h
|
||||
+++ b/gcc/config/arm/linux-elf.h
|
||||
@@ -58,8 +58,6 @@
|
||||
%{shared:-lc} \
|
||||
%{!shared:%{profile:-lc_p}%{!profile:-lc}}"
|
||||
|
||||
-#define LIBGCC_SPEC "%{mfloat-abi=soft*:-lfloat} -lgcc"
|
||||
-
|
||||
#define GLIBC_DYNAMIC_LINKER "/lib/ld-linux.so.2"
|
||||
|
||||
#define LINUX_TARGET_LINK_SPEC "%{h*} \
|
||||
44
toolchain/gcc/patches/12.2.0/820-libgcc_pic.patch
Normal file
44
toolchain/gcc/patches/12.2.0/820-libgcc_pic.patch
Normal file
@@ -0,0 +1,44 @@
|
||||
commit c96312958c0621e72c9b32da5bc224ffe2161384
|
||||
Author: Felix Fietkau <nbd@openwrt.org>
|
||||
Date: Mon Oct 19 23:26:09 2009 +0000
|
||||
|
||||
gcc: create a proper libgcc_pic.a static library for relinking (4.3.3+ for now, backport will follow)
|
||||
|
||||
SVN-Revision: 18086
|
||||
|
||||
--- a/libgcc/Makefile.in
|
||||
+++ b/libgcc/Makefile.in
|
||||
@@ -930,11 +930,12 @@ $(libgcov-driver-objects): %$(objext): $
|
||||
|
||||
# Static libraries.
|
||||
libgcc.a: $(libgcc-objects)
|
||||
+libgcc_pic.a: $(libgcc-s-objects)
|
||||
libgcov.a: $(libgcov-objects)
|
||||
libunwind.a: $(libunwind-objects)
|
||||
libgcc_eh.a: $(libgcc-eh-objects)
|
||||
|
||||
-libgcc.a libgcov.a libunwind.a libgcc_eh.a:
|
||||
+libgcc.a libgcov.a libunwind.a libgcc_eh.a libgcc_pic.a:
|
||||
-rm -f $@
|
||||
|
||||
objects="$(objects)"; \
|
||||
@@ -958,7 +959,7 @@ all: libunwind.a
|
||||
endif
|
||||
|
||||
ifeq ($(enable_shared),yes)
|
||||
-all: libgcc_eh.a libgcc_s$(SHLIB_EXT)
|
||||
+all: libgcc_eh.a libgcc_pic.a libgcc_s$(SHLIB_EXT)
|
||||
ifneq ($(LIBUNWIND),)
|
||||
all: libunwind$(SHLIB_EXT)
|
||||
libgcc_s$(SHLIB_EXT): libunwind$(SHLIB_EXT)
|
||||
@@ -1164,6 +1165,10 @@ install-shared:
|
||||
chmod 644 $(DESTDIR)$(inst_libdir)/libgcc_eh.a
|
||||
$(RANLIB) $(DESTDIR)$(inst_libdir)/libgcc_eh.a
|
||||
|
||||
+ $(INSTALL_DATA) libgcc_pic.a $(mapfile) $(DESTDIR)$(inst_libdir)/
|
||||
+ chmod 644 $(DESTDIR)$(inst_libdir)/libgcc_pic.a
|
||||
+ $(RANLIB) $(DESTDIR)$(inst_libdir)/libgcc_pic.a
|
||||
+
|
||||
$(subst @multilib_dir@,$(MULTIDIR),$(subst \
|
||||
@shlib_base_name@,libgcc_s,$(subst \
|
||||
@shlib_slibdir_qual@,$(MULTIOSSUBDIR),$(SHLIB_INSTALL))))
|
||||
@@ -0,0 +1,28 @@
|
||||
commit 7edc8ca5456d9743dd0075eb3cc5b04f4f24c8cc
|
||||
Author: Imre Kaloz <kaloz@openwrt.org>
|
||||
Date: Wed Feb 2 19:34:36 2011 +0000
|
||||
|
||||
add armv4 fixup patches
|
||||
|
||||
SVN-Revision: 25322
|
||||
|
||||
|
||||
--- a/gcc/config/arm/linux-eabi.h
|
||||
+++ b/gcc/config/arm/linux-eabi.h
|
||||
@@ -91,10 +91,15 @@
|
||||
#define MUSL_DYNAMIC_LINKER \
|
||||
"/lib/ld-musl-arm" MUSL_DYNAMIC_LINKER_E "%{mfloat-abi=hard:hf}%{mfdpic:-fdpic}.so.1"
|
||||
|
||||
+/* For armv4 we pass --fix-v4bx to linker to support EABI */
|
||||
+#undef TARGET_FIX_V4BX_SPEC
|
||||
+#define TARGET_FIX_V4BX_SPEC " %{mcpu=arm8|mcpu=arm810|mcpu=strongarm*"\
|
||||
+ "|march=armv4|mcpu=fa526|mcpu=fa626:--fix-v4bx}"
|
||||
+
|
||||
/* At this point, bpabi.h will have clobbered LINK_SPEC. We want to
|
||||
use the GNU/Linux version, not the generic BPABI version. */
|
||||
#undef LINK_SPEC
|
||||
-#define LINK_SPEC EABI_LINK_SPEC \
|
||||
+#define LINK_SPEC EABI_LINK_SPEC TARGET_FIX_V4BX_SPEC \
|
||||
LINUX_OR_ANDROID_LD (LINUX_TARGET_LINK_SPEC, \
|
||||
LINUX_TARGET_LINK_SPEC " " ANDROID_LINK_SPEC)
|
||||
|
||||
54
toolchain/gcc/patches/12.2.0/850-use_shared_libgcc.patch
Normal file
54
toolchain/gcc/patches/12.2.0/850-use_shared_libgcc.patch
Normal file
@@ -0,0 +1,54 @@
|
||||
commit dcfc40358b5a3cae7320c17f8d1cebd5ad5540cd
|
||||
Author: Felix Fietkau <nbd@openwrt.org>
|
||||
Date: Sun Feb 12 20:25:47 2012 +0000
|
||||
|
||||
gcc 4.6: port over the missing patch 850-use_shared_libgcc.patch to prevent libgcc crap from leaking into every single binary
|
||||
|
||||
SVN-Revision: 30486
|
||||
--- a/gcc/config/arm/linux-eabi.h
|
||||
+++ b/gcc/config/arm/linux-eabi.h
|
||||
@@ -132,10 +132,6 @@
|
||||
"%{Ofast|ffast-math|funsafe-math-optimizations:crtfastmath.o%s} " \
|
||||
LINUX_OR_ANDROID_LD (GNU_USER_TARGET_ENDFILE_SPEC, ANDROID_ENDFILE_SPEC)
|
||||
|
||||
-/* Use the default LIBGCC_SPEC, not the version in linux-elf.h, as we
|
||||
- do not use -lfloat. */
|
||||
-#undef LIBGCC_SPEC
|
||||
-
|
||||
/* Clear the instruction cache from `beg' to `end'. This is
|
||||
implemented in lib1funcs.S, so ensure an error if this definition
|
||||
is used. */
|
||||
--- a/gcc/config/linux.h
|
||||
+++ b/gcc/config/linux.h
|
||||
@@ -71,6 +71,10 @@ see the files COPYING3 and COPYING.RUNTI
|
||||
builtin_version ("CRuntime_Musl"); \
|
||||
} while (0)
|
||||
|
||||
+#ifndef LIBGCC_SPEC
|
||||
+#define LIBGCC_SPEC "%{static|static-libgcc:-lgcc}%{!static:%{!static-libgcc:-lgcc_s}}"
|
||||
+#endif
|
||||
+
|
||||
/* Determine which dynamic linker to use depending on whether GLIBC or
|
||||
uClibc or Bionic or musl is the default C library and whether
|
||||
-muclibc or -mglibc or -mbionic or -mmusl has been passed to change
|
||||
--- a/libgcc/mkmap-symver.awk
|
||||
+++ b/libgcc/mkmap-symver.awk
|
||||
@@ -136,5 +136,5 @@ function output(lib) {
|
||||
else if (inherit[lib])
|
||||
printf("} %s;\n", inherit[lib]);
|
||||
else
|
||||
- printf ("\n local:\n\t*;\n};\n");
|
||||
+ printf ("\n\t*;\n};\n");
|
||||
}
|
||||
--- a/gcc/config/rs6000/linux.h
|
||||
+++ b/gcc/config/rs6000/linux.h
|
||||
@@ -67,6 +67,9 @@
|
||||
#undef CPP_OS_DEFAULT_SPEC
|
||||
#define CPP_OS_DEFAULT_SPEC "%(cpp_os_linux)"
|
||||
|
||||
+#undef LIBGCC_SPEC
|
||||
+#define LIBGCC_SPEC "%{!static:%{!static-libgcc:-lgcc_s}} -lgcc"
|
||||
+
|
||||
#undef LINK_SHLIB_SPEC
|
||||
#define LINK_SHLIB_SPEC "%{shared:-shared} %{!shared: %{static:-static}} \
|
||||
%{static-pie:-static -pie --no-dynamic-linker -z text}"
|
||||
22
toolchain/gcc/patches/12.2.0/851-libgcc_no_compat.patch
Normal file
22
toolchain/gcc/patches/12.2.0/851-libgcc_no_compat.patch
Normal file
@@ -0,0 +1,22 @@
|
||||
commit 64661de100da1ec1061ef3e5e400285dce115e6b
|
||||
Author: Felix Fietkau <nbd@openwrt.org>
|
||||
Date: Sun May 10 13:16:35 2015 +0000
|
||||
|
||||
gcc: add some size optimization patches
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
|
||||
|
||||
SVN-Revision: 45664
|
||||
|
||||
--- a/libgcc/config/t-libunwind
|
||||
+++ b/libgcc/config/t-libunwind
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
HOST_LIBGCC2_CFLAGS += -DUSE_GAS_SYMVER
|
||||
|
||||
-LIB2ADDEH = $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c \
|
||||
- $(srcdir)/unwind-compat.c $(srcdir)/unwind-dw2-fde-compat.c
|
||||
+LIB2ADDEH = $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c
|
||||
LIB2ADDEHSTATIC = $(srcdir)/unwind-sjlj.c $(srcdir)/unwind-c.c
|
||||
|
||||
# Override the default value from t-slibgcc-elf-ver and mention -lunwind
|
||||
11
toolchain/gcc/patches/12.2.0/870-ppc_no_crtsavres.patch
Normal file
11
toolchain/gcc/patches/12.2.0/870-ppc_no_crtsavres.patch
Normal file
@@ -0,0 +1,11 @@
|
||||
--- a/gcc/config/rs6000/rs6000-logue.cc
|
||||
+++ b/gcc/config/rs6000/rs6000-logue.cc
|
||||
@@ -348,7 +348,7 @@ rs6000_savres_strategy (rs6000_stack_t *
|
||||
/* Define cutoff for using out-of-line functions to save registers. */
|
||||
if (DEFAULT_ABI == ABI_V4 || TARGET_ELF)
|
||||
{
|
||||
- if (!optimize_size)
|
||||
+ if (1)
|
||||
{
|
||||
strategy |= SAVE_INLINE_FPRS | REST_INLINE_FPRS;
|
||||
strategy |= SAVE_INLINE_GPRS | REST_INLINE_GPRS;
|
||||
11
toolchain/gcc/patches/12.2.0/881-no_tm_section.patch
Normal file
11
toolchain/gcc/patches/12.2.0/881-no_tm_section.patch
Normal file
@@ -0,0 +1,11 @@
|
||||
--- a/libgcc/crtstuff.c
|
||||
+++ b/libgcc/crtstuff.c
|
||||
@@ -152,7 +152,7 @@ call_ ## FUNC (void) \
|
||||
#endif
|
||||
|
||||
#if !defined(USE_TM_CLONE_REGISTRY) && defined(OBJECT_FORMAT_ELF)
|
||||
-# define USE_TM_CLONE_REGISTRY 1
|
||||
+# define USE_TM_CLONE_REGISTRY 0
|
||||
#elif !defined(USE_TM_CLONE_REGISTRY)
|
||||
# define USE_TM_CLONE_REGISTRY 0
|
||||
#endif
|
||||
9
toolchain/gcc/patches/12.2.0/900-bad-mips16-crt.patch
Normal file
9
toolchain/gcc/patches/12.2.0/900-bad-mips16-crt.patch
Normal file
@@ -0,0 +1,9 @@
|
||||
--- a/libgcc/config/mips/t-mips16
|
||||
+++ b/libgcc/config/mips/t-mips16
|
||||
@@ -43,3 +43,6 @@ SYNC_CFLAGS = -mno-mips16
|
||||
|
||||
# Version these symbols if building libgcc.so.
|
||||
SHLIB_MAPFILES += $(srcdir)/config/mips/libgcc-mips16.ver
|
||||
+
|
||||
+CRTSTUFF_T_CFLAGS += -mno-mips16
|
||||
+CRTSTUFF_T_CFLAGS_S += -mno-mips16
|
||||
146
toolchain/gcc/patches/12.2.0/910-mbsd_multi.patch
Normal file
146
toolchain/gcc/patches/12.2.0/910-mbsd_multi.patch
Normal file
@@ -0,0 +1,146 @@
|
||||
commit 99368862e44740ff4fd33760893f04e14f9dbdf1
|
||||
Author: Felix Fietkau <nbd@openwrt.org>
|
||||
Date: Tue Jul 31 00:52:27 2007 +0000
|
||||
|
||||
Port the mbsd_multi patch from freewrt, which adds -fhonour-copts. This will emit warnings in packages that don't use our target cflags properly
|
||||
|
||||
SVN-Revision: 8256
|
||||
|
||||
This patch brings over a feature from MirBSD:
|
||||
* -fhonour-copts
|
||||
If this option is not given, it's warned (depending
|
||||
on environment variables). This is to catch errors
|
||||
of misbuilt packages which override CFLAGS themselves.
|
||||
|
||||
This patch was authored by Thorsten Glaser <tg at mirbsd.de>
|
||||
with copyright assignment to the FSF in effect.
|
||||
|
||||
--- a/gcc/c-family/c-opts.cc
|
||||
+++ b/gcc/c-family/c-opts.cc
|
||||
@@ -107,6 +107,9 @@ static dump_flags_t original_dump_flags;
|
||||
/* Whether any standard preincluded header has been preincluded. */
|
||||
static bool done_preinclude;
|
||||
|
||||
+/* Check if a port honours COPTS. */
|
||||
+static int honour_copts = 0;
|
||||
+
|
||||
static void handle_OPT_d (const char *);
|
||||
static void set_std_cxx98 (int);
|
||||
static void set_std_cxx11 (int);
|
||||
@@ -478,6 +481,12 @@ c_common_handle_option (size_t scode, co
|
||||
flag_no_builtin = !value;
|
||||
break;
|
||||
|
||||
+ case OPT_fhonour_copts:
|
||||
+ if (c_language == clk_c) {
|
||||
+ honour_copts++;
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
case OPT_fconstant_string_class_:
|
||||
constant_string_class_name = arg;
|
||||
break;
|
||||
@@ -1218,6 +1227,47 @@ c_common_init (void)
|
||||
return false;
|
||||
}
|
||||
|
||||
+ if (c_language == clk_c) {
|
||||
+ char *ev = getenv ("GCC_HONOUR_COPTS");
|
||||
+ int evv;
|
||||
+ if (ev == NULL)
|
||||
+ evv = -1;
|
||||
+ else if ((*ev == '0') || (*ev == '\0'))
|
||||
+ evv = 0;
|
||||
+ else if (*ev == '1')
|
||||
+ evv = 1;
|
||||
+ else if (*ev == '2')
|
||||
+ evv = 2;
|
||||
+ else if (*ev == 's')
|
||||
+ evv = -1;
|
||||
+ else {
|
||||
+ warning (0, "unknown GCC_HONOUR_COPTS value, assuming 1");
|
||||
+ evv = 1; /* maybe depend this on something like MIRBSD_NATIVE? */
|
||||
+ }
|
||||
+ if (evv == 1) {
|
||||
+ if (honour_copts == 0) {
|
||||
+ error ("someone does not honour COPTS at all in lenient mode");
|
||||
+ return false;
|
||||
+ } else if (honour_copts != 1) {
|
||||
+ warning (0, "someone does not honour COPTS correctly, passed %d times",
|
||||
+ honour_copts);
|
||||
+ }
|
||||
+ } else if (evv == 2) {
|
||||
+ if (honour_copts == 0) {
|
||||
+ error ("someone does not honour COPTS at all in strict mode");
|
||||
+ return false;
|
||||
+ } else if (honour_copts != 1) {
|
||||
+ error ("someone does not honour COPTS correctly, passed %d times",
|
||||
+ honour_copts);
|
||||
+ return false;
|
||||
+ }
|
||||
+ } else if (evv == 0) {
|
||||
+ if (honour_copts != 1)
|
||||
+ inform (UNKNOWN_LOCATION, "someone does not honour COPTS correctly, passed %d times",
|
||||
+ honour_copts);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
return true;
|
||||
}
|
||||
|
||||
--- a/gcc/c-family/c.opt
|
||||
+++ b/gcc/c-family/c.opt
|
||||
@@ -1755,6 +1755,9 @@ C++ ObjC++ Optimization Alias(fexception
|
||||
fhonor-std
|
||||
C++ ObjC++ WarnRemoved
|
||||
|
||||
+fhonour-copts
|
||||
+C ObjC C++ ObjC++ RejectNegative
|
||||
+
|
||||
fhosted
|
||||
C ObjC
|
||||
Assume normal C execution environment.
|
||||
--- a/gcc/common.opt
|
||||
+++ b/gcc/common.opt
|
||||
@@ -1770,6 +1770,9 @@ fharden-conditional-branches
|
||||
Common Var(flag_harden_conditional_branches) Optimization
|
||||
Harden conditional branches by checking reversed conditions.
|
||||
|
||||
+fhonour-copts
|
||||
+Common RejectNegative
|
||||
+
|
||||
; Nonzero means ignore `#ident' directives. 0 means handle them.
|
||||
; Generate position-independent code for executables if possible
|
||||
; On SVR4 targets, it also controls whether or not to emit a
|
||||
--- a/gcc/doc/invoke.texi
|
||||
+++ b/gcc/doc/invoke.texi
|
||||
@@ -9596,6 +9596,17 @@ This option is only supported for C and
|
||||
@option{-Wall} and by @option{-Wpedantic}, which can be disabled with
|
||||
@option{-Wno-pointer-sign}.
|
||||
|
||||
+@item -fhonour-copts
|
||||
+@opindex fhonour-copts
|
||||
+If @env{GCC_HONOUR_COPTS} is set to 1, abort if this option is not
|
||||
+given at least once, and warn if it is given more than once.
|
||||
+If @env{GCC_HONOUR_COPTS} is set to 2, abort if this option is not
|
||||
+given exactly once.
|
||||
+If @env{GCC_HONOUR_COPTS} is set to 0 or unset, warn if this option
|
||||
+is not given exactly once.
|
||||
+The warning is quelled if @env{GCC_HONOUR_COPTS} is set to @samp{s}.
|
||||
+This flag and environment variable only affect the C language.
|
||||
+
|
||||
@item -Wstack-protector
|
||||
@opindex Wstack-protector
|
||||
@opindex Wno-stack-protector
|
||||
--- a/gcc/opts.cc
|
||||
+++ b/gcc/opts.cc
|
||||
@@ -2692,6 +2692,9 @@ common_handle_option (struct gcc_options
|
||||
add_comma_separated_to_vector (&opts->x_flag_ignored_attributes, arg);
|
||||
break;
|
||||
|
||||
+ case OPT_fhonour_copts:
|
||||
+ break;
|
||||
+
|
||||
case OPT_Werror:
|
||||
dc->warning_as_error_requested = value;
|
||||
break;
|
||||
22
toolchain/gcc/patches/12.2.0/920-specs_nonfatal_getenv.patch
Normal file
22
toolchain/gcc/patches/12.2.0/920-specs_nonfatal_getenv.patch
Normal file
@@ -0,0 +1,22 @@
|
||||
Author: Jo-Philipp Wich <jow@openwrt.org>
|
||||
Date: Sat Apr 21 03:02:39 2012 +0000
|
||||
|
||||
gcc: add patch to make the getenv() spec function nonfatal if requested environment variable is unset
|
||||
|
||||
SVN-Revision: 31390
|
||||
|
||||
--- a/gcc/gcc.cc
|
||||
+++ b/gcc/gcc.cc
|
||||
@@ -10213,8 +10213,10 @@ getenv_spec_function (int argc, const ch
|
||||
}
|
||||
|
||||
if (!value)
|
||||
- fatal_error (input_location,
|
||||
- "environment variable %qs not defined", varname);
|
||||
+ {
|
||||
+ warning (input_location, "environment variable %qs not defined", varname);
|
||||
+ value = "";
|
||||
+ }
|
||||
|
||||
/* We have to escape every character of the environment variable so
|
||||
they are not interpreted as active spec characters. A
|
||||
@@ -0,0 +1,67 @@
|
||||
From dda6b050cd74a352670787a294596a9c56c21327 Mon Sep 17 00:00:00 2001
|
||||
From: Yousong Zhou <yszhou4tech@gmail.com>
|
||||
Date: Fri, 4 May 2018 18:20:53 +0800
|
||||
Subject: [PATCH] gotools: fix compilation when making cross compiler
|
||||
|
||||
libgo is "the runtime support library for the Go programming language.
|
||||
This library is intended for use with the Go frontend."
|
||||
|
||||
gccgo will link target files with libgo.so which depends on libgcc_s.so.1, but
|
||||
the linker will complain that it cannot find it. That's because shared libgcc
|
||||
is not present in the install directory yet. libgo.so was made without problem
|
||||
because gcc will emit -lgcc_s when compiled with -shared option. When gotools
|
||||
were being made, it was supplied with -static-libgcc thus no link option was
|
||||
provided. Check LIBGO in gcc/go/gcc-spec.c for how gccgo make a builtin spec
|
||||
for linking with libgo.so
|
||||
|
||||
- GccgoCrossCompilation, https://github.com/golang/go/wiki/GccgoCrossCompilation
|
||||
- Cross-building instructions, http://www.eglibc.org/archives/patches/msg00078.html
|
||||
|
||||
When 3-pass GCC compilation is used, shared libgcc runtime libraries will be
|
||||
available after gcc pass2 completed and will meet the gotools link requirement
|
||||
at gcc pass3
|
||||
---
|
||||
gotools/Makefile.am | 4 +++-
|
||||
gotools/Makefile.in | 4 +++-
|
||||
2 files changed, 6 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/gotools/Makefile.am
|
||||
+++ b/gotools/Makefile.am
|
||||
@@ -26,6 +26,7 @@ PWD_COMMAND = $${PWDCMD-pwd}
|
||||
STAMP = echo timestamp >
|
||||
|
||||
libgodir = ../$(target_noncanonical)/libgo
|
||||
+libgccdir = ../$(target_noncanonical)/libgcc
|
||||
LIBGODEP = $(libgodir)/libgo.la
|
||||
|
||||
LIBGOTOOL = $(libgodir)/libgotool.a
|
||||
@@ -41,7 +42,8 @@ GOCFLAGS = $(CFLAGS_FOR_TARGET)
|
||||
GOCOMPILE = $(GOCOMPILER) $(GOCFLAGS)
|
||||
|
||||
AM_GOCFLAGS = -I $(libgodir)
|
||||
-AM_LDFLAGS = -L $(libgodir) -L $(libgodir)/.libs
|
||||
+AM_LDFLAGS = -L $(libgodir) -L $(libgodir)/.libs \
|
||||
+ -L $(libgccdir) -L $(libgccdir)/.libs -lgcc_s
|
||||
GOLINK = $(GOCOMPILER) $(GOCFLAGS) $(AM_GOCFLAGS) $(LDFLAGS) $(AM_LDFLAGS) -o $@
|
||||
|
||||
libgosrcdir = $(srcdir)/../libgo/go
|
||||
--- a/gotools/Makefile.in
|
||||
+++ b/gotools/Makefile.in
|
||||
@@ -337,6 +337,7 @@ mkinstalldirs = $(SHELL) $(toplevel_srcd
|
||||
PWD_COMMAND = $${PWDCMD-pwd}
|
||||
STAMP = echo timestamp >
|
||||
libgodir = ../$(target_noncanonical)/libgo
|
||||
+libgccdir = ../$(target_noncanonical)/libgcc
|
||||
LIBGODEP = $(libgodir)/libgo.la
|
||||
LIBGOTOOL = $(libgodir)/libgotool.a
|
||||
@NATIVE_FALSE@GOCOMPILER = $(GOC)
|
||||
@@ -346,7 +347,8 @@ LIBGOTOOL = $(libgodir)/libgotool.a
|
||||
GOCFLAGS = $(CFLAGS_FOR_TARGET)
|
||||
GOCOMPILE = $(GOCOMPILER) $(GOCFLAGS)
|
||||
AM_GOCFLAGS = -I $(libgodir)
|
||||
-AM_LDFLAGS = -L $(libgodir) -L $(libgodir)/.libs
|
||||
+AM_LDFLAGS = -L $(libgodir) -L $(libgodir)/.libs \
|
||||
+ -L $(libgccdir) -L $(libgccdir)/.libs -lgcc_s
|
||||
GOLINK = $(GOCOMPILER) $(GOCFLAGS) $(AM_GOCFLAGS) $(LDFLAGS) $(AM_LDFLAGS) -o $@
|
||||
libgosrcdir = $(srcdir)/../libgo/go
|
||||
cmdsrcdir = $(libgosrcdir)/cmd
|
||||
@@ -0,0 +1,45 @@
|
||||
commit 9c6e71079b46ad5433165feaa2001450f2017b56
|
||||
Author: Przemysław Buczkowski <prem@prem.moe>
|
||||
Date: Mon Aug 16 13:16:21 2021 +0100
|
||||
|
||||
GCC: Patch for Apple Silicon compatibility
|
||||
|
||||
This patch fixes a linker error occuring when compiling
|
||||
the cross-compiler on macOS and ARM64 architecture.
|
||||
|
||||
Adapted from:
|
||||
https://github.com/richfelker/musl-cross-make/issues/116#issuecomment-823612404
|
||||
|
||||
Change-Id: Ia3ee98a163bbb62689f42e2da83a5ef36beb0913
|
||||
Reviewed-on: https://review.haiku-os.org/c/buildtools/+/4329
|
||||
Reviewed-by: John Scipione <jscipione@gmail.com>
|
||||
Reviewed-by: Adrien Destugues <pulkomandy@gmail.com>
|
||||
|
||||
--- a/gcc/config/aarch64/aarch64.h
|
||||
+++ b/gcc/config/aarch64/aarch64.h
|
||||
@@ -1290,7 +1290,7 @@ extern const char *aarch64_rewrite_mcpu
|
||||
#define MCPU_TO_MARCH_SPEC_FUNCTIONS \
|
||||
{ "rewrite_mcpu", aarch64_rewrite_mcpu },
|
||||
|
||||
-#if defined(__aarch64__)
|
||||
+#if defined(__aarch64__) && ! defined(__APPLE__)
|
||||
extern const char *host_detect_local_cpu (int argc, const char **argv);
|
||||
#define HAVE_LOCAL_CPU_DETECT
|
||||
# define EXTRA_SPEC_FUNCTIONS \
|
||||
--- a/gcc/config/host-darwin.cc
|
||||
+++ b/gcc/config/host-darwin.cc
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "options.h"
|
||||
#include "diagnostic-core.h"
|
||||
#include "config/host-darwin.h"
|
||||
+#include "hosthooks.h"
|
||||
+#include "hosthooks-def.h"
|
||||
#include <errno.h>
|
||||
|
||||
/* For Darwin (macOS only) platforms, without ASLR (PIE) enabled on the
|
||||
@@ -181,3 +183,5 @@ darwin_gt_pch_use_address (void *&addr,
|
||||
|
||||
return 1;
|
||||
}
|
||||
+
|
||||
+const struct host_hooks host_hooks = HOST_HOOKS_INITIALIZER;
|
||||
@@ -8,12 +8,12 @@ include $(TOPDIR)/rules.mk
|
||||
include $(INCLUDE_DIR)/target.mk
|
||||
|
||||
PKG_NAME:=musl
|
||||
PKG_VERSION:=1.2.2
|
||||
PKG_VERSION:=1.2.3
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://musl.libc.org/releases/
|
||||
PKG_HASH:=9b969322012d796dc23dda27a35866034fa67d8fb67e0e2c45c913c3d43219dd
|
||||
PKG_HASH:=7d5b0b6062521e4627e099e4c9dc8248d32a30285e959b7eecaa780cf8cfd4a4
|
||||
|
||||
LIBC_SO_VERSION:=$(PKG_VERSION)
|
||||
PATCH_DIR:=$(PATH_PREFIX)/patches
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/src/time/__tz.c
|
||||
+++ b/src/time/__tz.c
|
||||
@@ -31,6 +31,9 @@ static int r0[5], r1[5];
|
||||
@@ -32,6 +32,9 @@ static int r0[5], r1[5];
|
||||
static const unsigned char *zi, *trans, *index, *types, *abbrevs, *abbrevs_end;
|
||||
static size_t map_size;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
static char old_tz_buf[32];
|
||||
static char *old_tz = old_tz_buf;
|
||||
static size_t old_tz_size = sizeof old_tz_buf;
|
||||
@@ -132,6 +135,15 @@ static void do_tzset()
|
||||
@@ -133,6 +136,15 @@ static void do_tzset()
|
||||
"/usr/share/zoneinfo/\0/share/zoneinfo/\0/etc/zoneinfo/\0";
|
||||
|
||||
s = getenv("TZ");
|
||||
|
||||
Reference in New Issue
Block a user