/** Assumes ino is an opened inode and loads a block list cache for it. If !ino->first_block then this function does nothing but returns whefs_rc.OK, otherwise... For each block in the chain starting at ino->first_block, the block is loaded and appended to ino->blocks.list. On success ino->blocks.list contains ino->blocks.count whefs_block items representing ino's block chain. To add items to the list use whefs_inode_block_list_append(). To remove them, change ino->blocks.count, empty out the now-unused entries, and update the on-disk blocks. ino->blocks.count should be 0 when this function is called. If not, it may emit a warning debug message but will return a success value. */ static int whefs_inode_block_list_load( whefs_fs * fs, whefs_inode * ino ) { if( ! whefs_inode_is_valid(fs,ino) ) return whefs_rc.ArgError; if( ! ino->first_block ) return whefs_rc.OK; if( ino->blocks.count ) { WHEFS_DBG_WARN("this function shouldn't be called when ino->blocks.count is !0. inode=#%u", ino->id); return whefs_rc.OK; } whefs_block bl = whefs_block_empty; int rc = whefs_block_read( fs, ino->first_block, &bl ); if( whefs_rc.OK != rc ) return rc; #if 0 if( ! ino->blocks.list ) { rc = whefs_inode_block_list_reserve( fs, ino, 5 /* arbitrarily chosen */ ); if( whefs_rc.OK != rc ) return rc; } #endif rc = whefs_inode_block_list_append( fs, ino, &bl ); if( whefs_rc.OK != rc ) return rc; while( bl.next_block ) { rc = whefs_block_read_next( fs, &bl, &bl ); if( whefs_rc.OK != rc ) return rc; rc = whefs_inode_block_list_append( fs, ino, &bl ); if( whefs_rc.OK != rc ) return rc; } //WHEFS_DBG("Loaded block chain of %u block(s) for inode #%u[%s].", ino->blocks.count, ino->id, ino->name ); return whefs_rc.OK; }
/** This function is the heart of the pseudofile i/o beast... It tries to map a logical file position to a data block associated with an inode. It starts with ino's first block and increments blocks until the block in which pos would land is found. If ino doesn't have enough blocks, the behaviour is defined by the expands parameter: If expands is true then it will add blocks to the inode's chain in order to reach the destination pos, if necessary. If expands is false and pos is not within the inode's current data size then the function fails. On success, tgt is populated with the block associated with the given position and inode, and ino *may* be updated (if it had no blocks associated with it beforehand). To figure out the proper offset of pos to use within the block use (pos%whefs_fs_options_get(fs)->block_size). This function never actually changes the logical size of the inode, but may allocate new blocks to it. On success whefs_rc.OK is returned, else some other error value. Some possibilities include: - whefs_rc.RangeError = pos it past EOF and expands is false. - whefs_rc.FSFull = ran out of blocks while trying to expand. - whefs_rc.ArgEror = !fs, !tgt, or ino is not valid BIG FAT HAIRY WARNING: It is intended that the ino argument be an inode which has been opened via whefs_inode_open(), but this function does not check that because doing so is relatively costly and this routine is called from the i/o device implementation for every read and write. Because the ino argument *may* be updated, it is imperative that if ino refers to an opened inode, that the ino argument actually be a pointer to that object, as opposed to being a copy of it. Failure to follow this may result in mis-synchronization of the node's state or a memory leak. Specifically, if the inode previously had no blocks, and this function adds at least one, then ino must be updated. If ino has the same ID as an opened inode but is not that opened inode object (see whefs_inode_search_opened()), then ino will be updated but the opened inode will not, which will probably lead to any new blocks allocated by this call to become lost the next time the opened inode is flushed. BIGGER, FATTER, HAIRIER WARNING: Because profiling has shown that this function spends a significant amount of time validating fs and ino (by calling whefs_inode_is_valid()), that check has been removed. Since this function "can only" be called from the whefs_nodedev implementation, we're relying on that to do the validation (which it does). */ static int whefs_block_for_pos( whefs_fs * fs, whefs_inode * ino, whio_size_t pos, whefs_block * tgt, bool expand ) { whefs_id_type bc; whio_size_t bs; int rc = whefs_rc.OK; whefs_block bl = whefs_block_empty; whefs_block * blP = 0; /*if( !tgt || !whefs_inode_is_valid( fs, ino ) ) return whefs_rc.ArgError; */ if( (ino->data_size <= pos) && !expand ) { /*WHEFS_DBG("return whefs_rc.RangeError"); */ return whefs_rc.RangeError; } bs = whefs_fs_options_get(fs)->block_size; bc = /* how many blocks will we need? */ 1+(pos/bs) ; if(0) WHEFS_DBG("pos=%"WHIO_SIZE_T_PFMT" bs=%"WHIO_SIZE_T_PFMT" bc=%"WHEFS_ID_TYPE_PFMT,pos,bs,bc); /** ^^^ does this leave us with one too many blocks when we truncate() to an exact multiple of blocksize? */ if( bc > whefs_fs_options_get(fs)->block_count ) { WHEFS_DBG_WARN("VFS doesn't have enough blocks " "(%"WHEFS_ID_TYPE_PFMT") to satisfy the " "request for position %"WHIO_SIZE_T_PFMT " of inode #%"WHEFS_ID_TYPE_PFMT, whefs_fs_options_get(fs)->block_count, pos, ino->id ); return whefs_rc.RangeError; } if( ! ino->blocks.list ) { rc = whefs_inode_block_list_load( fs, ino ); if( whefs_rc.OK != rc ) return rc; } if( !expand && (ino->blocks.count < bc) ) { /* can't grow list for this request. */ return whefs_rc.RangeError; } /* TODO: check number of available inodes here, and don't try to expand if we can't reach the end */ /*WHEFS_DBG("About to search inode #%u for %u block(s) (size=%u) to find position %u", ino->id, bc, bs, pos ); */ rc = whefs_rc.OK; if( bc <= ino->blocks.count) { blP = &ino->blocks.list[bc-1]; /* jump right to it */; } else { /* expand the list */ whefs_id_type i; if( ! expand ) { if(0) WHEFS_DBG("Cannot expand to %"WHEFS_ID_TYPE_PFMT" blocks for position %"WHIO_SIZE_T_PFMT" because [expand] parameter is false.", bc, pos ); return whefs_rc.RangeError; } /*bl = ino->blocks.list[ino->blocks.count-1]; */ i = ino->blocks.count; blP = NULL; for( ; i < bc; ++i ) { rc = whefs_block_next_free( fs, &bl, true ); if( whefs_rc.OK == rc ) rc = whefs_inode_block_list_append( fs, ino, &bl ); if( whefs_rc.OK != rc ) return rc; /** We "might" want to truncate the inode back to its previous length, but why add room for yet another error on top of the one we just encountered? */ } blP = &bl; } if( whefs_rc.OK == rc ) { *tgt = *blP; } /*WHEFS_DBG("Using block id #%u for pos %u of inode #%u", blP->id, pos, ino->id ); */ return rc; }