267 lines
6.1 KiB
C
267 lines
6.1 KiB
C
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include "falloc.h"
|
|
|
|
#define FA_MAGIC 0x02050920
|
|
|
|
typedef unsigned int u32; /* this could be wrong someday */
|
|
|
|
struct faFileHeader{
|
|
u32 magic;
|
|
u32 firstFree;
|
|
};
|
|
|
|
/* the free list is kept *sorted* to keep fragment compaction fast */
|
|
|
|
struct faBlock {
|
|
u32 isFree;
|
|
u32 next; /* only meaningful if free */
|
|
u32 prev; /* only meaningful if free */
|
|
u32 size;
|
|
};
|
|
|
|
/* flags here is the same as for open(2) - NULL returned on error */
|
|
faFile faOpen(char * path, int flags, int perms) {
|
|
struct faFileHeader newHdr;
|
|
struct faFile_s fas;
|
|
faFile fa;
|
|
off_t end;
|
|
|
|
if (flags & O_WRONLY) {
|
|
return NULL;
|
|
}
|
|
if (flags & O_RDWR) {
|
|
fas.readOnly = 0;
|
|
} else {
|
|
fas.readOnly = 1;
|
|
}
|
|
|
|
fas.fd = open(path, flags, perms);
|
|
if (fas.fd < 0) return NULL;
|
|
|
|
/* is this file brand new? */
|
|
end = lseek(fas.fd, 0, SEEK_END);
|
|
if (!end) {
|
|
newHdr.magic = FA_MAGIC;
|
|
newHdr.firstFree = 0;
|
|
if (write(fas.fd, &newHdr, sizeof(newHdr)) != sizeof(newHdr)) {
|
|
close(fas.fd);
|
|
return NULL;
|
|
}
|
|
fas.firstFree = 0;
|
|
fas.fileSize = sizeof(newHdr);
|
|
}
|
|
else {
|
|
lseek(fas.fd, 0, SEEK_SET);
|
|
if (read(fas.fd, &newHdr, sizeof(newHdr)) != sizeof(newHdr)) {
|
|
close(fas.fd);
|
|
return NULL;
|
|
}
|
|
if (newHdr.magic != FA_MAGIC) {
|
|
close(fas.fd);
|
|
return NULL;
|
|
}
|
|
fas.firstFree = newHdr.firstFree;
|
|
|
|
if (lseek(fas.fd, 0, SEEK_END) < 0) {
|
|
close(fas.fd);
|
|
return NULL;
|
|
}
|
|
|
|
fas.fileSize = lseek(fas.fd, 0, SEEK_CUR);
|
|
}
|
|
|
|
fa = malloc(sizeof(*fa));
|
|
if (fa) *fa = fas;
|
|
|
|
return fa;
|
|
}
|
|
|
|
unsigned int faAlloc(faFile fa, unsigned int size) { /* returns 0 on failure */
|
|
u32 nextFreeBlock;
|
|
u32 bestFreeBlock = 0;
|
|
u32 bestFreeSize = 0;
|
|
unsigned int newBlock;
|
|
struct faBlock block, prevBlock, nextBlock;
|
|
int failed = 0;
|
|
|
|
/* Make sure they are allocing multiples of four bytes. It'll keep
|
|
things smoother that way */
|
|
(size % 4) ? size += (4 - (size % 4)) : 0;
|
|
|
|
/* first, look through the free list for the best fit */
|
|
nextFreeBlock = fa->firstFree;
|
|
while (nextFreeBlock) {
|
|
if (lseek(fa->fd, nextFreeBlock, SEEK_SET) < 0) return 0;
|
|
if (read(fa->fd, &block, sizeof(block)) != sizeof(block)) return 0;
|
|
|
|
if (block.size >= size) {
|
|
if (bestFreeBlock) {
|
|
if (block.size < bestFreeSize) {
|
|
bestFreeSize = block.size;
|
|
bestFreeBlock = nextFreeBlock;
|
|
}
|
|
} else {
|
|
bestFreeSize = block.size;
|
|
bestFreeBlock = nextFreeBlock;
|
|
}
|
|
}
|
|
|
|
nextFreeBlock = block.next;
|
|
}
|
|
|
|
if (bestFreeBlock) {
|
|
if (lseek(fa->fd, bestFreeBlock, SEEK_SET) < 0) return 0;
|
|
if (read(fa->fd, &block, sizeof(block)) != sizeof(block))
|
|
return 0;
|
|
|
|
/* update the free list chain */
|
|
if (lseek(fa->fd, block.prev, SEEK_SET) < 0) return 0;
|
|
if (read(fa->fd, &prevBlock, sizeof(prevBlock)) != sizeof(prevBlock))
|
|
return 0;
|
|
if (lseek(fa->fd, block.next, SEEK_SET) < 0) return 0;
|
|
if (read(fa->fd, &nextBlock, sizeof(nextBlock)) != sizeof(nextBlock))
|
|
return 0;
|
|
|
|
prevBlock.next = block.next;
|
|
nextBlock.prev = block.prev;
|
|
|
|
if (lseek(fa->fd, block.prev, SEEK_SET) < 0) return 0;
|
|
if (write(fa->fd, &prevBlock, sizeof(prevBlock)) != sizeof(prevBlock))
|
|
return 0;
|
|
|
|
if (lseek(fa->fd, block.next, SEEK_SET) < 0) {
|
|
failed = 1;
|
|
} else {
|
|
if (write(fa->fd, &nextBlock, sizeof(nextBlock)) !=
|
|
sizeof(nextBlock)) {
|
|
failed = 1;
|
|
}
|
|
}
|
|
|
|
if (failed) {
|
|
/* try and restore the "prev" block, this won't be a complete
|
|
disaster */
|
|
prevBlock.next = bestFreeBlock;
|
|
|
|
lseek(fa->fd, block.prev, SEEK_SET);
|
|
write(fa->fd, &prevBlock, sizeof(prevBlock));
|
|
|
|
return 0;
|
|
}
|
|
|
|
block.isFree = 0; /* mark it as used */
|
|
block.prev = block.next = 0;
|
|
|
|
/* At some point, we should split this block into two if it's
|
|
bigger then the amount that's being allocated. Any space left
|
|
at the end of this block is wasted right now ***/
|
|
|
|
if (lseek(fa->fd, bestFreeBlock, SEEK_SET) < 0) {
|
|
failed = 1;
|
|
} else {
|
|
if (write(fa->fd, &block, sizeof(block)) != sizeof(block)) {
|
|
failed = 1;
|
|
}
|
|
}
|
|
|
|
if (failed) {
|
|
/* this space is gone :-( this really shouldn't ever happen. It
|
|
won't result in furthur date coruption though, so lets not
|
|
make it worse! */
|
|
return 0;
|
|
}
|
|
|
|
newBlock = bestFreeBlock;
|
|
} else {
|
|
char * space;
|
|
|
|
/* make a new block */
|
|
newBlock = fa->fileSize;
|
|
|
|
space = calloc(1, size);
|
|
if (!space) return 0;
|
|
|
|
block.next = block.prev = 0;
|
|
block.size = size;
|
|
block.isFree = 0;
|
|
|
|
lseek(fa->fd, newBlock, SEEK_SET);
|
|
if (write(fa->fd, &block, sizeof(block)) != sizeof(block)) {
|
|
free(space);
|
|
return 0;
|
|
}
|
|
if (write(fa->fd, space, size) != size) {
|
|
free(space);
|
|
return 0;
|
|
}
|
|
free(space);
|
|
|
|
fa->fileSize += sizeof(block) + size;
|
|
}
|
|
|
|
return newBlock + sizeof(block);
|
|
}
|
|
|
|
int faFree(faFile fa, unsigned int offset) {
|
|
struct faBlock block;
|
|
|
|
/* this is *really* bad ****/
|
|
|
|
offset -= sizeof(block);
|
|
if (lseek(fa->fd, offset, SEEK_SET) < 0) return 0;
|
|
if (read(fa->fd, &block, sizeof(block)) != sizeof(block)) return 0;
|
|
|
|
block.isFree = 1;
|
|
if (lseek(fa->fd, offset, SEEK_SET) < 0) return 0;
|
|
if (write(fa->fd, &block, sizeof(block)) != sizeof(block)) return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void faClose(faFile fa) {
|
|
close(fa->fd);
|
|
free(fa);
|
|
}
|
|
|
|
unsigned int faFirstOffset(faFile fa) {
|
|
return faNextOffset(fa, 0);
|
|
}
|
|
|
|
unsigned int faNextOffset(faFile fa, unsigned int lastOffset) {
|
|
struct faBlock block;
|
|
int offset;
|
|
|
|
if (lastOffset) {
|
|
offset = lastOffset - sizeof(block);
|
|
} else {
|
|
offset = sizeof(struct faFileHeader);
|
|
}
|
|
|
|
if (offset >= fa->fileSize) return 0;
|
|
|
|
lseek(fa->fd, offset, SEEK_SET);
|
|
if (read(fa->fd, &block, sizeof(block)) != sizeof(block)) {
|
|
return 0;
|
|
}
|
|
if (!lastOffset && !block.isFree) return (offset + sizeof(block));
|
|
|
|
do {
|
|
offset += sizeof(block) + block.size;
|
|
|
|
lseek(fa->fd, offset, SEEK_SET);
|
|
if (read(fa->fd, &block, sizeof(block)) != sizeof(block)) {
|
|
return 0;
|
|
}
|
|
|
|
if (!block.isFree) break;
|
|
} while (offset < fa->fileSize && block.isFree);
|
|
|
|
if (offset < fa->fileSize)
|
|
return (offset + sizeof(block));
|
|
else
|
|
return 0;
|
|
}
|