Example #1
0
status_t csi_write_block(struct csi *csi, uint8 *buffer)
{
    ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0);
    if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
        return EINVAL;

    return cached_write(csi->vol->fd, _csi_to_block_(csi), buffer, 1, csi->vol->bytes_per_sector);
}
Example #2
0
status_t csi_write_blocks(struct csi *csi, uint8 *buffer, ssize_t len)
{
    struct csi old_csi;
    uint32 sectors;
    off_t block;
    status_t err;

    ASSERT(len >= csi->vol->bytes_per_sector);

    ASSERT(_validate_cs_(csi->vol, csi->cluster, csi->sector) == 0);
    if (_validate_cs_(csi->vol, csi->cluster, csi->sector) != 0)
        return EINVAL;

    sectors = 1;
    block = _csi_to_block_(csi);

    while (1) {
        old_csi = *csi;
        err = iter_csi(csi, 1);
        if (len < (sectors + 1) * csi->vol->bytes_per_sector)
            break;
        if ((err < B_OK) || (block + sectors != _csi_to_block_(csi)))
            break;
        sectors++;
    }

    err = cached_write(csi->vol->fd, block, buffer, sectors, csi->vol->bytes_per_sector);
    if (err < B_OK)
        return err;

    /* return the last state of the iterator because that's what dosfs_write
     * expects. this lets it meaningfully cache the state even when it's
     * writing to the end of the file. */
    *csi = old_csi;

    return sectors * csi->vol->bytes_per_sector;
}
/* Writes SIZE bytes from BUFFER into INODE, starting at OFFSET.
   Returns the number of bytes actually written, which may be
   less than SIZE if end of file is reached or an error occurs.
   (Normally a write at end of file would extend the inode, but
   growth is not yet implemented.) */
off_t
inode_write_at (struct inode *inode, const void *buffer_, off_t size,
                off_t offset) 
{
  const uint8_t *buffer = buffer_;
  off_t bytes_written = 0;
  uint8_t *bounce = NULL;

  if (inode->deny_write_cnt)
    return 0;

  while (size > 0) 
    {
      /* Sector to write, starting byte offset within sector. */
      block_sector_t sector_idx = byte_to_sector (inode, offset);
      int sector_ofs = offset % BLOCK_SECTOR_SIZE;

      /* Bytes left in inode, bytes left in sector, lesser of the two. */
      //off_t inode_left = inode_length (inode) - offset;
      int sector_left = BLOCK_SECTOR_SIZE - sector_ofs;
      //int min_left = inode_left < sector_left ? inode_left : sector_left;
      int min_left = sector_left;
      

      /* Number of bytes to actually write into this sector. */
      int chunk_size = size < min_left ? size : min_left;
      if(offset > inode_length(inode)) inode->data.length += chunk_size;
      if (chunk_size <= 0)
        break;

      if (sector_ofs == 0 && chunk_size == BLOCK_SECTOR_SIZE)
        {
          /* Write full sector directly to disk. */
          cached_write (NULL, sector_idx, buffer + bytes_written);
        }
      else 
        {
          /* We need a bounce buffer. */
          if (bounce == NULL) 
            {
              bounce = malloc (BLOCK_SECTOR_SIZE);
              if (bounce == NULL)
                break;
            }

          /* If the sector contains data before or after the chunk
             we're writing, then we need to read in the sector
             first.  Otherwise we start with a sector of all zeros. */
          if (sector_ofs > 0 || chunk_size < sector_left) 
            cached_read (inode, sector_idx, bounce);
          else
            memset (bounce, 0, BLOCK_SECTOR_SIZE);
          memcpy (bounce + sector_ofs, buffer + bytes_written, chunk_size);
          cached_write (NULL, sector_idx, bounce);
        }

      /* Advance. */
      size -= chunk_size;
      offset += chunk_size;
      bytes_written += chunk_size;
    }
  free (bounce);

  return bytes_written;
}
Example #4
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 );
}