int tfs_read(struct file *file, void *buf, int blocks) { struct tfs_sb_info *sbi = TFS_SBI(file->fs); struct cache_struct *cs; uint32_t block; int index = file->offset >> sbi->s_block_shift; int bytes_read = 0; TFS_DEBUG("rbuf: %p, blocks: %d, offset: %d\n", buf, blocks, file->offset); if (!blocks) return 0; if (file->offset >= file->inode->i_size) return -1; while (blocks--) { block = tfs_bmap(file->inode, index++); if (!block) break; cs = get_cache_block(file->fs, block); if (!cs) return -EIO; memcpy(buf, cs->data, sbi->s_block_size); bytes_read += sbi->s_block_size; buf += sbi->s_block_size; } return bytes_read; }
/* * Returns: * NULL, entry not found * ERROR, errors happend. * cs, entry found */ struct cache_struct * tfs_find_entry(struct inode *inode, const char *dname, struct tfs_dir_entry **res) { uint32_t block; int index = 0; struct tfs_dir_entry *de; struct cache_struct *cs; struct tfs_sb_info *sbi = TFS_SBI(inode->i_fs); block = inode->i_data[index++]; if (!block) return NULL; cs = get_cache_block(inode->i_fs, block); if (!cs) return ERR_PTR(-EIO); de = (struct tfs_dir_entry *)cs->data; while(1) { if ((char *)de >= (char *)cs->data + sbi->s_block_size) { if ((block = inode->i_data[index++]) < sbi->s_data_area) return NULL; cs = get_cache_block(inode->i_fs, block); if (!cs) return ERR_PTR(-EIO); de = (struct tfs_dir_entry *)cs->data; } if (de->d_inode == 0) { de++; continue; } if (tfs_match_entry(dname, de)) { *res = de; return cs; } de++; } return NULL; }
/* read one directry entry at a time */ struct dirent * tfs_readdir(struct file *file) { struct dirent *dirent; struct tfs_dir_entry *de; struct cache_struct *cs; struct inode *inode = file->inode; struct tfs_sb_info *sbi = TFS_SBI(file->fs); int index = file->offset >> sbi->s_block_shift; uint32_t block; if (!(block = tfs_bmap(inode, index))) return NULL; cs = get_cache_block(file->fs, block); de = (struct tfs_dir_entry *)(cs->data + (file->offset & (sbi->s_block_size- 1))); if (!(dirent = malloc(sizeof(*dirent)))) { printk("malloc dirent structure in tfs_readdir error!\n"); return NULL; } memset(dirent, 0, sizeof(*dirent)); dirent->d_ino = de->d_inode; dirent->d_off = file->offset; dirent->d_reclen = sizeof(struct tfs_dir_entry); dirent->d_type = 0; memcpy(dirent->d_name, de->d_name, TFS_NAME_LEN); file->offset += sizeof(struct tfs_dir_entry); /* Skip the invalid one */ if (de->d_inode == 0) { free(dirent); return tfs_readdir(file); } return dirent; }
int tfs_add_entry(struct inode *dir, const char *name, int inr, int * dirty) { uint32_t block; int index = 0; struct cache_struct *cs; struct tfs_dir_entry *de; struct tfs_sb_info *sbi = TFS_SBI(dir->i_fs); if (strlen(name) > TFS_NAME_LEN) return -ENAMETOOLONG; if (!(block = dir->i_data[index++])) goto alloc_new_block; cs = get_cache_block(dir->i_fs, block); if (!cs) return -EIO; de = (struct tfs_dir_entry *)cs->data; while (1) { if ((void *)de >= cs->data + sbi->s_block_size) { if (!(block = dir->i_data[index++])) break; cs = get_cache_block(dir->i_fs, block); if (!cs) return -EIO; de = (struct tfs_dir_entry *)cs->data; } if (!de->d_inode) break; de++; } *dirty = 0; alloc_new_block: /* allocate a new block to hold the new entry */ if (!block) { block = tfs_alloc_block(sbi, sbi->s_data_area); if (block < 0) return -ENOSPC; if (index > TFS_N_BLOCKS) return -EFBIG; dir->i_data[index - 1] = block; cs = get_cache_block(dir->i_fs, block); if (!cs) return -EIO; de = (struct tfs_dir_entry *)cs->data; memset(cs->data, 0, sbi->s_block_size); } /* Add a new entry at last */ dir->i_size += sizeof(struct tfs_dir_entry); /* tell the caller to update this inode */ *dirty = 1; memset(de, 0, sizeof(*de)); de->d_inode = inr; memcpy(de->d_name, name, strlen(name)); /* write the entry back to disk */ if (tfs_bwrite(sbi, block, cs->data)) return -EIO; return 0; }
/** Write to an AFS file * \par Description: * Write from the given buffer into the given file starting at the given offset for * the given length. First, if the offset is not block aligned, read the first * block/extent, write from the buffer into it starting at offset, and write it back * out. Next, for all the complete blocks/extents, in the range, write to them from * the buffer. Finally, if there is more to write, read the last extent/buffer, * write from the buffer into the beginning of the block/extent, and write it out. * \par Note: Directory data does not appear to be cached. This could be a huge slowdown. * \par Warning: * \param psVolume AFS filesystem pointer * \param psInode AFS Inode to write to * \param pBuffer Buffer to write from * \param nPos Start position in file to write at * \param a_nSize Number of octets to write * \return 0 on success, negative error code on failure * \sa *****************************************************************************/ int afs_do_write( AfsVolume_s * psVolume, AfsInode_s * psInode, const char *pBuffer, off_t nPos, size_t a_nSize ) { const int nBlockSize = psVolume->av_psSuperBlock->as_nBlockSize; int nSize = a_nSize; off_t nFirstBlock = nPos / nBlockSize; off_t nLastBlock =( nPos + nSize + nBlockSize - 1 ) / nBlockSize; off_t nNumBlocks = nLastBlock - nFirstBlock + 1; int nOffset = nPos % nBlockSize; off_t nBlockAddr; char *pBlock = NULL; int nRunLength; int nError; if( ( nPos + nSize ) > ( afs_get_inode_block_count( psInode ) * nBlockSize ) ) { if( S_ISDIR( psInode->ai_nMode ) ) { printk( "Panic: afs_do_write() dir to small!!\n" ); } else { printk( "Panic: afs_do_write() file to small!!\n" ); } return( -ENOSPC ); } if( S_ISDIR( psInode->ai_nMode ) ) { pBlock = afs_alloc_block_buffer( psVolume ); if( pBlock == NULL ) { printk( "Error: afs_do_write() no memory for data buffer\n" ); return( -ENOMEM ); } } nError = afs_get_stream_blocks( psVolume, &psInode->ai_sData, nFirstBlock, nNumBlocks, &nBlockAddr, &nRunLength ); if( nError < 0 ) { printk( "Error: afs_do_write() 1 afs_get_stream_blocks() failed with code %d\n", nError ); } if( nError >= 0 && nOffset != 0 ) { if( S_ISDIR( psInode->ai_nMode ) ) { nError = afs_logged_read( psVolume, pBlock, nBlockAddr ); } else { pBlock =( char * )get_cache_block( psVolume->av_nDevice, nBlockAddr, nBlockSize ); if( pBlock == NULL ) { nError = -EIO; } } if( nError >= 0 ) { off_t nLen = min( nSize, nBlockSize - nOffset ); memcpy( pBlock + nOffset, pBuffer, nLen ); if( S_ISDIR( psInode->ai_nMode ) ) { nError = afs_logged_write( psVolume, pBlock, nBlockAddr ); } else { mark_blocks_dirty( psVolume->av_nDevice, nBlockAddr, 1 ); release_cache_block( psVolume->av_nDevice, nBlockAddr ); } if( nError >= 0 ) { pBuffer += nLen; nSize -= nLen; nBlockAddr++; nFirstBlock++; nRunLength--; nNumBlocks--; } } } while( nSize >= nBlockSize && nError >= 0 ) { off_t nLen; if( nRunLength == 0 ) { nError = afs_get_stream_blocks( psVolume, &psInode->ai_sData, nFirstBlock, nNumBlocks, &nBlockAddr, &nRunLength ); if( nError < 0 ) { printk( "Error: afs_do_write() 2 afs_get_stream_blocks() failed with code %d\n", nError ); } } if( nError >= 0 ) { int i; nLen = min( nSize / nBlockSize, nRunLength ); if( S_ISDIR( psInode->ai_nMode ) ) { nError = 0; for( i = 0; i < nLen; ++i ) { nError = afs_logged_write( psVolume, pBuffer, nBlockAddr ); if( nError < 0 ) { printk( "Error: afs_do_write() failed to write directory data block\n" ); break; } nRunLength--; nNumBlocks--; nFirstBlock++; nBlockAddr++; nSize -= nBlockSize; pBuffer += nBlockSize; kassertw( nRunLength >= 0 ); kassertw( nSize >= 0 ); } } else { nError = cached_write( psVolume->av_nDevice, nBlockAddr, pBuffer, nLen, nBlockSize ); if( nError >= 0 ) { nRunLength -= nLen; nNumBlocks -= nLen; nFirstBlock += nLen; nBlockAddr += nLen; nSize -= nLen * nBlockSize; pBuffer += nLen * nBlockSize; kassertw( nRunLength >= 0 ); kassertw( nSize >= 0 ); } } } } if( nSize > 0 && nError >= 0 ) { if( nRunLength == 0 ) { nError = afs_get_stream_blocks( psVolume, &psInode->ai_sData, nFirstBlock, nNumBlocks, &nBlockAddr, &nRunLength ); if( nError < 0 ) { printk( "Error: afs_do_write() 3 afs_get_stream_blocks() failed with code %d\n", nError ); } } if( nError >= 0 ) { if( S_ISDIR( psInode->ai_nMode ) ) { if( psInode->ai_sData.ds_nSize > ( nPos + a_nSize ) ) { nError = afs_logged_read( psVolume, pBlock, nBlockAddr ); } } else { if( psInode->ai_sData.ds_nSize > ( nPos + a_nSize ) ) { pBlock =( char * )get_cache_block( psVolume->av_nDevice, nBlockAddr, nBlockSize ); } else { pBlock =( char * )get_empty_block( psVolume->av_nDevice, nBlockAddr, nBlockSize ); } if( pBlock == NULL ) { nError = -EIO; } } kassertw( nSize < nBlockSize ); if( nError >= 0 ) { off_t nLen = min( nSize, nBlockSize ); memcpy( pBlock, pBuffer, nSize ); pBuffer += nLen; nSize -= nLen; if( S_ISDIR( psInode->ai_nMode ) ) { nError = afs_logged_write( psVolume, pBlock, nBlockAddr ); if( nError < 0 ) { printk( "Error: afs_do_write() failed to write last partial block to directory\n" ); } } else { mark_blocks_dirty( psVolume->av_nDevice, nBlockAddr, 1 ); release_cache_block( psVolume->av_nDevice, nBlockAddr ); } } } } if( S_ISDIR( psInode->ai_nMode ) ) { afs_free_block_buffer( psVolume, pBlock ); } if( nError >= 0 ) { if( ( nPos + a_nSize ) > psInode->ai_sData.ds_nSize ) { psInode->ai_sData.ds_nSize = nPos + a_nSize; } psInode->ai_nModifiedTime = get_real_time(); psInode->ai_nFlags |= INF_WAS_WRITTEN | INF_STAT_CHANGED; } return( nError ); }
/** Read from an AFS file * \par Description: * Read from the given file into the given buffer starting at the given offset for * the given length. First, if the start position is not block aligned, read the * first block/extant and copy from it into the destination buffer. Next, loop * through the blocks/extants, reading until there are no more full blocks, copying * into the destination buffer. Finally, if there's any data left to read, read the * last block/extant and copy the correct data into the destination buffer. * \par Note: Directory data does not appear to be cached. This could be a huge slowdown. * \par Warning: Does not check for a read past the end of the file. Use afs_read_pos * \param psVolume AFS filesystem pointer * \param psInode AFS Inode to read from * \param pBuffer Destination buffer for the data * \param nPos Start position in file * \param nSize Number of octets to read * \return 0 on success, negative error code on failure * \sa *****************************************************************************/ static int afs_do_read( AfsVolume_s * psVolume, AfsInode_s * psInode, char *pBuffer, off_t nPos, size_t nSize ) { const int nBlockSize = psVolume->av_psSuperBlock->as_nBlockSize; off_t nFirstBlock = nPos / nBlockSize; off_t nLastBlock =( nPos + nSize + nBlockSize - 1 ) / nBlockSize; off_t nNumBlocks = nLastBlock - nFirstBlock + 1; int nOffset = nPos % nBlockSize; off_t nBlockAddr; char *pBlock = NULL; int nRunLength; int nError; if( S_ISDIR( psInode->ai_nMode ) ) { pBlock = afs_alloc_block_buffer( psVolume ); if( pBlock == NULL ) { printk( "Error: afs_do_read() no memory for data buffer\n" ); return( -ENOMEM ); } } nError = afs_get_stream_blocks( psVolume, &psInode->ai_sData, nFirstBlock, nNumBlocks, &nBlockAddr, &nRunLength ); if( nError < 0 ) { printk( "Error: afs_do_read() 1 afs_get_stream_blocks() failed with code %d\n", nError ); } if( nError >= 0 && nOffset != 0 ) { if( S_ISDIR( psInode->ai_nMode ) ) { nError = afs_logged_read( psVolume, pBlock, nBlockAddr ); } else { pBlock =( char * )get_cache_block( psVolume->av_nDevice, nBlockAddr, nBlockSize ); if( pBlock == NULL ) { nError = -EIO; } } if( nError >= 0 ) { off_t nLen = min( nSize, nBlockSize - nOffset ); memcpy( pBuffer, pBlock + nOffset, nLen ); pBuffer += nLen; nSize -= nLen; if( S_ISDIR( psInode->ai_nMode ) == false ) { release_cache_block( psVolume->av_nDevice, nBlockAddr ); } } nBlockAddr++; nFirstBlock++; nRunLength--; nNumBlocks--; } for( ; nSize >= nBlockSize && nError >= 0; ) { if( nRunLength == 0 ) { nError = afs_get_stream_blocks( psVolume, &psInode->ai_sData, nFirstBlock, nNumBlocks, &nBlockAddr, &nRunLength ); if( nError < 0 ) { printk( "Error: afs_do_read() 2 afs_get_stream_blocks( %Ld, %Ld ) failed with code %d\n", nFirstBlock, nNumBlocks, nError ); } } if( nError >= 0 ) { off_t nLen = min( nSize / nBlockSize, nRunLength ); if( S_ISDIR( psInode->ai_nMode ) ) { int i; nError = 0; for( i = 0; i < nLen; ++i ) { nError = afs_logged_read( psVolume, pBuffer, nBlockAddr ); if( nError < 0 ) { printk( "Error: afs_do_read() failed to read directory data block\n" ); break; } nRunLength--; nNumBlocks--; nFirstBlock++; nBlockAddr++; nSize -= nBlockSize; pBuffer += nBlockSize; kassertw( nRunLength >= 0 ); kassertw( nSize >= 0 ); } } else { nError = cached_read( psVolume->av_nDevice, nBlockAddr, pBuffer, nLen, nBlockSize ); if( nError >= 0 ) { nRunLength -= nLen; nNumBlocks -= nLen; nFirstBlock += nLen; nBlockAddr += nLen; nSize -= nLen * nBlockSize; pBuffer += nLen * nBlockSize; kassertw( nRunLength >= 0 ); kassertw( nSize >= 0 ); } } } } if( nSize > 0 && nError >= 0 ) { if( nRunLength == 0 ) { nError = afs_get_stream_blocks( psVolume, &psInode->ai_sData, nFirstBlock, nNumBlocks, &nBlockAddr, &nRunLength ); if( nError < 0 ) { printk( "Error: afs_do_read() 3 afs_get_stream_blocks() failed with code %d\n", nError ); } } if( nError >= 0 ) { if( S_ISDIR( psInode->ai_nMode ) ) { nError = afs_logged_read( psVolume, pBlock, nBlockAddr ); } else { pBlock =( char * )get_cache_block( psVolume->av_nDevice, nBlockAddr, nBlockSize ); // printk( "read:3 %d:%d -> %d blocks at %d\n", // psInode->ai_sInodeNum.group, psInode->ai_sInodeNum.start, 1, nBlockAddr); if( pBlock == NULL ) { nError = -EIO; } } kassertw( nSize < nBlockSize ); if( nError >= 0 ) { off_t nLen = min( nSize, nBlockSize ); memcpy( pBuffer, pBlock, nSize ); pBuffer += nLen; nSize -= nLen; if( S_ISDIR( psInode->ai_nMode ) == false ) { release_cache_block( psVolume->av_nDevice, nBlockAddr ); } } } else { printk( "Error : afs_do_read() Failed to locate last data block\n" ); } } if( S_ISDIR( psInode->ai_nMode ) ) { afs_free_block_buffer( psVolume, pBlock ); } return( nError ); }
int tfs_write(struct file *file, void *buf, int blocks) { struct tfs_sb_info *sbi = TFS_SBI(file->fs); struct cache_struct *cs; int index = file->offset >> sbi->s_block_shift; int block; int bufoff = file->offset & (sbi->s_block_size - 1); int bytes_written = 0; TFS_DEBUG("wbuf: %p, blocks: %d, offset: %d\n", buf, blocks, file->offset); if (!blocks) return 0; block = tfs_bmap(file->inode, index++); if (!block) { if (index - 1 < TFS_N_BLOCKS) { block = tfs_alloc_block(sbi, sbi->s_data_area); if (block < 0) return -ENOSPC; file->inode->i_data[index - 1] = block; } else { /* file too big */ return -EFBIG; } } cs = get_cache_block(file->fs, block); if (!cs) return -EIO; bytes_written = sbi->s_block_size - bufoff; memmove(cs->data + bufoff, buf, bytes_written); buf += bytes_written; file->inode->i_size += MAX(0, file->offset + bytes_written - file->inode->i_size); /* write back to disk */ if (tfs_bwrite(sbi, block, cs->data)) return -EIO; blocks--; while (blocks--) { int bytes_need; block = tfs_bmap(file->inode, index++); if (!block) { if (index - 1 < TFS_N_BLOCKS) { block = tfs_alloc_block(sbi, sbi->s_data_area); if (block < 0) return -ENOSPC; file->inode->i_data[index - 1] = block; } else { /* fle too big */ return -EFBIG; } } bytes_need = sbi->s_block_size; cs = get_cache_block(file->fs, block); if (!cs) return -EIO; memcpy(cs->data, buf, bytes_need); bytes_written += bytes_need; buf += bytes_need; file->inode->i_size += MAX(0, file->offset + bytes_written - file->inode->i_size); if (tfs_bwrite(sbi, block, cs->data)) return -EIO; } if (tfs_iwrite(file->inode)) return -EIO; return bytes_written; }