Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
linux-davinci
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Redmine
Redmine
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
linux
linux-davinci
Commits
2bfb4fff
Commit
2bfb4fff
authored
Mar 09, 2005
by
Anton Altaparmakov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
NTFS: Add fs/ntfs/attrib.[hc]::ntfs_attr_make_non_resident().
Signed-off-by:
Anton Altaparmakov
<
aia21@cantab.net
>
parent
c0c1cc0e
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
303 additions
and
0 deletions
+303
-0
fs/ntfs/ChangeLog
fs/ntfs/ChangeLog
+1
-0
fs/ntfs/attrib.c
fs/ntfs/attrib.c
+300
-0
fs/ntfs/attrib.h
fs/ntfs/attrib.h
+2
-0
No files found.
fs/ntfs/ChangeLog
View file @
2bfb4fff
...
...
@@ -97,6 +97,7 @@ ToDo/Notes:
whether someone else did not already do the work we wanted to do.
- Rename fs/ntfs/attrib.c::ntfs_find_vcn_nolock() to
ntfs_attr_find_vcn_nolock() and update all callers.
- Add fs/ntfs/attrib.[hc]::ntfs_attr_make_non_resident().
2.1.22 - Many bug and race fixes and error handling improvements.
...
...
fs/ntfs/attrib.c
View file @
2bfb4fff
...
...
@@ -25,6 +25,8 @@
#include "attrib.h"
#include "debug.h"
#include "layout.h"
#include "lcnalloc.h"
#include "malloc.h"
#include "mft.h"
#include "ntfs.h"
#include "types.h"
...
...
@@ -1226,6 +1228,304 @@ int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size)
return
0
;
}
/**
* ntfs_attr_make_non_resident - convert a resident to a non-resident attribute
* @ni: ntfs inode describing the attribute to convert
*
* Convert the resident ntfs attribute described by the ntfs inode @ni to a
* non-resident one.
*
* Return 0 on success and -errno on error. The following error return codes
* are defined:
* -EPERM - The attribute is not allowed to be non-resident.
* -ENOMEM - Not enough memory.
* -ENOSPC - Not enough disk space.
* -EINVAL - Attribute not defined on the volume.
* -EIO - I/o error or other error.
*
* NOTE to self: No changes in the attribute list are required to move from
* a resident to a non-resident attribute.
*
* Locking: - The caller must hold i_sem on the inode.
*/
int
ntfs_attr_make_non_resident
(
ntfs_inode
*
ni
)
{
s64
new_size
;
struct
inode
*
vi
=
VFS_I
(
ni
);
ntfs_volume
*
vol
=
ni
->
vol
;
ntfs_inode
*
base_ni
;
MFT_RECORD
*
m
;
ATTR_RECORD
*
a
;
ntfs_attr_search_ctx
*
ctx
;
struct
page
*
page
;
runlist_element
*
rl
;
u8
*
kaddr
;
unsigned
long
flags
;
int
mp_size
,
mp_ofs
,
name_ofs
,
arec_size
,
err
,
err2
;
u32
attr_size
;
u8
old_res_attr_flags
;
/* Check that the attribute is allowed to be non-resident. */
err
=
ntfs_attr_can_be_non_resident
(
vol
,
ni
->
type
);
if
(
unlikely
(
err
))
{
if
(
err
==
-
EPERM
)
ntfs_debug
(
"Attribute is not allowed to be "
"non-resident."
);
else
ntfs_debug
(
"Attribute not defined on the NTFS "
"volume!"
);
return
err
;
}
/*
* The size needs to be aligned to a cluster boundary for allocation
* purposes.
*/
new_size
=
(
i_size_read
(
vi
)
+
vol
->
cluster_size
-
1
)
&
~
(
vol
->
cluster_size
-
1
);
if
(
new_size
>
0
)
{
/*
* Will need the page later and since the page lock nests
* outside all ntfs locks, we need to get the page now.
*/
page
=
find_or_create_page
(
vi
->
i_mapping
,
0
,
mapping_gfp_mask
(
vi
->
i_mapping
));
if
(
unlikely
(
!
page
))
return
-
ENOMEM
;
/* Start by allocating clusters to hold the attribute value. */
rl
=
ntfs_cluster_alloc
(
vol
,
0
,
new_size
>>
vol
->
cluster_size_bits
,
-
1
,
DATA_ZONE
);
if
(
IS_ERR
(
rl
))
{
err
=
PTR_ERR
(
rl
);
ntfs_debug
(
"Failed to allocate cluster%s, error code "
"%i.
\n
"
,
(
new_size
>>
vol
->
cluster_size_bits
)
>
1
?
"s"
:
""
,
err
);
goto
page_err_out
;
}
}
else
{
rl
=
NULL
;
page
=
NULL
;
}
/* Determine the size of the mapping pairs array. */
mp_size
=
ntfs_get_size_for_mapping_pairs
(
vol
,
rl
,
0
);
if
(
unlikely
(
mp_size
<
0
))
{
err
=
mp_size
;
ntfs_debug
(
"Failed to get size for mapping pairs array, error "
"code %i."
,
err
);
goto
rl_err_out
;
}
down_write
(
&
ni
->
runlist
.
lock
);
if
(
!
NInoAttr
(
ni
))
base_ni
=
ni
;
else
base_ni
=
ni
->
ext
.
base_ntfs_ino
;
m
=
map_mft_record
(
base_ni
);
if
(
IS_ERR
(
m
))
{
err
=
PTR_ERR
(
m
);
m
=
NULL
;
ctx
=
NULL
;
goto
err_out
;
}
ctx
=
ntfs_attr_get_search_ctx
(
base_ni
,
m
);
if
(
unlikely
(
!
ctx
))
{
err
=
-
ENOMEM
;
goto
err_out
;
}
err
=
ntfs_attr_lookup
(
ni
->
type
,
ni
->
name
,
ni
->
name_len
,
CASE_SENSITIVE
,
0
,
NULL
,
0
,
ctx
);
if
(
unlikely
(
err
))
{
if
(
err
==
-
ENOENT
)
err
=
-
EIO
;
goto
err_out
;
}
m
=
ctx
->
mrec
;
a
=
ctx
->
attr
;
BUG_ON
(
NInoNonResident
(
ni
));
BUG_ON
(
a
->
non_resident
);
/*
* Calculate new offsets for the name and the mapping pairs array.
* We assume the attribute is not compressed or sparse.
*/
name_ofs
=
(
offsetof
(
ATTR_REC
,
data
.
non_resident
.
compressed_size
)
+
7
)
&
~
7
;
mp_ofs
=
(
name_ofs
+
a
->
name_length
*
sizeof
(
ntfschar
)
+
7
)
&
~
7
;
/*
* Determine the size of the resident part of the now non-resident
* attribute record.
*/
arec_size
=
(
mp_ofs
+
mp_size
+
7
)
&
~
7
;
/*
* If the page is not uptodate bring it uptodate by copying from the
* attribute value.
*/
attr_size
=
le32_to_cpu
(
a
->
data
.
resident
.
value_length
);
BUG_ON
(
attr_size
!=
i_size_read
(
vi
));
if
(
page
&&
!
PageUptodate
(
page
))
{
kaddr
=
kmap_atomic
(
page
,
KM_USER0
);
memcpy
(
kaddr
,
(
u8
*
)
a
+
le16_to_cpu
(
a
->
data
.
resident
.
value_offset
),
attr_size
);
memset
(
kaddr
+
attr_size
,
0
,
PAGE_CACHE_SIZE
-
attr_size
);
kunmap_atomic
(
kaddr
,
KM_USER0
);
flush_dcache_page
(
page
);
SetPageUptodate
(
page
);
}
/* Backup the attribute flag. */
old_res_attr_flags
=
a
->
data
.
resident
.
flags
;
/* Resize the resident part of the attribute record. */
err
=
ntfs_attr_record_resize
(
m
,
a
,
arec_size
);
if
(
unlikely
(
err
))
goto
err_out
;
/* Setup the in-memory attribute structure to be non-resident. */
NInoSetNonResident
(
ni
);
ni
->
runlist
.
rl
=
rl
;
write_lock_irqsave
(
&
ni
->
size_lock
,
flags
);
ni
->
allocated_size
=
new_size
;
write_unlock_irqrestore
(
&
ni
->
size_lock
,
flags
);
/*
* FIXME: For now just clear all of these as we do not support them
* when writing.
*/
NInoClearCompressed
(
ni
);
NInoClearSparse
(
ni
);
NInoClearEncrypted
(
ni
);
/*
* Convert the resident part of the attribute record to describe a
* non-resident attribute.
*/
a
->
non_resident
=
1
;
/* Move the attribute name if it exists and update the offset. */
if
(
a
->
name_length
)
memmove
((
u8
*
)
a
+
name_ofs
,
(
u8
*
)
a
+
le16_to_cpu
(
a
->
name_offset
),
a
->
name_length
*
sizeof
(
ntfschar
));
a
->
name_offset
=
cpu_to_le16
(
name_ofs
);
/* Update the flags to match the in-memory ones. */
a
->
flags
&=
cpu_to_le16
(
0xffff
&
~
le16_to_cpu
(
ATTR_IS_SPARSE
|
ATTR_IS_ENCRYPTED
|
ATTR_COMPRESSION_MASK
));
/* Setup the fields specific to non-resident attributes. */
a
->
data
.
non_resident
.
lowest_vcn
=
0
;
a
->
data
.
non_resident
.
highest_vcn
=
cpu_to_sle64
((
new_size
-
1
)
>>
vol
->
cluster_size_bits
);
a
->
data
.
non_resident
.
mapping_pairs_offset
=
cpu_to_le16
(
mp_ofs
);
a
->
data
.
non_resident
.
compression_unit
=
0
;
memset
(
&
a
->
data
.
non_resident
.
reserved
,
0
,
sizeof
(
a
->
data
.
non_resident
.
reserved
));
a
->
data
.
non_resident
.
allocated_size
=
cpu_to_sle64
(
new_size
);
a
->
data
.
non_resident
.
data_size
=
a
->
data
.
non_resident
.
initialized_size
=
cpu_to_sle64
(
attr_size
);
/* Generate the mapping pairs array into the attribute record. */
err
=
ntfs_mapping_pairs_build
(
vol
,
(
u8
*
)
a
+
mp_ofs
,
arec_size
-
mp_ofs
,
rl
,
0
,
NULL
);
if
(
unlikely
(
err
))
{
ntfs_debug
(
"Failed to build mapping pairs, error code %i."
,
err
);
goto
undo_err_out
;
}
/* Mark the mft record dirty, so it gets written back. */
flush_dcache_mft_record_page
(
ctx
->
ntfs_ino
);
mark_mft_record_dirty
(
ctx
->
ntfs_ino
);
ntfs_attr_put_search_ctx
(
ctx
);
unmap_mft_record
(
base_ni
);
up_write
(
&
ni
->
runlist
.
lock
);
if
(
page
)
{
set_page_dirty
(
page
);
unlock_page
(
page
);
page_cache_release
(
page
);
}
ntfs_debug
(
"Done."
);
return
0
;
undo_err_out:
/* Convert the attribute back into a resident attribute. */
a
->
non_resident
=
0
;
/* Move the attribute name if it exists and update the offset. */
name_ofs
=
(
offsetof
(
ATTR_RECORD
,
data
.
resident
.
reserved
)
+
sizeof
(
a
->
data
.
resident
.
reserved
)
+
7
)
&
~
7
;
if
(
a
->
name_length
)
memmove
((
u8
*
)
a
+
name_ofs
,
(
u8
*
)
a
+
le16_to_cpu
(
a
->
name_offset
),
a
->
name_length
*
sizeof
(
ntfschar
));
mp_ofs
=
(
name_ofs
+
a
->
name_length
*
sizeof
(
ntfschar
)
+
7
)
&
~
7
;
a
->
name_offset
=
cpu_to_le16
(
name_ofs
);
arec_size
=
(
mp_ofs
+
attr_size
+
7
)
&
~
7
;
/* Resize the resident part of the attribute record. */
err2
=
ntfs_attr_record_resize
(
m
,
a
,
arec_size
);
if
(
unlikely
(
err2
))
{
/*
* This cannot happen (well if memory corruption is at work it
* could happen in theory), but deal with it as well as we can.
* If the old size is too small, truncate the attribute,
* otherwise simply give it a larger allocated size.
* FIXME: Should check whether chkdsk complains when the
* allocated size is much bigger than the resident value size.
*/
arec_size
=
le32_to_cpu
(
a
->
length
);
if
((
mp_ofs
+
attr_size
)
>
arec_size
)
{
err2
=
attr_size
;
attr_size
=
arec_size
-
mp_ofs
;
ntfs_error
(
vol
->
sb
,
"Failed to undo partial resident "
"to non-resident attribute "
"conversion. Truncating inode 0x%lx, "
"attribute type 0x%x from %i bytes to "
"%i bytes to maintain metadata "
"consistency. THIS MEANS YOU ARE "
"LOSING %i BYTES DATA FROM THIS %s."
,
vi
->
i_ino
,
(
unsigned
)
le32_to_cpu
(
ni
->
type
),
err2
,
attr_size
,
err2
-
attr_size
,
((
ni
->
type
==
AT_DATA
)
&&
!
ni
->
name_len
)
?
"FILE"
:
"ATTRIBUTE"
);
write_lock_irqsave
(
&
ni
->
size_lock
,
flags
);
ni
->
initialized_size
=
attr_size
;
i_size_write
(
vi
,
attr_size
);
write_unlock_irqrestore
(
&
ni
->
size_lock
,
flags
);
}
}
/* Setup the fields specific to resident attributes. */
a
->
data
.
resident
.
value_length
=
cpu_to_le32
(
attr_size
);
a
->
data
.
resident
.
value_offset
=
cpu_to_le16
(
mp_ofs
);
a
->
data
.
resident
.
flags
=
old_res_attr_flags
;
memset
(
&
a
->
data
.
resident
.
reserved
,
0
,
sizeof
(
a
->
data
.
resident
.
reserved
));
/* Copy the data from the page back to the attribute value. */
if
(
page
)
{
kaddr
=
kmap_atomic
(
page
,
KM_USER0
);
memcpy
((
u8
*
)
a
+
mp_ofs
,
kaddr
,
attr_size
);
kunmap_atomic
(
kaddr
,
KM_USER0
);
}
/* Finally setup the ntfs inode appropriately. */
write_lock_irqsave
(
&
ni
->
size_lock
,
flags
);
ni
->
allocated_size
=
arec_size
-
mp_ofs
;
write_unlock_irqrestore
(
&
ni
->
size_lock
,
flags
);
NInoClearNonResident
(
ni
);
/* Mark the mft record dirty, so it gets written back. */
flush_dcache_mft_record_page
(
ctx
->
ntfs_ino
);
mark_mft_record_dirty
(
ctx
->
ntfs_ino
);
err_out:
if
(
ctx
)
ntfs_attr_put_search_ctx
(
ctx
);
if
(
m
)
unmap_mft_record
(
base_ni
);
ni
->
runlist
.
rl
=
NULL
;
up_write
(
&
ni
->
runlist
.
lock
);
rl_err_out:
if
(
rl
)
{
if
(
ntfs_cluster_free_from_rl
(
vol
,
rl
)
<
0
)
{
ntfs_free
(
rl
);
ntfs_error
(
vol
->
sb
,
"Failed to release allocated "
"cluster(s) in error code path. Run "
"chkdsk to recover the lost "
"cluster(s)."
);
NVolSetErrors
(
vol
);
}
page_err_out:
unlock_page
(
page
);
page_cache_release
(
page
);
}
if
(
err
==
-
EINVAL
)
err
=
-
EIO
;
return
err
;
}
/**
* ntfs_attr_set - fill (a part of) an attribute with a byte
* @ni: ntfs inode describing the attribute to fill
...
...
fs/ntfs/attrib.h
View file @
2bfb4fff
...
...
@@ -98,6 +98,8 @@ extern int ntfs_attr_can_be_resident(const ntfs_volume *vol,
extern
int
ntfs_attr_record_resize
(
MFT_RECORD
*
m
,
ATTR_RECORD
*
a
,
u32
new_size
);
extern
int
ntfs_attr_make_non_resident
(
ntfs_inode
*
ni
);
extern
int
ntfs_attr_set
(
ntfs_inode
*
ni
,
const
s64
ofs
,
const
s64
cnt
,
const
u8
val
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment