add gcc11 and ntfs3

This commit is contained in:
GitHub
2022-09-11 07:29:26 +00:00
parent e6a6593ce9
commit 6f3e5e4382
80 changed files with 31639 additions and 131 deletions

View File

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

View File

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

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

View 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

File diff suppressed because it is too large Load Diff

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

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

File diff suppressed because it is too large Load Diff

View 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

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

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

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

View 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);
}

View 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);
}

View 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);
}

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

View 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

View File

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

View 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*} \

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

View File

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

View 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}"

View 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

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

View 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

View 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

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

View 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

View File

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

View File

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

View File

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

View File

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