mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-27 14:14:24 +08:00
driver ntfs3 for linux 6.5
-----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEh0DEKNP0I9IjwfWEqbAzH4MkB7YFAmSoF+sACgkQqbAzH4Mk B7b8pQ/+LSjMlS4qH5hHilmXtXcNpEumP3w5poNgwhx5hnBk6cbkgrOlvB5GlCHT BxElAcgzBBihNL/OsJ12Hr3+oDBv2eQYHSlw6vLve8UlAAiN5hWIyiBH59Xau6uv i/4JkPhnP+dC9EArq3qz7q2R8Cyc14i037zrQs1mF0iLJVDP27nLaQf1MNKU5uwd nHgl4T5bqEOfo2sXH6qNJhoTTF+uHyPrid9riaCx0YVuVpkypjZUOqDzFSPIJMFh 7PxnfaeljgLVIBSGOfCuEB/ZXAxmp9aj8bbktFXju/LHQbB41Nr9tdhinzLopszS gfEu40z/infyKjS8nAQPy2uAmuMfw1HCk80FMaVIDsG3EZE5TwoZwgNlxMBqhcuc YWJhYqJh5VwIvCJYAUZkXsHEb/v3Cp7XFP0R6r12M86zisTzil6xwkrB+P2pHYbn izURPIF+kh7Sg3khUgu8/s95EHxq98xEVnKK/0P4zTLSAttx5ZEaV/FCeZvAMxMA t4WkYNpSJ1kUsq925K6KwWumHJ1vj+i8DXCPLOQCVbknlbfKU791edvvBxSTxHVq FxwLdiBADEOvbsXiHveN29Wb+j0g6mHVoA8VvPgN/z7HasE7dvTOSlZ2vvTDOAeh 9GTGo1+UFsUtvs9yZ5xn41mi8+a4ANrcNfsN1gpZvuzo1S7g3Y4= =UnlH -----END PGP SIGNATURE----- Merge tag 'ntfs3_for_6.5' of https://github.com/Paragon-Software-Group/linux-ntfs3 Pull ntfs3 updates from Konstantin Komarov: "Updates: - support /proc/fs/ntfs3/<dev>/volinfo and label - alternative boot if primary boot is corrupted - small optimizations Fixes: - fix endian problems - fix logic errors - code refactoring and reformatting" * tag 'ntfs3_for_6.5' of https://github.com/Paragon-Software-Group/linux-ntfs3: fs/ntfs3: Correct mode for label entry inside /proc/fs/ntfs3/ fs/ntfs3: Add support /proc/fs/ntfs3/<dev>/volinfo and /proc/fs/ntfs3/<dev>/label fs/ntfs3: Fix endian problem fs/ntfs3: Add ability to format new mft records with bigger/smaller header fs/ntfs3: Code refactoring fs/ntfs3: Code formatting fs/ntfs3: Do not update primary boot in ntfs_init_from_boot() fs/ntfs3: Alternative boot if primary boot is corrupted fs/ntfs3: Mark ntfs dirty when on-disk struct is corrupted fs/ntfs3: Fix ntfs_atomic_open fs/ntfs3: Correct checking while generating attr_list fs/ntfs3: Use __GFP_NOWARN allocation at ntfs_load_attr_list() fs: ntfs3: Fix possible null-pointer dereferences in mi_read() fs/ntfs3: Return error for inconsistent extended attributes fs/ntfs3: Enhance sanity check while generating attr_list fs/ntfs3: Use wrapper i_blocksize() in ntfs_zero_range() ntfs: Fix panic about slab-out-of-bounds caused by ntfs_listxattr()
This commit is contained in:
commit
36b93aed9e
@ -573,7 +573,7 @@ add_alloc_in_same_attr_seg:
|
||||
sbi, run, vcn, lcn, to_allocate, &pre_alloc,
|
||||
is_mft ? ALLOCATE_MFT : ALLOCATE_DEF, &alen,
|
||||
is_mft ? 0 :
|
||||
(sbi->record_size -
|
||||
(sbi->record_size -
|
||||
le32_to_cpu(rec->used) + 8) /
|
||||
3 +
|
||||
1,
|
||||
|
@ -52,7 +52,7 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
|
||||
|
||||
if (!attr->non_res) {
|
||||
lsize = le32_to_cpu(attr->res.data_size);
|
||||
le = kmalloc(al_aligned(lsize), GFP_NOFS);
|
||||
le = kmalloc(al_aligned(lsize), GFP_NOFS | __GFP_NOWARN);
|
||||
if (!le) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
@ -80,7 +80,7 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
le = kmalloc(al_aligned(lsize), GFP_NOFS);
|
||||
le = kmalloc(al_aligned(lsize), GFP_NOFS | __GFP_NOWARN);
|
||||
if (!le) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
@ -375,8 +375,7 @@ bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le)
|
||||
* 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)
|
||||
const __le16 *name, u8 name_len, const struct MFT_REF *ref)
|
||||
{
|
||||
u16 size;
|
||||
struct ATTR_LIST_ENTRY *le;
|
||||
|
@ -287,8 +287,8 @@ static void wnd_add_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len,
|
||||
/* Check bits before 'bit'. */
|
||||
ib = wnd->zone_bit == wnd->zone_end ||
|
||||
bit < wnd->zone_end ?
|
||||
0 :
|
||||
wnd->zone_end;
|
||||
0 :
|
||||
wnd->zone_end;
|
||||
|
||||
while (bit > ib && wnd_is_free_hlp(wnd, bit - 1, 1)) {
|
||||
bit -= 1;
|
||||
@ -298,8 +298,8 @@ static void wnd_add_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len,
|
||||
/* Check bits after 'end_in'. */
|
||||
ib = wnd->zone_bit == wnd->zone_end ||
|
||||
end_in > wnd->zone_bit ?
|
||||
wnd->nbits :
|
||||
wnd->zone_bit;
|
||||
wnd->nbits :
|
||||
wnd->zone_bit;
|
||||
|
||||
while (end_in < ib && wnd_is_free_hlp(wnd, end_in, 1)) {
|
||||
end_in += 1;
|
||||
@ -418,7 +418,7 @@ static void wnd_remove_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len)
|
||||
n3 = rb_first(&wnd->count_tree);
|
||||
wnd->extent_max =
|
||||
n3 ? rb_entry(n3, struct e_node, count.node)->count.key :
|
||||
0;
|
||||
0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -179,7 +179,7 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)
|
||||
{
|
||||
int err = 0;
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
u32 blocksize = 1 << inode->i_blkbits;
|
||||
u32 blocksize = i_blocksize(inode);
|
||||
pgoff_t idx = vbo >> PAGE_SHIFT;
|
||||
u32 from = vbo & (PAGE_SIZE - 1);
|
||||
pgoff_t idx_end = (vbo_to + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
@ -192,7 +192,7 @@ static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)
|
||||
for (; idx < idx_end; idx += 1, from = 0) {
|
||||
page_off = (loff_t)idx << PAGE_SHIFT;
|
||||
to = (page_off + PAGE_SIZE) > vbo_to ? (vbo_to - page_off) :
|
||||
PAGE_SIZE;
|
||||
PAGE_SIZE;
|
||||
iblock = page_off >> inode->i_blkbits;
|
||||
|
||||
page = find_or_create_page(mapping, idx,
|
||||
@ -1078,7 +1078,7 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
goto out;
|
||||
|
||||
ret = is_compressed(ni) ? ntfs_compress_write(iocb, from) :
|
||||
__generic_file_write_iter(iocb, from);
|
||||
__generic_file_write_iter(iocb, from);
|
||||
|
||||
out:
|
||||
inode_unlock(inode);
|
||||
|
@ -77,7 +77,7 @@ struct ATTR_STD_INFO *ni_std(struct ntfs_inode *ni)
|
||||
|
||||
attr = mi_find_attr(&ni->mi, NULL, ATTR_STD, NULL, 0, NULL);
|
||||
return attr ? resident_data_ex(attr, sizeof(struct ATTR_STD_INFO)) :
|
||||
NULL;
|
||||
NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -92,7 +92,7 @@ struct ATTR_STD_INFO5 *ni_std5(struct ntfs_inode *ni)
|
||||
attr = mi_find_attr(&ni->mi, NULL, ATTR_STD, NULL, 0, NULL);
|
||||
|
||||
return attr ? resident_data_ex(attr, sizeof(struct ATTR_STD_INFO5)) :
|
||||
NULL;
|
||||
NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -236,6 +236,7 @@ struct ATTRIB *ni_find_attr(struct ntfs_inode *ni, struct ATTRIB *attr,
|
||||
return attr;
|
||||
|
||||
out:
|
||||
ntfs_inode_err(&ni->vfs_inode, "failed to parse mft record");
|
||||
ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
@ -384,7 +385,7 @@ bool ni_add_subrecord(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi)
|
||||
* ni_remove_attr - Remove all attributes for the given type/name/id.
|
||||
*/
|
||||
int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
|
||||
const __le16 *name, size_t name_len, bool base_only,
|
||||
const __le16 *name, u8 name_len, bool base_only,
|
||||
const __le16 *id)
|
||||
{
|
||||
int err;
|
||||
@ -517,6 +518,9 @@ out:
|
||||
*/
|
||||
static int ni_repack(struct ntfs_inode *ni)
|
||||
{
|
||||
#if 1
|
||||
return 0;
|
||||
#else
|
||||
int err = 0;
|
||||
struct ntfs_sb_info *sbi = ni->mi.sbi;
|
||||
struct mft_inode *mi, *mi_p = NULL;
|
||||
@ -639,6 +643,7 @@ static int ni_repack(struct ntfs_inode *ni)
|
||||
|
||||
run_close(&run);
|
||||
return err;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
@ -813,10 +818,8 @@ int ni_create_attr_list(struct ntfs_inode *ni)
|
||||
* Looks like one record_size is always enough.
|
||||
*/
|
||||
le = kmalloc(al_aligned(rs), GFP_NOFS);
|
||||
if (!le) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!le)
|
||||
return -ENOMEM;
|
||||
|
||||
mi_get_ref(&ni->mi, &le->ref);
|
||||
ni->attr_list.le = le;
|
||||
@ -865,15 +868,16 @@ int ni_create_attr_list(struct ntfs_inode *ni)
|
||||
|
||||
if (to_free > free_b) {
|
||||
err = -EINVAL;
|
||||
goto out1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate child MFT. */
|
||||
err = ntfs_look_free_mft(sbi, &rno, is_mft, ni, &mi);
|
||||
if (err)
|
||||
goto out1;
|
||||
goto out;
|
||||
|
||||
err = -EINVAL;
|
||||
/* Call mi_remove_attr() in reverse order to keep pointers 'arr_move' valid. */
|
||||
while (to_free > 0) {
|
||||
struct ATTRIB *b = arr_move[--nb];
|
||||
@ -882,7 +886,8 @@ int ni_create_attr_list(struct ntfs_inode *ni)
|
||||
|
||||
attr = mi_insert_attr(mi, b->type, Add2Ptr(b, name_off),
|
||||
b->name_len, asize, name_off);
|
||||
WARN_ON(!attr);
|
||||
if (!attr)
|
||||
goto out;
|
||||
|
||||
mi_get_ref(mi, &le_b[nb]->ref);
|
||||
le_b[nb]->id = attr->id;
|
||||
@ -892,17 +897,20 @@ int ni_create_attr_list(struct ntfs_inode *ni)
|
||||
attr->id = le_b[nb]->id;
|
||||
|
||||
/* Remove from primary record. */
|
||||
WARN_ON(!mi_remove_attr(NULL, &ni->mi, b));
|
||||
if (!mi_remove_attr(NULL, &ni->mi, b))
|
||||
goto out;
|
||||
|
||||
if (to_free <= asize)
|
||||
break;
|
||||
to_free -= asize;
|
||||
WARN_ON(!nb);
|
||||
if (!nb)
|
||||
goto out;
|
||||
}
|
||||
|
||||
attr = mi_insert_attr(&ni->mi, ATTR_LIST, NULL, 0,
|
||||
lsize + SIZEOF_RESIDENT, SIZEOF_RESIDENT);
|
||||
WARN_ON(!attr);
|
||||
if (!attr)
|
||||
goto out;
|
||||
|
||||
attr->non_res = 0;
|
||||
attr->flags = 0;
|
||||
@ -916,14 +924,12 @@ int ni_create_attr_list(struct ntfs_inode *ni)
|
||||
ni->attr_list.dirty = false;
|
||||
|
||||
mark_inode_dirty(&ni->vfs_inode);
|
||||
goto out;
|
||||
return 0;
|
||||
|
||||
out1:
|
||||
out:
|
||||
kfree(ni->attr_list.le);
|
||||
ni->attr_list.le = NULL;
|
||||
ni->attr_list.size = 0;
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -1638,14 +1644,13 @@ int ni_delete_all(struct ntfs_inode *ni)
|
||||
* Return: File name attribute by its value.
|
||||
*/
|
||||
struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni,
|
||||
const struct cpu_str *uni,
|
||||
const struct le_str *uni,
|
||||
const struct MFT_REF *home_dir,
|
||||
struct mft_inode **mi,
|
||||
struct ATTR_LIST_ENTRY **le)
|
||||
{
|
||||
struct ATTRIB *attr = NULL;
|
||||
struct ATTR_FILE_NAME *fname;
|
||||
struct le_str *fns;
|
||||
|
||||
if (le)
|
||||
*le = NULL;
|
||||
@ -1669,10 +1674,9 @@ next:
|
||||
if (uni->len != fname->name_len)
|
||||
goto next;
|
||||
|
||||
fns = (struct le_str *)&fname->name_len;
|
||||
if (ntfs_cmp_names_cpu(uni, fns, NULL, false))
|
||||
if (ntfs_cmp_names(uni->name, uni->len, fname->name, uni->len, NULL,
|
||||
false))
|
||||
goto next;
|
||||
|
||||
return fname;
|
||||
}
|
||||
|
||||
@ -1757,8 +1761,8 @@ int ni_new_attr_flags(struct ntfs_inode *ni, enum FILE_ATTRIBUTE new_fa)
|
||||
|
||||
/* Resize nonresident empty attribute in-place only. */
|
||||
new_asize = (new_aflags & (ATTR_FLAG_COMPRESSED | ATTR_FLAG_SPARSED)) ?
|
||||
(SIZEOF_NONRESIDENT_EX + 8) :
|
||||
(SIZEOF_NONRESIDENT + 8);
|
||||
(SIZEOF_NONRESIDENT_EX + 8) :
|
||||
(SIZEOF_NONRESIDENT + 8);
|
||||
|
||||
if (!mi_resize_attr(mi, attr, new_asize - le32_to_cpu(attr->size)))
|
||||
return -EOPNOTSUPP;
|
||||
@ -2910,7 +2914,7 @@ int ni_remove_name(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
|
||||
/* Find name in record. */
|
||||
mi_get_ref(&dir_ni->mi, &de_name->home);
|
||||
|
||||
fname = ni_fname_name(ni, (struct cpu_str *)&de_name->name_len,
|
||||
fname = ni_fname_name(ni, (struct le_str *)&de_name->name_len,
|
||||
&de_name->home, &mi, &le);
|
||||
if (!fname)
|
||||
return -ENOENT;
|
||||
@ -3160,8 +3164,8 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup,
|
||||
__le64 valid_le;
|
||||
|
||||
dup->alloc_size = is_attr_ext(attr) ?
|
||||
attr->nres.total_size :
|
||||
attr->nres.alloc_size;
|
||||
attr->nres.total_size :
|
||||
attr->nres.alloc_size;
|
||||
dup->data_size = attr->nres.data_size;
|
||||
|
||||
if (new_valid > data_size)
|
||||
|
@ -828,8 +828,8 @@ static inline struct RESTART_TABLE *extend_rsttbl(struct RESTART_TABLE *tbl,
|
||||
memcpy(rt + 1, tbl + 1, esize * used);
|
||||
|
||||
rt->free_goal = free_goal == ~0u ?
|
||||
cpu_to_le32(~0u) :
|
||||
cpu_to_le32(sizeof(struct RESTART_TABLE) +
|
||||
cpu_to_le32(~0u) :
|
||||
cpu_to_le32(sizeof(struct RESTART_TABLE) +
|
||||
free_goal * esize);
|
||||
|
||||
if (tbl->first_free) {
|
||||
@ -1090,8 +1090,8 @@ static inline u64 base_lsn(struct ntfs_log *log,
|
||||
<< log->file_data_bits) +
|
||||
((((is_log_record_end(hdr) &&
|
||||
h_lsn <= le64_to_cpu(hdr->record_hdr.last_end_lsn)) ?
|
||||
le16_to_cpu(hdr->record_hdr.next_record_off) :
|
||||
log->page_size) +
|
||||
le16_to_cpu(hdr->record_hdr.next_record_off) :
|
||||
log->page_size) +
|
||||
lsn) >>
|
||||
3);
|
||||
|
||||
@ -1299,8 +1299,8 @@ static void log_init_pg_hdr(struct ntfs_log *log, u32 sys_page_size,
|
||||
log->clst_per_page = 1;
|
||||
|
||||
log->first_page = major_ver >= 2 ?
|
||||
0x22 * page_size :
|
||||
((sys_page_size << 1) + (page_size << 1));
|
||||
0x22 * page_size :
|
||||
((sys_page_size << 1) + (page_size << 1));
|
||||
log->major_ver = major_ver;
|
||||
log->minor_ver = minor_ver;
|
||||
}
|
||||
@ -1513,8 +1513,8 @@ static u32 current_log_avail(struct ntfs_log *log)
|
||||
* If there is no oldest lsn then start at the first page of the file.
|
||||
*/
|
||||
oldest_off = (log->l_flags & NTFSLOG_NO_OLDEST_LSN) ?
|
||||
log->first_page :
|
||||
(log->oldest_lsn_off & ~log->sys_page_mask);
|
||||
log->first_page :
|
||||
(log->oldest_lsn_off & ~log->sys_page_mask);
|
||||
|
||||
/*
|
||||
* We will use the next log page offset to compute the next free page.
|
||||
@ -1522,9 +1522,9 @@ static u32 current_log_avail(struct ntfs_log *log)
|
||||
* If we are at the first page then use the end of the file.
|
||||
*/
|
||||
next_free_off = (log->l_flags & NTFSLOG_REUSE_TAIL) ?
|
||||
log->next_page + log->page_size :
|
||||
log->next_page + log->page_size :
|
||||
log->next_page == log->first_page ? log->l_size :
|
||||
log->next_page;
|
||||
log->next_page;
|
||||
|
||||
/* If the two offsets are the same then there is no available space. */
|
||||
if (oldest_off == next_free_off)
|
||||
@ -1535,8 +1535,8 @@ static u32 current_log_avail(struct ntfs_log *log)
|
||||
*/
|
||||
free_bytes =
|
||||
oldest_off < next_free_off ?
|
||||
log->total_avail_pages - (next_free_off - oldest_off) :
|
||||
oldest_off - next_free_off;
|
||||
log->total_avail_pages - (next_free_off - oldest_off) :
|
||||
oldest_off - next_free_off;
|
||||
|
||||
free_bytes >>= log->page_bits;
|
||||
return free_bytes * log->reserved;
|
||||
@ -1671,7 +1671,7 @@ next_tail:
|
||||
|
||||
best_lsn1 = first_tail ? base_lsn(log, first_tail, first_file_off) : 0;
|
||||
best_lsn2 = second_tail ? base_lsn(log, second_tail, second_file_off) :
|
||||
0;
|
||||
0;
|
||||
|
||||
if (first_tail && second_tail) {
|
||||
if (best_lsn1 > best_lsn2) {
|
||||
@ -1767,7 +1767,7 @@ tail_read:
|
||||
page_cnt = page_pos = 1;
|
||||
|
||||
curpage_off = seq_base == log->seq_num ? min(log->next_page, page_off) :
|
||||
log->next_page;
|
||||
log->next_page;
|
||||
|
||||
wrapped_file =
|
||||
curpage_off == log->first_page &&
|
||||
@ -1826,8 +1826,8 @@ use_cur_page:
|
||||
((lsn_cur >> log->file_data_bits) +
|
||||
((curpage_off <
|
||||
(lsn_to_vbo(log, lsn_cur) & ~log->page_mask)) ?
|
||||
1 :
|
||||
0)) != expected_seq) {
|
||||
1 :
|
||||
0)) != expected_seq) {
|
||||
goto check_tail;
|
||||
}
|
||||
|
||||
@ -2643,8 +2643,8 @@ static inline bool check_index_root(const struct ATTRIB *attr,
|
||||
const struct INDEX_ROOT *root = resident_data(attr);
|
||||
u8 index_bits = le32_to_cpu(root->index_block_size) >=
|
||||
sbi->cluster_size ?
|
||||
sbi->cluster_bits :
|
||||
SECTOR_SHIFT;
|
||||
sbi->cluster_bits :
|
||||
SECTOR_SHIFT;
|
||||
u8 block_clst = root->index_block_clst;
|
||||
|
||||
if (le32_to_cpu(attr->res.data_size) < sizeof(struct INDEX_ROOT) ||
|
||||
@ -3861,9 +3861,9 @@ check_restart_area:
|
||||
|
||||
/* If we have a valid page then grab a pointer to the restart area. */
|
||||
ra2 = rst_info.valid_page ?
|
||||
Add2Ptr(rst_info.r_page,
|
||||
Add2Ptr(rst_info.r_page,
|
||||
le16_to_cpu(rst_info.r_page->ra_off)) :
|
||||
NULL;
|
||||
NULL;
|
||||
|
||||
if (rst_info.chkdsk_was_run ||
|
||||
(ra2 && ra2->client_idx[1] == LFS_NO_CLIENT_LE)) {
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <linux/buffer_head.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/nls.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "ntfs.h"
|
||||
@ -173,12 +174,12 @@ int ntfs_fix_post_read(struct NTFS_RECORD_HEADER *rhdr, size_t bytes,
|
||||
|
||||
fo = le16_to_cpu(rhdr->fix_off);
|
||||
fn = simple ? ((bytes >> SECTOR_SHIFT) + 1) :
|
||||
le16_to_cpu(rhdr->fix_num);
|
||||
le16_to_cpu(rhdr->fix_num);
|
||||
|
||||
/* Check errors. */
|
||||
if ((fo & 1) || fo + fn * sizeof(short) > SECTOR_SIZE || !fn-- ||
|
||||
fn * SECTOR_SIZE > bytes) {
|
||||
return -EINVAL; /* Native chkntfs returns ok! */
|
||||
return -E_NTFS_CORRUPT;
|
||||
}
|
||||
|
||||
/* Get fixup pointer. */
|
||||
@ -1661,7 +1662,8 @@ int ntfs_vbo_to_lbo(struct ntfs_sb_info *sbi, const struct runs_tree *run,
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ntfs_inode *ntfs_new_inode(struct ntfs_sb_info *sbi, CLST rno, bool dir)
|
||||
struct ntfs_inode *ntfs_new_inode(struct ntfs_sb_info *sbi, CLST rno,
|
||||
enum RECORD_FLAG flag)
|
||||
{
|
||||
int err = 0;
|
||||
struct super_block *sb = sbi->sb;
|
||||
@ -1673,8 +1675,7 @@ struct ntfs_inode *ntfs_new_inode(struct ntfs_sb_info *sbi, CLST rno, bool dir)
|
||||
|
||||
ni = ntfs_i(inode);
|
||||
|
||||
err = mi_format_new(&ni->mi, sbi, rno, dir ? RECORD_FLAG_DIR : 0,
|
||||
false);
|
||||
err = mi_format_new(&ni->mi, sbi, rno, flag, false);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@ -1937,7 +1938,7 @@ int ntfs_security_init(struct ntfs_sb_info *sbi)
|
||||
break;
|
||||
|
||||
sii_e = (struct NTFS_DE_SII *)ne;
|
||||
if (le16_to_cpu(ne->view.data_size) < SIZEOF_SECURITY_HDR)
|
||||
if (le16_to_cpu(ne->view.data_size) < sizeof(sii_e->sec_hdr))
|
||||
continue;
|
||||
|
||||
next_id = le32_to_cpu(sii_e->sec_id) + 1;
|
||||
@ -1998,18 +1999,18 @@ int ntfs_get_security_by_id(struct ntfs_sb_info *sbi, __le32 security_id,
|
||||
goto out;
|
||||
|
||||
t32 = le32_to_cpu(sii_e->sec_hdr.size);
|
||||
if (t32 < SIZEOF_SECURITY_HDR) {
|
||||
if (t32 < sizeof(struct SECURITY_HDR)) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (t32 > SIZEOF_SECURITY_HDR + 0x10000) {
|
||||
if (t32 > sizeof(struct SECURITY_HDR) + 0x10000) {
|
||||
/* Looks like too big security. 0x10000 - is arbitrary big number. */
|
||||
err = -EFBIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*size = t32 - SIZEOF_SECURITY_HDR;
|
||||
*size = t32 - sizeof(struct SECURITY_HDR);
|
||||
|
||||
p = kmalloc(*size, GFP_NOFS);
|
||||
if (!p) {
|
||||
@ -2023,14 +2024,14 @@ int ntfs_get_security_by_id(struct ntfs_sb_info *sbi, __le32 security_id,
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (memcmp(&d_security, &sii_e->sec_hdr, SIZEOF_SECURITY_HDR)) {
|
||||
if (memcmp(&d_security, &sii_e->sec_hdr, sizeof(d_security))) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ntfs_read_run_nb(sbi, &ni->file.run,
|
||||
le64_to_cpu(sii_e->sec_hdr.off) +
|
||||
SIZEOF_SECURITY_HDR,
|
||||
sizeof(struct SECURITY_HDR),
|
||||
p, *size, NULL);
|
||||
if (err)
|
||||
goto out;
|
||||
@ -2069,7 +2070,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi,
|
||||
struct NTFS_DE_SDH sdh_e;
|
||||
struct NTFS_DE_SII sii_e;
|
||||
struct SECURITY_HDR *d_security;
|
||||
u32 new_sec_size = size_sd + SIZEOF_SECURITY_HDR;
|
||||
u32 new_sec_size = size_sd + sizeof(struct SECURITY_HDR);
|
||||
u32 aligned_sec_size = ALIGN(new_sec_size, 16);
|
||||
struct SECURITY_KEY hash_key;
|
||||
struct ntfs_fnd *fnd_sdh = NULL;
|
||||
@ -2207,14 +2208,14 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi,
|
||||
/* Fill SII entry. */
|
||||
sii_e.de.view.data_off =
|
||||
cpu_to_le16(offsetof(struct NTFS_DE_SII, sec_hdr));
|
||||
sii_e.de.view.data_size = cpu_to_le16(SIZEOF_SECURITY_HDR);
|
||||
sii_e.de.view.data_size = cpu_to_le16(sizeof(struct SECURITY_HDR));
|
||||
sii_e.de.view.res = 0;
|
||||
sii_e.de.size = cpu_to_le16(SIZEOF_SII_DIRENTRY);
|
||||
sii_e.de.size = cpu_to_le16(sizeof(struct NTFS_DE_SII));
|
||||
sii_e.de.key_size = cpu_to_le16(sizeof(d_security->key.sec_id));
|
||||
sii_e.de.flags = 0;
|
||||
sii_e.de.res = 0;
|
||||
sii_e.sec_id = d_security->key.sec_id;
|
||||
memcpy(&sii_e.sec_hdr, d_security, SIZEOF_SECURITY_HDR);
|
||||
memcpy(&sii_e.sec_hdr, d_security, sizeof(struct SECURITY_HDR));
|
||||
|
||||
err = indx_insert_entry(indx_sii, ni, &sii_e.de, NULL, NULL, 0);
|
||||
if (err)
|
||||
@ -2223,7 +2224,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi,
|
||||
/* Fill SDH entry. */
|
||||
sdh_e.de.view.data_off =
|
||||
cpu_to_le16(offsetof(struct NTFS_DE_SDH, sec_hdr));
|
||||
sdh_e.de.view.data_size = cpu_to_le16(SIZEOF_SECURITY_HDR);
|
||||
sdh_e.de.view.data_size = cpu_to_le16(sizeof(struct SECURITY_HDR));
|
||||
sdh_e.de.view.res = 0;
|
||||
sdh_e.de.size = cpu_to_le16(SIZEOF_SDH_DIRENTRY);
|
||||
sdh_e.de.key_size = cpu_to_le16(sizeof(sdh_e.key));
|
||||
@ -2231,7 +2232,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi,
|
||||
sdh_e.de.res = 0;
|
||||
sdh_e.key.hash = d_security->key.hash;
|
||||
sdh_e.key.sec_id = d_security->key.sec_id;
|
||||
memcpy(&sdh_e.sec_hdr, d_security, SIZEOF_SECURITY_HDR);
|
||||
memcpy(&sdh_e.sec_hdr, d_security, sizeof(struct SECURITY_HDR));
|
||||
sdh_e.magic[0] = cpu_to_le16('I');
|
||||
sdh_e.magic[1] = cpu_to_le16('I');
|
||||
|
||||
@ -2522,7 +2523,8 @@ out:
|
||||
/*
|
||||
* run_deallocate - Deallocate clusters.
|
||||
*/
|
||||
int run_deallocate(struct ntfs_sb_info *sbi, struct runs_tree *run, bool trim)
|
||||
int run_deallocate(struct ntfs_sb_info *sbi, const struct runs_tree *run,
|
||||
bool trim)
|
||||
{
|
||||
CLST lcn, len;
|
||||
size_t idx = 0;
|
||||
@ -2578,13 +2580,13 @@ static inline bool name_has_forbidden_chars(const struct le_str *fname)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool is_reserved_name(struct ntfs_sb_info *sbi,
|
||||
static inline bool is_reserved_name(const struct ntfs_sb_info *sbi,
|
||||
const struct le_str *fname)
|
||||
{
|
||||
int port_digit;
|
||||
const __le16 *name = fname->name;
|
||||
int len = fname->len;
|
||||
u16 *upcase = sbi->upcase;
|
||||
const u16 *upcase = sbi->upcase;
|
||||
|
||||
/* check for 3 chars reserved names (device names) */
|
||||
/* name by itself or with any extension is forbidden */
|
||||
@ -2618,3 +2620,60 @@ bool valid_windows_name(struct ntfs_sb_info *sbi, const struct le_str *fname)
|
||||
return !name_has_forbidden_chars(fname) &&
|
||||
!is_reserved_name(sbi, fname);
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_set_label - updates current ntfs label.
|
||||
*/
|
||||
int ntfs_set_label(struct ntfs_sb_info *sbi, u8 *label, int len)
|
||||
{
|
||||
int err;
|
||||
struct ATTRIB *attr;
|
||||
struct ntfs_inode *ni = sbi->volume.ni;
|
||||
const u8 max_ulen = 0x80; /* TODO: use attrdef to get maximum length */
|
||||
/* Allocate PATH_MAX bytes. */
|
||||
struct cpu_str *uni = __getname();
|
||||
|
||||
if (!uni)
|
||||
return -ENOMEM;
|
||||
|
||||
err = ntfs_nls_to_utf16(sbi, label, len, uni, (PATH_MAX - 2) / 2,
|
||||
UTF16_LITTLE_ENDIAN);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
if (uni->len > max_ulen) {
|
||||
ntfs_warn(sbi->sb, "new label is too long");
|
||||
err = -EFBIG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ni_lock(ni);
|
||||
|
||||
/* Ignore any errors. */
|
||||
ni_remove_attr(ni, ATTR_LABEL, NULL, 0, false, NULL);
|
||||
|
||||
err = ni_insert_resident(ni, uni->len * sizeof(u16), ATTR_LABEL, NULL,
|
||||
0, &attr, NULL, NULL);
|
||||
if (err < 0)
|
||||
goto unlock_out;
|
||||
|
||||
/* write new label in on-disk struct. */
|
||||
memcpy(resident_data(attr), uni->name, uni->len * sizeof(u16));
|
||||
|
||||
/* update cached value of current label. */
|
||||
if (len >= ARRAY_SIZE(sbi->volume.label))
|
||||
len = ARRAY_SIZE(sbi->volume.label) - 1;
|
||||
memcpy(sbi->volume.label, label, len);
|
||||
sbi->volume.label[len] = 0;
|
||||
mark_inode_dirty_sync(&ni->vfs_inode);
|
||||
|
||||
unlock_out:
|
||||
ni_unlock(ni);
|
||||
|
||||
if (!err)
|
||||
err = _ni_write_inode(&ni->vfs_inode, 0);
|
||||
|
||||
out:
|
||||
__putname(uni);
|
||||
return err;
|
||||
}
|
@ -432,8 +432,8 @@ next_run:
|
||||
nbits = 8 * (data_size - vbo);
|
||||
|
||||
ok = nbits > from ?
|
||||
(*fn)((ulong *)bh->b_data, from, nbits, ret) :
|
||||
false;
|
||||
(*fn)((ulong *)bh->b_data, from, nbits, ret) :
|
||||
false;
|
||||
put_bh(bh);
|
||||
|
||||
if (ok) {
|
||||
@ -1113,6 +1113,12 @@ ok:
|
||||
*node = in;
|
||||
|
||||
out:
|
||||
if (err == -E_NTFS_CORRUPT) {
|
||||
ntfs_inode_err(&ni->vfs_inode, "directory corrupted");
|
||||
ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_ERROR);
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
if (ib != in->index)
|
||||
kfree(ib);
|
||||
|
||||
@ -1676,8 +1682,8 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
|
||||
/* Create alloc and bitmap attributes (if not). */
|
||||
err = run_is_empty(&indx->alloc_run) ?
|
||||
indx_create_allocate(indx, ni, &new_vbn) :
|
||||
indx_add_allocate(indx, ni, &new_vbn);
|
||||
indx_create_allocate(indx, ni, &new_vbn) :
|
||||
indx_add_allocate(indx, ni, &new_vbn);
|
||||
|
||||
/* Layout of record may be changed, so rescan root. */
|
||||
root = indx_get_root(indx, ni, &attr, &mi);
|
||||
@ -1868,8 +1874,8 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
(*indx->cmp)(new_de + 1, le16_to_cpu(new_de->key_size),
|
||||
up_e + 1, le16_to_cpu(up_e->key_size),
|
||||
ctx) < 0 ?
|
||||
hdr2 :
|
||||
hdr1,
|
||||
hdr2 :
|
||||
hdr1,
|
||||
new_de, NULL, ctx);
|
||||
|
||||
indx_mark_used(indx, ni, new_vbn >> indx->idx2vbn_bits);
|
||||
@ -2340,7 +2346,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
|
||||
re, ctx,
|
||||
fnd->level - 1,
|
||||
fnd) :
|
||||
indx_insert_into_root(indx, ni, re, e,
|
||||
indx_insert_into_root(indx, ni, re, e,
|
||||
ctx, fnd, 0);
|
||||
kfree(re);
|
||||
|
||||
|
@ -263,7 +263,7 @@ next_attr:
|
||||
goto next_attr;
|
||||
|
||||
run = ino == MFT_REC_BITMAP ? &sbi->used.bitmap.run :
|
||||
&ni->file.run;
|
||||
&ni->file.run;
|
||||
break;
|
||||
|
||||
case ATTR_ROOT:
|
||||
@ -291,8 +291,8 @@ next_attr:
|
||||
goto out;
|
||||
|
||||
mode = sb->s_root ?
|
||||
(S_IFDIR | (0777 & sbi->options->fs_dmask_inv)) :
|
||||
(S_IFDIR | 0777);
|
||||
(S_IFDIR | (0777 & sbi->options->fs_dmask_inv)) :
|
||||
(S_IFDIR | 0777);
|
||||
goto next_attr;
|
||||
|
||||
case ATTR_ALLOC:
|
||||
@ -450,7 +450,7 @@ end_enum:
|
||||
inode->i_op = &ntfs_file_inode_operations;
|
||||
inode->i_fop = &ntfs_file_operations;
|
||||
inode->i_mapping->a_ops = is_compressed(ni) ? &ntfs_aops_cmpr :
|
||||
&ntfs_aops;
|
||||
&ntfs_aops;
|
||||
if (ino != MFT_REC_MFT)
|
||||
init_rwsem(&ni->file.run_lock);
|
||||
} else if (S_ISCHR(mode) || S_ISBLK(mode) || S_ISFIFO(mode) ||
|
||||
@ -787,7 +787,7 @@ static ssize_t ntfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
|
||||
|
||||
ret = blockdev_direct_IO(iocb, inode, iter,
|
||||
wr ? ntfs_get_block_direct_IO_W :
|
||||
ntfs_get_block_direct_IO_R);
|
||||
ntfs_get_block_direct_IO_R);
|
||||
|
||||
if (ret > 0)
|
||||
end = vbo + ret;
|
||||
@ -1191,11 +1191,11 @@ out:
|
||||
* - ntfs_symlink
|
||||
* - ntfs_mkdir
|
||||
* - ntfs_atomic_open
|
||||
*
|
||||
*
|
||||
* NOTE: if fnd != NULL (ntfs_atomic_open) then @dir is locked
|
||||
*/
|
||||
struct inode *ntfs_create_inode(struct mnt_idmap *idmap,
|
||||
struct inode *dir, struct dentry *dentry,
|
||||
struct inode *ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry,
|
||||
const struct cpu_str *uni, umode_t mode,
|
||||
dev_t dev, const char *symname, u32 size,
|
||||
struct ntfs_fnd *fnd)
|
||||
@ -1309,7 +1309,7 @@ struct inode *ntfs_create_inode(struct mnt_idmap *idmap,
|
||||
if (err)
|
||||
goto out2;
|
||||
|
||||
ni = ntfs_new_inode(sbi, ino, fa & FILE_ATTRIBUTE_DIRECTORY);
|
||||
ni = ntfs_new_inode(sbi, ino, S_ISDIR(mode) ? RECORD_FLAG_DIR : 0);
|
||||
if (IS_ERR(ni)) {
|
||||
err = PTR_ERR(ni);
|
||||
ni = NULL;
|
||||
@ -1437,8 +1437,7 @@ struct inode *ntfs_create_inode(struct mnt_idmap *idmap,
|
||||
|
||||
root = Add2Ptr(attr, sizeof(I30_NAME) + SIZEOF_RESIDENT);
|
||||
memcpy(root, dir_root, offsetof(struct INDEX_ROOT, ihdr));
|
||||
root->ihdr.de_off =
|
||||
cpu_to_le32(sizeof(struct INDEX_HDR)); // 0x10
|
||||
root->ihdr.de_off = cpu_to_le32(sizeof(struct INDEX_HDR));
|
||||
root->ihdr.used = cpu_to_le32(sizeof(struct INDEX_HDR) +
|
||||
sizeof(struct NTFS_DE));
|
||||
root->ihdr.total = root->ihdr.used;
|
||||
@ -1605,7 +1604,7 @@ struct inode *ntfs_create_inode(struct mnt_idmap *idmap,
|
||||
inode->i_op = &ntfs_file_inode_operations;
|
||||
inode->i_fop = &ntfs_file_operations;
|
||||
inode->i_mapping->a_ops = is_compressed(ni) ? &ntfs_aops_cmpr :
|
||||
&ntfs_aops;
|
||||
&ntfs_aops;
|
||||
init_rwsem(&ni->file.run_lock);
|
||||
} else {
|
||||
inode->i_op = &ntfs_special_inode_operations;
|
||||
|
@ -297,7 +297,7 @@ next:
|
||||
struct lznt *get_lznt_ctx(int level)
|
||||
{
|
||||
struct lznt *r = kzalloc(level ? offsetof(struct lznt, hash) :
|
||||
sizeof(struct lznt),
|
||||
sizeof(struct lznt),
|
||||
GFP_NOFS);
|
||||
|
||||
if (r)
|
||||
@ -393,8 +393,8 @@ ssize_t decompress_lznt(const void *cmpr, size_t cmpr_size, void *unc,
|
||||
} else {
|
||||
/* This chunk does not contain compressed data. */
|
||||
unc_use = unc_chunk + LZNT_CHUNK_SIZE > unc_end ?
|
||||
unc_end - unc_chunk :
|
||||
LZNT_CHUNK_SIZE;
|
||||
unc_end - unc_chunk :
|
||||
LZNT_CHUNK_SIZE;
|
||||
|
||||
if (cmpr_chunk + sizeof(chunk_hdr) + unc_use >
|
||||
cmpr_end) {
|
||||
|
@ -109,8 +109,8 @@ static int ntfs_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
inode = ntfs_create_inode(idmap, dir, dentry, NULL, S_IFREG | mode,
|
||||
0, NULL, 0, NULL);
|
||||
inode = ntfs_create_inode(idmap, dir, dentry, NULL, S_IFREG | mode, 0,
|
||||
NULL, 0, NULL);
|
||||
|
||||
return IS_ERR(inode) ? PTR_ERR(inode) : 0;
|
||||
}
|
||||
@ -125,8 +125,8 @@ static int ntfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
inode = ntfs_create_inode(idmap, dir, dentry, NULL, mode, rdev,
|
||||
NULL, 0, NULL);
|
||||
inode = ntfs_create_inode(idmap, dir, dentry, NULL, mode, rdev, NULL, 0,
|
||||
NULL);
|
||||
|
||||
return IS_ERR(inode) ? PTR_ERR(inode) : 0;
|
||||
}
|
||||
@ -199,8 +199,8 @@ static int ntfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
|
||||
u32 size = strlen(symname);
|
||||
struct inode *inode;
|
||||
|
||||
inode = ntfs_create_inode(idmap, dir, dentry, NULL, S_IFLNK | 0777,
|
||||
0, symname, size, NULL);
|
||||
inode = ntfs_create_inode(idmap, dir, dentry, NULL, S_IFLNK | 0777, 0,
|
||||
symname, size, NULL);
|
||||
|
||||
return IS_ERR(inode) ? PTR_ERR(inode) : 0;
|
||||
}
|
||||
@ -213,8 +213,8 @@ static int ntfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
inode = ntfs_create_inode(idmap, dir, dentry, NULL, S_IFDIR | mode,
|
||||
0, NULL, 0, NULL);
|
||||
inode = ntfs_create_inode(idmap, dir, dentry, NULL, S_IFDIR | mode, 0,
|
||||
NULL, 0, NULL);
|
||||
|
||||
return IS_ERR(inode) ? PTR_ERR(inode) : 0;
|
||||
}
|
||||
@ -422,19 +422,10 @@ static int ntfs_atomic_open(struct inode *dir, struct dentry *dentry,
|
||||
* fnd contains tree's path to insert to.
|
||||
* If fnd is not NULL then dir is locked.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Unfortunately I don't know how to get here correct 'struct nameidata *nd'
|
||||
* or 'struct mnt_idmap *idmap'.
|
||||
* See atomic_open in fs/namei.c.
|
||||
* This is why xfstest/633 failed.
|
||||
* Looks like ntfs_atomic_open must accept 'struct mnt_idmap *idmap' as argument.
|
||||
*/
|
||||
|
||||
inode = ntfs_create_inode(&nop_mnt_idmap, dir, dentry, uni, mode, 0,
|
||||
NULL, 0, fnd);
|
||||
inode = ntfs_create_inode(mnt_idmap(file->f_path.mnt), dir, dentry, uni,
|
||||
mode, 0, NULL, 0, fnd);
|
||||
err = IS_ERR(inode) ? PTR_ERR(inode) :
|
||||
finish_open(file, dentry, ntfs_file_open);
|
||||
finish_open(file, dentry, ntfs_file_open);
|
||||
dput(d);
|
||||
|
||||
out2:
|
||||
|
117
fs/ntfs3/ntfs.h
117
fs/ntfs3/ntfs.h
@ -95,11 +95,10 @@ enum RECORD_NUM {
|
||||
MFT_REC_BITMAP = 6,
|
||||
MFT_REC_BOOT = 7,
|
||||
MFT_REC_BADCLUST = 8,
|
||||
//MFT_REC_QUOTA = 9,
|
||||
MFT_REC_SECURE = 9, // NTFS 3.0
|
||||
MFT_REC_SECURE = 9,
|
||||
MFT_REC_UPCASE = 10,
|
||||
MFT_REC_EXTEND = 11, // NTFS 3.0
|
||||
MFT_REC_RESERVED = 11,
|
||||
MFT_REC_EXTEND = 11,
|
||||
MFT_REC_RESERVED = 12,
|
||||
MFT_REC_FREE = 16,
|
||||
MFT_REC_USER = 24,
|
||||
};
|
||||
@ -109,7 +108,6 @@ enum ATTR_TYPE {
|
||||
ATTR_STD = cpu_to_le32(0x10),
|
||||
ATTR_LIST = cpu_to_le32(0x20),
|
||||
ATTR_NAME = cpu_to_le32(0x30),
|
||||
// ATTR_VOLUME_VERSION on Nt4
|
||||
ATTR_ID = cpu_to_le32(0x40),
|
||||
ATTR_SECURE = cpu_to_le32(0x50),
|
||||
ATTR_LABEL = cpu_to_le32(0x60),
|
||||
@ -118,7 +116,6 @@ enum ATTR_TYPE {
|
||||
ATTR_ROOT = cpu_to_le32(0x90),
|
||||
ATTR_ALLOC = cpu_to_le32(0xA0),
|
||||
ATTR_BITMAP = cpu_to_le32(0xB0),
|
||||
// ATTR_SYMLINK on Nt4
|
||||
ATTR_REPARSE = cpu_to_le32(0xC0),
|
||||
ATTR_EA_INFO = cpu_to_le32(0xD0),
|
||||
ATTR_EA = cpu_to_le32(0xE0),
|
||||
@ -144,6 +141,7 @@ enum FILE_ATTRIBUTE {
|
||||
FILE_ATTRIBUTE_ENCRYPTED = cpu_to_le32(0x00004000),
|
||||
FILE_ATTRIBUTE_VALID_FLAGS = cpu_to_le32(0x00007fb7),
|
||||
FILE_ATTRIBUTE_DIRECTORY = cpu_to_le32(0x10000000),
|
||||
FILE_ATTRIBUTE_INDEX = cpu_to_le32(0x20000000)
|
||||
};
|
||||
|
||||
static_assert(sizeof(enum FILE_ATTRIBUTE) == 4);
|
||||
@ -266,7 +264,7 @@ enum RECORD_FLAG {
|
||||
RECORD_FLAG_IN_USE = cpu_to_le16(0x0001),
|
||||
RECORD_FLAG_DIR = cpu_to_le16(0x0002),
|
||||
RECORD_FLAG_SYSTEM = cpu_to_le16(0x0004),
|
||||
RECORD_FLAG_UNKNOWN = cpu_to_le16(0x0008),
|
||||
RECORD_FLAG_INDEX = cpu_to_le16(0x0008),
|
||||
};
|
||||
|
||||
/* MFT Record structure. */
|
||||
@ -290,6 +288,15 @@ struct MFT_REC {
|
||||
|
||||
#define MFTRECORD_FIXUP_OFFSET_1 offsetof(struct MFT_REC, res)
|
||||
#define MFTRECORD_FIXUP_OFFSET_3 offsetof(struct MFT_REC, fixups)
|
||||
/*
|
||||
* define MFTRECORD_FIXUP_OFFSET as MFTRECORD_FIXUP_OFFSET_3 (0x30)
|
||||
* to format new mft records with bigger header (as current ntfs.sys does)
|
||||
*
|
||||
* define MFTRECORD_FIXUP_OFFSET as MFTRECORD_FIXUP_OFFSET_1 (0x2A)
|
||||
* to format new mft records with smaller header (as old ntfs.sys did)
|
||||
* Both variants are valid.
|
||||
*/
|
||||
#define MFTRECORD_FIXUP_OFFSET MFTRECORD_FIXUP_OFFSET_1
|
||||
|
||||
static_assert(MFTRECORD_FIXUP_OFFSET_1 == 0x2A);
|
||||
static_assert(MFTRECORD_FIXUP_OFFSET_3 == 0x30);
|
||||
@ -331,18 +338,18 @@ struct ATTR_NONRESIDENT {
|
||||
__le64 svcn; // 0x10: Starting VCN of this segment.
|
||||
__le64 evcn; // 0x18: End VCN of this segment.
|
||||
__le16 run_off; // 0x20: Offset to packed runs.
|
||||
// Unit of Compression size for this stream, expressed
|
||||
// as a log of the cluster size.
|
||||
// Unit of Compression size for this stream, expressed
|
||||
// as a log of the cluster size.
|
||||
//
|
||||
// 0 means file is not compressed
|
||||
// 1, 2, 3, and 4 are potentially legal values if the
|
||||
// stream is compressed, however the implementation
|
||||
// may only choose to use 4, or possibly 3. Note
|
||||
// that 4 means cluster size time 16. If convenient
|
||||
// the implementation may wish to accept a
|
||||
// reasonable range of legal values here (1-5?),
|
||||
// even if the implementation only generates
|
||||
// a smaller set of values itself.
|
||||
// 0 means file is not compressed
|
||||
// 1, 2, 3, and 4 are potentially legal values if the
|
||||
// stream is compressed, however the implementation
|
||||
// may only choose to use 4, or possibly 3.
|
||||
// Note that 4 means cluster size time 16.
|
||||
// If convenient the implementation may wish to accept a
|
||||
// reasonable range of legal values here (1-5?),
|
||||
// even if the implementation only generates
|
||||
// a smaller set of values itself.
|
||||
u8 c_unit; // 0x22:
|
||||
u8 res1[5]; // 0x23:
|
||||
__le64 alloc_size; // 0x28: The allocated size of attribute in bytes.
|
||||
@ -836,16 +843,22 @@ static_assert(sizeof(struct ATTR_DEF_ENTRY) == 0xa0);
|
||||
/* Object ID (0x40) */
|
||||
struct OBJECT_ID {
|
||||
struct GUID ObjId; // 0x00: Unique Id assigned to file.
|
||||
struct GUID BirthVolumeId; // 0x10: Birth Volume Id is the Object Id of the Volume on.
|
||||
// which the Object Id was allocated. It never changes.
|
||||
struct GUID BirthObjectId; // 0x20: Birth Object Id is the first Object Id that was
|
||||
// ever assigned to this MFT Record. I.e. If the Object Id
|
||||
// is changed for some reason, this field will reflect the
|
||||
// original value of the Object Id.
|
||||
struct GUID DomainId; // 0x30: Domain Id is currently unused but it is intended to be
|
||||
// used in a network environment where the local machine is
|
||||
// part of a Windows 2000 Domain. This may be used in a Windows
|
||||
// 2000 Advanced Server managed domain.
|
||||
|
||||
// Birth Volume Id is the Object Id of the Volume on.
|
||||
// which the Object Id was allocated. It never changes.
|
||||
struct GUID BirthVolumeId; //0x10:
|
||||
|
||||
// Birth Object Id is the first Object Id that was
|
||||
// ever assigned to this MFT Record. I.e. If the Object Id
|
||||
// is changed for some reason, this field will reflect the
|
||||
// original value of the Object Id.
|
||||
struct GUID BirthObjectId; // 0x20:
|
||||
|
||||
// Domain Id is currently unused but it is intended to be
|
||||
// used in a network environment where the local machine is
|
||||
// part of a Windows 2000 Domain. This may be used in a Windows
|
||||
// 2000 Advanced Server managed domain.
|
||||
struct GUID DomainId; // 0x30:
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct OBJECT_ID) == 0x40);
|
||||
@ -855,32 +868,35 @@ struct NTFS_DE_O {
|
||||
struct NTFS_DE de;
|
||||
struct GUID ObjId; // 0x10: Unique Id assigned to file.
|
||||
struct MFT_REF ref; // 0x20: MFT record number with this file.
|
||||
struct GUID BirthVolumeId; // 0x28: Birth Volume Id is the Object Id of the Volume on
|
||||
// which the Object Id was allocated. It never changes.
|
||||
struct GUID BirthObjectId; // 0x38: Birth Object Id is the first Object Id that was
|
||||
// ever assigned to this MFT Record. I.e. If the Object Id
|
||||
// is changed for some reason, this field will reflect the
|
||||
// original value of the Object Id.
|
||||
// This field is valid if data_size == 0x48.
|
||||
struct GUID BirthDomainId; // 0x48: Domain Id is currently unused but it is intended
|
||||
// to be used in a network environment where the local
|
||||
// machine is part of a Windows 2000 Domain. This may be
|
||||
// used in a Windows 2000 Advanced Server managed domain.
|
||||
|
||||
// Birth Volume Id is the Object Id of the Volume on
|
||||
// which the Object Id was allocated. It never changes.
|
||||
struct GUID BirthVolumeId; // 0x28:
|
||||
|
||||
// Birth Object Id is the first Object Id that was
|
||||
// ever assigned to this MFT Record. I.e. If the Object Id
|
||||
// is changed for some reason, this field will reflect the
|
||||
// original value of the Object Id.
|
||||
// This field is valid if data_size == 0x48.
|
||||
struct GUID BirthObjectId; // 0x38:
|
||||
|
||||
// Domain Id is currently unused but it is intended
|
||||
// to be used in a network environment where the local
|
||||
// machine is part of a Windows 2000 Domain. This may be
|
||||
// used in a Windows 2000 Advanced Server managed domain.
|
||||
struct GUID BirthDomainId; // 0x48:
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct NTFS_DE_O) == 0x58);
|
||||
|
||||
#define NTFS_OBJECT_ENTRY_DATA_SIZE1 \
|
||||
0x38 // struct NTFS_DE_O.BirthDomainId is not used
|
||||
#define NTFS_OBJECT_ENTRY_DATA_SIZE2 \
|
||||
0x48 // struct NTFS_DE_O.BirthDomainId is used
|
||||
|
||||
/* Q Directory entry structure ( rule = 0x11 ) */
|
||||
struct NTFS_DE_Q {
|
||||
struct NTFS_DE de;
|
||||
__le32 owner_id; // 0x10: Unique Id assigned to file
|
||||
|
||||
/* here is 0x30 bytes of user quota. NOTE: 4 byte aligned! */
|
||||
__le32 Version; // 0x14: 0x02
|
||||
__le32 flags2; // 0x18: Quota flags, see above
|
||||
__le32 Flags; // 0x18: Quota flags, see above
|
||||
__le64 BytesUsed; // 0x1C:
|
||||
__le64 ChangeTime; // 0x24:
|
||||
__le64 WarningLimit; // 0x28:
|
||||
@ -888,9 +904,9 @@ struct NTFS_DE_Q {
|
||||
__le64 ExceededTime; // 0x3C:
|
||||
|
||||
// SID is placed here
|
||||
}; // sizeof() = 0x44
|
||||
}__packed; // sizeof() = 0x44
|
||||
|
||||
#define SIZEOF_NTFS_DE_Q 0x44
|
||||
static_assert(sizeof(struct NTFS_DE_Q) == 0x44);
|
||||
|
||||
#define SecurityDescriptorsBlockSize 0x40000 // 256K
|
||||
#define SecurityDescriptorMaxSize 0x20000 // 128K
|
||||
@ -912,7 +928,7 @@ struct SECURITY_HDR {
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
#define SIZEOF_SECURITY_HDR 0x14
|
||||
static_assert(sizeof(struct SECURITY_HDR) == 0x14);
|
||||
|
||||
/* SII Directory entry structure */
|
||||
struct NTFS_DE_SII {
|
||||
@ -921,7 +937,8 @@ struct NTFS_DE_SII {
|
||||
struct SECURITY_HDR sec_hdr; // 0x14:
|
||||
} __packed;
|
||||
|
||||
#define SIZEOF_SII_DIRENTRY 0x28
|
||||
static_assert(offsetof(struct NTFS_DE_SII, sec_hdr) == 0x14);
|
||||
static_assert(sizeof(struct NTFS_DE_SII) == 0x28);
|
||||
|
||||
/* SDH Directory entry structure */
|
||||
struct NTFS_DE_SDH {
|
||||
@ -1155,7 +1172,7 @@ struct REPARSE_DATA_BUFFER {
|
||||
|
||||
#define FILE_NEED_EA 0x80 // See ntifs.h
|
||||
/*
|
||||
*FILE_NEED_EA, indicates that the file to which the EA belongs cannot be
|
||||
* FILE_NEED_EA, indicates that the file to which the EA belongs cannot be
|
||||
* interpreted without understanding the associated extended attributes.
|
||||
*/
|
||||
struct EA_INFO {
|
||||
|
@ -53,6 +53,8 @@ enum utf16_endian;
|
||||
#define E_NTFS_NONRESIDENT 556
|
||||
/* NTFS specific error code about punch hole. */
|
||||
#define E_NTFS_NOTALIGNED 557
|
||||
/* NTFS specific error code when on-disk struct is corrupted. */
|
||||
#define E_NTFS_CORRUPT 558
|
||||
|
||||
|
||||
/* sbi->flags */
|
||||
@ -274,7 +276,7 @@ struct ntfs_sb_info {
|
||||
__le16 flags; // Cached current VOLUME_INFO::flags, VOLUME_FLAG_DIRTY.
|
||||
u8 major_ver;
|
||||
u8 minor_ver;
|
||||
char label[65];
|
||||
char label[256];
|
||||
bool real_dirty; // Real fs state.
|
||||
} volume;
|
||||
|
||||
@ -284,7 +286,6 @@ struct ntfs_sb_info {
|
||||
struct ntfs_inode *ni;
|
||||
u32 next_id;
|
||||
u64 next_off;
|
||||
|
||||
__le32 def_security_id;
|
||||
} security;
|
||||
|
||||
@ -312,6 +313,7 @@ struct ntfs_sb_info {
|
||||
|
||||
struct ntfs_mount_options *options;
|
||||
struct ratelimit_state msg_ratelimit;
|
||||
struct proc_dir_entry *procdir;
|
||||
};
|
||||
|
||||
/* One MFT record(usually 1024 bytes), consists of attributes. */
|
||||
@ -465,8 +467,7 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name,
|
||||
struct ATTR_LIST_ENTRY **new_le);
|
||||
bool al_remove_le(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le);
|
||||
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);
|
||||
const __le16 *name, u8 name_len, const struct MFT_REF *ref);
|
||||
int al_update(struct ntfs_inode *ni, int sync);
|
||||
static inline size_t al_aligned(size_t size)
|
||||
{
|
||||
@ -525,7 +526,7 @@ struct ATTRIB *ni_load_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
|
||||
int ni_load_all_mi(struct ntfs_inode *ni);
|
||||
bool ni_add_subrecord(struct ntfs_inode *ni, CLST rno, struct mft_inode **mi);
|
||||
int ni_remove_attr(struct ntfs_inode *ni, enum ATTR_TYPE type,
|
||||
const __le16 *name, size_t name_len, bool base_only,
|
||||
const __le16 *name, u8 name_len, bool base_only,
|
||||
const __le16 *id);
|
||||
int ni_create_attr_list(struct ntfs_inode *ni);
|
||||
int ni_expand_list(struct ntfs_inode *ni);
|
||||
@ -542,7 +543,7 @@ void ni_remove_attr_le(struct ntfs_inode *ni, struct ATTRIB *attr,
|
||||
struct mft_inode *mi, struct ATTR_LIST_ENTRY *le);
|
||||
int ni_delete_all(struct ntfs_inode *ni);
|
||||
struct ATTR_FILE_NAME *ni_fname_name(struct ntfs_inode *ni,
|
||||
const struct cpu_str *uni,
|
||||
const struct le_str *uni,
|
||||
const struct MFT_REF *home,
|
||||
struct mft_inode **mi,
|
||||
struct ATTR_LIST_ENTRY **entry);
|
||||
@ -629,7 +630,7 @@ int ntfs_bio_fill_1(struct ntfs_sb_info *sbi, const struct runs_tree *run);
|
||||
int ntfs_vbo_to_lbo(struct ntfs_sb_info *sbi, const struct runs_tree *run,
|
||||
u64 vbo, u64 *lbo, u64 *bytes);
|
||||
struct ntfs_inode *ntfs_new_inode(struct ntfs_sb_info *sbi, CLST nRec,
|
||||
bool dir);
|
||||
enum RECORD_FLAG flag);
|
||||
extern const u8 s_default_security[0x50];
|
||||
bool is_sd_valid(const struct SECURITY_DESCRIPTOR_RELATIVE *sd, u32 len);
|
||||
int ntfs_security_init(struct ntfs_sb_info *sbi);
|
||||
@ -647,8 +648,10 @@ int ntfs_insert_reparse(struct ntfs_sb_info *sbi, __le32 rtag,
|
||||
int ntfs_remove_reparse(struct ntfs_sb_info *sbi, __le32 rtag,
|
||||
const struct MFT_REF *ref);
|
||||
void mark_as_free_ex(struct ntfs_sb_info *sbi, CLST lcn, CLST len, bool trim);
|
||||
int run_deallocate(struct ntfs_sb_info *sbi, struct runs_tree *run, bool trim);
|
||||
int run_deallocate(struct ntfs_sb_info *sbi, const struct runs_tree *run,
|
||||
bool trim);
|
||||
bool valid_windows_name(struct ntfs_sb_info *sbi, const struct le_str *name);
|
||||
int ntfs_set_label(struct ntfs_sb_info *sbi, u8 *label, int len);
|
||||
|
||||
/* Globals from index.c */
|
||||
int indx_used_bit(struct ntfs_index *indx, struct ntfs_inode *ni, size_t *bit);
|
||||
@ -706,8 +709,8 @@ int ntfs_sync_inode(struct inode *inode);
|
||||
int ntfs_flush_inodes(struct super_block *sb, struct inode *i1,
|
||||
struct inode *i2);
|
||||
int inode_write_data(struct inode *inode, const void *data, size_t bytes);
|
||||
struct inode *ntfs_create_inode(struct mnt_idmap *idmap,
|
||||
struct inode *dir, struct dentry *dentry,
|
||||
struct inode *ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir,
|
||||
struct dentry *dentry,
|
||||
const struct cpu_str *uni, umode_t mode,
|
||||
dev_t dev, const char *symname, u32 size,
|
||||
struct ntfs_fnd *fnd);
|
||||
@ -736,7 +739,7 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr);
|
||||
// TODO: 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);
|
||||
u8 name_len, const __le16 *id);
|
||||
static inline struct ATTRIB *rec_find_attr_le(struct mft_inode *rec,
|
||||
struct ATTR_LIST_ENTRY *le)
|
||||
{
|
||||
@ -856,12 +859,12 @@ unsigned long ntfs_names_hash(const u16 *name, size_t len, const u16 *upcase,
|
||||
|
||||
/* globals from xattr.c */
|
||||
#ifdef CONFIG_NTFS3_FS_POSIX_ACL
|
||||
struct posix_acl *ntfs_get_acl(struct mnt_idmap *idmap,
|
||||
struct dentry *dentry, int type);
|
||||
struct posix_acl *ntfs_get_acl(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
int type);
|
||||
int ntfs_set_acl(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
struct posix_acl *acl, int type);
|
||||
int ntfs_init_acl(struct mnt_idmap *idmap, struct inode *inode,
|
||||
struct inode *dir);
|
||||
struct inode *dir);
|
||||
#else
|
||||
#define ntfs_get_acl NULL
|
||||
#define ntfs_set_acl NULL
|
||||
|
@ -124,7 +124,7 @@ int mi_read(struct mft_inode *mi, bool is_mft)
|
||||
struct rw_semaphore *rw_lock = NULL;
|
||||
|
||||
if (is_mounted(sbi)) {
|
||||
if (!is_mft) {
|
||||
if (!is_mft && mft_ni) {
|
||||
rw_lock = &mft_ni->file.run_lock;
|
||||
down_read(rw_lock);
|
||||
}
|
||||
@ -148,7 +148,7 @@ int mi_read(struct mft_inode *mi, bool is_mft)
|
||||
ni_lock(mft_ni);
|
||||
down_write(rw_lock);
|
||||
}
|
||||
err = attr_load_runs_vcn(mft_ni, ATTR_DATA, NULL, 0, &mft_ni->file.run,
|
||||
err = attr_load_runs_vcn(mft_ni, ATTR_DATA, NULL, 0, run,
|
||||
vbo >> sbi->cluster_bits);
|
||||
if (rw_lock) {
|
||||
up_write(rw_lock);
|
||||
@ -180,6 +180,12 @@ ok:
|
||||
return 0;
|
||||
|
||||
out:
|
||||
if (err == -E_NTFS_CORRUPT) {
|
||||
ntfs_err(sbi->sb, "mft corrupted");
|
||||
ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -296,7 +302,7 @@ struct ATTRIB *mi_enum_attr(struct mft_inode *mi, struct ATTRIB *attr)
|
||||
*/
|
||||
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)
|
||||
u8 name_len, const __le16 *id)
|
||||
{
|
||||
u32 type_in = le32_to_cpu(type);
|
||||
u32 atype;
|
||||
@ -382,6 +388,8 @@ int mi_format_new(struct mft_inode *mi, struct ntfs_sb_info *sbi, CLST rno,
|
||||
|
||||
rec->seq = cpu_to_le16(seq);
|
||||
rec->flags = RECORD_FLAG_IN_USE | flags;
|
||||
if (MFTRECORD_FIXUP_OFFSET == MFTRECORD_FIXUP_OFFSET_3)
|
||||
rec->mft_record = cpu_to_le32(rno);
|
||||
|
||||
mi->dirty = true;
|
||||
|
||||
|
@ -434,8 +434,8 @@ requires_new_range:
|
||||
|
||||
if (should_add_tail) {
|
||||
tail_lcn = r->lcn == SPARSE_LCN ?
|
||||
SPARSE_LCN :
|
||||
(r->lcn + Tovcn);
|
||||
SPARSE_LCN :
|
||||
(r->lcn + Tovcn);
|
||||
tail_vcn = r->vcn + Tovcn;
|
||||
tail_len = r->len - Tovcn;
|
||||
}
|
||||
|
280
fs/ntfs3/super.c
280
fs/ntfs3/super.c
@ -57,6 +57,7 @@
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nls.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/statfs.h>
|
||||
|
||||
@ -116,8 +117,8 @@ void ntfs_inode_printk(struct inode *inode, const char *fmt, ...)
|
||||
|
||||
/* Use static allocated buffer, if possible. */
|
||||
name = atomic_dec_and_test(&s_name_buf_cnt) ?
|
||||
s_name_buf :
|
||||
kmalloc(sizeof(s_name_buf), GFP_NOFS);
|
||||
s_name_buf :
|
||||
kmalloc(sizeof(s_name_buf), GFP_NOFS);
|
||||
|
||||
if (name) {
|
||||
struct dentry *de = d_find_alias(inode);
|
||||
@ -257,6 +258,7 @@ enum Opt {
|
||||
Opt_err,
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
static const struct fs_parameter_spec ntfs_fs_parameters[] = {
|
||||
fsparam_u32("uid", Opt_uid),
|
||||
fsparam_u32("gid", Opt_gid),
|
||||
@ -277,9 +279,13 @@ static const struct fs_parameter_spec ntfs_fs_parameters[] = {
|
||||
fsparam_flag_no("nocase", Opt_nocase),
|
||||
{}
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
/*
|
||||
* Load nls table or if @nls is utf8 then return NULL.
|
||||
*
|
||||
* It is good idea to use here "const char *nls".
|
||||
* But load_nls accepts "char*".
|
||||
*/
|
||||
static struct nls_table *ntfs_load_nls(char *nls)
|
||||
{
|
||||
@ -436,6 +442,103 @@ static int ntfs_fs_reconfigure(struct fs_context *fc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static struct proc_dir_entry *proc_info_root;
|
||||
|
||||
/*
|
||||
* ntfs3_volinfo:
|
||||
*
|
||||
* The content of /proc/fs/ntfs3/<dev>/volinfo
|
||||
*
|
||||
* ntfs3.1
|
||||
* cluster size
|
||||
* number of clusters
|
||||
*/
|
||||
static int ntfs3_volinfo(struct seq_file *m, void *o)
|
||||
{
|
||||
struct super_block *sb = m->private;
|
||||
struct ntfs_sb_info *sbi = sb->s_fs_info;
|
||||
|
||||
seq_printf(m, "ntfs%d.%d\n%u\n%zu\n", sbi->volume.major_ver,
|
||||
sbi->volume.minor_ver, sbi->cluster_size,
|
||||
sbi->used.bitmap.nbits);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ntfs3_volinfo_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ntfs3_volinfo, pde_data(inode));
|
||||
}
|
||||
|
||||
/* read /proc/fs/ntfs3/<dev>/label */
|
||||
static int ntfs3_label_show(struct seq_file *m, void *o)
|
||||
{
|
||||
struct super_block *sb = m->private;
|
||||
struct ntfs_sb_info *sbi = sb->s_fs_info;
|
||||
|
||||
seq_printf(m, "%s\n", sbi->volume.label);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* write /proc/fs/ntfs3/<dev>/label */
|
||||
static ssize_t ntfs3_label_write(struct file *file, const char __user *buffer,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int err;
|
||||
struct super_block *sb = pde_data(file_inode(file));
|
||||
struct ntfs_sb_info *sbi = sb->s_fs_info;
|
||||
ssize_t ret = count;
|
||||
u8 *label = kmalloc(count, GFP_NOFS);
|
||||
|
||||
if (!label)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(label, buffer, ret)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
while (ret > 0 && label[ret - 1] == '\n')
|
||||
ret -= 1;
|
||||
|
||||
err = ntfs_set_label(sbi, label, ret);
|
||||
|
||||
if (err < 0) {
|
||||
ntfs_err(sb, "failed (%d) to write label", err);
|
||||
ret = err;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*ppos += count;
|
||||
ret = count;
|
||||
out:
|
||||
kfree(label);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ntfs3_label_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ntfs3_label_show, pde_data(inode));
|
||||
}
|
||||
|
||||
static const struct proc_ops ntfs3_volinfo_fops = {
|
||||
.proc_read = seq_read,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = single_release,
|
||||
.proc_open = ntfs3_volinfo_open,
|
||||
};
|
||||
|
||||
static const struct proc_ops ntfs3_label_fops = {
|
||||
.proc_read = seq_read,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = single_release,
|
||||
.proc_open = ntfs3_label_open,
|
||||
.proc_write = ntfs3_label_write,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static struct kmem_cache *ntfs_inode_cachep;
|
||||
|
||||
static struct inode *ntfs_alloc_inode(struct super_block *sb)
|
||||
@ -510,6 +613,16 @@ static void ntfs_put_super(struct super_block *sb)
|
||||
{
|
||||
struct ntfs_sb_info *sbi = sb->s_fs_info;
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
// Remove /proc/fs/ntfs3/..
|
||||
if (sbi->procdir) {
|
||||
remove_proc_entry("label", sbi->procdir);
|
||||
remove_proc_entry("volinfo", sbi->procdir);
|
||||
remove_proc_entry(sb->s_id, proc_info_root);
|
||||
sbi->procdir = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Mark rw ntfs as clear, if possible. */
|
||||
ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
|
||||
|
||||
@ -711,9 +824,16 @@ static u32 true_sectors_per_clst(const struct NTFS_BOOT *boot)
|
||||
|
||||
/*
|
||||
* ntfs_init_from_boot - Init internal info from on-disk boot sector.
|
||||
*
|
||||
* NTFS mount begins from boot - special formatted 512 bytes.
|
||||
* There are two boots: the first and the last 512 bytes of volume.
|
||||
* The content of boot is not changed during ntfs life.
|
||||
*
|
||||
* NOTE: ntfs.sys checks only first (primary) boot.
|
||||
* chkdsk checks both boots.
|
||||
*/
|
||||
static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
u64 dev_size)
|
||||
u64 dev_size, struct NTFS_BOOT **boot2)
|
||||
{
|
||||
struct ntfs_sb_info *sbi = sb->s_fs_info;
|
||||
int err;
|
||||
@ -724,6 +844,8 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
struct MFT_REC *rec;
|
||||
u16 fn, ao;
|
||||
u8 cluster_bits;
|
||||
u32 boot_off = 0;
|
||||
const char *hint = "Primary boot";
|
||||
|
||||
sbi->volume.blocks = dev_size >> PAGE_SHIFT;
|
||||
|
||||
@ -731,11 +853,12 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
if (!bh)
|
||||
return -EIO;
|
||||
|
||||
check_boot:
|
||||
err = -EINVAL;
|
||||
boot = (struct NTFS_BOOT *)bh->b_data;
|
||||
boot = (struct NTFS_BOOT *)Add2Ptr(bh->b_data, boot_off);
|
||||
|
||||
if (memcmp(boot->system_id, "NTFS ", sizeof("NTFS ") - 1)) {
|
||||
ntfs_err(sb, "Boot's signature is not NTFS.");
|
||||
ntfs_err(sb, "%s signature is not NTFS.", hint);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -748,14 +871,16 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
boot->bytes_per_sector[0];
|
||||
if (boot_sector_size < SECTOR_SIZE ||
|
||||
!is_power_of_2(boot_sector_size)) {
|
||||
ntfs_err(sb, "Invalid bytes per sector %u.", boot_sector_size);
|
||||
ntfs_err(sb, "%s: invalid bytes per sector %u.", hint,
|
||||
boot_sector_size);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* cluster size: 512, 1K, 2K, 4K, ... 2M */
|
||||
sct_per_clst = true_sectors_per_clst(boot);
|
||||
if ((int)sct_per_clst < 0 || !is_power_of_2(sct_per_clst)) {
|
||||
ntfs_err(sb, "Invalid sectors per cluster %u.", sct_per_clst);
|
||||
ntfs_err(sb, "%s: invalid sectors per cluster %u.", hint,
|
||||
sct_per_clst);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -771,20 +896,20 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
if (mlcn * sct_per_clst >= sectors || mlcn2 * sct_per_clst >= sectors) {
|
||||
ntfs_err(
|
||||
sb,
|
||||
"Start of MFT 0x%llx (0x%llx) is out of volume 0x%llx.",
|
||||
mlcn, mlcn2, sectors);
|
||||
"%s: start of MFT 0x%llx (0x%llx) is out of volume 0x%llx.",
|
||||
hint, mlcn, mlcn2, sectors);
|
||||
goto out;
|
||||
}
|
||||
|
||||
sbi->record_size = record_size =
|
||||
boot->record_size < 0 ? 1 << (-boot->record_size) :
|
||||
(u32)boot->record_size << cluster_bits;
|
||||
(u32)boot->record_size << cluster_bits;
|
||||
sbi->record_bits = blksize_bits(record_size);
|
||||
sbi->attr_size_tr = (5 * record_size >> 4); // ~320 bytes
|
||||
|
||||
/* Check MFT record size. */
|
||||
if (record_size < SECTOR_SIZE || !is_power_of_2(record_size)) {
|
||||
ntfs_err(sb, "Invalid bytes per MFT record %u (%d).",
|
||||
ntfs_err(sb, "%s: invalid bytes per MFT record %u (%d).", hint,
|
||||
record_size, boot->record_size);
|
||||
goto out;
|
||||
}
|
||||
@ -796,18 +921,18 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
}
|
||||
|
||||
sbi->index_size = boot->index_size < 0 ?
|
||||
1u << (-boot->index_size) :
|
||||
(u32)boot->index_size << cluster_bits;
|
||||
1u << (-boot->index_size) :
|
||||
(u32)boot->index_size << cluster_bits;
|
||||
|
||||
/* Check index record size. */
|
||||
if (sbi->index_size < SECTOR_SIZE || !is_power_of_2(sbi->index_size)) {
|
||||
ntfs_err(sb, "Invalid bytes per index %u(%d).", sbi->index_size,
|
||||
boot->index_size);
|
||||
ntfs_err(sb, "%s: invalid bytes per index %u(%d).", hint,
|
||||
sbi->index_size, boot->index_size);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sbi->index_size > MAXIMUM_BYTES_PER_INDEX) {
|
||||
ntfs_err(sb, "Unsupported bytes per index %u.",
|
||||
ntfs_err(sb, "%s: unsupported bytes per index %u.", hint,
|
||||
sbi->index_size);
|
||||
goto out;
|
||||
}
|
||||
@ -834,7 +959,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
|
||||
/* Compare boot's cluster and sector. */
|
||||
if (sbi->cluster_size < boot_sector_size) {
|
||||
ntfs_err(sb, "Invalid bytes per cluster (%u).",
|
||||
ntfs_err(sb, "%s: invalid bytes per cluster (%u).", hint,
|
||||
sbi->cluster_size);
|
||||
goto out;
|
||||
}
|
||||
@ -850,7 +975,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
}
|
||||
|
||||
sbi->max_bytes_per_attr =
|
||||
record_size - ALIGN(MFTRECORD_FIXUP_OFFSET_1, 8) -
|
||||
record_size - ALIGN(MFTRECORD_FIXUP_OFFSET, 8) -
|
||||
ALIGN(((record_size >> SECTOR_SHIFT) * sizeof(short)), 8) -
|
||||
ALIGN(sizeof(enum ATTR_TYPE), 8);
|
||||
|
||||
@ -892,10 +1017,10 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
|
||||
sbi->new_rec = rec;
|
||||
rec->rhdr.sign = NTFS_FILE_SIGNATURE;
|
||||
rec->rhdr.fix_off = cpu_to_le16(MFTRECORD_FIXUP_OFFSET_1);
|
||||
rec->rhdr.fix_off = cpu_to_le16(MFTRECORD_FIXUP_OFFSET);
|
||||
fn = (sbi->record_size >> SECTOR_SHIFT) + 1;
|
||||
rec->rhdr.fix_num = cpu_to_le16(fn);
|
||||
ao = ALIGN(MFTRECORD_FIXUP_OFFSET_1 + sizeof(short) * fn, 8);
|
||||
ao = ALIGN(MFTRECORD_FIXUP_OFFSET + sizeof(short) * fn, 8);
|
||||
rec->attr_off = cpu_to_le16(ao);
|
||||
rec->used = cpu_to_le32(ao + ALIGN(sizeof(enum ATTR_TYPE), 8));
|
||||
rec->total = cpu_to_le32(sbi->record_size);
|
||||
@ -930,7 +1055,34 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
|
||||
|
||||
err = 0;
|
||||
|
||||
if (bh->b_blocknr && !sb_rdonly(sb)) {
|
||||
/*
|
||||
* Alternative boot is ok but primary is not ok.
|
||||
* Do not update primary boot here 'cause it may be faked boot.
|
||||
* Let ntfs to be mounted and update boot later.
|
||||
*/
|
||||
*boot2 = kmemdup(boot, sizeof(*boot), GFP_NOFS | __GFP_NOWARN);
|
||||
}
|
||||
|
||||
out:
|
||||
if (err == -EINVAL && !bh->b_blocknr && dev_size > PAGE_SHIFT) {
|
||||
u32 block_size = min_t(u32, sector_size, PAGE_SIZE);
|
||||
u64 lbo = dev_size - sizeof(*boot);
|
||||
|
||||
/*
|
||||
* Try alternative boot (last sector)
|
||||
*/
|
||||
brelse(bh);
|
||||
|
||||
sb_set_blocksize(sb, block_size);
|
||||
bh = ntfs_bread(sb, lbo >> blksize_bits(block_size));
|
||||
if (!bh)
|
||||
return -EINVAL;
|
||||
|
||||
boot_off = lbo & (block_size - 1);
|
||||
hint = "Alternative boot";
|
||||
goto check_boot;
|
||||
}
|
||||
brelse(bh);
|
||||
|
||||
return err;
|
||||
@ -955,6 +1107,8 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
struct ATTR_DEF_ENTRY *t;
|
||||
u16 *shared;
|
||||
struct MFT_REF ref;
|
||||
bool ro = sb_rdonly(sb);
|
||||
struct NTFS_BOOT *boot2 = NULL;
|
||||
|
||||
ref.high = 0;
|
||||
|
||||
@ -985,7 +1139,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
|
||||
/* Parse boot. */
|
||||
err = ntfs_init_from_boot(sb, bdev_logical_block_size(bdev),
|
||||
bdev_nr_bytes(bdev));
|
||||
bdev_nr_bytes(bdev), &boot2);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@ -1035,6 +1189,10 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
sbi->volume.minor_ver = info->minor_ver;
|
||||
sbi->volume.flags = info->flags;
|
||||
sbi->volume.ni = ni;
|
||||
if (info->flags & VOLUME_FLAG_DIRTY) {
|
||||
sbi->volume.real_dirty = true;
|
||||
ntfs_info(sb, "It is recommened to use chkdsk.");
|
||||
}
|
||||
|
||||
/* Load $MFTMirr to estimate recs_mirr. */
|
||||
ref.low = cpu_to_le32(MFT_REC_MIRR);
|
||||
@ -1069,21 +1227,16 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
|
||||
iput(inode);
|
||||
|
||||
if (sbi->flags & NTFS_FLAGS_NEED_REPLAY) {
|
||||
if (!sb_rdonly(sb)) {
|
||||
ntfs_warn(sb,
|
||||
"failed to replay log file. Can't mount rw!");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
} else if (sbi->volume.flags & VOLUME_FLAG_DIRTY) {
|
||||
if (!sb_rdonly(sb) && !options->force) {
|
||||
ntfs_warn(
|
||||
sb,
|
||||
"volume is dirty and \"force\" flag is not set!");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if ((sbi->flags & NTFS_FLAGS_NEED_REPLAY) && !ro) {
|
||||
ntfs_warn(sb, "failed to replay log file. Can't mount rw!");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((sbi->volume.flags & VOLUME_FLAG_DIRTY) && !ro && !options->force) {
|
||||
ntfs_warn(sb, "volume is dirty and \"force\" flag is not set!");
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Load $MFT. */
|
||||
@ -1173,7 +1326,7 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
|
||||
bad_len += len;
|
||||
bad_frags += 1;
|
||||
if (sb_rdonly(sb))
|
||||
if (ro)
|
||||
continue;
|
||||
|
||||
if (wnd_set_used_safe(&sbi->used.bitmap, lcn, len, &tt) || tt) {
|
||||
@ -1368,6 +1521,44 @@ load_root:
|
||||
goto put_inode_out;
|
||||
}
|
||||
|
||||
if (boot2) {
|
||||
/*
|
||||
* Alternative boot is ok but primary is not ok.
|
||||
* Volume is recognized as NTFS. Update primary boot.
|
||||
*/
|
||||
struct buffer_head *bh0 = sb_getblk(sb, 0);
|
||||
if (bh0) {
|
||||
if (buffer_locked(bh0))
|
||||
__wait_on_buffer(bh0);
|
||||
|
||||
lock_buffer(bh0);
|
||||
memcpy(bh0->b_data, boot2, sizeof(*boot2));
|
||||
set_buffer_uptodate(bh0);
|
||||
mark_buffer_dirty(bh0);
|
||||
unlock_buffer(bh0);
|
||||
if (!sync_dirty_buffer(bh0))
|
||||
ntfs_warn(sb, "primary boot is updated");
|
||||
put_bh(bh0);
|
||||
}
|
||||
|
||||
kfree(boot2);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
/* Create /proc/fs/ntfs3/.. */
|
||||
if (proc_info_root) {
|
||||
struct proc_dir_entry *e = proc_mkdir(sb->s_id, proc_info_root);
|
||||
static_assert((S_IRUGO | S_IWUSR) == 0644);
|
||||
if (e) {
|
||||
proc_create_data("volinfo", S_IRUGO, e,
|
||||
&ntfs3_volinfo_fops, sb);
|
||||
proc_create_data("label", S_IRUGO | S_IWUSR, e,
|
||||
&ntfs3_label_fops, sb);
|
||||
sbi->procdir = e;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
put_inode_out:
|
||||
@ -1380,6 +1571,7 @@ out:
|
||||
put_mount_options(sbi->options);
|
||||
put_ntfs(sbi);
|
||||
sb->s_fs_info = NULL;
|
||||
kfree(boot2);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -1473,12 +1665,14 @@ static void ntfs_fs_free(struct fs_context *fc)
|
||||
put_mount_options(opts);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
static const struct fs_context_operations ntfs_context_ops = {
|
||||
.parse_param = ntfs_fs_parse_param,
|
||||
.get_tree = ntfs_fs_get_tree,
|
||||
.reconfigure = ntfs_fs_reconfigure,
|
||||
.free = ntfs_fs_free,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
/*
|
||||
* ntfs_init_fs_context - Initialize sbi and opts
|
||||
@ -1559,6 +1753,12 @@ static int __init init_ntfs_fs(void)
|
||||
if (IS_ENABLED(CONFIG_NTFS3_LZX_XPRESS))
|
||||
pr_info("ntfs3: Read-only LZX/Xpress compression included\n");
|
||||
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
/* Create "/proc/fs/ntfs3" */
|
||||
proc_info_root = proc_mkdir("fs/ntfs3", NULL);
|
||||
#endif
|
||||
|
||||
err = ntfs3_init_bitmap();
|
||||
if (err)
|
||||
return err;
|
||||
@ -1590,6 +1790,12 @@ static void __exit exit_ntfs_fs(void)
|
||||
kmem_cache_destroy(ntfs_inode_cachep);
|
||||
unregister_filesystem(&ntfs_fs_type);
|
||||
ntfs3_exit_bitmap();
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
if (proc_info_root)
|
||||
remove_proc_entry("fs/ntfs3", NULL);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -24,7 +24,7 @@
|
||||
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,
|
||||
ALIGN(struct_size(ea, name,
|
||||
1 + ea->name_len +
|
||||
le16_to_cpu(ea->elength)),
|
||||
4);
|
||||
@ -141,6 +141,7 @@ static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea,
|
||||
|
||||
memset(Add2Ptr(ea_p, size), 0, add_bytes);
|
||||
|
||||
err = -EINVAL;
|
||||
/* Check all attributes for consistency. */
|
||||
for (off = 0; off < size; off += ea_size) {
|
||||
const struct EA_FULL *ef = Add2Ptr(ea_p, off);
|
||||
@ -214,6 +215,9 @@ static ssize_t ntfs_list_ea(struct ntfs_inode *ni, char *buffer,
|
||||
ea = Add2Ptr(ea_all, off);
|
||||
ea_size = unpacked_ea_size(ea);
|
||||
|
||||
if (!ea->name_len)
|
||||
break;
|
||||
|
||||
if (buffer) {
|
||||
if (ret + ea->name_len + 1 > bytes_per_buffer) {
|
||||
err = -ERANGE;
|
||||
@ -524,8 +528,8 @@ out:
|
||||
/*
|
||||
* ntfs_get_acl - inode_operations::get_acl
|
||||
*/
|
||||
struct posix_acl *ntfs_get_acl(struct mnt_idmap *idmap,
|
||||
struct dentry *dentry, int type)
|
||||
struct posix_acl *ntfs_get_acl(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
int type)
|
||||
{
|
||||
struct inode *inode = d_inode(dentry);
|
||||
struct ntfs_inode *ni = ntfs_i(inode);
|
||||
@ -592,8 +596,7 @@ static noinline int ntfs_set_acl_ex(struct mnt_idmap *idmap,
|
||||
case ACL_TYPE_ACCESS:
|
||||
/* Do not change i_mode if we are in init_acl */
|
||||
if (acl && !init_acl) {
|
||||
err = posix_acl_update_mode(idmap, inode, &mode,
|
||||
&acl);
|
||||
err = posix_acl_update_mode(idmap, inode, &mode, &acl);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@ -816,10 +819,9 @@ out:
|
||||
* ntfs_setxattr - inode_operations::setxattr
|
||||
*/
|
||||
static noinline int ntfs_setxattr(const struct xattr_handler *handler,
|
||||
struct mnt_idmap *idmap,
|
||||
struct dentry *de, struct inode *inode,
|
||||
const char *name, const void *value,
|
||||
size_t size, int flags)
|
||||
struct mnt_idmap *idmap, 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);
|
||||
|
Loading…
Reference in New Issue
Block a user