Commit 86a44e90 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'ntfs3_for_5.15' of git://github.com/Paragon-Software-Group/linux-ntfs3

Pull ntfs3 fixes from Konstantin Komarov:
 "Use the new api for mounting as requested by Christoph.

  Also fixed:

   - some memory leaks and panic

   - xfstests (tested on x86_64) generic/016 generic/021 generic/022
     generic/041 generic/274 generic/423

   - some typos, wrong returned error codes, dead code, etc"

* tag 'ntfs3_for_5.15' of git://github.com/Paragon-Software-Group/linux-ntfs3: (70 commits)
  fs/ntfs3: Check for NULL pointers in ni_try_remove_attr_list
  fs/ntfs3: Refactor ntfs_read_mft
  fs/ntfs3: Refactor ni_parse_reparse
  fs/ntfs3: Refactor ntfs_create_inode
  fs/ntfs3: Refactor ntfs_readlink_hlp
  fs/ntfs3: Rework ntfs_utf16_to_nls
  fs/ntfs3: Fix memory leak if fill_super failed
  fs/ntfs3: Keep prealloc for all types of files
  fs/ntfs3: Remove unnecessary functions
  fs/ntfs3: Forbid FALLOC_FL_PUNCH_HOLE for normal files
  fs/ntfs3: Refactoring of ntfs_set_ea
  fs/ntfs3: Remove locked argument in ntfs_set_ea
  fs/ntfs3: Use available posix_acl_release instead of ntfs_posix_acl_release
  fs/ntfs3: Check for NULL if ATTR_EA_INFO is incorrect
  fs/ntfs3: Refactoring of ntfs_init_from_boot
  fs/ntfs3: Reject mount if boot's cluster size < media sector size
  fs/ntfs3: Refactoring lock in ntfs_init_acl
  fs/ntfs3: Change posix_acl_equiv_mode to posix_acl_update_mode
  fs/ntfs3: Pass flags to ntfs_set_ea in ntfs_set_acl_ex
  fs/ntfs3: Refactor ntfs_get_acl_ex for better readability
  ...
