I've identified what I believe are a few problems with the block_seek() implementation in io_block.c:
/* parameter offset is relative address at here */ static int block_seek(io_entity_t *entity, int mode, signed long long offset) { block_dev_state_t *cur; assert(entity->info != (uintptr_t)NULL); cur = (block_dev_state_t *)entity->info; assert((offset >= 0) && ((unsigned long long)offset < cur->size)); switch (mode) { case IO_SEEK_SET: cur->file_pos = (unsigned long long)offset; break; case IO_SEEK_CUR: cur->file_pos += (unsigned long long)offset; break; default: return -EINVAL; } assert(cur->file_pos < cur->size); return 0; }
- The code uses only assert() and includes no release-mode error checks. IOW it will always return 0 as long as mode is valid.
- The first assert() is only correct when mode == SEEK_SET, because the semantics of offset depend on the value of mode:
- SEEK_SET: offset is a new file position and should be bound to 0 >= offset < size.
- SEEK_CUR: offset is a relative position, and can be negative. It should also be limited to the distance to the beginning (negative) or end (positive) of the device.
- When mode == SEEK_CUR, offset is allowed to be negative (a backwards seek), so it should not be cast to unsigned.
- This was changed in 70cb0bff7d
I think it should look something like this:
/* parameter offset is relative address at here */ static int block_seek(io_entity_t *entity, int mode, signed long long offset) { block_dev_state_t *cur; assert(entity->info != (uintptr_t)NULL); cur = (block_dev_state_t *)entity->info; switch (mode) { case IO_SEEK_SET: if ((offset < 0) || (unsigned long long)offset >= cur->size)) return -EINVAL; cur->file_pos = (unsigned long long)offset; break; case IO_SEEK_CUR: if ((cur->file_pos + offset < 0) || (cur->file_pos + offset >= cur->size)) return -EINVAL; cur->file_pos += offset; break; default: return -EINVAL; } assert(cur->file_pos < cur->size); return 0; }