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
7cd4efed
Commit
7cd4efed
authored
Nov 23, 2007
by
Linus Torvalds
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Import 2.4.0-test6pre5
parent
d5c12956
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
268 additions
and
267 deletions
+268
-267
fs/ext2/inode.c
fs/ext2/inode.c
+268
-267
No files found.
fs/ext2/inode.c
View file @
7cd4efed
...
@@ -18,6 +18,8 @@
...
@@ -18,6 +18,8 @@
* David S. Miller (davem@caip.rutgers.edu), 1995
* David S. Miller (davem@caip.rutgers.edu), 1995
* 64-bit file support on 64-bit platforms by Jakub Jelinek
* 64-bit file support on 64-bit platforms by Jakub Jelinek
* (jj@sunsite.ms.mff.cuni.cz)
* (jj@sunsite.ms.mff.cuni.cz)
*
* Assorted race fixes, rewrite of ext2_get_block() by Al Viro, 2000
*/
*/
#include <linux/fs.h>
#include <linux/fs.h>
...
@@ -26,8 +28,6 @@
...
@@ -26,8 +28,6 @@
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/highuid.h>
#include <linux/highuid.h>
static
int
ext2_update_inode
(
struct
inode
*
inode
,
int
do_sync
);
static
int
ext2_update_inode
(
struct
inode
*
inode
,
int
do_sync
);
/*
/*
...
@@ -64,23 +64,18 @@ void ext2_delete_inode (struct inode * inode)
...
@@ -64,23 +64,18 @@ void ext2_delete_inode (struct inode * inode)
clear_inode
(
inode
);
/* We must guarantee clearing of inode... */
clear_inode
(
inode
);
/* We must guarantee clearing of inode... */
}
}
/*
* ext2_discard_prealloc and ext2_alloc_block are atomic wrt. the
* superblock in the same manner as are ext2_free_blocks and
* ext2_new_block. We just wait on the super rather than locking it
* here, since ext2_new_block will do the necessary locking and we
* can't block until then.
*/
void
ext2_discard_prealloc
(
struct
inode
*
inode
)
void
ext2_discard_prealloc
(
struct
inode
*
inode
)
{
{
#ifdef EXT2_PREALLOCATE
#ifdef EXT2_PREALLOCATE
unsigned
short
total
;
lock_kernel
();
lock_kernel
();
/* Writer: ->i_prealloc* */
if
(
inode
->
u
.
ext2_i
.
i_prealloc_count
)
{
if
(
inode
->
u
.
ext2_i
.
i_prealloc_count
)
{
total
=
inode
->
u
.
ext2_i
.
i_prealloc_count
;
unsigned
short
total
=
inode
->
u
.
ext2_i
.
i_prealloc_count
;
unsigned
long
block
=
inode
->
u
.
ext2_i
.
i_prealloc_block
;
inode
->
u
.
ext2_i
.
i_prealloc_count
=
0
;
inode
->
u
.
ext2_i
.
i_prealloc_count
=
0
;
ext2_free_blocks
(
inode
,
inode
->
u
.
ext2_i
.
i_prealloc_block
,
total
);
inode
->
u
.
ext2_i
.
i_prealloc_block
=
0
;
/* Writer: end */
ext2_free_blocks
(
inode
,
block
,
total
);
}
}
unlock_kernel
();
unlock_kernel
();
#endif
#endif
...
@@ -93,22 +88,26 @@ static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err)
...
@@ -93,22 +88,26 @@ static int ext2_alloc_block (struct inode * inode, unsigned long goal, int *err)
#endif
#endif
unsigned
long
result
;
unsigned
long
result
;
wait_on_super
(
inode
->
i_sb
);
#ifdef EXT2_PREALLOCATE
#ifdef EXT2_PREALLOCATE
/* Writer: ->i_prealloc* */
if
(
inode
->
u
.
ext2_i
.
i_prealloc_count
&&
if
(
inode
->
u
.
ext2_i
.
i_prealloc_count
&&
(
goal
==
inode
->
u
.
ext2_i
.
i_prealloc_block
||
(
goal
==
inode
->
u
.
ext2_i
.
i_prealloc_block
||
goal
+
1
==
inode
->
u
.
ext2_i
.
i_prealloc_block
))
goal
+
1
==
inode
->
u
.
ext2_i
.
i_prealloc_block
))
{
{
result
=
inode
->
u
.
ext2_i
.
i_prealloc_block
++
;
result
=
inode
->
u
.
ext2_i
.
i_prealloc_block
++
;
inode
->
u
.
ext2_i
.
i_prealloc_count
--
;
inode
->
u
.
ext2_i
.
i_prealloc_count
--
;
/* Writer: end */
#ifdef EXT2FS_DEBUG
ext2_debug
(
"preallocation hit (%lu/%lu).
\n
"
,
ext2_debug
(
"preallocation hit (%lu/%lu).
\n
"
,
++
alloc_hits
,
++
alloc_attempts
);
++
alloc_hits
,
++
alloc_attempts
);
#endif
}
else
{
}
else
{
ext2_discard_prealloc
(
inode
);
ext2_discard_prealloc
(
inode
);
#ifdef EXT2FS_DEBUG
ext2_debug
(
"preallocation miss (%lu/%lu).
\n
"
,
ext2_debug
(
"preallocation miss (%lu/%lu).
\n
"
,
alloc_hits
,
++
alloc_attempts
);
alloc_hits
,
++
alloc_attempts
);
#endif
if
(
S_ISREG
(
inode
->
i_mode
))
if
(
S_ISREG
(
inode
->
i_mode
))
result
=
ext2_new_block
(
inode
,
goal
,
result
=
ext2_new_block
(
inode
,
goal
,
&
inode
->
u
.
ext2_i
.
i_prealloc_count
,
&
inode
->
u
.
ext2_i
.
i_prealloc_count
,
...
@@ -299,307 +298,309 @@ static inline Indirect *ext2_get_branch(struct inode *inode,
...
@@ -299,307 +298,309 @@ static inline Indirect *ext2_get_branch(struct inode *inode,
return
p
;
return
p
;
}
}
static
struct
buffer_head
*
inode_getblk
(
struct
inode
*
inode
,
int
nr
,
/**
int
new_block
,
int
*
err
,
int
metadata
,
long
*
phys
,
int
*
new
)
* ext2_find_near - find a place for allocation with sufficient locality
* @inode: owner
* @ind: descriptor of indirect block.
*
* This function returns the prefered place for block allocation.
* It is used when heuristic for sequential allocation fails.
* Rules are:
* + if there is a block to the left of our position - allocate near it.
* + if pointer will live in indirect block - allocate near that block.
* + if pointer will live in inode - allocate in the same cylinder group.
* Caller must make sure that @ind is valid and will stay that way.
*/
static
inline
unsigned
long
ext2_find_near
(
struct
inode
*
inode
,
Indirect
*
ind
)
{
{
u32
*
p
;
u32
*
start
=
ind
->
bh
?
(
u32
*
)
ind
->
bh
->
b_data
:
inode
->
u
.
ext2_i
.
i_data
;
int
tmp
,
goal
=
0
;
u32
*
p
;
struct
buffer_head
*
result
;
int
blocksize
=
inode
->
i_sb
->
s_blocksize
;
p
=
inode
->
u
.
ext2_i
.
i_data
+
nr
;
/* Try to find previous block */
repeat:
for
(
p
=
ind
->
p
-
1
;
p
>=
start
;
p
--
)
tmp
=
le32_to_cpu
(
*
p
);
if
(
*
p
)
if
(
tmp
)
{
return
le32_to_cpu
(
*
p
);
if
(
metadata
)
{
result
=
getblk
(
inode
->
i_dev
,
tmp
,
blocksize
);
if
(
tmp
==
le32_to_cpu
(
*
p
))
return
result
;
brelse
(
result
);
goto
repeat
;
}
else
{
*
phys
=
tmp
;
return
NULL
;
}
}
if
(
inode
->
u
.
ext2_i
.
i_next_alloc_block
==
new_block
)
/* No such thing, so let's try location of indirect block */
goal
=
inode
->
u
.
ext2_i
.
i_next_alloc_goal
;
if
(
ind
->
bh
)
return
ind
->
bh
->
b_blocknr
;
ext2_debug
(
"hint = %d,"
,
goal
);
/*
* It is going to be refered from inode itself? OK, just put it into
* the same cylinder group then.
*/
return
(
inode
->
u
.
ext2_i
.
i_block_group
*
EXT2_BLOCKS_PER_GROUP
(
inode
->
i_sb
))
+
le32_to_cpu
(
inode
->
i_sb
->
u
.
ext2_sb
.
s_es
->
s_first_data_block
);
}
if
(
!
goal
)
{
/**
for
(
tmp
=
nr
-
1
;
tmp
>=
0
;
tmp
--
)
{
* ext2_find_goal - find a prefered place for allocation.
if
(
inode
->
u
.
ext2_i
.
i_data
[
tmp
])
{
* @inode: owner
goal
=
le32_to_cpu
(
inode
->
u
.
ext2_i
.
i_data
[
tmp
]);
* @block: block we want
break
;
* @chain: chain of indirect blocks
}
* @partial: pointer to the last triple within a chain
}
* @goal: place to store the result.
if
(
!
goal
)
*
goal
=
(
inode
->
u
.
ext2_i
.
i_block_group
*
* Normally this function find the prefered place for block allocation,
EXT2_BLOCKS_PER_GROUP
(
inode
->
i_sb
))
+
* stores it in *@goal and returns zero. If the branch had been changed
le32_to_cpu
(
inode
->
i_sb
->
u
.
ext2_sb
.
s_es
->
s_first_data_block
);
* under us we return -EAGAIN.
}
*/
ext2_debug
(
"goal = %d.
\n
"
,
goal
);
static
inline
int
ext2_find_goal
(
struct
inode
*
inode
,
long
block
,
tmp
=
ext2_alloc_block
(
inode
,
goal
,
err
);
Indirect
chain
[
4
],
if
(
!
tmp
)
Indirect
*
partial
,
return
NULL
;
unsigned
long
*
goal
)
{
if
(
metadata
)
{
/* Writer: ->i_next_alloc* */
result
=
getblk
(
inode
->
i_dev
,
tmp
,
blocksize
);
if
(
block
==
inode
->
u
.
ext2_i
.
i_next_alloc_block
+
1
)
{
if
(
!
buffer_uptodate
(
result
))
inode
->
u
.
ext2_i
.
i_next_alloc_block
++
;
wait_on_buffer
(
result
);
inode
->
u
.
ext2_i
.
i_next_alloc_goal
++
;
memset
(
result
->
b_data
,
0
,
blocksize
);
}
mark_buffer_uptodate
(
result
,
1
);
/* Writer: end */
mark_buffer_dirty
(
result
,
1
);
/* Reader: pointers, ->i_next_alloc* */
if
(
*
p
)
{
if
(
verify_chain
(
chain
,
partial
))
{
ext2_free_blocks
(
inode
,
tmp
,
1
);
/*
bforget
(
result
);
* try the heuristic for sequential allocation,
goto
repeat
;
* failing that at least try to get decent locality.
}
*/
}
else
{
if
(
block
==
inode
->
u
.
ext2_i
.
i_next_alloc_block
)
if
(
*
p
)
{
*
goal
=
inode
->
u
.
ext2_i
.
i_next_alloc_goal
;
/*
if
(
!*
goal
)
* Nobody is allowed to change block allocation
*
goal
=
ext2_find_near
(
inode
,
partial
);
* state from under us:
return
0
;
*/
ext2_error
(
inode
->
i_sb
,
"block_getblk"
,
"data block filled under us"
);
BUG
();
ext2_free_blocks
(
inode
,
tmp
,
1
);
goto
repeat
;
}
*
phys
=
tmp
;
result
=
NULL
;
*
err
=
0
;
*
new
=
1
;
}
}
*
p
=
cpu_to_le32
(
tmp
);
/* Reader: end */
return
-
EAGAIN
;
inode
->
u
.
ext2_i
.
i_next_alloc_block
=
new_block
;
inode
->
u
.
ext2_i
.
i_next_alloc_goal
=
tmp
;
inode
->
i_ctime
=
CURRENT_TIME
;
inode
->
i_blocks
+=
blocksize
/
512
;
if
(
IS_SYNC
(
inode
)
||
inode
->
u
.
ext2_i
.
i_osync
)
ext2_sync_inode
(
inode
);
else
mark_inode_dirty
(
inode
);
return
result
;
}
}
/*
/**
* metadata / data
* ext2_alloc_branch - allocate and set up a chain of blocks.
* possibly create / access
* @inode: owner
* can fail due to: - not present
* @num: depth of the chain (number of blocks to allocate)
* - out of space
* @offsets: offsets (in the blocks) to store the pointers to next.
* @branch: place to store the chain in.
*
* This function allocates @num blocks, zeroes out all but the last one,
* links them into chain and (if we are synchronous) writes them to disk.
* In other words, it prepares a branch that can be spliced onto the
* inode. It stores the information about that chain in the branch[], in
* the same format as ext2_get_branch() would do. We are calling it after
* we had read the existing part of chain and partial points to the last
* triple of that (one with zero ->key). Upon the exit we have the same
* picture as after the successful ext2_get_block(), excpet that in one
* place chain is disconnected - *branch->p is still zero (we did not
* set the last link), but branch->key contains the number that should
* be placed into *branch->p to fill that gap.
*
*
* NULL return in the data case is mandatory.
* If allocation fails we free all blocks we've allocated (and forget
* ther buffer_heads) and return the error value the from failed
* ext2_alloc_block() (normally -ENOSPC). Otherwise we set the chain
* as described above and return 0.
*/
*/
static
struct
buffer_head
*
block_getblk
(
struct
inode
*
inode
,
struct
buffer_head
*
bh
,
int
nr
,
static
int
ext2_alloc_branch
(
struct
inode
*
inode
,
int
new_block
,
int
*
err
,
int
metadata
,
long
*
phys
,
int
*
new
)
int
num
,
unsigned
long
goal
,
int
*
offsets
,
Indirect
*
branch
)
{
{
int
tmp
,
goal
=
0
;
u32
*
p
;
struct
buffer_head
*
result
;
int
blocksize
=
inode
->
i_sb
->
s_blocksize
;
int
blocksize
=
inode
->
i_sb
->
s_blocksize
;
int
n
=
0
;
int
err
;
int
i
;
int
parent
=
ext2_alloc_block
(
inode
,
goal
,
&
err
);
result
=
NULL
;
branch
[
0
].
key
=
cpu_to_le32
(
parent
);
if
(
!
bh
)
if
(
parent
)
for
(
n
=
1
;
n
<
num
;
n
++
)
{
goto
out
;
struct
buffer_head
*
bh
;
if
(
!
buffer_uptodate
(
bh
))
{
/* Allocate the next block */
ll_rw_block
(
READ
,
1
,
&
bh
);
int
nr
=
ext2_alloc_block
(
inode
,
parent
,
&
err
);
wait_on_buffer
(
bh
);
if
(
!
nr
)
break
;
branch
[
n
].
key
=
cpu_to_le32
(
nr
);
/*
* Get buffer_head for parent block, zero it out and set
* the pointer to new one, then send parent to disk.
*/
bh
=
getblk
(
inode
->
i_dev
,
parent
,
blocksize
);
if
(
!
buffer_uptodate
(
bh
))
if
(
!
buffer_uptodate
(
bh
))
goto
out
;
wait_on_buffer
(
bh
);
}
memset
(
bh
->
b_data
,
0
,
blocksize
);
p
=
(
u32
*
)
bh
->
b_data
+
nr
;
branch
[
n
].
bh
=
bh
;
repeat:
branch
[
n
].
p
=
(
u32
*
)
bh
->
b_data
+
offsets
[
n
];
tmp
=
le32_to_cpu
(
*
p
);
*
branch
[
n
].
p
=
branch
[
n
].
key
;
if
(
tmp
)
{
mark_buffer_uptodate
(
bh
,
1
);
if
(
metadata
)
{
mark_buffer_dirty
(
bh
,
1
);
result
=
getblk
(
bh
->
b_dev
,
tmp
,
blocksize
);
if
(
IS_SYNC
(
inode
)
||
inode
->
u
.
ext2_i
.
i_osync
)
{
if
(
tmp
==
le32_to_cpu
(
*
p
))
ll_rw_block
(
WRITE
,
1
,
&
bh
);
goto
out
;
wait_on_buffer
(
bh
);
brelse
(
result
);
goto
repeat
;
}
else
{
*
phys
=
tmp
;
/* result == NULL */
goto
out
;
}
}
parent
=
nr
;
}
}
if
(
n
==
num
)
return
0
;
if
(
inode
->
u
.
ext2_i
.
i_next_alloc_block
==
new_block
)
/* Allocation failed, free what we already allocated */
goal
=
inode
->
u
.
ext2_i
.
i_next_alloc_goal
;
for
(
i
=
1
;
i
<
n
;
i
++
)
if
(
!
goal
)
{
bforget
(
branch
[
i
].
bh
);
for
(
tmp
=
nr
-
1
;
tmp
>=
0
;
tmp
--
)
{
for
(
i
=
0
;
i
<
n
;
i
++
)
if
(
le32_to_cpu
(((
u32
*
)
bh
->
b_data
)[
tmp
]))
{
ext2_free_blocks
(
inode
,
le32_to_cpu
(
branch
[
i
].
key
),
1
);
goal
=
le32_to_cpu
(((
u32
*
)
bh
->
b_data
)[
tmp
]);
return
err
;
break
;
}
}
}
/**
if
(
!
goal
)
* ext2_splice_branch - splice the allocated branch onto inode.
goal
=
bh
->
b_blocknr
;
* @inode: owner
}
* @block: (logical) number of block we are adding
tmp
=
ext2_alloc_block
(
inode
,
goal
,
err
);
* @chain: chain of indirect blocks (with a missing link - see
if
(
!
tmp
)
* ext2_alloc_branch)
goto
out
;
* @where: location of missing link
if
(
metadata
)
{
* @num: number of blocks we are adding
result
=
getblk
(
bh
->
b_dev
,
tmp
,
blocksize
);
*
if
(
!
buffer_uptodate
(
result
))
* This function verifies that chain (up to the missing link) had not
wait_on_buffer
(
result
);
* changed, fills the missing link and does all housekeeping needed in
memset
(
result
->
b_data
,
0
,
inode
->
i_sb
->
s_blocksize
);
* inode (->i_blocks, etc.). In case of success we end up with the full
mark_buffer_uptodate
(
result
,
1
);
* chain to new block and return 0. Otherwise (== chain had been changed)
mark_buffer_dirty
(
result
,
1
);
* we free the new blocks (forgetting their buffer_heads, indeed) and
if
(
*
p
)
{
* return -EAGAIN.
ext2_free_blocks
(
inode
,
tmp
,
1
);
*/
bforget
(
result
);
goto
repeat
;
static
inline
int
ext2_splice_branch
(
struct
inode
*
inode
,
}
long
block
,
}
else
{
Indirect
chain
[
4
],
if
(
*
p
)
{
Indirect
*
where
,
/*
int
num
)
* Nobody is allowed to change block allocation
{
* state from under us:
int
i
;
*/
ext2_error
(
inode
->
i_sb
,
"block_getblk"
,
/* Verify that place we are splicing to is still there and vacant */
"data block filled under us"
);
BUG
();
/* Writer: pointers, ->i_next_alloc*, ->i_blocks */
ext2_free_blocks
(
inode
,
tmp
,
1
);
if
(
!
verify_chain
(
chain
,
where
-
1
)
||
*
where
->
p
)
goto
repeat
;
/* Writer: end */
goto
changed
;
/* That's it */
*
where
->
p
=
where
->
key
;
inode
->
u
.
ext2_i
.
i_next_alloc_block
=
block
;
inode
->
u
.
ext2_i
.
i_next_alloc_goal
=
le32_to_cpu
(
where
[
num
-
1
].
key
);
inode
->
i_blocks
+=
num
*
inode
->
i_sb
->
s_blocksize
/
512
;
/* Writer: end */
/* We are done with atomic stuff, now do the rest of housekeeping */
inode
->
i_ctime
=
CURRENT_TIME
;
/* had we spliced it onto indirect block? */
if
(
where
->
bh
)
{
mark_buffer_dirty
(
where
->
bh
,
1
);
if
(
IS_SYNC
(
inode
)
||
inode
->
u
.
ext2_i
.
i_osync
)
{
ll_rw_block
(
WRITE
,
1
,
&
where
->
bh
);
wait_on_buffer
(
where
->
bh
);
}
}
*
phys
=
tmp
;
*
new
=
1
;
}
*
p
=
le32_to_cpu
(
tmp
);
mark_buffer_dirty
(
bh
,
1
);
if
(
IS_SYNC
(
inode
)
||
inode
->
u
.
ext2_i
.
i_osync
)
{
ll_rw_block
(
WRITE
,
1
,
&
bh
);
wait_on_buffer
(
bh
);
}
}
inode
->
i_ctime
=
CURRENT_TIME
;
inode
->
i_blocks
+=
blocksize
/
512
;
if
(
IS_SYNC
(
inode
)
||
inode
->
u
.
ext2_i
.
i_osync
)
mark_inode_dirty
(
inode
);
ext2_sync_inode
(
inode
);
inode
->
u
.
ext2_i
.
i_next_alloc_block
=
new_block
;
else
inode
->
u
.
ext2_i
.
i_next_alloc_goal
=
tmp
;
mark_inode_dirty
(
inode
);
*
err
=
0
;
return
0
;
out:
brelse
(
bh
);
changed:
return
result
;
for
(
i
=
1
;
i
<
num
;
i
++
)
bforget
(
where
[
i
].
bh
);
for
(
i
=
0
;
i
<
num
;
i
++
)
ext2_free_blocks
(
inode
,
le32_to_cpu
(
where
[
i
].
key
),
1
);
return
-
EAGAIN
;
}
}
/*
* Allocation strategy is simple: if we have to allocate something, we will
* have to go the whole way to leaf. So let's do it before attaching anything
* to tree, set linkage between the newborn blocks, write them if sync is
* required, recheck the path, free and repeat if check fails, otherwise
* set the last missing link (that will protect us from any truncate-generated
* removals - all blocks on the path are immune now) and possibly force the
* write on the parent block.
* That has a nice additional property: no special recovery from the failed
* allocations is needed - we simply release blocks and do not touch anything
* reachable from inode.
*/
static
int
ext2_get_block
(
struct
inode
*
inode
,
long
iblock
,
struct
buffer_head
*
bh_result
,
int
create
)
static
int
ext2_get_block
(
struct
inode
*
inode
,
long
iblock
,
struct
buffer_head
*
bh_result
,
int
create
)
{
{
int
ret
,
err
,
new
;
int
err
=
-
EIO
;
struct
buffer_head
*
bh
;
unsigned
long
phys
;
int
offsets
[
4
];
int
offsets
[
4
];
int
*
p
;
Indirect
chain
[
4
];
Indirect
chain
[
4
];
Indirect
*
partial
;
Indirect
*
partial
;
int
depth
;
unsigned
long
goal
;
int
left
;
int
depth
=
ext2_block_to_path
(
inode
,
iblock
,
offsets
);
depth
=
ext2_block_to_path
(
inode
,
iblock
,
offsets
);
if
(
depth
==
0
)
if
(
depth
==
0
)
goto
abor
t
;
goto
ou
t
;
lock_kernel
();
lock_kernel
();
reread:
partial
=
ext2_get_branch
(
inode
,
depth
,
offsets
,
chain
,
&
err
);
partial
=
ext2_get_branch
(
inode
,
depth
,
offsets
,
chain
,
&
err
);
/* Simplest case - block found, no allocation needed */
if
(
!
partial
)
{
if
(
!
partial
)
{
unlock_kernel
();
got_it:
for
(
partial
=
chain
+
depth
-
1
;
partial
>
chain
;
partial
--
)
brelse
(
partial
->
bh
);
bh_result
->
b_dev
=
inode
->
i_dev
;
bh_result
->
b_dev
=
inode
->
i_dev
;
bh_result
->
b_blocknr
=
le32_to_cpu
(
chain
[
depth
-
1
].
key
);
bh_result
->
b_blocknr
=
le32_to_cpu
(
chain
[
depth
-
1
].
key
);
bh_result
->
b_state
|=
(
1UL
<<
BH_Mapped
);
bh_result
->
b_state
|=
(
1UL
<<
BH_Mapped
);
return
0
;
/* Clean up and exit */
partial
=
chain
+
depth
-
1
;
/* the whole chain */
goto
cleanup
;
}
}
while
(
partial
>
chain
)
{
/* Next simple case - plain lookup or failed read of indirect block */
brelse
(
partial
->
bh
);
if
(
!
create
||
err
==
-
EIO
)
{
partial
--
;
cleanup:
}
while
(
partial
>
chain
)
{
brelse
(
partial
->
bh
);
if
(
!
create
)
{
partial
--
;
}
unlock_kernel
();
unlock_kernel
();
return
0
;
out:
return
err
;
}
}
err
=
-
EIO
;
new
=
0
;
ret
=
0
;
bh
=
NULL
;
/*
/*
* I
f this is a sequential block allocation, set the next_alloc_block
* I
ndirect block might be removed by truncate while we were
*
to this block now so that all the indblock and data block
*
reading it. Handling of that case (forget what we've got and
*
allocations use the same goal zone
*
reread) is taken out of the main path.
*/
*/
if
(
err
==
-
EAGAIN
)
goto
changed
;
ext2_debug
(
"block %lu, next %lu, goal %lu.
\n
"
,
iblock
,
if
(
ext2_find_goal
(
inode
,
iblock
,
chain
,
partial
,
&
goal
)
<
0
)
inode
->
u
.
ext2_i
.
i_next_alloc_block
,
goto
changed
;
inode
->
u
.
ext2_i
.
i_next_alloc_goal
);
if
(
iblock
==
inode
->
u
.
ext2_i
.
i_next_alloc_block
+
1
)
{
left
=
(
chain
+
depth
)
-
partial
;
inode
->
u
.
ext2_i
.
i_next_alloc_block
++
;
err
=
ext2_alloc_branch
(
inode
,
left
,
goal
,
inode
->
u
.
ext2_i
.
i_next_alloc_goal
++
;
offsets
+
(
partial
-
chain
),
partial
);
}
if
(
err
)
goto
cleanup
;
err
=
0
;
if
(
ext2_splice_branch
(
inode
,
iblock
,
chain
,
partial
,
left
)
<
0
)
goto
changed
;
/*
bh_result
->
b_state
|=
(
1UL
<<
BH_New
);
* ok, these macros clean the logic up a bit and make
goto
got_it
;
* it much more readable:
*/
#define GET_INODE_DATABLOCK(x) \
inode_getblk(inode, x, iblock, &err, 0, &phys, &new)
#define GET_INODE_PTR(x) \
inode_getblk(inode, x, iblock, &err, 1, NULL, NULL)
#define GET_INDIRECT_DATABLOCK(x) \
block_getblk (inode, bh, x, iblock, &err, 0, &phys, &new);
#define GET_INDIRECT_PTR(x) \
block_getblk (inode, bh, x, iblock, &err, 1, NULL, NULL);
p
=
offsets
;
if
(
depth
==
1
)
{
bh
=
GET_INODE_DATABLOCK
(
*
p
);
goto
out
;
}
bh
=
GET_INODE_PTR
(
*
p
);
switch
(
depth
)
{
default:
/* case 4: */
bh
=
GET_INDIRECT_PTR
(
*++
p
);
case
3
:
bh
=
GET_INDIRECT_PTR
(
*++
p
);
case
2
:
bh
=
GET_INDIRECT_DATABLOCK
(
*++
p
);
}
#undef GET_INODE_DATABLOCK
#undef GET_INODE_PTR
#undef GET_INDIRECT_DATABLOCK
#undef GET_INDIRECT_PTR
out:
changed:
if
(
bh
)
while
(
partial
>
chain
)
{
BUG
();
// temporary debugging check
bforget
(
partial
->
bh
);
if
(
err
)
partial
--
;
goto
abort
;
}
if
(
!
phys
)
goto
reread
;
BUG
();
// must not happen either
bh_result
->
b_dev
=
inode
->
i_dev
;
bh_result
->
b_blocknr
=
phys
;
bh_result
->
b_state
|=
(
1UL
<<
BH_Mapped
);
/* safe */
if
(
new
)
bh_result
->
b_state
|=
(
1UL
<<
BH_New
);
unlock_kernel
();
abort:
return
err
;
}
}
struct
buffer_head
*
ext2_getblk
(
struct
inode
*
inode
,
long
block
,
int
create
,
int
*
err
)
struct
buffer_head
*
ext2_getblk
(
struct
inode
*
inode
,
long
block
,
int
create
,
int
*
err
)
...
...
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