fat: Fix error handling in buffer IO functions
* Have bread() output a NULL *bpp when returning an error (consistent
with FreeBSD).
* Enable brelse() to handle a NULL argument (consistent with FreeBSD).
* Ensure that when getblkx() returns an error, no clean-up remains to
be done by bread() or getblk().
* Let allocate_data() set buf::b_owned, and set it only if successful
in allocating, so the flag won't be misleading to put_buf() after a
failure.
* Revise vput() to avoid situations when, after an error, it might
call put_vnode() before the node is published to the VFS.
Change-Id: I42cc1684fe5b68333284b149e72a794c93ac71ff
Reviewed-on: https://review.haiku-os.org/c/haiku/+/9393
Reviewed-by: waddlesplash <waddlesplash@gmail.com>
Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
Diff
src/add-ons/kernel/file_systems/fat/bsd/kern/vfs_bio.c | 31 +++++++++++++++++++++++--------
src/add-ons/kernel/file_systems/fat/bsd/sys/vnode.h | 6 +++++-
2 files changed, 26 insertions(+), 11 deletions(-)
@@ -95,6 +95,8 @@
if (error == 0)
*bpp = buf;
else
*bpp = NULL;
return error;
}
@@ -198,12 +200,15 @@
}
/*! Each bread call must be balanced with either a b(d/a)write (to write changes) or a brelse.
/*! Each successful bread call must be balanced with either a b(d/a)write (to write changes) or a
brelse.
*/
void
brelse(struct buf* bp)
{
if (bp == NULL)
return;
if (bp->b_vreg != NULL) {
put_buf(bp);
return;
@@ -262,6 +267,7 @@
@param splflag Ignored in the port.
@param slptimeo Ignored in the port.
@param flags Ignored in the port.
@param bpp If no error is returned, *bpp contains the address of the requested buf.
*/
int
getblkx(struct vnode* vp, daddr_t blkno, daddr_t dblkno, int size, int slpflag, int slptimeo,
@@ -363,10 +369,11 @@
off_t fileOffset;
size_t bytesRead;
newBuf->b_owned = true;
status = allocate_data(newBuf, size);
if (status != B_OK)
if (status != B_OK) {
put_buf(newBuf);
return B_TO_POSIX_ERROR(status);
}
@@ -374,7 +381,7 @@
(*bpp) = newBuf;
return B_TO_POSIX_ERROR(status);
return 0;
}
fileOffset = de_cn2off(fatVolume, blkno);
@@ -402,10 +409,11 @@
newBuf->b_bufsize = CACHED_BLOCK_SIZE;
} else {
newBuf->b_owned = true;
status = allocate_data(newBuf, size);
if (status != 0)
if (status != 0) {
put_buf(newBuf);
return B_TO_POSIX_ERROR(status);
}
#ifdef _KERNEL_MODE
@@ -415,7 +423,7 @@
}
#endif
for (i = 0; i < cBlockCount && status == B_OK; i++) {
for (i = 0; i < cBlockCount; i++) {
if (readOnly == true)
newBuf->b_bcpointers[i] = (void*)block_cache_get(blockCache, dblkno + i);
else
@@ -438,7 +446,7 @@
*bpp = newBuf;
return B_TO_POSIX_ERROR(status);
return 0;
}
@@ -503,7 +511,8 @@
/*! Added for the Haiku port. Ensure that buf->b_data points to 'size' bytes of zero'd memory.
@post If no error is returned, buf->b_owned is set to true. If an error is returned,
no memory is allocated for buf->b_data.
*/
static status_t
allocate_data(struct buf* buf, int size)
@@ -527,6 +536,8 @@
buf->b_bufsize = size;
}
}
buf->b_owned = true;
return B_OK;
}
@@ -246,7 +246,11 @@
static inline void
vput(struct vnode* vp)
{
put_vnode(vp->v_mount->mnt_fsvolume, VTODE(vp)->de_inode);
if (vp->v_state != VSTATE_UNINITIALIZED)
put_vnode(vp->v_mount->mnt_fsvolume, VTODE(vp)->de_inode);
rw_lock_write_unlock(&vp->v_vnlock->haikuRW);
}