(Logical change 1.259)

edge.strict_endians
cantab.net!aia21 2004-01-09 12:38:56 +00:00
parent 8999f2dc1d
commit 0636632d5a
1 changed files with 217 additions and 0 deletions

217
libntfs/compress.c 100644
View File

@ -0,0 +1,217 @@
/*
* compress.c - Compressed attribute handling code. Part of the Linux-NTFS
* project.
*
* Copyright (c) 2004 Anton Altaparmakov
*
* This program/include file is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program/include file is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (in the main directory of the Linux-NTFS
* distribution in the file COPYING); if not, write to the Free Software
* Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "attrib.h"
#include "debug.h"
#include "volume.h"
#include "types.h"
#include "layout.h"
#include "runlist.h"
/**
* ntfs_compressed_attr_pread - read from a compressed attribute
* @na: ntfs attribute to read from
* @pos: byte position in the attribute to begin reading from
* @count: number of bytes to read
* @b: output data buffer
*
* NOTE: You probably want to be using attrib.c::ntfs_attr_pread() instead.
*
* This function will read @count bytes starting at offset @pos from the
* compressed ntfs attribute @na into the data buffer @b.
*
* On success, return the number of successfully read bytes. If this number
* is lower than @count this means that the read reached end of file or that
* an error was encountered during the read so that the read is partial.
* 0 means end of file or nothing was read (also return 0 when @count is 0).
*
* On error and nothing has been read, return -1 with errno set appropriately
* to the return code of ntfs_pread(), or to EINVAL in case of invalid
* arguments.
*/
s64 ntfs_compressed_attr_pread(ntfs_attr *na, const s64 pos, s64 count,
void *b)
{
s64 br, to_read, ofs, total, total2;
u64 cb_size_mask;
VCN start_vcn, end_vcn;
ntfs_volume *vol;
runlist_element *rl;
u8 *ntfs_compression_buffer, *ntfs_uncompressed_buffer;
u8 *cb, *cb_pos, *cb_end;
u32 cb_size;
unsigned int nr_cbs, cb_clusters;
Dprintf("%s(): Entering for inode 0x%Lx, attr 0x%x, pos 0x%Lx, "
"count 0x%Lx.\n", __FUNCTION__,
(unsigned long long)na->ni->mft_no, na->type,
(long long)pos, (long long)count);
if (!na || !NAttrCompressed(na) || !na->ni || !na->ni->vol || !b ||
pos < 0 || count < 0) {
errno = EINVAL;
return -1;
}
/*
* Encrypted attributes are not supported. We return access denied,
* which is what Windows NT4 does, too.
*/
if (NAttrEncrypted(na)) {
errno = EACCES;
return -1;
}
if (!count)
return 0;
/* Truncate reads beyond end of attribute. */
if (pos + count > na->data_size) {
if (pos >= na->data_size)
return 0;
count = na->data_size - pos;
}
/* If it is a resident attribute, simply use ntfs_attr_pread(). */
if (!NAttrNonResident(na))
return ntfs_attr_pread(na, pos, count, b);
total = total2 = 0;
/* Zero out reads beyond initialized size. */
if (pos + count > na->initialized_size) {
if (pos >= na->initialized_size) {
memset(b, 0, count);
return count;
}
total2 = pos + count - na->initialized_size;
count -= total2;
memset((u8*)b + count, 0, total2);
}
vol = na->ni->vol;
cb_size = na->compression_block_size;
cb_size_mask = cb_size - 1UL;
cb_clusters = na->compression_block_clusters;
/* Need a temporary buffer for each loaded compression block. */
ntfs_compression_buffer = malloc(cb_size);
if (!ntfs_compression_buffer)
return -1;
/* Need a temporary buffer for each uncompressed block. */
ntfs_uncompressed_buffer = malloc(cb_size);
if (!ntfs_uncompressed_buffer) {
int eo = errno;
free(ntfs_compression_buffer);
errno = eo;
return -1;
}
/*
* The first vcn in the first compression block (cb) which we need to
* decompress.
*/
start_vcn = (pos & ~cb_size_mask) >> vol->cluster_size_bits;
/*
* The first vcn in the cb after the last cb which we need to
* decompress.
*/
end_vcn = ((pos + count + cb_size - 1) & ~cb_size_mask) >>
vol->cluster_size_bits;
/* Number of compression blocks (cbs) in the wanted vcn range. */
nr_cbs = (end_vcn - start_vcn) << vol->cluster_size_bits >>
na->compression_block_size_bits;
do_next_cb:
nr_cbs--;
cb_pos = cb = ntfs_compression_buffer;
cb_end = cb + cb_size;
// FIXME: I am here... (AIA)
free(ntfs_compression_buffer);
free(ntfs_uncompressed_buffer);
errno = ENOTSUP;
return -1;
/* Find the runlist element containing the vcn. */
rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits);
if (!rl) {
/*
* If the vcn is not present it is an out of bounds read.
* However, we already truncated the read to the data_size,
* so getting this here is an error.
*/
if (errno == ENOENT)
errno = EIO;
return -1;
}
/*
* Gather the requested data into the linear destination buffer. Note,
* a partial final vcn is taken care of by the @count capping of read
* length.
*/
ofs = pos - (rl->vcn << vol->cluster_size_bits);
for (; count; rl++, ofs = 0) {
if (!rl->length)
goto rl_err_out;
if (rl->lcn < (LCN)0) {
if (rl->lcn != (LCN)LCN_HOLE)
goto rl_err_out;
/* It is a hole, just zero the matching @b range. */
to_read = min(count, (rl->length <<
vol->cluster_size_bits) - ofs);
memset(b, 0, to_read);
/* Update progress counters. */
total += to_read;
count -= to_read;
(u8*)b += to_read;
continue;
}
/* It is a real lcn, read it into @dst. */
to_read = min(count, (rl->length << vol->cluster_size_bits) -
ofs);
retry:
Dprintf("%s(): Reading 0x%Lx bytes from vcn 0x%Lx, lcn 0x%Lx, "
"ofs 0x%Lx.\n", __FUNCTION__, to_read,
rl->vcn, rl->lcn, ofs);
br = ntfs_pread(vol->dev, (rl->lcn << vol->cluster_size_bits) +
ofs, to_read, b);
/* If everything ok, update progress counters and continue. */
if (br > 0) {
total += br;
count -= br;
(u8*)b += br;
continue;
}
/* If the syscall was interrupted, try again. */
if (br == (s64)-1 && errno == EINTR)
goto retry;
if (total)
return total;
if (!br)
errno = EIO;
return -1;
}
/* Finally, return the number of bytes read. */
return total + total2;
rl_err_out:
if (total)
return total;
errno = EIO;
return -1;
}