Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
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
Show 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