parents ec681c53 8607954c
......@@ -4,103 +4,112 @@
NTFS3
=====
Summary and Features
====================
NTFS3 is fully functional NTFS Read-Write driver. The driver works with
NTFS versions up to 3.1, normal/compressed/sparse files
and journal replaying. File system type to use on mount is 'ntfs3'.
NTFS3 is fully functional NTFS Read-Write driver. The driver works with NTFS
versions up to 3.1. File system type to use on mount is *ntfs3*.
- This driver implements NTFS read/write support for normal, sparse and
compressed files.
- Supports native journal replaying;
- Supports extended attributes
Predefined extended attributes:
- 'system.ntfs_security' gets/sets security
descriptor (SECURITY_DESCRIPTOR_RELATIVE)
- 'system.ntfs_attrib' gets/sets ntfs file/dir attributes.
Note: applied to empty files, this allows to switch type between
sparse(0x200), compressed(0x800) and normal;
- Supports native journal replaying.
- Supports NFS export of mounted NTFS volumes.
- Supports extended attributes. Predefined extended attributes:
- *system.ntfs_security* gets/sets security
Descriptor: SECURITY_DESCRIPTOR_RELATIVE
- *system.ntfs_attrib* gets/sets ntfs file/dir attributes.
Note: Applied to empty files, this allows to switch type between
sparse(0x200), compressed(0x800) and normal.
Mount Options
=============
The list below describes mount options supported by NTFS3 driver in addition to
generic ones.
generic ones. You can use every mount option with **no** option. If it is in
this table marked with no it means default is without **no**.
===============================================================================
.. flat-table::
:widths: 1 5
:fill-cells:
nls=name This option informs the driver how to interpret path
strings and translate them to Unicode and back. If
this option is not set, the default codepage will be
used (CONFIG_NLS_DEFAULT).
Examples:
'nls=utf8'
* - iocharset=name
- This option informs the driver how to interpret path strings and
translate them to Unicode and back. If this option is not set, the
default codepage will be used (CONFIG_NLS_DEFAULT).
uid=
gid=
umask= Controls the default permissions for files/directories created
after the NTFS volume is mounted.
Example: iocharset=utf8
fmask=
dmask= Instead of specifying umask which applies both to
files and directories, fmask applies only to files and
dmask only to directories.
* - uid=
- :rspan:`1`
* - gid=
nohidden Files with the Windows-specific HIDDEN (FILE_ATTRIBUTE_HIDDEN)
attribute will not be shown under Linux.
* - umask=
- Controls the default permissions for files/directories created after
the NTFS volume is mounted.
sys_immutable Files with the Windows-specific SYSTEM
(FILE_ATTRIBUTE_SYSTEM) attribute will be marked as system
immutable files.
* - dmask=
- :rspan:`1` Instead of specifying umask which applies both to files and
directories, fmask applies only to files and dmask only to directories.
* - fmask=
discard Enable support of the TRIM command for improved performance
on delete operations, which is recommended for use with the
solid-state drives (SSD).
* - noacsrules
- "No access rules" mount option sets access rights for files/folders to
777 and owner/group to root. This mount option absorbs all other
permissions.
force Forces the driver to mount partitions even if 'dirty' flag
(volume dirty) is set. Not recommended for use.
- Permissions change for files/folders will be reported as successful,
but they will remain 777.
sparse Create new files as "sparse".
- Owner/group change will be reported as successful, butthey will stay
as root.
showmeta Use this parameter to show all meta-files (System Files) on
a mounted NTFS partition.
By default, all meta-files are hidden.
* - nohidden
- Files with the Windows-specific HIDDEN (FILE_ATTRIBUTE_HIDDEN) attribute
will not be shown under Linux.
prealloc Preallocate space for files excessively when file size is
increasing on writes. Decreases fragmentation in case of
parallel write operations to different files.
* - sys_immutable
- Files with the Windows-specific SYSTEM (FILE_ATTRIBUTE_SYSTEM) attribute
will be marked as system immutable files.
no_acs_rules "No access rules" mount option sets access rights for
files/folders to 777 and owner/group to root. This mount
option absorbs all other permissions:
- permissions change for files/folders will be reported
as successful, but they will remain 777;
- owner/group change will be reported as successful, but
they will stay as root
* - discard
- Enable support of the TRIM command for improved performance on delete
operations, which is recommended for use with the solid-state drives
(SSD).
acl Support POSIX ACLs (Access Control Lists). Effective if
supported by Kernel. Not to be confused with NTFS ACLs.
The option specified as acl enables support for POSIX ACLs.
* - force
- Forces the driver to mount partitions even if volume is marked dirty.
Not recommended for use.
noatime All files and directories will not update their last access
time attribute if a partition is mounted with this parameter.
This option can speed up file system operation.
* - sparse
- Create new files as sparse.
===============================================================================
* - showmeta
- Use this parameter to show all meta-files (System Files) on a mounted
NTFS partition. By default, all meta-files are hidden.
ToDo list
=========
* - prealloc
- Preallocate space for files excessively when file size is increasing on
writes. Decreases fragmentation in case of parallel write operations to
different files.
- Full journaling support (currently journal replaying is supported) over JBD.
* - acl
- Support POSIX ACLs (Access Control Lists). Effective if supported by
Kernel. Not to be confused with NTFS ACLs. The option specified as acl
enables support for POSIX ACLs.
Todo list
=========
- Full journaling support over JBD. Currently journal replaying is supported
which is not necessarily as effectice as JBD would be.
References
==========
https://www.paragon-software.com/home/ntfs-linux-professional/
- Commercial version of the NTFS driver for Linux.
- Commercial version of the NTFS driver for Linux.
https://www.paragon-software.com/home/ntfs-linux-professional/
almaz.alexandrovich@paragon-software.com
- Direct e-mail address for feedback and requests on the NTFS3 implementation.
- Direct e-mail address for feedback and requests on the NTFS3 implementation.
almaz.alexandrovich@paragon-software.com
......@@ -6,13 +6,9 @@
* TODO: Merge attr_set_size/attr_data_get_block/attr_allocate_frame?
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/hash.h>
#include <linux/nls.h>
#include <linux/ratelimit.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include "debug.h"
#include "ntfs.h"
......@@ -291,7 +287,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr,
if (!rsize) {
/* Empty resident -> Non empty nonresident. */
} else if (!is_data) {
err = ntfs_sb_write_run(sbi, run, 0, data, rsize);
err = ntfs_sb_write_run(sbi, run, 0, data, rsize, 0);
if (err)
goto out2;
} else if (!page) {
......@@ -451,11 +447,8 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
again_1:
align = sbi->cluster_size;
if (is_ext) {
if (is_ext)
align <<= attr_b->nres.c_unit;
if (is_attr_sparsed(attr_b))
keep_prealloc = false;
}
old_valid = le64_to_cpu(attr_b->nres.valid_size);
old_size = le64_to_cpu(attr_b->nres.data_size);
......@@ -465,9 +458,6 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
new_alloc = (new_size + align - 1) & ~(u64)(align - 1);
new_alen = new_alloc >> cluster_bits;
if (keep_prealloc && is_ext)
keep_prealloc = false;
if (keep_prealloc && new_size < old_size) {
attr_b->nres.data_size = cpu_to_le64(new_size);
mi_b->dirty = true;
......@@ -529,7 +519,7 @@ int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
} else if (pre_alloc == -1) {
pre_alloc = 0;
if (type == ATTR_DATA && !name_len &&
sbi->options.prealloc) {
sbi->options->prealloc) {
CLST new_alen2 = bytes_to_cluster(
sbi, get_pre_allocated(new_size));
pre_alloc = new_alen2 - new_alen;
......@@ -1966,7 +1956,7 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size)
return 0;
from = vbo;
to = (vbo + bytes) < data_size ? (vbo + bytes) : data_size;
to = min_t(u64, vbo + bytes, data_size);
memset(Add2Ptr(resident_data(attr_b), from), 0, to - from);
return 0;
}
......
......@@ -5,10 +5,7 @@
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/nls.h>
#include "debug.h"
#include "ntfs.h"
......@@ -336,7 +333,7 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name,
if (attr && attr->non_res) {
err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le,
al->size);
al->size, 0);
if (err)
return err;
al->dirty = false;
......@@ -423,7 +420,7 @@ bool al_delete_le(struct ntfs_inode *ni, enum ATTR_TYPE type, CLST vcn,
return true;
}
int al_update(struct ntfs_inode *ni)
int al_update(struct ntfs_inode *ni, int sync)
{
int err;
struct ATTRIB *attr;
......@@ -445,7 +442,7 @@ int al_update(struct ntfs_inode *ni)
memcpy(resident_data(attr), al->le, al->size);
} else {
err = ntfs_sb_write_run(ni->mi.sbi, &al->run, 0, al->le,
al->size);
al->size, sync);
if (err)
goto out;
......
......@@ -5,13 +5,8 @@
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/nls.h>
#include <linux/types.h>
#include "debug.h"
#include "ntfs.h"
#include "ntfs_fs.h"
#define BITS_IN_SIZE_T (sizeof(size_t) * 8)
......@@ -124,8 +119,7 @@ bool are_bits_set(const ulong *lmap, size_t bit, size_t nbits)
pos = nbits & 7;
if (pos) {
u8 mask = fill_mask[pos];
mask = fill_mask[pos];
if ((*map & mask) != mask)
return false;
}
......
......@@ -10,12 +10,10 @@
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/nls.h>
#include <linux/kernel.h>
#include "debug.h"
#include "ntfs.h"
#include "ntfs_fs.h"
......@@ -435,7 +433,7 @@ static void wnd_remove_free_ext(struct wnd_bitmap *wnd, size_t bit, size_t len)
;
} else {
n3 = rb_next(&e->count.node);
max_new_len = len > new_len ? len : new_len;
max_new_len = max(len, new_len);
if (!n3) {
wnd->extent_max = max_new_len;
} else {
......@@ -731,7 +729,7 @@ int wnd_set_free(struct wnd_bitmap *wnd, size_t bit, size_t bits)
wbits = wnd->bits_last;
tail = wbits - wbit;
op = tail < bits ? tail : bits;
op = min_t(u32, tail, bits);
bh = wnd_map(wnd, iw);
if (IS_ERR(bh)) {
......@@ -784,7 +782,7 @@ int wnd_set_used(struct wnd_bitmap *wnd, size_t bit, size_t bits)
wbits = wnd->bits_last;
tail = wbits - wbit;
op = tail < bits ? tail : bits;
op = min_t(u32, tail, bits);
bh = wnd_map(wnd, iw);
if (IS_ERR(bh)) {
......@@ -834,7 +832,7 @@ static bool wnd_is_free_hlp(struct wnd_bitmap *wnd, size_t bit, size_t bits)
wbits = wnd->bits_last;
tail = wbits - wbit;
op = tail < bits ? tail : bits;
op = min_t(u32, tail, bits);
if (wbits != wnd->free_bits[iw]) {
bool ret;
......@@ -926,7 +924,7 @@ bool wnd_is_used(struct wnd_bitmap *wnd, size_t bit, size_t bits)
wbits = wnd->bits_last;
tail = wbits - wbit;
op = tail < bits ? tail : bits;
op = min_t(u32, tail, bits);
if (wnd->free_bits[iw]) {
bool ret;
......
......@@ -11,6 +11,9 @@
#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)))
......
......@@ -7,10 +7,7 @@
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/iversion.h>
#include <linux/nls.h>
#include "debug.h"
......@@ -18,30 +15,27 @@
#include "ntfs_fs.h"
/* Convert little endian UTF-16 to NLS string. */
int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const struct le_str *uni,
int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const __le16 *name, u32 len,
u8 *buf, int buf_len)
{
int ret, uni_len, warn;
const __le16 *ip;
int ret, warn;
u8 *op;
struct nls_table *nls = sbi->options.nls;
struct nls_table *nls = sbi->options->nls;
static_assert(sizeof(wchar_t) == sizeof(__le16));
if (!nls) {
/* UTF-16 -> UTF-8 */
ret = utf16s_to_utf8s((wchar_t *)uni->name, uni->len,
UTF16_LITTLE_ENDIAN, buf, buf_len);
ret = utf16s_to_utf8s(name, len, UTF16_LITTLE_ENDIAN, buf,
buf_len);
buf[ret] = '\0';
return ret;
}
ip = uni->name;
op = buf;
uni_len = uni->len;
warn = 0;
while (uni_len--) {
while (len--) {
u16 ec;
int charlen;
char dump[5];
......@@ -52,7 +46,7 @@ int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const struct le_str *uni,
break;
}
ec = le16_to_cpu(*ip++);
ec = le16_to_cpu(*name++);
charlen = nls->uni2char(ec, op, buf_len);
if (charlen > 0) {
......@@ -186,7 +180,7 @@ int ntfs_nls_to_utf16(struct ntfs_sb_info *sbi, const u8 *name, u32 name_len,
{
int ret, slen;
const u8 *end;
struct nls_table *nls = sbi->options.nls;
struct nls_table *nls = sbi->options->nls;
u16 *uname = uni->name;
static_assert(sizeof(wchar_t) == sizeof(u16));
......@@ -301,14 +295,14 @@ static inline int ntfs_filldir(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
return 0;
/* Skip meta files. Unless option to show metafiles is set. */
if (!sbi->options.showmeta && ntfs_is_meta_file(sbi, ino))
if (!sbi->options->showmeta && ntfs_is_meta_file(sbi, ino))
return 0;
if (sbi->options.nohidden && (fname->dup.fa & FILE_ATTRIBUTE_HIDDEN))
if (sbi->options->nohidden && (fname->dup.fa & FILE_ATTRIBUTE_HIDDEN))
return 0;
name_len = ntfs_utf16_to_nls(sbi, (struct le_str *)&fname->name_len,
name, PATH_MAX);
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);
......
......@@ -12,7 +12,6 @@
#include <linux/compat.h>
#include <linux/falloc.h>
#include <linux/fiemap.h>
#include <linux/nls.h>
#include "debug.h"
#include "ntfs.h"
......@@ -588,8 +587,11 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
truncate_pagecache(inode, vbo_down);
if (!is_sparsed(ni) && !is_compressed(ni)) {
/* Normal file. */
err = ntfs_zero_range(inode, vbo, end);
/*
* Normal file, can't make hole.
* TODO: Try to find way to save info about hole.
*/
err = -EOPNOTSUPP;
goto out;
}
......@@ -737,7 +739,7 @@ int ntfs3_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
umode_t mode = inode->i_mode;
int err;
if (sbi->options.no_acs_rules) {
if (sbi->options->noacsrules) {
/* "No access rules" - Force any changes of time etc. */
attr->ia_valid |= ATTR_FORCE;
/* and disable for editing some attributes. */
......@@ -1185,7 +1187,7 @@ static int ntfs_file_release(struct inode *inode, struct file *file)
int err = 0;
/* If we are last writer on the inode, drop the block reservation. */
if (sbi->options.prealloc && ((file->f_mode & FMODE_WRITE) &&
if (sbi->options->prealloc && ((file->f_mode & FMODE_WRITE) &&
atomic_read(&inode->i_writecount) == 1)) {
ni_lock(ni);
down_write(&ni->file.run_lock);
......
......@@ -5,11 +5,8 @@
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fiemap.h>
#include <linux/fs.h>
#include <linux/nls.h>
#include <linux/vmalloc.h>
#include "debug.h"
......@@ -708,18 +705,35 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni)
continue;
mi = ni_find_mi(ni, ino_get(&le->ref));
if (!mi) {
/* Should never happened, 'cause already checked. */
goto bad;
}
attr = mi_find_attr(mi, NULL, le->type, le_name(le),
le->name_len, &le->id);
if (!attr) {
/* Should never happened, 'cause already checked. */
goto bad;
}
asize = le32_to_cpu(attr->size);
/* Insert into primary record. */
attr_ins = mi_insert_attr(&ni->mi, le->type, le_name(le),
le->name_len, asize,
le16_to_cpu(attr->name_off));
id = attr_ins->id;
if (!attr_ins) {
/*
* Internal error.
* Either no space in primary record (already checked).
* Either tried to insert another
* non indexed attribute (logic error).
*/
goto bad;
}
/* Copy all except id. */
id = attr_ins->id;
memcpy(attr_ins, attr, asize);
attr_ins->id = id;
......@@ -735,6 +749,10 @@ static int ni_try_remove_attr_list(struct ntfs_inode *ni)
ni->attr_list.dirty = false;
return 0;
bad:
ntfs_inode_err(&ni->vfs_inode, "Internal error");
make_bad_inode(&ni->vfs_inode);
return -EINVAL;
}
/*
......@@ -956,6 +974,13 @@ static int ni_ins_attr_ext(struct ntfs_inode *ni, struct ATTR_LIST_ENTRY *le,
continue;
}
/*
* Do not try to insert this attribute
* if there is no room in record.
*/
if (le32_to_cpu(mi->mrec->used) + asize > sbi->record_size)
continue;
/* Try to insert attribute into this subrecord. */
attr = ni_ins_new_attr(ni, mi, le, type, name, name_len, asize,
name_off, svcn, ins_le);
......@@ -1451,7 +1476,7 @@ int ni_insert_resident(struct ntfs_inode *ni, u32 data_size,
attr->res.flags = RESIDENT_FLAG_INDEXED;
/* is_attr_indexed(attr)) == true */
le16_add_cpu(&ni->mi.mrec->hard_links, +1);
le16_add_cpu(&ni->mi.mrec->hard_links, 1);
ni->mi.dirty = true;
}
attr->res.res = 0;
......@@ -1606,7 +1631,7 @@ struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type,
*le = NULL;
if (FILE_NAME_POSIX == name_type)
if (name_type == FILE_NAME_POSIX)
return NULL;
/* Enumerate all names. */
......@@ -1706,18 +1731,16 @@ int ni_new_attr_flags(struct ntfs_inode *ni, enum FILE_ATTRIBUTE new_fa)
/*
* ni_parse_reparse
*
* Buffer is at least 24 bytes.
* buffer - memory for reparse buffer header
*/
enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,
void *buffer)
struct REPARSE_DATA_BUFFER *buffer)
{
const struct REPARSE_DATA_BUFFER *rp = NULL;
u8 bits;
u16 len;
typeof(rp->CompressReparseBuffer) *cmpr;
static_assert(sizeof(struct REPARSE_DATA_BUFFER) <= 24);
/* Try to estimate reparse point. */
if (!attr->non_res) {
rp = resident_data_ex(attr, sizeof(struct REPARSE_DATA_BUFFER));
......@@ -1803,6 +1826,9 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,
return REPARSE_NONE;
}
if (buffer != rp)
memcpy(buffer, rp, sizeof(struct REPARSE_DATA_BUFFER));
/* Looks like normal symlink. */
return REPARSE_LINK;
}
......@@ -2906,10 +2932,9 @@ bool ni_remove_name_undo(struct ntfs_inode *dir_ni, struct ntfs_inode *ni,
memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), de + 1, de_key_size);
mi_get_ref(&ni->mi, &de->ref);
if (indx_insert_entry(&dir_ni->dir, dir_ni, de, sbi, NULL, 1)) {
if (indx_insert_entry(&dir_ni->dir, dir_ni, de, sbi, NULL, 1))
return false;
}
}
return true;
}
......@@ -3077,6 +3102,8 @@ static bool ni_update_parent(struct ntfs_inode *ni, struct NTFS_DUP_INFO *dup,
const struct EA_INFO *info;
info = resident_data_ex(attr, sizeof(struct EA_INFO));
/* If ATTR_EA_INFO exists 'info' can't be NULL. */
if (info)
dup->ea_size = info->size_pack;
}
}
......@@ -3205,7 +3232,7 @@ int ni_write_inode(struct inode *inode, int sync, const char *hint)
goto out;
}
err = al_update(ni);
err = al_update(ni, sync);
if (err)
goto out;
}
......
......@@ -6,12 +6,8 @@
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/hash.h>
#include <linux/nls.h>
#include <linux/random.h>
#include <linux/ratelimit.h>
#include <linux/slab.h>
#include "debug.h"
......@@ -2219,7 +2215,7 @@ static int last_log_lsn(struct ntfs_log *log)
err = ntfs_sb_write_run(log->ni->mi.sbi,
&log->ni->file.run, off, page,
log->page_size);
log->page_size, 0);
if (err)
goto out;
......@@ -3710,7 +3706,7 @@ static int do_action(struct ntfs_log *log, struct OPEN_ATTR_ENRTY *oe,
if (a_dirty) {
attr = oa->attr;
err = ntfs_sb_write_run(sbi, oa->run1, vbo, buffer_le, bytes);
err = ntfs_sb_write_run(sbi, oa->run1, vbo, buffer_le, bytes, 0);
if (err)
goto out;
}
......@@ -5152,10 +5148,10 @@ int log_replay(struct ntfs_inode *ni, bool *initialized)
ntfs_fix_pre_write(&rh->rhdr, log->page_size);
err = ntfs_sb_write_run(sbi, &ni->file.run, 0, rh, log->page_size);
err = ntfs_sb_write_run(sbi, &ni->file.run, 0, rh, log->page_size, 0);
if (!err)
err = ntfs_sb_write_run(sbi, &log->ni->file.run, log->page_size,
rh, log->page_size);
rh, log->page_size, 0);
kfree(rh);
if (err)
......
......@@ -8,7 +8,7 @@
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/nls.h>
#include <linux/kernel.h>
#include "debug.h"
#include "ntfs.h"
......@@ -358,7 +358,7 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len,
enum ALLOCATE_OPT opt)
{
int err;
CLST alen = 0;
CLST alen;
struct super_block *sb = sbi->sb;
size_t alcn, zlen, zeroes, zlcn, zlen2, ztrim, new_zlen;
struct wnd_bitmap *wnd = &sbi->used.bitmap;
......@@ -370,27 +370,28 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len,
if (!zlen) {
err = ntfs_refresh_zone(sbi);
if (err)
goto out;
goto up_write;
zlen = wnd_zone_len(wnd);
}
if (!zlen) {
ntfs_err(sbi->sb, "no free space to extend mft");
goto out;
err = -ENOSPC;
goto up_write;
}
lcn = wnd_zone_bit(wnd);
alen = zlen > len ? len : zlen;
alen = min_t(CLST, len, zlen);
wnd_zone_set(wnd, lcn + alen, zlen - alen);
err = wnd_set_used(wnd, lcn, alen);
if (err) {
up_write(&wnd->rw_lock);
return err;
}
if (err)
goto up_write;
alcn = lcn;
goto out;
goto space_found;
}
/*
* 'Cause cluster 0 is always used this value means that we should use
......@@ -404,36 +405,35 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len,
alen = wnd_find(wnd, len, lcn, BITMAP_FIND_MARK_AS_USED, &alcn);
if (alen)
goto out;
goto space_found;
/* Try to use clusters from MftZone. */
zlen = wnd_zone_len(wnd);
zeroes = wnd_zeroes(wnd);
/* Check too big request */
if (len > zeroes + zlen || zlen <= NTFS_MIN_MFT_ZONE)
goto out;
if (len > zeroes + zlen || zlen <= NTFS_MIN_MFT_ZONE) {
err = -ENOSPC;
goto up_write;
}
/* How many clusters to cat from zone. */
zlcn = wnd_zone_bit(wnd);
zlen2 = zlen >> 1;
ztrim = len > zlen ? zlen : (len > zlen2 ? len : zlen2);
new_zlen = zlen - ztrim;
if (new_zlen < NTFS_MIN_MFT_ZONE) {
new_zlen = NTFS_MIN_MFT_ZONE;
if (new_zlen > zlen)
new_zlen = zlen;
}
ztrim = clamp_val(len, zlen2, zlen);
new_zlen = max_t(size_t, zlen - ztrim, NTFS_MIN_MFT_ZONE);
wnd_zone_set(wnd, zlcn, new_zlen);
/* Allocate continues clusters. */
alen = wnd_find(wnd, len, 0,
BITMAP_FIND_MARK_AS_USED | BITMAP_FIND_FULL, &alcn);
if (!alen) {
err = -ENOSPC;
goto up_write;
}
out:
if (alen) {
space_found:
err = 0;
*new_len = alen;
*new_lcn = alcn;
......@@ -443,10 +443,7 @@ int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len,
/* Set hint for next requests. */
if (!(opt & ALLOCATE_MFT))
sbi->used.next_free_lcn = alcn + alen;
} else {
err = -ENOSPC;
}
up_write:
up_write(&wnd->rw_lock);
return err;
}
......@@ -1080,7 +1077,7 @@ int ntfs_sb_write(struct super_block *sb, u64 lbo, size_t bytes,
}
int ntfs_sb_write_run(struct ntfs_sb_info *sbi, const struct runs_tree *run,
u64 vbo, const void *buf, size_t bytes)
u64 vbo, const void *buf, size_t bytes, int sync)
{
struct super_block *sb = sbi->sb;
u8 cluster_bits = sbi->cluster_bits;
......@@ -1099,8 +1096,8 @@ int ntfs_sb_write_run(struct ntfs_sb_info *sbi, const struct runs_tree *run,
len = ((u64)clen << cluster_bits) - off;
for (;;) {
u32 op = len < bytes ? len : bytes;
int err = ntfs_sb_write(sb, lbo, op, buf, 0);
u32 op = min_t(u64, len, bytes);
int err = ntfs_sb_write(sb, lbo, op, buf, sync);
if (err)
return err;
......@@ -1300,7 +1297,7 @@ int ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo,
nb->off = off = lbo & (blocksize - 1);
for (;;) {
u32 len32 = len < bytes ? len : bytes;
u32 len32 = min_t(u64, len, bytes);
sector_t block = lbo >> sb->s_blocksize_bits;
do {
......@@ -2175,7 +2172,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi,
/* Write main SDS bucket. */
err = ntfs_sb_write_run(sbi, &ni->file.run, sbi->security.next_off,
d_security, aligned_sec_size);
d_security, aligned_sec_size, 0);
if (err)
goto out;
......@@ -2193,7 +2190,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi,
/* Write copy SDS bucket. */
err = ntfs_sb_write_run(sbi, &ni->file.run, mirr_off, d_security,
aligned_sec_size);
aligned_sec_size, 0);
if (err)
goto out;
......
......@@ -8,7 +8,7 @@
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/nls.h>
#include <linux/kernel.h>
#include "debug.h"
#include "ntfs.h"
......@@ -671,138 +671,74 @@ static struct NTFS_DE *hdr_find_e(const struct ntfs_index *indx,
const struct INDEX_HDR *hdr, const void *key,
size_t key_len, const void *ctx, int *diff)
{
struct NTFS_DE *e;
struct NTFS_DE *e, *found = NULL;
NTFS_CMP_FUNC cmp = indx->cmp;
int min_idx = 0, mid_idx, max_idx = 0;
int diff2;
int table_size = 8;
u32 e_size, e_key_len;
u32 end = le32_to_cpu(hdr->used);
u32 off = le32_to_cpu(hdr->de_off);
u16 offs[128];
#ifdef NTFS3_INDEX_BINARY_SEARCH
int max_idx = 0, fnd, min_idx;
int nslots = 64;
u16 *offs;
if (end > 0x10000)
goto next;
offs = kmalloc(sizeof(u16) * nslots, GFP_NOFS);
if (!offs)
goto next;
fill_table:
if (off + sizeof(struct NTFS_DE) > end)
return NULL;
/* Use binary search algorithm. */
next1:
if (off + sizeof(struct NTFS_DE) > end) {
e = NULL;
goto out1;
}
e = Add2Ptr(hdr, off);
e_size = le16_to_cpu(e->size);
if (e_size < sizeof(struct NTFS_DE) || off + e_size > end) {
e = NULL;
goto out1;
}
if (max_idx >= nslots) {
u16 *ptr;
int new_slots = ALIGN(2 * nslots, 8);
ptr = kmalloc(sizeof(u16) * new_slots, GFP_NOFS);
if (ptr)
memcpy(ptr, offs, sizeof(u16) * max_idx);
kfree(offs);
offs = ptr;
nslots = new_slots;
if (!ptr)
goto next;
}
/* Store entry table. */
offs[max_idx] = off;
if (e_size < sizeof(struct NTFS_DE) || off + e_size > end)
return NULL;
if (!de_is_last(e)) {
offs[max_idx] = off;
off += e_size;
max_idx += 1;
goto next1;
}
/*
* Table of pointers is created.
* Use binary search to find entry that is <= to the search value.
*/
fnd = -1;
min_idx = 0;
while (min_idx <= max_idx) {
int mid_idx = min_idx + ((max_idx - min_idx) >> 1);
int diff2;
max_idx++;
if (max_idx < table_size)
goto fill_table;
e = Add2Ptr(hdr, offs[mid_idx]);
max_idx--;
}
binary_search:
e_key_len = le16_to_cpu(e->key_size);
diff2 = (*cmp)(key, key_len, e + 1, e_key_len, ctx);
if (diff2 > 0) {
if (found) {
min_idx = mid_idx + 1;
} else {
if (de_is_last(e))
return NULL;
if (!diff2) {
*diff = 0;
goto out1;
max_idx = 0;
table_size = min(table_size * 2,
(int)ARRAY_SIZE(offs));
goto fill_table;
}
if (diff2 < 0) {
} else if (diff2 < 0) {
if (found)
max_idx = mid_idx - 1;
fnd = mid_idx;
if (!fnd)
break;
} else {
min_idx = mid_idx + 1;
}
}
else
max_idx--;
if (fnd == -1) {
e = NULL;
goto out1;
found = e;
} else {
*diff = 0;
return e;
}
if (min_idx > max_idx) {
*diff = -1;
e = Add2Ptr(hdr, offs[fnd]);
out1:
kfree(offs);
return e;
#endif
next:
/*
* Entries index are sorted.
* Enumerate all entries until we find entry
* that is <= to the search value.
*/
if (off + sizeof(struct NTFS_DE) > end)
return NULL;
e = Add2Ptr(hdr, off);
e_size = le16_to_cpu(e->size);
if (e_size < sizeof(struct NTFS_DE) || off + e_size > end)
return NULL;
off += e_size;
e_key_len = le16_to_cpu(e->key_size);
*diff = (*cmp)(key, key_len, e + 1, e_key_len, ctx);
if (!*diff)
return e;
return found;
}
if (*diff <= 0)
return e;
mid_idx = (min_idx + max_idx) >> 1;
e = Add2Ptr(hdr, offs[mid_idx]);
if (de_is_last(e)) {
*diff = 1;
return e;
}
goto next;
goto binary_search;
}
/*
......@@ -1136,9 +1072,7 @@ int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni,
if (!e)
return -EINVAL;
if (fnd)
fnd->root_de = e;
err = 0;
for (;;) {
......@@ -1401,7 +1335,7 @@ int indx_find_raw(struct ntfs_index *indx, struct ntfs_inode *ni,
static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni,
CLST *vbn)
{
int err = -ENOMEM;
int err;
struct ntfs_sb_info *sbi = ni->mi.sbi;
struct ATTRIB *bitmap;
struct ATTRIB *alloc;
......
......@@ -5,10 +5,8 @@
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/iversion.h>
#include <linux/mpage.h>
#include <linux/namei.h>
#include <linux/nls.h>
......@@ -49,8 +47,8 @@ static struct inode *ntfs_read_mft(struct inode *inode,
inode->i_op = NULL;
/* Setup 'uid' and 'gid' */
inode->i_uid = sbi->options.fs_uid;
inode->i_gid = sbi->options.fs_gid;
inode->i_uid = sbi->options->fs_uid;
inode->i_gid = sbi->options->fs_gid;
err = mi_init(&ni->mi, sbi, ino);
if (err)
......@@ -224,12 +222,9 @@ static struct inode *ntfs_read_mft(struct inode *inode,
if (!attr->non_res) {
ni->i_valid = inode->i_size = rsize;
inode_set_bytes(inode, rsize);
t32 = asize;
} else {
t32 = le16_to_cpu(attr->nres.run_off);
}
mode = S_IFREG | (0777 & sbi->options.fs_fmask_inv);
mode = S_IFREG | (0777 & sbi->options->fs_fmask_inv);
if (!attr->non_res) {
ni->ni_flags |= NI_FLAG_RESIDENT;
......@@ -272,7 +267,7 @@ static struct inode *ntfs_read_mft(struct inode *inode,
goto out;
mode = sb->s_root
? (S_IFDIR | (0777 & sbi->options.fs_dmask_inv))
? (S_IFDIR | (0777 & sbi->options->fs_dmask_inv))
: (S_IFDIR | 0777);
goto next_attr;
......@@ -315,17 +310,14 @@ static struct inode *ntfs_read_mft(struct inode *inode,
rp_fa = ni_parse_reparse(ni, attr, &rp);
switch (rp_fa) {
case REPARSE_LINK:
if (!attr->non_res) {
inode->i_size = rsize;
inode_set_bytes(inode, rsize);
t32 = asize;
} else {
inode->i_size =
le64_to_cpu(attr->nres.data_size);
t32 = le16_to_cpu(attr->nres.run_off);
}
/*
* Normal symlink.
* Assume one unicode symbol == one utf8.
*/
inode->i_size = le16_to_cpu(rp.SymbolicLinkReparseBuffer
.PrintNameLength) /
sizeof(u16);
/* Looks like normal symlink. */
ni->i_valid = inode->i_size;
/* Clear directory bit. */
......@@ -422,7 +414,7 @@ static struct inode *ntfs_read_mft(struct inode *inode,
ni->std_fa &= ~FILE_ATTRIBUTE_DIRECTORY;
inode->i_op = &ntfs_link_inode_operations;
inode->i_fop = NULL;
inode_nohighmem(inode); // ??
inode_nohighmem(inode);
} else if (S_ISREG(mode)) {
ni->std_fa &= ~FILE_ATTRIBUTE_DIRECTORY;
inode->i_op = &ntfs_file_inode_operations;
......@@ -443,7 +435,7 @@ static struct inode *ntfs_read_mft(struct inode *inode,
goto out;
}
if ((sbi->options.sys_immutable &&
if ((sbi->options->sys_immutable &&
(std5->fa & FILE_ATTRIBUTE_SYSTEM)) &&
!S_ISFIFO(mode) && !S_ISSOCK(mode) && !S_ISLNK(mode)) {
inode->i_flags |= S_IMMUTABLE;
......@@ -1200,9 +1192,13 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
struct REPARSE_DATA_BUFFER *rp = NULL;
bool rp_inserted = false;
ni_lock_dir(dir_ni);
dir_root = indx_get_root(&dir_ni->dir, dir_ni, NULL, NULL);
if (!dir_root)
return ERR_PTR(-EINVAL);
if (!dir_root) {
err = -EINVAL;
goto out1;
}
if (S_ISDIR(mode)) {
/* Use parent's directory attributes. */
......@@ -1244,7 +1240,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
* }
*/
} else if (S_ISREG(mode)) {
if (sbi->options.sparse) {
if (sbi->options->sparse) {
/* Sparsed regular file, cause option 'sparse'. */
fa = FILE_ATTRIBUTE_SPARSE_FILE |
FILE_ATTRIBUTE_ARCHIVE;
......@@ -1486,7 +1482,10 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
asize = ALIGN(SIZEOF_RESIDENT + nsize, 8);
t16 = PtrOffset(rec, attr);
/* 0x78 - the size of EA + EAINFO to store WSL */
/*
* Below function 'ntfs_save_wsl_perm' requires 0x78 bytes.
* It is good idea to keep extened attributes resident.
*/
if (asize + t16 + 0x78 + 8 > sbi->record_size) {
CLST alen;
CLST clst = bytes_to_cluster(sbi, nsize);
......@@ -1521,14 +1520,14 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
}
asize = SIZEOF_NONRESIDENT + ALIGN(err, 8);
inode->i_size = nsize;
} else {
attr->res.data_off = SIZEOF_RESIDENT_LE;
attr->res.data_size = cpu_to_le32(nsize);
memcpy(Add2Ptr(attr, SIZEOF_RESIDENT), rp, nsize);
inode->i_size = nsize;
nsize = 0;
}
/* Size of symlink equals the length of input string. */
inode->i_size = size;
attr->size = cpu_to_le32(asize);
......@@ -1551,6 +1550,9 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
if (err)
goto out6;
/* Unlock parent directory before ntfs_init_acl. */
ni_unlock(dir_ni);
inode->i_generation = le16_to_cpu(rec->seq);
dir->i_mtime = dir->i_ctime = inode->i_atime;
......@@ -1562,6 +1564,8 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
inode->i_op = &ntfs_link_inode_operations;
inode->i_fop = NULL;
inode->i_mapping->a_ops = &ntfs_aops;
inode->i_size = size;
inode_nohighmem(inode);
} else if (S_ISREG(mode)) {
inode->i_op = &ntfs_file_inode_operations;
inode->i_fop = &ntfs_file_operations;
......@@ -1577,7 +1581,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
if (!S_ISLNK(mode) && (sb->s_flags & SB_POSIXACL)) {
err = ntfs_init_acl(mnt_userns, inode, dir);
if (err)
goto out6;
goto out7;
} else
#endif
{
......@@ -1586,7 +1590,7 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
/* Write non resident data. */
if (nsize) {
err = ntfs_sb_write_run(sbi, &ni->file.run, 0, rp, nsize);
err = ntfs_sb_write_run(sbi, &ni->file.run, 0, rp, nsize, 0);
if (err)
goto out7;
}
......@@ -1607,8 +1611,10 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
out7:
/* Undo 'indx_insert_entry'. */
ni_lock_dir(dir_ni);
indx_delete_entry(&dir_ni->dir, dir_ni, new_de + 1,
le16_to_cpu(new_de->key_size), sbi);
/* ni_unlock(dir_ni); will be called later. */
out6:
if (rp_inserted)
ntfs_remove_reparse(sbi, IO_REPARSE_TAG_SYMLINK, &new_de->ref);
......@@ -1632,8 +1638,10 @@ struct inode *ntfs_create_inode(struct user_namespace *mnt_userns,
kfree(rp);
out1:
if (err)
if (err) {
ni_unlock(dir_ni);
return ERR_PTR(err);
}
unlock_new_inode(inode);
......@@ -1754,15 +1762,15 @@ void ntfs_evict_inode(struct inode *inode)
static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer,
int buflen)
{
int i, err = 0;
int i, err = -EINVAL;
struct ntfs_inode *ni = ntfs_i(inode);
struct super_block *sb = inode->i_sb;
struct ntfs_sb_info *sbi = sb->s_fs_info;
u64 i_size = inode->i_size;
u16 nlen = 0;
u64 size;
u16 ulen = 0;
void *to_free = NULL;
struct REPARSE_DATA_BUFFER *rp;
struct le_str *uni;
const __le16 *uname;
struct ATTRIB *attr;
/* Reparse data present. Try to parse it. */
......@@ -1771,68 +1779,64 @@ static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer,
*buffer = 0;
/* Read into temporal buffer. */
if (i_size > sbi->reparse.max_size || i_size <= sizeof(u32)) {
err = -EINVAL;
goto out;
}
attr = ni_find_attr(ni, NULL, NULL, ATTR_REPARSE, NULL, 0, NULL, NULL);
if (!attr) {
err = -EINVAL;
if (!attr)
goto out;
}
if (!attr->non_res) {
rp = resident_data_ex(attr, i_size);
if (!rp) {
err = -EINVAL;
rp = resident_data_ex(attr, sizeof(struct REPARSE_DATA_BUFFER));
if (!rp)
goto out;
}
size = le32_to_cpu(attr->res.data_size);
} else {
rp = kmalloc(i_size, GFP_NOFS);
size = le64_to_cpu(attr->nres.data_size);
rp = NULL;
}
if (size > sbi->reparse.max_size || size <= sizeof(u32))
goto out;
if (!rp) {
rp = kmalloc(size, GFP_NOFS);
if (!rp) {
err = -ENOMEM;
goto out;
}
to_free = rp;
err = ntfs_read_run_nb(sbi, &ni->file.run, 0, rp, i_size, NULL);
/* Read into temporal buffer. */
err = ntfs_read_run_nb(sbi, &ni->file.run, 0, rp, size, NULL);
if (err)
goto out;
}
err = -EINVAL;
/* Microsoft Tag. */
switch (rp->ReparseTag) {
case IO_REPARSE_TAG_MOUNT_POINT:
/* Mount points and junctions. */
/* Can we use 'Rp->MountPointReparseBuffer.PrintNameLength'? */
if (i_size <= offsetof(struct REPARSE_DATA_BUFFER,
if (size <= offsetof(struct REPARSE_DATA_BUFFER,
MountPointReparseBuffer.PathBuffer))
goto out;
uni = Add2Ptr(rp,
uname = Add2Ptr(rp,
offsetof(struct REPARSE_DATA_BUFFER,
MountPointReparseBuffer.PathBuffer) +
le16_to_cpu(rp->MountPointReparseBuffer
.PrintNameOffset) -
2);
nlen = le16_to_cpu(rp->MountPointReparseBuffer.PrintNameLength);
.PrintNameOffset));
ulen = le16_to_cpu(rp->MountPointReparseBuffer.PrintNameLength);
break;
case IO_REPARSE_TAG_SYMLINK:
/* FolderSymbolicLink */
/* Can we use 'Rp->SymbolicLinkReparseBuffer.PrintNameLength'? */
if (i_size <= offsetof(struct REPARSE_DATA_BUFFER,
if (size <= offsetof(struct REPARSE_DATA_BUFFER,
SymbolicLinkReparseBuffer.PathBuffer))
goto out;
uni = Add2Ptr(rp,
offsetof(struct REPARSE_DATA_BUFFER,
uname = Add2Ptr(
rp, offsetof(struct REPARSE_DATA_BUFFER,
SymbolicLinkReparseBuffer.PathBuffer) +
le16_to_cpu(rp->SymbolicLinkReparseBuffer
.PrintNameOffset) -
2);
nlen = le16_to_cpu(
.PrintNameOffset));
ulen = le16_to_cpu(
rp->SymbolicLinkReparseBuffer.PrintNameLength);
break;
......@@ -1864,29 +1868,28 @@ static noinline int ntfs_readlink_hlp(struct inode *inode, char *buffer,
goto out;
}
if (!IsReparseTagNameSurrogate(rp->ReparseTag) ||
i_size <= sizeof(struct REPARSE_POINT)) {
size <= sizeof(struct REPARSE_POINT)) {
goto out;
}
/* Users tag. */
uni = Add2Ptr(rp, sizeof(struct REPARSE_POINT) - 2);
nlen = le16_to_cpu(rp->ReparseDataLength) -
uname = Add2Ptr(rp, sizeof(struct REPARSE_POINT));
ulen = le16_to_cpu(rp->ReparseDataLength) -
sizeof(struct REPARSE_POINT);
}
/* Convert nlen from bytes to UNICODE chars. */
nlen >>= 1;
ulen >>= 1;
/* Check that name is available. */
if (!nlen || &uni->name[nlen] > (__le16 *)Add2Ptr(rp, i_size))
if (!ulen || uname + ulen > (__le16 *)Add2Ptr(rp, size))
goto out;
/* If name is already zero terminated then truncate it now. */
if (!uni->name[nlen - 1])
nlen -= 1;
uni->len = nlen;
if (!uname[ulen - 1])
ulen -= 1;
err = ntfs_utf16_to_nls(sbi, uni, buffer, buflen);
err = ntfs_utf16_to_nls(sbi, uname, ulen, buffer, buflen);
if (err < 0)
goto out;
......
......@@ -5,6 +5,9 @@
* 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>
......@@ -336,3 +339,5 @@ static forceinline u8 *lz_copy(u8 *dst, u32 length, u32 offset, const u8 *bufend
return dst;
}
#endif /* _LINUX_NTFS3_LIB_DECOMPRESS_COMMON_H */
......@@ -7,6 +7,10 @@
* - 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);
......@@ -24,3 +28,5 @@ 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 */
......@@ -5,13 +5,13 @@
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/nls.h>
#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.h"
#include "ntfs_fs.h"
// clang-format off
......
......@@ -5,11 +5,7 @@
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/iversion.h>
#include <linux/namei.h>
#include <linux/nls.h>
#include "debug.h"
......@@ -99,16 +95,11 @@ static struct dentry *ntfs_lookup(struct inode *dir, struct dentry *dentry,
static int ntfs_create(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode, bool excl)
{
struct ntfs_inode *ni = ntfs_i(dir);
struct inode *inode;
ni_lock_dir(ni);
inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFREG | mode,
0, NULL, 0, NULL);
ni_unlock(ni);
return IS_ERR(inode) ? PTR_ERR(inode) : 0;
}
......@@ -120,16 +111,11 @@ static int ntfs_create(struct user_namespace *mnt_userns, struct inode *dir,
static int ntfs_mknod(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode, dev_t rdev)
{
struct ntfs_inode *ni = ntfs_i(dir);
struct inode *inode;
ni_lock_dir(ni);
inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, mode, rdev,
NULL, 0, NULL);
ni_unlock(ni);
return IS_ERR(inode) ? PTR_ERR(inode) : 0;
}
......@@ -200,15 +186,10 @@ static int ntfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
{
u32 size = strlen(symname);
struct inode *inode;
struct ntfs_inode *ni = ntfs_i(dir);
ni_lock_dir(ni);
inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFLNK | 0777,
0, symname, size, NULL);
ni_unlock(ni);
return IS_ERR(inode) ? PTR_ERR(inode) : 0;
}
......@@ -219,15 +200,10 @@ static int ntfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode)
{
struct inode *inode;
struct ntfs_inode *ni = ntfs_i(dir);
ni_lock_dir(ni);
inode = ntfs_create_inode(mnt_userns, dir, dentry, NULL, S_IFDIR | mode,
0, NULL, 0, NULL);
ni_unlock(ni);
return IS_ERR(inode) ? PTR_ERR(inode) : 0;
}
......
......@@ -10,19 +10,27 @@
#ifndef _LINUX_NTFS3_NTFS_H
#define _LINUX_NTFS3_NTFS_H
/* TODO: Check 4K MFT record and 512 bytes cluster. */
#include <linux/blkdev.h>
#include <linux/build_bug.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/types.h>
#include "debug.h"
/* Activate this define to use binary search in indexes. */
#define NTFS3_INDEX_BINARY_SEARCH
/* TODO: Check 4K MFT record and 512 bytes cluster. */
/* Check each run for marked clusters. */
#define NTFS3_CHECK_FREE_CLST
#define NTFS_NAME_LEN 255
/* ntfs.sys used 500 maximum links on-disk struct allows up to 0xffff. */
#define NTFS_LINK_MAX 0x400
//#define NTFS_LINK_MAX 0xffff
/*
* ntfs.sys used 500 maximum links on-disk struct allows up to 0xffff.
* xfstest generic/041 creates 3003 hardlinks.
*/
#define NTFS_LINK_MAX 4000
/*
* Activate to use 64 bit clusters instead of 32 bits in ntfs.sys.
......
......@@ -9,6 +9,37 @@
#ifndef _LINUX_NTFS3_NTFS_FS_H
#define _LINUX_NTFS3_NTFS_FS_H
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/cleancache.h>
#include <linux/fs.h>
#include <linux/highmem.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/mutex.h>
#include <linux/page-flags.h>
#include <linux/pagemap.h>
#include <linux/rbtree.h>
#include <linux/rwsem.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/time64.h>
#include <linux/types.h>
#include <linux/uidgid.h>
#include <asm/div64.h>
#include <asm/page.h>
#include "debug.h"
#include "ntfs.h"
struct dentry;
struct fiemap_extent_info;
struct user_namespace;
struct page;
struct writeback_control;
enum utf16_endian;
#define MINUS_ONE_T ((size_t)(-1))
/* Biggest MFT / smallest cluster */
#define MAXIMUM_BYTES_PER_MFT 4096
......@@ -52,6 +83,7 @@
// clang-format on
struct ntfs_mount_options {
char *nls_name;
struct nls_table *nls;
kuid_t fs_uid;
......@@ -59,19 +91,16 @@ struct ntfs_mount_options {
u16 fs_fmask_inv;
u16 fs_dmask_inv;
unsigned uid : 1, /* uid was set. */
gid : 1, /* gid was set. */
fmask : 1, /* fmask was set. */
dmask : 1, /* dmask was set. */
sys_immutable : 1, /* Immutable system files. */
discard : 1, /* Issue discard requests on deletions. */
sparse : 1, /* Create sparse files. */
showmeta : 1, /* Show meta files. */
nohidden : 1, /* Do not show hidden files. */
force : 1, /* Rw mount dirty volume. */
no_acs_rules : 1, /*Exclude acs rules. */
prealloc : 1 /* Preallocate space when file is growing. */
;
unsigned fmask : 1; /* fmask was set. */
unsigned dmask : 1; /*dmask was set. */
unsigned sys_immutable : 1; /* Immutable system files. */
unsigned discard : 1; /* Issue discard requests on deletions. */
unsigned sparse : 1; /* Create sparse files. */
unsigned showmeta : 1; /* Show meta files. */
unsigned nohidden : 1; /* Do not show hidden files. */
unsigned force : 1; /* RW mount dirty volume. */
unsigned noacsrules : 1; /* Exclude acs rules. */
unsigned prealloc : 1; /* Preallocate space when file is growing. */
};
/* Special value to unpack and deallocate. */
......@@ -182,10 +211,8 @@ struct ntfs_sb_info {
u32 blocks_per_cluster; // cluster_size / sb->s_blocksize
u32 record_size;
u32 sector_size;
u32 index_size;
u8 sector_bits;
u8 cluster_bits;
u8 record_bits;
......@@ -279,7 +306,7 @@ struct ntfs_sb_info {
#endif
} compress;
struct ntfs_mount_options options;
struct ntfs_mount_options *options;
struct ratelimit_state msg_ratelimit;
};
......@@ -436,7 +463,7 @@ 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);
int al_update(struct ntfs_inode *ni);
int al_update(struct ntfs_inode *ni, int sync);
static inline size_t al_aligned(size_t size)
{
return (size + 1023) & ~(size_t)1023;
......@@ -448,7 +475,7 @@ bool are_bits_set(const ulong *map, size_t bit, size_t nbits);
size_t get_set_bits_ex(const ulong *map, size_t bit, size_t nbits);
/* Globals from dir.c */
int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const struct le_str *uni,
int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, const __le16 *name, u32 len,
u8 *buf, int buf_len);
int ntfs_nls_to_utf16(struct ntfs_sb_info *sbi, const u8 *name, u32 name_len,
struct cpu_str *uni, u32 max_ulen,
......@@ -520,7 +547,7 @@ struct ATTR_FILE_NAME *ni_fname_type(struct ntfs_inode *ni, u8 name_type,
struct ATTR_LIST_ENTRY **entry);
int ni_new_attr_flags(struct ntfs_inode *ni, enum FILE_ATTRIBUTE new_fa);
enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,
void *buffer);
struct REPARSE_DATA_BUFFER *buffer);
int ni_write_inode(struct inode *inode, int sync, const char *hint);
#define _ni_write_inode(i, w) ni_write_inode(i, w, __func__)
int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
......@@ -577,7 +604,7 @@ int ntfs_sb_read(struct super_block *sb, u64 lbo, size_t bytes, void *buffer);
int ntfs_sb_write(struct super_block *sb, u64 lbo, size_t bytes,
const void *buffer, int wait);
int ntfs_sb_write_run(struct ntfs_sb_info *sbi, const struct runs_tree *run,
u64 vbo, const void *buf, size_t bytes);
u64 vbo, const void *buf, size_t bytes, int sync);
struct buffer_head *ntfs_bread_run(struct ntfs_sb_info *sbi,
const struct runs_tree *run, u64 vbo);
int ntfs_read_run_nb(struct ntfs_sb_info *sbi, const struct runs_tree *run,
......
......@@ -5,10 +5,7 @@
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/nls.h>
#include "debug.h"
#include "ntfs.h"
......
......@@ -7,10 +7,8 @@
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/log2.h>
#include <linux/nls.h>
#include "debug.h"
#include "ntfs.h"
......
......@@ -23,16 +23,15 @@
*
*/
#include <linux/backing-dev.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/exportfs.h>
#include <linux/fs.h>
#include <linux/iversion.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/nls.h>
#include <linux/parser.h>
#include <linux/seq_file.h>
#include <linux/statfs.h>
......@@ -205,9 +204,11 @@ void *ntfs_put_shared(void *ptr)
return ret;
}
static inline void clear_mount_options(struct ntfs_mount_options *options)
static inline void put_mount_options(struct ntfs_mount_options *options)
{
kfree(options->nls_name);
unload_nls(options->nls);
kfree(options);
}
enum Opt {
......@@ -223,218 +224,175 @@ enum Opt {
Opt_nohidden,
Opt_showmeta,
Opt_acl,
Opt_noatime,
Opt_nls,
Opt_iocharset,
Opt_prealloc,
Opt_no_acs_rules,
Opt_noacsrules,
Opt_err,
};
static const match_table_t ntfs_tokens = {
{ Opt_uid, "uid=%u" },
{ Opt_gid, "gid=%u" },
{ Opt_umask, "umask=%o" },
{ Opt_dmask, "dmask=%o" },
{ Opt_fmask, "fmask=%o" },
{ Opt_immutable, "sys_immutable" },
{ Opt_discard, "discard" },
{ Opt_force, "force" },
{ Opt_sparse, "sparse" },
{ Opt_nohidden, "nohidden" },
{ Opt_acl, "acl" },
{ Opt_noatime, "noatime" },
{ Opt_showmeta, "showmeta" },
{ Opt_nls, "nls=%s" },
{ Opt_prealloc, "prealloc" },
{ Opt_no_acs_rules, "no_acs_rules" },
{ Opt_err, NULL },
static const struct fs_parameter_spec ntfs_fs_parameters[] = {
fsparam_u32("uid", Opt_uid),
fsparam_u32("gid", Opt_gid),
fsparam_u32oct("umask", Opt_umask),
fsparam_u32oct("dmask", Opt_dmask),
fsparam_u32oct("fmask", Opt_fmask),
fsparam_flag_no("sys_immutable", Opt_immutable),
fsparam_flag_no("discard", Opt_discard),
fsparam_flag_no("force", Opt_force),
fsparam_flag_no("sparse", Opt_sparse),
fsparam_flag_no("hidden", Opt_nohidden),
fsparam_flag_no("acl", Opt_acl),
fsparam_flag_no("showmeta", Opt_showmeta),
fsparam_flag_no("prealloc", Opt_prealloc),
fsparam_flag_no("acsrules", Opt_noacsrules),
fsparam_string("iocharset", Opt_iocharset),
{}
};
static noinline int ntfs_parse_options(struct super_block *sb, char *options,
int silent,
struct ntfs_mount_options *opts)
/*
* Load nls table or if @nls is utf8 then return NULL.
*/
static struct nls_table *ntfs_load_nls(char *nls)
{
char *p;
substring_t args[MAX_OPT_ARGS];
int option;
char nls_name[30];
struct nls_table *nls;
struct nls_table *ret;
opts->fs_uid = current_uid();
opts->fs_gid = current_gid();
opts->fs_fmask_inv = opts->fs_dmask_inv = ~current_umask();
nls_name[0] = 0;
if (!nls)
nls = CONFIG_NLS_DEFAULT;
if (!options)
goto out;
if (strcmp(nls, "utf8") == 0)
return NULL;
while ((p = strsep(&options, ","))) {
int token;
if (strcmp(nls, CONFIG_NLS_DEFAULT) == 0)
return load_nls_default();
if (!*p)
continue;
ret = load_nls(nls);
if (ret)
return ret;
token = match_token(p, ntfs_tokens, args);
switch (token) {
case Opt_immutable:
opts->sys_immutable = 1;
break;
return ERR_PTR(-EINVAL);
}
static int ntfs_fs_parse_param(struct fs_context *fc,
struct fs_parameter *param)
{
struct ntfs_mount_options *opts = fc->fs_private;
struct fs_parse_result result;
int opt;
opt = fs_parse(fc, ntfs_fs_parameters, param, &result);
if (opt < 0)
return opt;
switch (opt) {
case Opt_uid:
if (match_int(&args[0], &option))
return -EINVAL;
opts->fs_uid = make_kuid(current_user_ns(), option);
opts->fs_uid = make_kuid(current_user_ns(), result.uint_32);
if (!uid_valid(opts->fs_uid))
return -EINVAL;
opts->uid = 1;
return invalf(fc, "ntfs3: Invalid value for uid.");
break;
case Opt_gid:
if (match_int(&args[0], &option))
return -EINVAL;
opts->fs_gid = make_kgid(current_user_ns(), option);
opts->fs_gid = make_kgid(current_user_ns(), result.uint_32);
if (!gid_valid(opts->fs_gid))
return -EINVAL;
opts->gid = 1;
return invalf(fc, "ntfs3: Invalid value for gid.");
break;
case Opt_umask:
if (match_octal(&args[0], &option))
return -EINVAL;
opts->fs_fmask_inv = opts->fs_dmask_inv = ~option;
opts->fmask = opts->dmask = 1;
if (result.uint_32 & ~07777)
return invalf(fc, "ntfs3: Invalid value for umask.");
opts->fs_fmask_inv = ~result.uint_32;
opts->fs_dmask_inv = ~result.uint_32;
opts->fmask = 1;
opts->dmask = 1;
break;
case Opt_dmask:
if (match_octal(&args[0], &option))
return -EINVAL;
opts->fs_dmask_inv = ~option;
if (result.uint_32 & ~07777)
return invalf(fc, "ntfs3: Invalid value for dmask.");
opts->fs_dmask_inv = ~result.uint_32;
opts->dmask = 1;
break;
case Opt_fmask:
if (match_octal(&args[0], &option))
return -EINVAL;
opts->fs_fmask_inv = ~option;
if (result.uint_32 & ~07777)
return invalf(fc, "ntfs3: Invalid value for fmask.");
opts->fs_fmask_inv = ~result.uint_32;
opts->fmask = 1;
break;
case Opt_immutable:
opts->sys_immutable = result.negated ? 0 : 1;
break;
case Opt_discard:
opts->discard = 1;
opts->discard = result.negated ? 0 : 1;
break;
case Opt_force:
opts->force = 1;
opts->force = result.negated ? 0 : 1;
break;
case Opt_sparse:
opts->sparse = 1;
opts->sparse = result.negated ? 0 : 1;
break;
case Opt_nohidden:
opts->nohidden = 1;
opts->nohidden = result.negated ? 1 : 0;
break;
case Opt_acl:
if (!result.negated)
#ifdef CONFIG_NTFS3_FS_POSIX_ACL
sb->s_flags |= SB_POSIXACL;
break;
fc->sb_flags |= SB_POSIXACL;
#else
ntfs_err(sb, "support for ACL not compiled in!");
return -EINVAL;
return invalf(fc, "ntfs3: Support for ACL not compiled in!");
#endif
case Opt_noatime:
sb->s_flags |= SB_NOATIME;
else
fc->sb_flags &= ~SB_POSIXACL;
break;
case Opt_showmeta:
opts->showmeta = 1;
opts->showmeta = result.negated ? 0 : 1;
break;
case Opt_nls:
match_strlcpy(nls_name, &args[0], sizeof(nls_name));
case Opt_iocharset:
kfree(opts->nls_name);
opts->nls_name = param->string;
param->string = NULL;
break;
case Opt_prealloc:
opts->prealloc = 1;
opts->prealloc = result.negated ? 0 : 1;
break;
case Opt_no_acs_rules:
opts->no_acs_rules = 1;
case Opt_noacsrules:
opts->noacsrules = result.negated ? 1 : 0;
break;
default:
if (!silent)
ntfs_err(
sb,
"Unrecognized mount option \"%s\" or missing value",
p);
//return -EINVAL;
}
}
out:
if (!strcmp(nls_name[0] ? nls_name : CONFIG_NLS_DEFAULT, "utf8")) {
/*
* For UTF-8 use utf16s_to_utf8s()/utf8s_to_utf16s()
* instead of NLS.
*/
nls = NULL;
} else if (nls_name[0]) {
nls = load_nls(nls_name);
if (!nls) {
ntfs_err(sb, "failed to load \"%s\"", nls_name);
/* Should not be here unless we forget add case. */
return -EINVAL;
}
} else {
nls = load_nls_default();
if (!nls) {
ntfs_err(sb, "failed to load default nls");
return -EINVAL;
}
}
opts->nls = nls;
return 0;
}
static int ntfs_remount(struct super_block *sb, int *flags, char *data)
static int ntfs_fs_reconfigure(struct fs_context *fc)
{
int err, ro_rw;
struct super_block *sb = fc->root->d_sb;
struct ntfs_sb_info *sbi = sb->s_fs_info;
struct ntfs_mount_options old_opts;
char *orig_data = kstrdup(data, GFP_KERNEL);
if (data && !orig_data)
return -ENOMEM;
/* Store original options. */
memcpy(&old_opts, &sbi->options, sizeof(old_opts));
clear_mount_options(&sbi->options);
memset(&sbi->options, 0, sizeof(sbi->options));
struct ntfs_mount_options *new_opts = fc->fs_private;
int ro_rw;
err = ntfs_parse_options(sb, data, 0, &sbi->options);
if (err)
goto restore_opts;
ro_rw = sb_rdonly(sb) && !(*flags & SB_RDONLY);
ro_rw = sb_rdonly(sb) && !(fc->sb_flags & SB_RDONLY);
if (ro_rw && (sbi->flags & NTFS_FLAGS_NEED_REPLAY)) {
ntfs_warn(
sb,
"Couldn't remount rw because journal is not replayed. Please umount/remount instead\n");
err = -EINVAL;
goto restore_opts;
errorf(fc, "ntfs3: Couldn't remount rw because journal is not replayed. Please umount/remount instead\n");
return -EINVAL;
}
new_opts->nls = ntfs_load_nls(new_opts->nls_name);
if (IS_ERR(new_opts->nls)) {
new_opts->nls = NULL;
errorf(fc, "ntfs3: Cannot load iocharset %s", new_opts->nls_name);
return -EINVAL;
}
if (new_opts->nls != sbi->options->nls)
return invalf(fc, "ntfs3: Cannot use different iocharset when remounting!");
sync_filesystem(sb);
if (ro_rw && (sbi->volume.flags & VOLUME_FLAG_DIRTY) &&
!sbi->options.force) {
ntfs_warn(sb, "volume is dirty and \"force\" flag is not set!");
err = -EINVAL;
goto restore_opts;
!new_opts->force) {
errorf(fc, "ntfs3: Volume is dirty and \"force\" flag is not set!");
return -EINVAL;
}
clear_mount_options(&old_opts);
memcpy(sbi->options, new_opts, sizeof(*new_opts));
*flags = (*flags & ~SB_LAZYTIME) | (sb->s_flags & SB_LAZYTIME) |
SB_NODIRATIME | SB_NOATIME;
ntfs_info(sb, "re-mounted. Opts: %s", orig_data);
err = 0;
goto out;
restore_opts:
clear_mount_options(&sbi->options);
memcpy(&sbi->options, &old_opts, sizeof(old_opts));
out:
kfree(orig_data);
return err;
return 0;
}
static struct kmem_cache *ntfs_inode_cachep;
......@@ -513,8 +471,6 @@ static noinline void put_ntfs(struct ntfs_sb_info *sbi)
xpress_free_decompressor(sbi->compress.xpress);
lzx_free_decompressor(sbi->compress.lzx);
#endif
clear_mount_options(&sbi->options);
kfree(sbi);
}
......@@ -525,7 +481,9 @@ static void ntfs_put_super(struct super_block *sb)
/* Mark rw ntfs as clear, if possible. */
ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
put_mount_options(sbi->options);
put_ntfs(sbi);
sb->s_fs_info = NULL;
sync_blockdev(sb->s_bdev);
}
......@@ -552,13 +510,11 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root)
{
struct super_block *sb = root->d_sb;
struct ntfs_sb_info *sbi = sb->s_fs_info;
struct ntfs_mount_options *opts = &sbi->options;
struct ntfs_mount_options *opts = sbi->options;
struct user_namespace *user_ns = seq_user_ns(m);
if (opts->uid)
seq_printf(m, ",uid=%u",
from_kuid_munged(user_ns, opts->fs_uid));
if (opts->gid)
seq_printf(m, ",gid=%u",
from_kgid_munged(user_ns, opts->fs_gid));
if (opts->fmask)
......@@ -566,9 +522,9 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root)
if (opts->dmask)
seq_printf(m, ",dmask=%04o", ~opts->fs_dmask_inv);
if (opts->nls)
seq_printf(m, ",nls=%s", opts->nls->charset);
seq_printf(m, ",iocharset=%s", opts->nls->charset);
else
seq_puts(m, ",nls=utf8");
seq_puts(m, ",iocharset=utf8");
if (opts->sys_immutable)
seq_puts(m, ",sys_immutable");
if (opts->discard)
......@@ -581,14 +537,12 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root)
seq_puts(m, ",nohidden");
if (opts->force)
seq_puts(m, ",force");
if (opts->no_acs_rules)
seq_puts(m, ",no_acs_rules");
if (opts->noacsrules)
seq_puts(m, ",noacsrules");
if (opts->prealloc)
seq_puts(m, ",prealloc");
if (sb->s_flags & SB_POSIXACL)
seq_puts(m, ",acl");
if (sb->s_flags & SB_NOATIME)
seq_puts(m, ",noatime");
return 0;
}
......@@ -643,7 +597,6 @@ static const struct super_operations ntfs_sops = {
.statfs = ntfs_statfs,
.show_options = ntfs_show_options,
.sync_fs = ntfs_sync_fs,
.remount_fs = ntfs_remount,
.write_inode = ntfs3_write_inode,
};
......@@ -729,7 +682,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
struct ntfs_sb_info *sbi = sb->s_fs_info;
int err;
u32 mb, gb, boot_sector_size, sct_per_clst, record_size;
u64 sectors, clusters, fs_size, mlcn, mlcn2;
u64 sectors, clusters, mlcn, mlcn2;
struct NTFS_BOOT *boot;
struct buffer_head *bh;
struct MFT_REC *rec;
......@@ -787,20 +740,20 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
goto out;
}
sbi->sector_size = boot_sector_size;
sbi->sector_bits = blksize_bits(boot_sector_size);
fs_size = (sectors + 1) << sbi->sector_bits;
sbi->volume.size = sectors * boot_sector_size;
gb = format_size_gb(fs_size, &mb);
gb = format_size_gb(sbi->volume.size + boot_sector_size, &mb);
/*
* - Volume formatted and mounted with the same sector size.
* - Volume formatted 4K and mounted as 512.
* - Volume formatted 512 and mounted as 4K.
*/
if (sbi->sector_size != sector_size) {
ntfs_warn(sb,
"Different NTFS' sector size and media sector size");
if (boot_sector_size != sector_size) {
ntfs_warn(
sb,
"Different NTFS' sector size (%u) and media sector size (%u)",
boot_sector_size, sector_size);
dev_size += sector_size - 1;
}
......@@ -810,8 +763,19 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
sbi->mft.lbo = mlcn << sbi->cluster_bits;
sbi->mft.lbo2 = mlcn2 << sbi->cluster_bits;
if (sbi->cluster_size < sbi->sector_size)
/* Compare boot's cluster and sector. */
if (sbi->cluster_size < boot_sector_size)
goto out;
/* Compare boot's cluster and media sector. */
if (sbi->cluster_size < sector_size) {
/* No way to use ntfs_get_block in this case. */
ntfs_err(
sb,
"Failed to mount 'cause NTFS's cluster size (%u) is less than media sector size (%u)",
sbi->cluster_size, sector_size);
goto out;
}
sbi->cluster_mask = sbi->cluster_size - 1;
sbi->cluster_mask_inv = ~(u64)sbi->cluster_mask;
......@@ -836,10 +800,9 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
: (u32)boot->index_size << sbi->cluster_bits;
sbi->volume.ser_num = le64_to_cpu(boot->serial_num);
sbi->volume.size = sectors << sbi->sector_bits;
/* Warning if RAW volume. */
if (dev_size < fs_size) {
if (dev_size < sbi->volume.size + boot_sector_size) {
u32 mb0, gb0;
gb0 = format_size_gb(dev_size, &mb0);
......@@ -883,8 +846,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
rec->total = cpu_to_le32(sbi->record_size);
((struct ATTRIB *)Add2Ptr(rec, ao))->type = ATTR_END;
if (sbi->cluster_size < PAGE_SIZE)
sb_set_blocksize(sb, sbi->cluster_size);
sb_set_blocksize(sb, min_t(u32, sbi->cluster_size, PAGE_SIZE));
sbi->block_mask = sb->s_blocksize - 1;
sbi->blocks_per_cluster = sbi->cluster_size >> sb->s_blocksize_bits;
......@@ -897,9 +859,11 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
if (clusters >= (1ull << (64 - sbi->cluster_bits)))
sbi->maxbytes = -1;
sbi->maxbytes_sparse = -1;
sb->s_maxbytes = MAX_LFS_FILESIZE;
#else
/* Maximum size for sparse file. */
sbi->maxbytes_sparse = (1ull << (sbi->cluster_bits + 32)) - 1;
sb->s_maxbytes = 0xFFFFFFFFull << sbi->cluster_bits;
#endif
err = 0;
......@@ -913,14 +877,13 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
/*
* ntfs_fill_super - Try to mount.
*/
static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
{
int err;
struct ntfs_sb_info *sbi;
struct ntfs_sb_info *sbi = sb->s_fs_info;
struct block_device *bdev = sb->s_bdev;
struct inode *bd_inode = bdev->bd_inode;
struct request_queue *rq = bdev_get_queue(bdev);
struct inode *inode = NULL;
struct request_queue *rq;
struct inode *inode;
struct ntfs_inode *ni;
size_t i, tt;
CLST vcn, lcn, len;
......@@ -928,18 +891,11 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
const struct VOLUME_INFO *info;
u32 idx, done, bytes;
struct ATTR_DEF_ENTRY *t;
u16 *upcase = NULL;
u16 *shared;
bool is_ro;
struct MFT_REF ref;
ref.high = 0;
sbi = kzalloc(sizeof(struct ntfs_sb_info), GFP_NOFS);
if (!sbi)
return -ENOMEM;
sb->s_fs_info = sbi;
sbi->sb = sb;
sb->s_flags |= SB_NODIRATIME;
sb->s_magic = 0x7366746e; // "ntfs"
......@@ -948,41 +904,27 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
sb->s_time_gran = NTFS_TIME_GRAN; // 100 nsec
sb->s_xattr = ntfs_xattr_handlers;
ratelimit_state_init(&sbi->msg_ratelimit, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
err = ntfs_parse_options(sb, data, silent, &sbi->options);
if (err)
sbi->options->nls = ntfs_load_nls(sbi->options->nls_name);
if (IS_ERR(sbi->options->nls)) {
sbi->options->nls = NULL;
errorf(fc, "Cannot load nls %s", sbi->options->nls_name);
err = -EINVAL;
goto out;
}
if (!rq || !blk_queue_discard(rq) || !rq->limits.discard_granularity) {
;
} else {
rq = bdev_get_queue(bdev);
if (blk_queue_discard(rq) && rq->limits.discard_granularity) {
sbi->discard_granularity = rq->limits.discard_granularity;
sbi->discard_granularity_mask_inv =
~(u64)(sbi->discard_granularity - 1);
}
sb_set_blocksize(sb, PAGE_SIZE);
/* Parse boot. */
err = ntfs_init_from_boot(sb, rq ? queue_logical_block_size(rq) : 512,
bd_inode->i_size);
bdev->bd_inode->i_size);
if (err)
goto out;
#ifdef CONFIG_NTFS3_64BIT_CLUSTER
sb->s_maxbytes = MAX_LFS_FILESIZE;
#else
sb->s_maxbytes = 0xFFFFFFFFull << sbi->cluster_bits;
#endif
mutex_init(&sbi->compress.mtx_lznt);
#ifdef CONFIG_NTFS3_LZX_XPRESS
mutex_init(&sbi->compress.mtx_xpress);
mutex_init(&sbi->compress.mtx_lzx);
#endif
/*
* Load $Volume. This should be done before $LogFile
* 'cause 'sbi->volume.ni' is used 'ntfs_set_state'.
......@@ -991,9 +933,8 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
ref.seq = cpu_to_le16(MFT_REC_VOL);
inode = ntfs_iget5(sb, &ref, &NAME_VOLUME);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
ntfs_err(sb, "Failed to load $Volume.");
inode = NULL;
err = PTR_ERR(inode);
goto out;
}
......@@ -1015,36 +956,33 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
} else {
/* Should we break mounting here? */
//err = -EINVAL;
//goto out;
//goto put_inode_out;
}
attr = ni_find_attr(ni, attr, NULL, ATTR_VOL_INFO, NULL, 0, NULL, NULL);
if (!attr || is_attr_ext(attr)) {
err = -EINVAL;
goto out;
goto put_inode_out;
}
info = resident_data_ex(attr, SIZEOF_ATTRIBUTE_VOLUME_INFO);
if (!info) {
err = -EINVAL;
goto out;
goto put_inode_out;
}
sbi->volume.major_ver = info->major_ver;
sbi->volume.minor_ver = info->minor_ver;
sbi->volume.flags = info->flags;
sbi->volume.ni = ni;
inode = NULL;
/* Load $MFTMirr to estimate recs_mirr. */
ref.low = cpu_to_le32(MFT_REC_MIRR);
ref.seq = cpu_to_le16(MFT_REC_MIRR);
inode = ntfs_iget5(sb, &ref, &NAME_MIRROR);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
ntfs_err(sb, "Failed to load $MFTMirr.");
inode = NULL;
err = PTR_ERR(inode);
goto out;
}
......@@ -1058,9 +996,8 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
ref.seq = cpu_to_le16(MFT_REC_LOG);
inode = ntfs_iget5(sb, &ref, &NAME_LOGFILE);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
ntfs_err(sb, "Failed to load \x24LogFile.");
inode = NULL;
err = PTR_ERR(inode);
goto out;
}
......@@ -1068,22 +1005,19 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
err = ntfs_loadlog_and_replay(ni, sbi);
if (err)
goto out;
goto put_inode_out;
iput(inode);
inode = NULL;
is_ro = sb_rdonly(sbi->sb);
if (sbi->flags & NTFS_FLAGS_NEED_REPLAY) {
if (!is_ro) {
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 (!is_ro && !sbi->options.force) {
if (!sb_rdonly(sb) && !sbi->options->force) {
ntfs_warn(
sb,
"volume is dirty and \"force\" flag is not set!");
......@@ -1098,9 +1032,8 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
inode = ntfs_iget5(sb, &ref, &NAME_MFT);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
ntfs_err(sb, "Failed to load $MFT.");
inode = NULL;
err = PTR_ERR(inode);
goto out;
}
......@@ -1112,11 +1045,11 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
err = wnd_init(&sbi->mft.bitmap, sb, tt);
if (err)
goto out;
goto put_inode_out;
err = ni_load_all_mi(ni);
if (err)
goto out;
goto put_inode_out;
sbi->mft.ni = ni;
......@@ -1125,9 +1058,8 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
ref.seq = cpu_to_le16(MFT_REC_BADCLUST);
inode = ntfs_iget5(sb, &ref, &NAME_BADCLUS);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
ntfs_err(sb, "Failed to load $BadClus.");
inode = NULL;
err = PTR_ERR(inode);
goto out;
}
......@@ -1150,18 +1082,15 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
ref.seq = cpu_to_le16(MFT_REC_BITMAP);
inode = ntfs_iget5(sb, &ref, &NAME_BITMAP);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
ntfs_err(sb, "Failed to load $Bitmap.");
inode = NULL;
err = PTR_ERR(inode);
goto out;
}
ni = ntfs_i(inode);
#ifndef CONFIG_NTFS3_64BIT_CLUSTER
if (inode->i_size >> 32) {
err = -EINVAL;
goto out;
goto put_inode_out;
}
#endif
......@@ -1169,14 +1098,14 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
tt = sbi->used.bitmap.nbits;
if (inode->i_size < bitmap_size(tt)) {
err = -EINVAL;
goto out;
goto put_inode_out;
}
/* Not necessary. */
sbi->used.bitmap.set_tail = true;
err = wnd_init(&sbi->used.bitmap, sbi->sb, tt);
err = wnd_init(&sbi->used.bitmap, sb, tt);
if (err)
goto out;
goto put_inode_out;
iput(inode);
......@@ -1188,23 +1117,22 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
/* Load $AttrDef. */
ref.low = cpu_to_le32(MFT_REC_ATTR);
ref.seq = cpu_to_le16(MFT_REC_ATTR);
inode = ntfs_iget5(sbi->sb, &ref, &NAME_ATTRDEF);
inode = ntfs_iget5(sb, &ref, &NAME_ATTRDEF);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
ntfs_err(sb, "Failed to load $AttrDef -> %d", err);
inode = NULL;
err = PTR_ERR(inode);
goto out;
}
if (inode->i_size < sizeof(struct ATTR_DEF_ENTRY)) {
err = -EINVAL;
goto out;
goto put_inode_out;
}
bytes = inode->i_size;
sbi->def_table = t = kmalloc(bytes, GFP_NOFS);
if (!t) {
err = -ENOMEM;
goto out;
goto put_inode_out;
}
for (done = idx = 0; done < bytes; done += PAGE_SIZE, idx++) {
......@@ -1213,7 +1141,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
if (IS_ERR(page)) {
err = PTR_ERR(page);
goto out;
goto put_inode_out;
}
memcpy(Add2Ptr(t, done), page_address(page),
min(PAGE_SIZE, tail));
......@@ -1221,7 +1149,7 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
if (!idx && ATTR_STD != t->type) {
err = -EINVAL;
goto out;
goto put_inode_out;
}
}
......@@ -1254,33 +1182,24 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
ref.seq = cpu_to_le16(MFT_REC_UPCASE);
inode = ntfs_iget5(sb, &ref, &NAME_UPCASE);
if (IS_ERR(inode)) {
ntfs_err(sb, "Failed to load $UpCase.");
err = PTR_ERR(inode);
ntfs_err(sb, "Failed to load \x24LogFile.");
inode = NULL;
goto out;
}
ni = ntfs_i(inode);
if (inode->i_size != 0x10000 * sizeof(short)) {
err = -EINVAL;
goto out;
}
sbi->upcase = upcase = kvmalloc(0x10000 * sizeof(short), GFP_KERNEL);
if (!upcase) {
err = -ENOMEM;
goto out;
goto put_inode_out;
}
for (idx = 0; idx < (0x10000 * sizeof(short) >> PAGE_SHIFT); idx++) {
const __le16 *src;
u16 *dst = Add2Ptr(upcase, idx << PAGE_SHIFT);
u16 *dst = Add2Ptr(sbi->upcase, idx << PAGE_SHIFT);
struct page *page = ntfs_map_page(inode->i_mapping, idx);
if (IS_ERR(page)) {
err = PTR_ERR(page);
goto out;
goto put_inode_out;
}
src = page_address(page);
......@@ -1294,14 +1213,13 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
ntfs_unmap_page(page);
}
shared = ntfs_set_shared(upcase, 0x10000 * sizeof(short));
if (shared && upcase != shared) {
shared = ntfs_set_shared(sbi->upcase, 0x10000 * sizeof(short));
if (shared && sbi->upcase != shared) {
kvfree(sbi->upcase);
sbi->upcase = shared;
kvfree(upcase);
}
iput(inode);
inode = NULL;
if (is_ntfs3(sbi)) {
/* Load $Secure. */
......@@ -1331,34 +1249,31 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)
ref.seq = cpu_to_le16(MFT_REC_ROOT);
inode = ntfs_iget5(sb, &ref, &NAME_ROOT);
if (IS_ERR(inode)) {
err = PTR_ERR(inode);
ntfs_err(sb, "Failed to load root.");
inode = NULL;
err = PTR_ERR(inode);
goto out;
}
ni = ntfs_i(inode);
sb->s_root = d_make_root(inode);
if (!sb->s_root) {
err = -EINVAL;
goto out;
err = -ENOMEM;
goto put_inode_out;
}
fc->fs_private = NULL;
return 0;
out:
put_inode_out:
iput(inode);
if (sb->s_root) {
d_drop(sb->s_root);
sb->s_root = NULL;
}
out:
/*
* Free resources here.
* ntfs_fs_free will be called with fc->s_fs_info = NULL
*/
put_ntfs(sbi);
sb->s_fs_info = NULL;
return err;
}
......@@ -1403,7 +1318,7 @@ int ntfs_discard(struct ntfs_sb_info *sbi, CLST lcn, CLST len)
if (sbi->flags & NTFS_FLAGS_NODISCARD)
return -EOPNOTSUPP;
if (!sbi->options.discard)
if (!sbi->options->discard)
return -EOPNOTSUPP;
lbo = (u64)lcn << sbi->cluster_bits;
......@@ -1428,17 +1343,97 @@ int ntfs_discard(struct ntfs_sb_info *sbi, CLST lcn, CLST len)
return err;
}
static struct dentry *ntfs_mount(struct file_system_type *fs_type, int flags,
const char *dev_name, void *data)
static int ntfs_fs_get_tree(struct fs_context *fc)
{
return get_tree_bdev(fc, ntfs_fill_super);
}
/*
* ntfs_fs_free - Free fs_context.
*
* Note that this will be called after fill_super and reconfigure
* even when they pass. So they have to take pointers if they pass.
*/
static void ntfs_fs_free(struct fs_context *fc)
{
struct ntfs_mount_options *opts = fc->fs_private;
struct ntfs_sb_info *sbi = fc->s_fs_info;
if (sbi)
put_ntfs(sbi);
if (opts)
put_mount_options(opts);
}
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,
};
/*
* ntfs_init_fs_context - Initialize spi and opts
*
* This will called when mount/remount. We will first initiliaze
* options so that if remount we can use just that.
*/
static int ntfs_init_fs_context(struct fs_context *fc)
{
return mount_bdev(fs_type, flags, dev_name, data, ntfs_fill_super);
struct ntfs_mount_options *opts;
struct ntfs_sb_info *sbi;
opts = kzalloc(sizeof(struct ntfs_mount_options), GFP_NOFS);
if (!opts)
return -ENOMEM;
/* Default options. */
opts->fs_uid = current_uid();
opts->fs_gid = current_gid();
opts->fs_fmask_inv = ~current_umask();
opts->fs_dmask_inv = ~current_umask();
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE)
goto ok;
sbi = kzalloc(sizeof(struct ntfs_sb_info), GFP_NOFS);
if (!sbi)
goto free_opts;
sbi->upcase = kvmalloc(0x10000 * sizeof(short), GFP_KERNEL);
if (!sbi->upcase)
goto free_sbi;
ratelimit_state_init(&sbi->msg_ratelimit, DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
mutex_init(&sbi->compress.mtx_lznt);
#ifdef CONFIG_NTFS3_LZX_XPRESS
mutex_init(&sbi->compress.mtx_xpress);
mutex_init(&sbi->compress.mtx_lzx);
#endif
sbi->options = opts;
fc->s_fs_info = sbi;
ok:
fc->fs_private = opts;
fc->ops = &ntfs_context_ops;
return 0;
free_sbi:
kfree(sbi);
free_opts:
kfree(opts);
return -ENOMEM;
}
// clang-format off
static struct file_system_type ntfs_fs_type = {
.owner = THIS_MODULE,
.name = "ntfs3",
.mount = ntfs_mount,
.init_fs_context = ntfs_init_fs_context,
.parameters = ntfs_fs_parameters,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
};
......
......@@ -5,13 +5,9 @@
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/module.h>
#include <linux/nls.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include "debug.h"
#include "ntfs.h"
#include "ntfs_fs.h"
static inline u16 upcase_unicode_char(const u16 *upcase, u16 chr)
......
......@@ -5,10 +5,7 @@
*
*/
#include <linux/blkdev.h>
#include <linux/buffer_head.h>
#include <linux/fs.h>
#include <linux/nls.h>
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
#include <linux/xattr.h>
......@@ -78,6 +75,7 @@ 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;
......@@ -102,10 +100,10 @@ static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea,
/* Check Ea limit. */
size = le32_to_cpu((*info)->size);
if (size > ni->mi.sbi->ea_max_size)
if (size > sbi->ea_max_size)
return -EFBIG;
if (attr_size(attr_ea) > ni->mi.sbi->ea_max_size)
if (attr_size(attr_ea) > sbi->ea_max_size)
return -EFBIG;
/* Allocate memory for packed Ea. */
......@@ -113,15 +111,16 @@ static int ntfs_read_ea(struct ntfs_inode *ni, struct EA_FULL **ea,
if (!ea_p)
return -ENOMEM;
if (attr_ea->non_res) {
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(ni->mi.sbi, &run, 0, ea_p, size,
NULL);
err = ntfs_read_run_nb(sbi, &run, 0, ea_p, size, NULL);
run_close(&run);
if (err)
......@@ -260,7 +259,7 @@ static int ntfs_get_ea(struct inode *inode, const char *name, size_t name_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, int locked)
size_t val_size, int flags)
{
struct ntfs_inode *ni = ntfs_i(inode);
struct ntfs_sb_info *sbi = ni->mi.sbi;
......@@ -279,7 +278,6 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name,
u64 new_sz;
void *p;
if (!locked)
ni_lock(ni);
run_init(&ea_run);
......@@ -370,21 +368,22 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name,
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);
/* Should fit into 16 bits. */
if (new_pack > 0xffff) {
err = -EFBIG; // -EINVAL?
goto out;
}
ea_info.size_pack = cpu_to_le16(new_pack);
/* New size of ATTR_EA. */
size += add;
if (size > sbi->ea_max_size) {
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;
}
ea_info.size = cpu_to_le32(size);
update_ea:
......@@ -444,7 +443,7 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name,
/* 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);
err = ntfs_sb_write_run(sbi, &ea_run, 0, ea_all, size, 0);
if (err)
goto out;
} else {
......@@ -468,7 +467,6 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name,
mark_inode_dirty(&ni->vfs_inode);
out:
if (!locked)
ni_unlock(ni);
run_close(&ea_run);
......@@ -478,12 +476,6 @@ static noinline int ntfs_set_ea(struct inode *inode, const char *name,
}
#ifdef CONFIG_NTFS3_FS_POSIX_ACL
static inline void ntfs_posix_acl_release(struct posix_acl *acl)
{
if (acl && refcount_dec_and_test(&acl->a_refcount))
kfree(acl);
}
static struct posix_acl *ntfs_get_acl_ex(struct user_namespace *mnt_userns,
struct inode *inode, int type,
int locked)
......@@ -521,12 +513,15 @@ static struct posix_acl *ntfs_get_acl_ex(struct user_namespace *mnt_userns,
/* Translate extended attribute to acl. */
if (err >= 0) {
acl = posix_acl_from_xattr(mnt_userns, buf, err);
if (!IS_ERR(acl))
set_cached_acl(inode, type, acl);
} else if (err == -ENODATA) {
acl = NULL;
} else {
acl = err == -ENODATA ? NULL : ERR_PTR(err);
acl = ERR_PTR(err);
}
if (!IS_ERR(acl))
set_cached_acl(inode, type, acl);
__putname(buf);
return acl;
......@@ -546,12 +541,13 @@ struct posix_acl *ntfs_get_acl(struct inode *inode, int type, bool rcu)
static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns,
struct inode *inode, struct posix_acl *acl,
int type, int locked)
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;
......@@ -561,22 +557,15 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns,
if (acl) {
umode_t mode = inode->i_mode;
err = posix_acl_equiv_mode(acl, &mode);
if (err < 0)
return err;
err = posix_acl_update_mode(mnt_userns, inode, &mode,
&acl);
if (err)
goto out;
if (inode->i_mode != mode) {
inode->i_mode = mode;
mark_inode_dirty(inode);
}
if (!err) {
/*
* ACL can be exactly represented in the
* traditional file mode permission bits.
*/
acl = NULL;
}
}
name = XATTR_NAME_POSIX_ACL_ACCESS;
name_len = sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1;
......@@ -594,20 +583,24 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns,
}
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(mnt_userns, acl, value, size);
if (err < 0)
goto out;
flags = 0;
}
err = ntfs_set_ea(inode, name, name_len, value, size, 0, locked);
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);
......@@ -623,68 +616,7 @@ static noinline int ntfs_set_acl_ex(struct user_namespace *mnt_userns,
int ntfs_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
struct posix_acl *acl, int type)
{
return ntfs_set_acl_ex(mnt_userns, inode, acl, type, 0);
}
static int ntfs_xattr_get_acl(struct user_namespace *mnt_userns,
struct inode *inode, int type, void *buffer,
size_t size)
{
struct posix_acl *acl;
int err;
if (!(inode->i_sb->s_flags & SB_POSIXACL)) {
ntfs_inode_warn(inode, "add mount option \"acl\" to use acl");
return -EOPNOTSUPP;
}
acl = ntfs_get_acl(inode, type, false);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (!acl)
return -ENODATA;
err = posix_acl_to_xattr(mnt_userns, acl, buffer, size);
ntfs_posix_acl_release(acl);
return err;
}
static int ntfs_xattr_set_acl(struct user_namespace *mnt_userns,
struct inode *inode, int type, const void *value,
size_t size)
{
struct posix_acl *acl;
int err;
if (!(inode->i_sb->s_flags & SB_POSIXACL)) {
ntfs_inode_warn(inode, "add mount option \"acl\" to use acl");
return -EOPNOTSUPP;
}
if (!inode_owner_or_capable(mnt_userns, inode))
return -EPERM;
if (!value) {
acl = NULL;
} else {
acl = posix_acl_from_xattr(mnt_userns, value, size);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (acl) {
err = posix_acl_valid(mnt_userns, acl);
if (err)
goto release_and_out;
}
}
err = ntfs_set_acl(mnt_userns, inode, acl, type);
release_and_out:
ntfs_posix_acl_release(acl);
return err;
return ntfs_set_acl_ex(mnt_userns, inode, acl, type);
}
/*
......@@ -698,54 +630,27 @@ int ntfs_init_acl(struct user_namespace *mnt_userns, struct inode *inode,
struct posix_acl *default_acl, *acl;
int err;
/*
* TODO: Refactoring lock.
* ni_lock(dir) ... -> posix_acl_create(dir,...) -> ntfs_get_acl -> ni_lock(dir)
*/
inode->i_default_acl = NULL;
default_acl = ntfs_get_acl_ex(mnt_userns, dir, ACL_TYPE_DEFAULT, 1);
if (!default_acl || default_acl == ERR_PTR(-EOPNOTSUPP)) {
inode->i_mode &= ~current_umask();
err = 0;
goto out;
}
if (IS_ERR(default_acl)) {
err = PTR_ERR(default_acl);
goto out;
}
acl = default_acl;
err = __posix_acl_create(&acl, GFP_NOFS, &inode->i_mode);
if (err < 0)
goto out1;
if (!err) {
posix_acl_release(acl);
acl = NULL;
}
err = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
if (err)
return err;
if (!S_ISDIR(inode->i_mode)) {
if (default_acl) {
err = ntfs_set_acl_ex(mnt_userns, inode, default_acl,
ACL_TYPE_DEFAULT);
posix_acl_release(default_acl);
default_acl = NULL;
} else {
inode->i_default_acl = NULL;
}
if (default_acl)
err = ntfs_set_acl_ex(mnt_userns, inode, default_acl,
ACL_TYPE_DEFAULT, 1);
if (!acl)
inode->i_acl = NULL;
else if (!err)
err = ntfs_set_acl_ex(mnt_userns, inode, acl, ACL_TYPE_ACCESS,
1);
else {
if (!err)
err = ntfs_set_acl_ex(mnt_userns, inode, acl,
ACL_TYPE_ACCESS);
posix_acl_release(acl);
out1:
posix_acl_release(default_acl);
}
out:
return err;
}
#endif
......@@ -772,7 +677,7 @@ int ntfs_acl_chmod(struct user_namespace *mnt_userns, struct inode *inode)
int ntfs_permission(struct user_namespace *mnt_userns, struct inode *inode,
int mask)
{
if (ntfs_sb(inode->i_sb)->options.no_acs_rules) {
if (ntfs_sb(inode->i_sb)->options->noacsrules) {
/* "No access rules" mode - Allow all changes. */
return 0;
}
......@@ -880,23 +785,6 @@ static int ntfs_getxattr(const struct xattr_handler *handler, struct dentry *de,
goto out;
}
#ifdef CONFIG_NTFS3_FS_POSIX_ACL
if ((name_len == sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1 &&
!memcmp(name, XATTR_NAME_POSIX_ACL_ACCESS,
sizeof(XATTR_NAME_POSIX_ACL_ACCESS))) ||
(name_len == sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1 &&
!memcmp(name, XATTR_NAME_POSIX_ACL_DEFAULT,
sizeof(XATTR_NAME_POSIX_ACL_DEFAULT)))) {
/* TODO: init_user_ns? */
err = ntfs_xattr_get_acl(
&init_user_ns, inode,
name_len == sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1
? ACL_TYPE_ACCESS
: ACL_TYPE_DEFAULT,
buffer, size);
goto out;
}
#endif
/* Deal with NTFS extended attribute. */
err = ntfs_get_ea(inode, name, name_len, buffer, size, NULL);
......@@ -1009,24 +897,8 @@ static noinline int ntfs_setxattr(const struct xattr_handler *handler,
goto out;
}
#ifdef CONFIG_NTFS3_FS_POSIX_ACL
if ((name_len == sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1 &&
!memcmp(name, XATTR_NAME_POSIX_ACL_ACCESS,
sizeof(XATTR_NAME_POSIX_ACL_ACCESS))) ||
(name_len == sizeof(XATTR_NAME_POSIX_ACL_DEFAULT) - 1 &&
!memcmp(name, XATTR_NAME_POSIX_ACL_DEFAULT,
sizeof(XATTR_NAME_POSIX_ACL_DEFAULT)))) {
err = ntfs_xattr_set_acl(
mnt_userns, inode,
name_len == sizeof(XATTR_NAME_POSIX_ACL_ACCESS) - 1
? ACL_TYPE_ACCESS
: ACL_TYPE_DEFAULT,
value, size);
goto out;
}
#endif
/* Deal with NTFS extended attribute. */
err = ntfs_set_ea(inode, name, name_len, value, size, flags, 0);
err = ntfs_set_ea(inode, name, name_len, value, size, flags);
out:
return err;
......@@ -1042,28 +914,29 @@ 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, 0);
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, 0);
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, 0);
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, 0);
sizeof(value), 0);
if (err)
goto out;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment