#include "sys/param.h"
#include "sys/systm.h"
#include "sys/buf.h"
#include "sys/malloc.h"
#include "sys/vnode.h"
#include "sys/conf.h"
#include "fs/msdosfs/bpb.h"
#include "fs/msdosfs/denode.h"
#include "fs/msdosfs/direntry.h"
#include "fs/msdosfs/msdosfsmount.h"
#include "dosfs.h"
#ifdef USER
#define dprintf printf
#endif
int msdosfs_bmap(struct vnode* a_vp, daddr_t a_bn, struct bufobj** a_bop, daddr_t* a_bnp,
int* a_runp, int* a_runb);
int getblkx(struct vnode* vp, daddr_t blkno, daddr_t dblkno, int size, int slpflag, int slptimeo,
int flags, struct buf** bpp);
static status_t allocate_data(struct buf* buf, int size);
static status_t put_buf(struct buf* buf);
int
buf_dirty_count_severe(void)
{
return 0;
}
int
bread(struct vnode* vp, daddr_t blkno, int size, struct ucred* cred, struct buf** bpp)
{
struct buf* buf = NULL;
int error;
error = getblkx(vp, blkno, blkno, size, 0, 0, 0, &buf);
if (error == 0)
*bpp = buf;
else
*bpp = NULL;
return error;
}
static status_t
_bwrite(struct buf* buf)
{
struct vnode* deviceNode = buf->b_vp;
struct mount* bsdVolume = deviceNode->v_rdev->si_mountpt;
void* blockCache = bsdVolume->mnt_cache;
struct msdosfsmount* fatVolume = (struct msdosfsmount*)bsdVolume->mnt_data;
status_t status = B_OK;
ASSERT(MOUNTED_READ_ONLY(fatVolume) == 0);
if (buf->b_vreg != NULL) {
struct vnode* bsdNode = buf->b_vreg;
struct denode* fatNode = (struct denode*)bsdNode->v_data;
off_t fileOffset = 0;
size_t bytesWritten = 0;
if (bsdNode->v_resizing == true)
return status;
ASSERT((fatNode->de_Attributes & ATTR_READONLY) == 0);
fileOffset = de_cn2off(fatVolume, buf->b_lblkno);
ASSERT_ALWAYS((u_long)(fileOffset + buf->b_bufsize) <= fatNode->de_FileSize);
bytesWritten = (size_t)buf->b_bufsize;
status = file_cache_write(bsdNode->v_cache, NULL, fileOffset, buf->b_data, &bytesWritten);
if (bytesWritten != (size_t)buf->b_bufsize)
return EIO;
} else if (buf->b_owned == false) {
block_cache_put(blockCache, BLOCK_TO_SECTOR(fatVolume, buf->b_blkno));
} else {
size_t cBlockSize = fatVolume->pm_BytesPerSec;
off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, buf->b_blkno);
uint32 cBlockCount = buf->b_bufsize / cBlockSize;
uint32 i;
for (i = 0; i < cBlockCount && buf->b_bcpointers[i] != NULL; ++i) {
memcpy((caddr_t)buf->b_bcpointers[i], buf->b_data + (i * cBlockSize), cBlockSize);
block_cache_put(blockCache, cachedBlock + i);
buf->b_bcpointers[i] = NULL;
}
}
return status;
}
void
bdwrite(struct buf* bp)
{
if (_bwrite(bp) != B_OK)
return;
put_buf(bp);
return;
}
void
bawrite(struct buf* bp)
{
_bwrite(bp);
if (bp->b_vreg != NULL) {
if (bp->b_vreg->v_resizing == false)
file_cache_sync(bp->b_vreg->v_cache);
} else {
struct mount* bsdVolume = bp->b_vp->v_rdev->si_mountpt;
struct msdosfsmount* fatVolume = (struct msdosfsmount*)bsdVolume->mnt_data;
void* blockCache = bsdVolume->mnt_cache;
off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, bp->b_blkno);
if (bp->b_owned == false) {
block_cache_sync_etc(blockCache, cachedBlock, 1);
} else {
block_cache_sync_etc(blockCache, cachedBlock,
howmany(bp->b_bufsize, fatVolume->pm_BytesPerSec));
}
}
put_buf(bp);
return;
}
void
brelse(struct buf* bp)
{
if (bp == NULL)
return;
if (bp->b_vreg != NULL) {
put_buf(bp);
return;
}
struct mount* bsdVolume = bp->b_vp->v_rdev->si_mountpt;
struct msdosfsmount* fatVolume = (struct msdosfsmount*)bsdVolume->mnt_data;
void* blockCache = bsdVolume->mnt_cache;
off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, bp->b_blkno);
bool readOnly = MOUNTED_READ_ONLY(VFSTOMSDOSFS(bsdVolume));
if (bp->b_owned == false) {
if (readOnly == true)
block_cache_set_dirty(blockCache, cachedBlock, false, -1);
block_cache_put(blockCache, cachedBlock);
put_buf(bp);
} else {
uint32 cBlockCount = bp->b_bufsize / fatVolume->pm_BytesPerSec;
uint32 i;
for (i = 0; i < cBlockCount && bp->b_bcpointers[i] != NULL; ++i) {
if (readOnly == true)
block_cache_set_dirty(blockCache, cachedBlock + i, false, -1);
block_cache_put(blockCache, cachedBlock + i);
bp->b_bcpointers[i] = NULL;
}
put_buf(bp);
}
return;
}
struct buf*
getblk(struct vnode* vp, daddr_t blkno, int size, int slpflag, int slptimeo, int flags)
{
struct buf* buf = NULL;
int error = 0;
error = getblkx(vp, blkno, blkno, size, slpflag, slptimeo, flags, &buf);
if (error != 0)
return NULL;
return buf;
}
int
getblkx(struct vnode* vp, daddr_t blkno, daddr_t dblkno, int size, int slpflag, int slptimeo,
int flags, struct buf** bpp)
{
struct msdosfsmount* fatVolume;
struct vnode* deviceNode;
status_t status = B_OK;
bool readOnly = true;
bool foundExisting = false;
uint32 i;
void* blockCache = NULL;
size_t cBlockSize = 0;
uint32 cBlockCount;
struct buf* newBuf = NULL;
if (vp->v_type == VREG) {
fatVolume = vp->v_mount->mnt_data;
msdosfs_bmap(vp, blkno, NULL, &dblkno, NULL, NULL);
blockCache = vp->v_mount->mnt_cache;
readOnly
= MOUNTED_READ_ONLY(fatVolume) || ((VTODE(vp))->de_Attributes & ATTR_READONLY) != 0;
deviceNode = fatVolume->pm_devvp;
} else if (vp->v_type == VBLK) {
fatVolume = vp->v_rdev->si_mountpt->mnt_data;
blockCache = vp->v_rdev->si_mountpt->mnt_cache;
readOnly = MOUNTED_READ_ONLY(fatVolume);
deviceNode = vp;
} else {
return ENOTSUP;
}
cBlockSize = fatVolume->pm_BytesPerSec;
rw_lock_write_lock(&deviceNode->v_bufobj.bo_lock.haikuRW);
if ((size_t)size == cBlockSize && vp->v_type != VREG
&& SLIST_EMPTY(&deviceNode->v_bufobj.bo_emptybufs) == false) {
newBuf = SLIST_FIRST(&deviceNode->v_bufobj.bo_emptybufs);
SLIST_REMOVE_HEAD(&deviceNode->v_bufobj.bo_emptybufs, link);
--deviceNode->v_bufobj.bo_empties;
foundExisting = true;
} else if (size == (int)fatVolume->pm_bpcluster
&& SLIST_EMPTY(&deviceNode->v_bufobj.bo_clusterbufs) == false) {
newBuf = SLIST_FIRST(&deviceNode->v_bufobj.bo_clusterbufs);
SLIST_REMOVE_HEAD(&deviceNode->v_bufobj.bo_clusterbufs, link);
--deviceNode->v_bufobj.bo_clusters;
foundExisting = true;
} else if (size == (int)fatVolume->pm_fatblocksize
&& SLIST_EMPTY(&deviceNode->v_bufobj.bo_fatbufs) == false) {
newBuf = SLIST_FIRST(&deviceNode->v_bufobj.bo_fatbufs);
SLIST_REMOVE_HEAD(&deviceNode->v_bufobj.bo_fatbufs, link);
--deviceNode->v_bufobj.bo_fatblocks;
foundExisting = true;
}
rw_lock_write_unlock(&deviceNode->v_bufobj.bo_lock.haikuRW);
if (foundExisting == false) {
newBuf = malloc(sizeof(struct buf), 0, 0);
newBuf->b_data = NULL;
newBuf->b_bufsize = 0;
for (i = 0; i < 128; ++i)
newBuf->b_bcpointers[i] = NULL;
}
newBuf->b_bcount = size;
newBuf->b_resid = size;
newBuf->b_blkno = dblkno;
newBuf->b_flags = 0;
newBuf->b_lblkno = blkno;
newBuf->b_vp = deviceNode;
newBuf->b_owned = false;
newBuf->b_vreg = vp->v_type == VREG ? vp : NULL;
ASSERT(size == newBuf->b_resid);
cBlockCount = howmany(size, cBlockSize);
if (vp->v_type == VREG) {
off_t fileOffset;
size_t bytesRead;
status = allocate_data(newBuf, size);
if (status != B_OK) {
put_buf(newBuf);
return B_TO_POSIX_ERROR(status);
}
if (vp->v_resizing == true) {
(*bpp) = newBuf;
return 0;
}
fileOffset = de_cn2off(fatVolume, blkno);
ASSERT(size <= (int)newBuf->b_bufsize);
bytesRead = (size_t)size;
status = file_cache_read(vp->v_cache, NULL, fileOffset, newBuf->b_data, &bytesRead);
if (status != B_OK) {
put_buf(newBuf);
return B_TO_POSIX_ERROR(status);
}
if (bytesRead != (size_t)size) {
put_buf(newBuf);
return EIO;
}
} else if ((size_t)size == cBlockSize && vp->v_type != VREG) {
off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, dblkno);
if (readOnly == true)
newBuf->b_data = (void*)block_cache_get(blockCache, cachedBlock);
else
newBuf->b_data = block_cache_get_writable(blockCache, cachedBlock, -1);
if (newBuf->b_data == NULL) {
put_buf(newBuf);
return EIO;
}
newBuf->b_bufsize = cBlockSize;
} else {
off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, dblkno);
status = allocate_data(newBuf, size);
if (status != 0) {
put_buf(newBuf);
return B_TO_POSIX_ERROR(status);
}
#ifdef _KERNEL_MODE
if (cBlockCount > 4) {
size_t prefetchBlocks = cBlockCount;
block_cache_prefetch(blockCache, cachedBlock, &prefetchBlocks);
}
#endif
for (i = 0; i < cBlockCount; i++) {
if (readOnly == true)
newBuf->b_bcpointers[i] = (void*)block_cache_get(blockCache, cachedBlock + i);
else
newBuf->b_bcpointers[i] = block_cache_get_writable(blockCache, cachedBlock + i,
-1);
if (newBuf->b_bcpointers[i] == NULL) {
put_buf(newBuf);
return EIO;
}
}
ASSERT(cBlockCount * cBlockSize == (u_long)newBuf->b_bufsize);
for (i = 0; i < cBlockCount; i++) {
memcpy(newBuf->b_data + (i * cBlockSize), (caddr_t)newBuf->b_bcpointers[i],
cBlockSize);
}
}
newBuf->b_resid -= size;
ASSERT(newBuf->b_resid == 0);
*bpp = newBuf;
return 0;
}
void
vfs_bio_clrbuf(struct buf* bp)
{
return;
}
void
vfs_bio_bzero_buf(struct buf* bp, int base, int size)
{
return;
}
int
bwrite(struct buf* bp)
{
status_t status = _bwrite(bp);
if (status != B_OK) {
put_buf(bp);
return B_TO_POSIX_ERROR(status);
}
if (bp->b_vreg != NULL) {
if (bp->b_vreg->v_resizing == false) {
bp->b_vreg->v_sync = true;
status = file_cache_sync(bp->b_vreg->v_cache);
bp->b_vreg->v_sync = false;
}
} else {
struct mount* bsdVolume = bp->b_vp->v_rdev->si_mountpt;
struct msdosfsmount* fatVolume = (struct msdosfsmount*)bsdVolume->mnt_data;
void* blockCache = bsdVolume->mnt_cache;
off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, bp->b_blkno);
if (bp->b_owned == false) {
status = block_cache_sync_etc(blockCache, cachedBlock, 1);
} else {
status = block_cache_sync_etc(blockCache, cachedBlock,
howmany(bp->b_bufsize, fatVolume->pm_BytesPerSec));
}
}
put_buf(bp);
return B_TO_POSIX_ERROR(status);
}
static status_t
allocate_data(struct buf* buf, int size)
{
if (buf->b_data == NULL) {
buf->b_data = (caddr_t)calloc(size, sizeof(char));
if (buf->b_data == NULL)
return B_NO_MEMORY;
buf->b_bufsize = size;
} else {
if (buf->b_bufsize == size) {
bzero(buf->b_data, buf->b_bufsize);
} else {
free(buf->b_data, 0);
buf->b_data = (caddr_t)calloc(size, sizeof(char));
if (buf->b_data == NULL)
return B_NO_MEMORY;
buf->b_bufsize = size;
}
}
buf->b_owned = true;
return B_OK;
}
static status_t
put_buf(struct buf* buf)
{
struct vnode* deviceNode = buf->b_vp;
struct msdosfsmount* fatVolume = (struct msdosfsmount*)deviceNode->v_rdev->si_mountpt->mnt_data;
rw_lock_write_lock(&deviceNode->v_bufobj.bo_lock.haikuRW);
if (buf->b_owned != 0) {
if ((u_long)buf->b_bufsize == fatVolume->pm_bpcluster
&& deviceNode->v_bufobj.bo_clusters < BUF_CACHE_SIZE) {
SLIST_INSERT_HEAD(&deviceNode->v_bufobj.bo_clusterbufs, buf, link);
++deviceNode->v_bufobj.bo_clusters;
} else if ((u_long)buf->b_bufsize == fatVolume->pm_fatblocksize
&& deviceNode->v_bufobj.bo_fatblocks < BUF_CACHE_SIZE) {
SLIST_INSERT_HEAD(&deviceNode->v_bufobj.bo_fatbufs, buf, link);
++deviceNode->v_bufobj.bo_fatblocks;
} else {
free(buf->b_data, 0);
free(buf, 0);
}
} else if (deviceNode->v_bufobj.bo_empties < BUF_CACHE_SIZE) {
buf->b_data = NULL;
buf->b_bufsize = 0;
SLIST_INSERT_HEAD(&deviceNode->v_bufobj.bo_emptybufs, buf, link);
++deviceNode->v_bufobj.bo_empties;
} else {
free(buf, 0);
}
rw_lock_write_unlock(&deviceNode->v_bufobj.bo_lock.haikuRW);
return B_OK;
}