diff --git a/Kernel/filesys.c b/Kernel/filesys.c index be24b711b..a25db3385 100644 --- a/Kernel/filesys.c +++ b/Kernel/filesys.c @@ -923,37 +923,89 @@ uint16_t devnum(inoptr ino) } -/* F_trunc frees all the blocks associated with the file, if it - * is a disk file. +/* + * f_trunc_blocks frees all the blocks associated with the file, if it + * is a disk file. The blocks are freed in reverse order. This is + * very important so that they end up on the freelist in the + * order we want to allocate them. */ -int f_trunc(register inoptr ino) +int f_trunc_blocks(register inoptr ino, uint16_t nblock) { register uint16_t dev; register int_fast8_t j; + uint16_t map1 = 0; + uint16_t map2 = 0; if (ino->c_flags & CRDONLY) { udata.u_error = EROFS; return -1; } + + /* Block offsets are + 0-17 direct + 18 256 blocks (18-273) + 19 256 * 256 blocks (274-65810) + + (We only allow 65535 block offset in order to keep a lot of stuff + uint16_t - FIXME to fix u writei()) + + We don't support triple indirect blocks. + + When we are called nblock is the number of blocks that will + remain in the file when we truncate it + + We set map1 to the number of blocks we must purge for single + indirect. We set map2 for the number of blocks we must purge + of double indirect. + + freeblk frees full subblocks above the block passed, and then frees + blocks >> 8 on the last iteration to partially clear the last set + */ + + if (nblock > 17 && nblock < 274) + map1 = (nblock - 18) << 8; + else if (nblock > 273) + map2 = nblock - 273; dev = ino->c_dev; + /* FIXME: ideally zero the indirect pointers before we write the + free lists */ + /* First deallocate the double indirect blocks */ - freeblk(dev, ino->c_node.i_addr[19], 2); + freeblk(dev, ino->c_node.i_addr[19], 2, map2); + if (map2) + ino->c_node.i_addr[19] = 0; /* Also deallocate the indirect blocks */ - freeblk(dev, ino->c_node.i_addr[18], 1); + freeblk(dev, ino->c_node.i_addr[18], 1, map1); + if (map1 == 0 && map2 == 0) /* ???? should this just be if map1 */ + ino->c_node.i_addr[18] = 0; /* Finally, free the direct blocks */ - for(j = 17; j >= 0; --j) - freeblk(dev, ino->c_node.i_addr[j], 0); - - memset((uint8_t *)ino->c_node.i_addr, 0, sizeof(ino->c_node.i_addr)); + /* FIXME: use pointers for efficiency ? */ + /* At this point nblock is definitely < 0x8000 so forcing a signed + compare does what we want */ + for(j = 17; j >= (int)nblock; --j) { + freeblk(dev, ino->c_node.i_addr[j], 0, 0); + ino->c_node.i_addr[j] = 0; + } ino->c_flags |= CDIRTY; - ino->c_node.i_size = 0; return 0; } + +/* Truncate a file back to nothing using f_trunc_blocks and then write + the inode size as 0 */ +int f_trunc(regptr inoptr ino) +{ + /* Is it worth checking size already 0 ? */ + if (f_trunc_blocks(ino, 0)) + return -1; + ino->c_node.i_size = 0; + return 0; +} + /* Companion function to f_trunc(). This is the one case where we can't hide the difference between an internal @@ -963,11 +1015,12 @@ int f_trunc(register inoptr ino) This is annoying and it would be nice one day to find a clean solution */ #ifdef CONFIG_BLKBUF_EXTERNAL -void freeblk(uint16_t dev, blkno_t blk, uint_fast8_t level) +void freeblk(uint16_t dev, blkno_t blk, uint_fast8_t level, uint16_t nblock) { struct blkbuf *buf; regptr blkno_t *bn; int16_t j; + int_fast8_t nblock1 = nblock >> 8; if(!blk) return; @@ -978,9 +1031,12 @@ void freeblk(uint16_t dev, blkno_t blk, uint_fast8_t level) corrupt_fs(dev); return; } - for(j = BLKSIZE / 2 - 1; j >= 0; --j) { + for(j = BLKSIZE / 2 - 1; j >= nblock1; --j) { + uint8_t b = 0; + if (j == nblock1) + b = nblock & 0xFF; blktok(&bn, buf, j * sizeof(blkno_t), sizeof(blkno_t)); - freeblk(dev, bn[j], level-1); + freeblk(dev, bn[j], level - 1, b); } brelse(buf); } @@ -992,11 +1048,12 @@ void freeblk(uint16_t dev, blkno_t blk, uint_fast8_t level) #else -void freeblk(uint16_t dev, blkno_t blk, uint_fast8_t level) +void freeblk(uint16_t dev, blkno_t blk, uint_fast8_t level, uint16_t nblock) { struct blkbuf *buf; regptr blkno_t *bn; int16_t j; + int_fast8_t nblock1 = nblock >> 8; if(!blk) return; @@ -1008,8 +1065,14 @@ void freeblk(uint16_t dev, blkno_t blk, uint_fast8_t level) return; } bn = blkptr(buf, 0, BLKSIZE); - for(j = BLKSIZE / 2 - 1; j >= 0; --j) - freeblk(dev, bn[j], level-1); + for(j = BLKSIZE / 2 - 1; j >= 0; --j) { + /* When we hit nblock1 we are doing the final partial clear, so + only tell the child freeblk to do a partial clear */ + uint_fast8_t b = 0; + if (j == nblock1) + b = nblock & 0xFF; + freeblk(dev, bn[j], level-1, b); + } brelse(buf); } #ifdef CONFIG_TRIM diff --git a/Kernel/include/kdata.h b/Kernel/include/kdata.h index 1b4a69e20..be18eb288 100644 --- a/Kernel/include/kdata.h +++ b/Kernel/include/kdata.h @@ -88,7 +88,7 @@ extern struct runload loadavg[]; #ifdef CONFIG_LEVEL_2 #define FUZIX_SYSCALL_COUNT 80 #else -#define FUZIX_SYSCALL_COUNT 67 +#define FUZIX_SYSCALL_COUNT 68 #endif typedef arg_t (*syscall_t)(void); diff --git a/Kernel/include/kernel.h b/Kernel/include/kernel.h index 3c63461a2..189bf4079 100644 --- a/Kernel/include/kernel.h +++ b/Kernel/include/kernel.h @@ -1032,8 +1032,9 @@ extern void i_deref(inoptr ino); extern void corrupt_fs(uint16_t devno); extern void wr_inode(inoptr ino); extern bool isdevice(inoptr ino); +extern int f_trunc_blocks(inoptr ino, uint16_t nblock); extern int f_trunc(inoptr ino); -extern void freeblk(uint16_t dev, blkno_t blk, uint_fast8_t level); +extern void freeblk(uint16_t dev, blkno_t blk, uint_fast8_t level, uint16_t nblock); extern blkno_t bmap(inoptr ip, blkno_t bn, unsigned int rwflg); extern void validblk(uint16_t dev, blkno_t num); extern inoptr getinode(uint_fast8_t uindex); @@ -1317,6 +1318,7 @@ extern arg_t _sched_yield(void); /* FUZIX system call 62 */ extern arg_t _acct(void); /* FUZIX system call 63 */ extern arg_t _memalloc(void); /* FUZIX system call 64 */ extern arg_t _memfree(void); /* FUZIX system call 65 */ +extern arg_t _ftruncate(void); /* FUZIX system call 67 */ #if defined(CONFIG_32BIT) #include "kernel32.h" diff --git a/Kernel/include/syscall_name.h b/Kernel/include/syscall_name.h index a73c755bd..57f2ab6d9 100644 --- a/Kernel/include/syscall_name.h +++ b/Kernel/include/syscall_name.h @@ -70,7 +70,7 @@ const char *syscall_name[NR_SYSCALL] = { "memalloc", "memfree", "__netcall", - "_nosys67", + "_ftruncate", "_nosys68", "_nosys69", "_nosys70", @@ -153,7 +153,7 @@ int syscall_args[NR_SYSCALL] = { 1, //memalloc 1, //memfree 1, //netcall - 0, //nosys 67 + 2, //ftruncate 0, //nosys 68 0, //nosys 69 0, //nosys 70 diff --git a/Kernel/kdata.c b/Kernel/kdata.c index 9708687b3..c0fc42ef9 100644 --- a/Kernel/kdata.c +++ b/Kernel/kdata.c @@ -108,9 +108,9 @@ const syscall_t syscall_dispatch[FUZIX_SYSCALL_COUNT] = { #else _nosys, #endif + _ftruncate, /* Fuzix system call 67 */ #if defined(CONFIG_LEVEL_2) - _nosys, /* 67-71 reserved */ - _nosys, + _nosys, /* 68-71 reserved */ _nosys, _nosys, _nosys, diff --git a/Kernel/syscall_fs3.c b/Kernel/syscall_fs3.c index 7f9bbc592..2664956f7 100644 --- a/Kernel/syscall_fs3.c +++ b/Kernel/syscall_fs3.c @@ -439,3 +439,47 @@ arg_t _flock(void) #undef file #undef lockop + +/******************************************* + ftruncate (file, offset) Function call 67 + int16_t file; + uint32_t *offset; + ********************************************/ +#define file (uint16_t)udata.u_argn +#define offset (uint32_t *)udata.u_argn1 + +/* We copy the 32bit offset in and out rather than passing it + as a 32bit OS might */ +arg_t _ftruncate(void) +{ + register inoptr ino; + register struct oft *o; + off_t n; + + if (uget(offset, &n, sizeof(n))) + return -1; + + if ((ino = getinode(file)) == NULLINODE) + return (-1); + + o = &of_tab[udata.u_files[file]]; + if (n < 0 || (HIBYTE32(n) & BLKOVERSIZE32) || + O_ACCMODE(o->o_access) == O_RDONLY || + getmode(ino) != MODE_R(F_REG)) { + udata.u_error = EINVAL; + return -1; + } + + if (n == ino->c_node.i_size) + return 0; + if (n < ino->c_node.i_size) + f_trunc_blocks(ino, BLOCK(n + BLKSIZE - 1)); + ino->c_node.i_size = n; + setftime(ino, M_TIME | C_TIME); + wr_inode(ino); + return 0; +} + +#undef file +#undef offset +#undef flag