Extended transparent compression support to sequential writing to compressed files
parent
e2d1f3a1c3
commit
2fbaecd759
|
@ -23,8 +23,8 @@
|
|||
|
||||
# Autoconf
|
||||
AC_PREREQ(2.59)
|
||||
AC_INIT([ntfs-3g],[2009.4.4],[ntfs-3g-devel@lists.sf.net])
|
||||
LIBNTFS_3G_VERSION="54"
|
||||
AC_INIT([ntfs-3g],[2009.2.1],[ntfs-3g-devel@lists.sf.net])
|
||||
LIBNTFS_3G_VERSION="49"
|
||||
AC_CONFIG_SRCDIR([src/ntfs-3g.c])
|
||||
|
||||
# Environment
|
||||
|
|
|
@ -175,6 +175,7 @@ struct _ntfs_attr {
|
|||
runlist_element *rl;
|
||||
ntfs_inode *ni;
|
||||
ATTR_TYPES type;
|
||||
ATTR_FLAGS data_flags;
|
||||
ntfschar *name;
|
||||
u32 name_len;
|
||||
unsigned long state;
|
||||
|
@ -246,7 +247,8 @@ typedef union {
|
|||
} attr_val;
|
||||
|
||||
extern void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
|
||||
const BOOL compressed, const BOOL encrypted, const BOOL sparse,
|
||||
const ATTR_FLAGS data_flags, const BOOL encrypted,
|
||||
const BOOL sparse,
|
||||
const s64 allocated_size, const s64 data_size,
|
||||
const s64 initialized_size, const s64 compressed_size,
|
||||
const u8 compression_unit);
|
||||
|
@ -261,6 +263,7 @@ extern s64 ntfs_attr_pread(ntfs_attr *na, const s64 pos, s64 count,
|
|||
void *b);
|
||||
extern s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count,
|
||||
const void *b);
|
||||
extern int ntfs_attr_pclose(ntfs_attr *na);
|
||||
|
||||
extern void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type,
|
||||
ntfschar *name, u32 name_len, s64 *data_size);
|
||||
|
@ -295,6 +298,8 @@ extern int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx);
|
|||
|
||||
extern int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type,
|
||||
ntfschar *name, u8 name_len, u8 *val, s64 size);
|
||||
extern int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type,
|
||||
ntfschar *name, u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask);
|
||||
extern int ntfs_attr_rm(ntfs_attr *na);
|
||||
|
||||
extern int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size);
|
||||
|
|
|
@ -29,5 +29,11 @@
|
|||
extern s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count,
|
||||
void *b);
|
||||
|
||||
extern s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *brl, s64 wpos,
|
||||
s64 offs, s64 to_write, s64 rounded,
|
||||
const void *b, int compressed_part);
|
||||
|
||||
extern int ntfs_compressed_close(ntfs_attr *na, runlist_element *brl, s64 offs);
|
||||
|
||||
#endif /* defined _NTFS_COMPRESS_H */
|
||||
|
||||
|
|
|
@ -49,6 +49,8 @@ struct _runlist_element {/* In memory vcn to lcn mapping structure element. */
|
|||
s64 length; /* Run length in clusters. */
|
||||
};
|
||||
|
||||
extern runlist_element *ntfs_rl_extend(runlist_element *rl, int more_entries);
|
||||
|
||||
extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn);
|
||||
|
||||
extern s64 ntfs_rl_pread(const ntfs_volume *vol, const runlist_element *rl,
|
||||
|
|
|
@ -59,6 +59,8 @@
|
|||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
#define STANDARD_COMPRESSION_UNIT 4
|
||||
|
||||
ntfschar AT_UNNAMED[] = { const_cpu_to_le16('\0') };
|
||||
ntfschar STREAM_SDS[] = { const_cpu_to_le16('$'),
|
||||
const_cpu_to_le16('S'),
|
||||
|
@ -330,15 +332,17 @@ static void __ntfs_attr_init(ntfs_attr *na, ntfs_inode *ni,
|
|||
* Final initialization for an ntfs attribute.
|
||||
*/
|
||||
void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
|
||||
const BOOL compressed, const BOOL encrypted, const BOOL sparse,
|
||||
const ATTR_FLAGS data_flags,
|
||||
const BOOL encrypted, const BOOL sparse,
|
||||
const s64 allocated_size, const s64 data_size,
|
||||
const s64 initialized_size, const s64 compressed_size,
|
||||
const u8 compression_unit)
|
||||
{
|
||||
if (!NAttrInitialized(na)) {
|
||||
na->data_flags = data_flags;
|
||||
if (non_resident)
|
||||
NAttrSetNonResident(na);
|
||||
if (compressed)
|
||||
if (data_flags & ATTR_COMPRESSION_MASK)
|
||||
NAttrSetCompressed(na);
|
||||
if (encrypted)
|
||||
NAttrSetEncrypted(na);
|
||||
|
@ -347,7 +351,7 @@ void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident,
|
|||
na->allocated_size = allocated_size;
|
||||
na->data_size = data_size;
|
||||
na->initialized_size = initialized_size;
|
||||
if (compressed || sparse) {
|
||||
if ((data_flags & ATTR_COMPRESSION_MASK) || sparse) {
|
||||
ntfs_volume *vol = na->ni->vol;
|
||||
|
||||
na->compressed_size = compressed_size;
|
||||
|
@ -434,12 +438,25 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
|
|||
*/
|
||||
if (type == AT_ATTRIBUTE_LIST)
|
||||
a->flags = 0;
|
||||
|
||||
if ((type == AT_DATA) && !a->initialized_size) {
|
||||
/*
|
||||
* Define/redefine the compression state if stream is
|
||||
* empty, based on the compression mark on parent
|
||||
* directory (for unnamed data streams) or on current
|
||||
* inode (for named data streams). The compression mark
|
||||
* may change any time, the compression state can only
|
||||
* change when stream is void.
|
||||
*/
|
||||
a->flags &= ~ATTR_COMPRESSION_MASK;
|
||||
if (na->ni->flags & FILE_ATTR_COMPRESSED)
|
||||
a->flags |= ATTR_IS_COMPRESSED;
|
||||
}
|
||||
|
||||
cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE);
|
||||
|
||||
if (na->type == AT_DATA && na->name == AT_UNNAMED &&
|
||||
((!(a->flags & ATTR_IS_COMPRESSED) != !NAttrCompressed(na)) ||
|
||||
(!(a->flags & ATTR_IS_SPARSE) != !NAttrSparse(na)) ||
|
||||
((!(a->flags & ATTR_IS_SPARSE) != !NAttrSparse(na)) ||
|
||||
(!(a->flags & ATTR_IS_ENCRYPTED) != !NAttrEncrypted(na)))) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Inode %lld has corrupt attribute flags "
|
||||
|
@ -449,14 +466,15 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
|
|||
}
|
||||
|
||||
if (a->non_resident) {
|
||||
if ((a->flags & ATTR_IS_COMPRESSED) && !a->compression_unit) {
|
||||
if ((a->flags & ATTR_COMPRESSION_MASK)
|
||||
&& !a->compression_unit) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("Compressed inode %lld attr 0x%x has "
|
||||
"no compression unit",
|
||||
(unsigned long long)ni->mft_no, type);
|
||||
goto put_err_out;
|
||||
}
|
||||
ntfs_attr_init(na, TRUE, a->flags & ATTR_IS_COMPRESSED,
|
||||
ntfs_attr_init(na, TRUE, a->flags,
|
||||
a->flags & ATTR_IS_ENCRYPTED,
|
||||
a->flags & ATTR_IS_SPARSE,
|
||||
sle64_to_cpu(a->allocated_size),
|
||||
|
@ -466,7 +484,7 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type,
|
|||
cs ? a->compression_unit : 0);
|
||||
} else {
|
||||
s64 l = le32_to_cpu(a->value_length);
|
||||
ntfs_attr_init(na, FALSE, a->flags & ATTR_IS_COMPRESSED,
|
||||
ntfs_attr_init(na, FALSE, a->flags,
|
||||
a->flags & ATTR_IS_ENCRYPTED,
|
||||
a->flags & ATTR_IS_SPARSE, (l + 7) & ~7, l, l,
|
||||
cs ? (l + 7) & ~7 : 0, 0);
|
||||
|
@ -794,8 +812,16 @@ static s64 ntfs_attr_pread_i(ntfs_attr *na, const s64 pos, s64 count, void *b)
|
|||
|
||||
/* Sanity checking arguments is done in ntfs_attr_pread(). */
|
||||
|
||||
if (NAttrCompressed(na) && NAttrNonResident(na))
|
||||
return ntfs_compressed_attr_pread(na, pos, count, b);
|
||||
if ((na->data_flags & ATTR_COMPRESSION_MASK) && NAttrNonResident(na)) {
|
||||
if ((na->data_flags & ATTR_COMPRESSION_MASK)
|
||||
== ATTR_IS_COMPRESSED)
|
||||
return ntfs_compressed_attr_pread(na, pos, count, b);
|
||||
else {
|
||||
/* compression mode not supported */
|
||||
errno = EOPNOTSUPP;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Encrypted non-resident attributes are not supported. We return
|
||||
* access denied, which is what Windows NT4 does, too.
|
||||
|
@ -1034,6 +1060,7 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs,
|
|||
runlist_element **rl, VCN *update_from)
|
||||
{
|
||||
s64 to_write;
|
||||
s64 need;
|
||||
ntfs_volume *vol = na->ni->vol;
|
||||
int eo, ret = -1;
|
||||
runlist *rlc;
|
||||
|
@ -1067,7 +1094,16 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs,
|
|||
while (rlc->vcn) {
|
||||
rlc--;
|
||||
if (rlc->lcn >= 0) {
|
||||
lcn_seek_from = rlc->lcn + (from_vcn - rlc->vcn);
|
||||
/*
|
||||
* avoid fragmenting a compressed file
|
||||
* Windows does not do that, and that may
|
||||
* not be desirable for files which can
|
||||
* be updated
|
||||
*/
|
||||
if (na->data_flags & ATTR_COMPRESSION_MASK)
|
||||
lcn_seek_from = rlc->lcn + rlc->length;
|
||||
else
|
||||
lcn_seek_from = rlc->lcn + (from_vcn - rlc->vcn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1085,14 +1121,29 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs,
|
|||
}
|
||||
}
|
||||
|
||||
rlc = ntfs_cluster_alloc(vol, from_vcn,
|
||||
((*ofs + to_write - 1) >> vol->cluster_size_bits)
|
||||
+ 1 + (*rl)->vcn - from_vcn,
|
||||
need = ((*ofs + to_write - 1) >> vol->cluster_size_bits)
|
||||
+ 1 + (*rl)->vcn - from_vcn;
|
||||
if ((na->data_flags & ATTR_COMPRESSION_MASK)
|
||||
&& (need < na->compression_block_clusters)) {
|
||||
/*
|
||||
* for a compressed file, be sure to allocate the full hole.
|
||||
* We may need space to decompress existing compressed data.
|
||||
*/
|
||||
rlc = ntfs_cluster_alloc(vol, (*rl)->vcn, (*rl)->length,
|
||||
lcn_seek_from, DATA_ZONE);
|
||||
} else
|
||||
rlc = ntfs_cluster_alloc(vol, from_vcn, need,
|
||||
lcn_seek_from, DATA_ZONE);
|
||||
if (!rlc)
|
||||
goto err_out;
|
||||
|
||||
*rl = ntfs_runlists_merge(na->rl, rlc);
|
||||
/*
|
||||
* For a compressed attribute, we must be sure there is an
|
||||
* available entry, so reserve it before it gets too late.
|
||||
*/
|
||||
if (*rl && (na->data_flags & ATTR_COMPRESSION_MASK))
|
||||
*rl = ntfs_rl_extend(*rl,1);
|
||||
if (!*rl) {
|
||||
eo = errno;
|
||||
ntfs_log_perror("Failed to merge runlists");
|
||||
|
@ -1177,14 +1228,19 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
|
|||
s64 total = 0;
|
||||
VCN update_from = -1;
|
||||
ntfs_volume *vol;
|
||||
s64 fullcount;
|
||||
ntfs_attr_search_ctx *ctx = NULL;
|
||||
runlist_element *rl;
|
||||
s64 hole_end;
|
||||
int eo;
|
||||
int compressed_part;
|
||||
struct {
|
||||
unsigned int undo_initialized_size : 1;
|
||||
unsigned int undo_data_size : 1;
|
||||
} need_to = { 0, 0 };
|
||||
BOOL makingnonresident = FALSE;
|
||||
BOOL wasnonresident = FALSE;
|
||||
BOOL compressed;
|
||||
|
||||
ntfs_log_enter("Entering for inode %lld, attr 0x%x, pos 0x%llx, count "
|
||||
"0x%llx.\n", (long long)na->ni->mft_no, na->type,
|
||||
|
@ -1196,6 +1252,8 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
|
|||
goto errno_set;
|
||||
}
|
||||
vol = na->ni->vol;
|
||||
compressed = (na->data_flags & ATTR_COMPRESSION_MASK)
|
||||
!= const_cpu_to_le16(0);
|
||||
/*
|
||||
* Encrypted non-resident attributes are not supported. We return
|
||||
* access denied, which is what Windows NT4 does, too.
|
||||
|
@ -1205,16 +1263,32 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
|
|||
goto errno_set;
|
||||
}
|
||||
/* If this is a compressed attribute it needs special treatment. */
|
||||
if (NAttrCompressed(na)) {
|
||||
wasnonresident = NAttrNonResident(na) != 0;
|
||||
makingnonresident = wasnonresident /* yes : already changed */
|
||||
&& !pos && (count == na->initialized_size);
|
||||
/*
|
||||
* Writing to compressed files is currently restricted
|
||||
* to appending data. However we have to accept
|
||||
* recursive write calls to make the attribute non resident.
|
||||
* These are writing at position 0 up to initialized_size.
|
||||
* Compression is also restricted to data streams.
|
||||
* Only ATTR_IS_COMPRESSED compression mode is supported.
|
||||
*/
|
||||
if (compressed
|
||||
&& ((na->type != AT_DATA)
|
||||
|| ((na->data_flags & ATTR_COMPRESSION_MASK)
|
||||
!= ATTR_IS_COMPRESSED)
|
||||
|| ((pos != na->initialized_size)
|
||||
&& (pos || (count != na->initialized_size))))) {
|
||||
// TODO: Implement writing compressed attributes! (AIA)
|
||||
// return ntfs_attr_pwrite_compressed(ntfs_attr *na,
|
||||
// const s64 pos, s64 count, void *b);
|
||||
errno = EOPNOTSUPP;
|
||||
goto errno_set;
|
||||
}
|
||||
|
||||
if (!count)
|
||||
goto out;
|
||||
/* for a compressed file, get prepared to reserve a full block */
|
||||
fullcount = count;
|
||||
/* If the write reaches beyond the end, extend the attribute. */
|
||||
old_data_size = na->data_size;
|
||||
if (pos + count > na->data_size) {
|
||||
|
@ -1222,7 +1296,23 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
|
|||
ntfs_log_perror("Failed to enlarge attribute");
|
||||
goto errno_set;
|
||||
}
|
||||
/* resizing may change the compression mode */
|
||||
compressed = (na->data_flags & ATTR_COMPRESSION_MASK)
|
||||
!= const_cpu_to_le16(0);
|
||||
need_to.undo_data_size = 1;
|
||||
}
|
||||
/*
|
||||
* For compressed data, a single full block was allocated
|
||||
* to deal with compression, possibly in a previous call.
|
||||
* We are not able to process several blocks because
|
||||
* some clusters are freed after compression and
|
||||
* new allocations have to be done before proceeding,
|
||||
* so truncate the requested count if needed (big buffers).
|
||||
*/
|
||||
if (compressed) {
|
||||
fullcount = na->data_size - pos;
|
||||
if (count > fullcount)
|
||||
count = fullcount;
|
||||
}
|
||||
old_initialized_size = na->initialized_size;
|
||||
/* If it is a resident attribute, write the data to the mft record. */
|
||||
|
@ -1267,6 +1357,15 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
|
|||
if (pos + count > na->initialized_size) {
|
||||
if (ntfs_attr_map_whole_runlist(na))
|
||||
goto err_out;
|
||||
/*
|
||||
* For a compressed attribute, we must be sure there is an
|
||||
* available entry, so reserve it before it gets too late.
|
||||
*/
|
||||
if (compressed) {
|
||||
na->rl = ntfs_rl_extend(na->rl,1);
|
||||
if (!na->rl)
|
||||
goto err_out;
|
||||
}
|
||||
/* Set initialized_size to @pos + @count. */
|
||||
ctx = ntfs_attr_get_search_ctx(na->ni, NULL);
|
||||
if (!ctx)
|
||||
|
@ -1282,6 +1381,9 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
|
|||
goto err_out;
|
||||
|
||||
ctx->attr->initialized_size = cpu_to_sle64(pos + count);
|
||||
/* fix data_size for compressed files */
|
||||
if (compressed)
|
||||
ctx->attr->data_size = ctx->attr->initialized_size;
|
||||
if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no,
|
||||
ctx->mrec)) {
|
||||
/*
|
||||
|
@ -1315,16 +1417,57 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
|
|||
*/
|
||||
if (errno == ENOENT) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("%s: Failed to find VCN #1", __FUNCTION__);
|
||||
ntfs_log_perror("%s: Failed to find VCN #3", __FUNCTION__);
|
||||
}
|
||||
goto err_out;
|
||||
}
|
||||
ofs = pos - (rl->vcn << vol->cluster_size_bits);
|
||||
/*
|
||||
* Determine if there is compressed data in the current
|
||||
* compression block (when appending to an existing file).
|
||||
* If so, decompression will be needed, and the full block
|
||||
* must be allocated to be identified as uncompressed.
|
||||
* This comes in two variants, depending on whether
|
||||
* compression has saved at least one cluster.
|
||||
* The compressed size can never be over full size by
|
||||
* more than 485 (maximum for 15 compression blocks
|
||||
* compressed to 4098 and the last 3640 bytes compressed
|
||||
* to 3640 + 3640/8 = 4095, with 15*2 + 4095 - 3640 = 485)
|
||||
* This is less than the smallest cluster, so the hole is
|
||||
* is never beyond the cluster next to the position of
|
||||
* the first uncompressed byte to write.
|
||||
*/
|
||||
compressed_part = 0;
|
||||
if (compressed) {
|
||||
if ((rl->lcn >= 0) && (rl[1].lcn == (LCN)LCN_HOLE)) {
|
||||
s64 xofs;
|
||||
|
||||
if (wasnonresident)
|
||||
compressed_part = na->compression_block_clusters
|
||||
- rl[1].length;
|
||||
rl++;
|
||||
xofs = 0;
|
||||
if (ntfs_attr_fill_hole(na,
|
||||
rl->length << vol->cluster_size_bits,
|
||||
&xofs, &rl, &update_from))
|
||||
goto err_out;
|
||||
/* the fist allocated cluster was not merged */
|
||||
if (!xofs)
|
||||
rl--;
|
||||
} else
|
||||
if ((rl->lcn == (LCN)LCN_HOLE)
|
||||
&& wasnonresident
|
||||
&& (rl->length < na->compression_block_clusters))
|
||||
compressed_part
|
||||
= na->compression_block_clusters
|
||||
- rl->length;
|
||||
/* normal hole filling will do later */
|
||||
}
|
||||
/*
|
||||
* Scatter the data from the linear data buffer to the volume. Note, a
|
||||
* partial final vcn is taken care of by the @count capping of write
|
||||
* length.
|
||||
*/
|
||||
ofs = pos - (rl->vcn << vol->cluster_size_bits);
|
||||
for (hole_end = 0; count; rl++, ofs = 0, hole_end = 0) {
|
||||
if (rl->lcn == LCN_RL_NOT_MAPPED) {
|
||||
rl = ntfs_attr_find_vcn(na, rl->vcn);
|
||||
|
@ -1332,7 +1475,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
|
|||
if (errno == ENOENT) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("%s: Failed to find VCN"
|
||||
" #2", __FUNCTION__);
|
||||
" #4", __FUNCTION__);
|
||||
}
|
||||
goto rl_err_out;
|
||||
}
|
||||
|
@ -1345,7 +1488,6 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
|
|||
goto rl_err_out;
|
||||
}
|
||||
if (rl->lcn < (LCN)0) {
|
||||
|
||||
hole_end = rl->vcn + rl->length;
|
||||
|
||||
if (rl->lcn != (LCN)LCN_HOLE) {
|
||||
|
@ -1355,10 +1497,17 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b)
|
|||
(long long)rl->lcn);
|
||||
goto rl_err_out;
|
||||
}
|
||||
|
||||
if (ntfs_attr_fill_hole(na, count, &ofs, &rl, &update_from))
|
||||
if (ntfs_attr_fill_hole(na, fullcount, &ofs, &rl,
|
||||
&update_from))
|
||||
goto err_out;
|
||||
}
|
||||
if (compressed) {
|
||||
while (ofs >= (rl->length << vol->cluster_size_bits)) {
|
||||
ofs -= rl->length << vol->cluster_size_bits;
|
||||
rl++;
|
||||
}
|
||||
}
|
||||
|
||||
/* It is a real lcn, write it to the volume. */
|
||||
to_write = min(count, (rl->length << vol->cluster_size_bits) - ofs);
|
||||
retry:
|
||||
|
@ -1378,6 +1527,9 @@ retry:
|
|||
* This will cause the kernel not to seek and read disk
|
||||
* blocks during write(2) to fill the end of the buffer
|
||||
* which increases write speed by 2-10 fold typically.
|
||||
*
|
||||
* This is done even for compressed files, because
|
||||
* data is generally first written uncompressed.
|
||||
*/
|
||||
if (rounding && ((wend == na->initialized_size) ||
|
||||
(wend < (hole_end << vol->cluster_size_bits)))){
|
||||
|
@ -1393,13 +1545,27 @@ retry:
|
|||
memcpy(cb, b, to_write);
|
||||
memset(cb + to_write, 0, rounding - to_write);
|
||||
|
||||
written = ntfs_pwrite(vol->dev, wpos, rounding, cb);
|
||||
if (written == rounding)
|
||||
written = to_write;
|
||||
if (compressed) {
|
||||
written = ntfs_compressed_pwrite(na,
|
||||
rl, wpos, ofs, to_write,
|
||||
rounding, b, compressed_part);
|
||||
} else {
|
||||
written = ntfs_pwrite(vol->dev, wpos,
|
||||
rounding, cb);
|
||||
if (written == rounding)
|
||||
written = to_write;
|
||||
}
|
||||
|
||||
free(cb);
|
||||
} else
|
||||
written = ntfs_pwrite(vol->dev, wpos, to_write, b);
|
||||
} else {
|
||||
if (compressed) {
|
||||
written = ntfs_compressed_pwrite(na,
|
||||
rl, wpos, ofs, to_write,
|
||||
to_write, b, compressed_part);
|
||||
} else
|
||||
written = ntfs_pwrite(vol->dev, wpos,
|
||||
to_write, b);
|
||||
}
|
||||
} else
|
||||
written = to_write;
|
||||
/* If everything ok, update progress counters and continue. */
|
||||
|
@ -1408,20 +1574,23 @@ retry:
|
|||
count -= written;
|
||||
b = (const u8*)b + written;
|
||||
}
|
||||
if (written == to_write)
|
||||
continue;
|
||||
/* If the syscall was interrupted, try again. */
|
||||
if (written == (s64)-1 && errno == EINTR)
|
||||
goto retry;
|
||||
if (!written)
|
||||
errno = EIO;
|
||||
goto rl_err_out;
|
||||
if (written != to_write) {
|
||||
/* Partial write cannot be dealt with, stop there */
|
||||
/* If the syscall was interrupted, try again. */
|
||||
if (written == (s64)-1 && errno == EINTR)
|
||||
goto retry;
|
||||
if (!written)
|
||||
errno = EIO;
|
||||
goto rl_err_out;
|
||||
}
|
||||
compressed_part = 0;
|
||||
}
|
||||
done:
|
||||
if (ctx)
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
/* Update mapping pairs if needed. */
|
||||
if (update_from != -1)
|
||||
if ((update_from != -1)
|
||||
|| (compressed && !makingnonresident))
|
||||
if (ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/)) {
|
||||
/*
|
||||
* FIXME: trying to recover by goto rl_err_out;
|
||||
|
@ -1497,6 +1666,172 @@ errno_set:
|
|||
goto out;
|
||||
}
|
||||
|
||||
int ntfs_attr_pclose(ntfs_attr *na)
|
||||
{
|
||||
s64 written, ofs;
|
||||
BOOL ok = TRUE;
|
||||
VCN update_from = -1;
|
||||
ntfs_volume *vol;
|
||||
ntfs_attr_search_ctx *ctx = NULL;
|
||||
runlist_element *rl;
|
||||
int eo;
|
||||
s64 hole;
|
||||
int compressed_part;
|
||||
BOOL compressed;
|
||||
|
||||
ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x.\n",
|
||||
na->ni->mft_no, na->type);
|
||||
|
||||
if (!na || !na->ni || !na->ni->vol) {
|
||||
errno = EINVAL;
|
||||
ntfs_log_perror("%s", __FUNCTION__);
|
||||
goto errno_set;
|
||||
}
|
||||
vol = na->ni->vol;
|
||||
compressed = (na->data_flags & ATTR_COMPRESSION_MASK)
|
||||
!= const_cpu_to_le16(0);
|
||||
/*
|
||||
* Encrypted non-resident attributes are not supported. We return
|
||||
* access denied, which is what Windows NT4 does, too.
|
||||
*/
|
||||
if (NAttrEncrypted(na) && NAttrNonResident(na)) {
|
||||
errno = EACCES;
|
||||
goto errno_set;
|
||||
}
|
||||
/* If this is not a compressed attribute get out */
|
||||
/* same if it is resident */
|
||||
if (!compressed || !NAttrNonResident(na))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* For a compressed attribute, we must be sure there is an
|
||||
* available entry, so reserve it before it gets too late.
|
||||
*/
|
||||
if (ntfs_attr_map_whole_runlist(na))
|
||||
goto err_out;
|
||||
na->rl = ntfs_rl_extend(na->rl,1);
|
||||
if (!na->rl)
|
||||
goto err_out;
|
||||
/* Find the runlist element containing the terminal vcn. */
|
||||
rl = ntfs_attr_find_vcn(na, (na->initialized_size - 1) >> vol->cluster_size_bits);
|
||||
if (!rl) {
|
||||
/*
|
||||
* If the vcn is not present it is an out of bounds write.
|
||||
* However, we have already written the last byte uncompressed,
|
||||
* so getting this here must be an error of some kind.
|
||||
*/
|
||||
if (errno == ENOENT) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("%s: Failed to find VCN #5", __FUNCTION__);
|
||||
}
|
||||
goto err_out;
|
||||
}
|
||||
/*
|
||||
* Scatter the data from the linear data buffer to the volume. Note, a
|
||||
* partial final vcn is taken care of by the @count capping of write
|
||||
* length.
|
||||
*/
|
||||
compressed_part = 0;
|
||||
if ((rl->lcn >= 0) && (rl[1].lcn == (LCN)LCN_HOLE))
|
||||
compressed_part
|
||||
= na->compression_block_clusters - rl[1].length;
|
||||
else
|
||||
if ((rl->lcn == (LCN)LCN_HOLE)
|
||||
&& (rl->length < na->compression_block_clusters))
|
||||
compressed_part
|
||||
= na->compression_block_clusters
|
||||
- rl->length;
|
||||
/* done, if the last block set was compressed */
|
||||
if (compressed_part)
|
||||
goto out;
|
||||
|
||||
ofs = na->initialized_size - (rl->vcn << vol->cluster_size_bits);
|
||||
|
||||
if (rl->lcn == LCN_RL_NOT_MAPPED) {
|
||||
rl = ntfs_attr_find_vcn(na, rl->vcn);
|
||||
if (!rl) {
|
||||
if (errno == ENOENT) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("%s: Failed to find VCN"
|
||||
" #6", __FUNCTION__);
|
||||
}
|
||||
goto rl_err_out;
|
||||
}
|
||||
/* Needed for case when runs merged. */
|
||||
ofs = na->initialized_size - (rl->vcn << vol->cluster_size_bits);
|
||||
}
|
||||
if (!rl->length) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("%s: Zero run length", __FUNCTION__);
|
||||
goto rl_err_out;
|
||||
}
|
||||
if (rl->lcn < (LCN)0) {
|
||||
hole = rl->vcn + rl->length;
|
||||
if (rl->lcn != (LCN)LCN_HOLE) {
|
||||
errno = EIO;
|
||||
ntfs_log_perror("%s: Unexpected LCN (%lld)",
|
||||
__FUNCTION__,
|
||||
(long long)rl->lcn);
|
||||
goto rl_err_out;
|
||||
}
|
||||
|
||||
if (ntfs_attr_fill_hole(na, (s64)0, &ofs, &rl, &update_from))
|
||||
goto err_out;
|
||||
}
|
||||
while (ofs >= (rl->length << vol->cluster_size_bits)) {
|
||||
ofs -= rl->length << vol->cluster_size_bits;
|
||||
rl++;
|
||||
}
|
||||
|
||||
retry:
|
||||
if (!NVolReadOnly(vol)) {
|
||||
|
||||
written = ntfs_compressed_close(na, rl, ofs);
|
||||
/* If everything ok, update progress counters and continue. */
|
||||
if (!written)
|
||||
goto done;
|
||||
}
|
||||
/* If the syscall was interrupted, try again. */
|
||||
if (written == (s64)-1 && errno == EINTR)
|
||||
goto retry;
|
||||
if (!written)
|
||||
errno = EIO;
|
||||
goto rl_err_out;
|
||||
|
||||
done:
|
||||
if (ctx)
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
/* Update mapping pairs if needed. */
|
||||
if (ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/)) {
|
||||
/*
|
||||
* FIXME: trying to recover by goto rl_err_out;
|
||||
* could cause driver hang by infinite looping.
|
||||
*/
|
||||
ok = FALSE;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
ntfs_log_leave("\n");
|
||||
return (!ok);
|
||||
rl_err_out:
|
||||
/*
|
||||
* need not restore old sizes, only compressed_size
|
||||
* can have changed. It has been set according to
|
||||
* the current runlist while updating the mapping pairs,
|
||||
* and must be kept consistent with the runlists.
|
||||
*/
|
||||
err_out:
|
||||
eo = errno;
|
||||
if (ctx)
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
/* Update mapping pairs if needed. */
|
||||
ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/);
|
||||
errno = eo;
|
||||
errno_set:
|
||||
ok = FALSE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_attr_mst_pread - multi sector transfer protected ntfs attribute read
|
||||
* @na: multi sector transfer protected ntfs attribute to read from
|
||||
|
@ -2727,7 +3062,7 @@ int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size)
|
|||
*/
|
||||
int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
|
||||
ntfschar *name, u8 name_len, u8 *val, u32 size,
|
||||
ATTR_FLAGS flags)
|
||||
ATTR_FLAGS data_flags)
|
||||
{
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
u32 length;
|
||||
|
@ -2791,8 +3126,9 @@ int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
|
|||
a->non_resident = 0;
|
||||
a->name_length = name_len;
|
||||
a->name_offset = (name_len
|
||||
? cpu_to_le16(offsetof(ATTR_RECORD, resident_end)) : 0);
|
||||
a->flags = flags;
|
||||
? cpu_to_le16(offsetof(ATTR_RECORD, resident_end))
|
||||
: const_cpu_to_le16(0));
|
||||
a->flags = data_flags;
|
||||
a->instance = m->next_attr_instance;
|
||||
a->value_length = cpu_to_le32(size);
|
||||
a->value_offset = cpu_to_le16(length - ((size + 7) & ~7));
|
||||
|
@ -2929,7 +3265,8 @@ int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type,
|
|||
a->instance = m->next_attr_instance;
|
||||
a->lowest_vcn = cpu_to_sle64(lowest_vcn);
|
||||
a->mapping_pairs_offset = cpu_to_le16(length - dataruns_size);
|
||||
a->compression_unit = (flags & ATTR_IS_COMPRESSED) ? 4 : 0;
|
||||
a->compression_unit = (flags & ATTR_IS_COMPRESSED)
|
||||
? STANDARD_COMPRESSION_UNIT : 0;
|
||||
/* If @lowest_vcn == 0, than setup empty attribute. */
|
||||
if (!lowest_vcn) {
|
||||
a->highest_vcn = cpu_to_sle64(-1);
|
||||
|
@ -3255,9 +3592,17 @@ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type,
|
|||
|
||||
add_attr_record:
|
||||
if (is_resident) {
|
||||
ATTR_FLAGS data_flags;
|
||||
|
||||
if ((ni->flags & FILE_ATTR_COMPRESSED)
|
||||
&& ((type == AT_DATA)
|
||||
|| ((type == AT_INDEX_ROOT) && (name == NTFS_INDEX_I30))))
|
||||
data_flags = ATTR_IS_COMPRESSED;
|
||||
else
|
||||
data_flags = const_cpu_to_le16(0);
|
||||
/* Add resident attribute. */
|
||||
offset = ntfs_resident_attr_record_add(attr_ni, type, name,
|
||||
name_len, val, size, 0);
|
||||
name_len, val, size, data_flags);
|
||||
if (offset < 0) {
|
||||
if (errno == ENOSPC && can_be_non_resident)
|
||||
goto add_non_resident;
|
||||
|
@ -3271,7 +3616,7 @@ add_attr_record:
|
|||
add_non_resident:
|
||||
/* Add non resident attribute. */
|
||||
offset = ntfs_non_resident_attr_record_add(attr_ni, type, name,
|
||||
name_len, 0, 8, 0);
|
||||
name_len, 0, 8, const_cpu_to_le16(0));
|
||||
if (offset < 0) {
|
||||
err = errno;
|
||||
ntfs_log_perror("Failed to add non resident attribute");
|
||||
|
@ -3318,6 +3663,34 @@ err_out:
|
|||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change an attribute flag
|
||||
*/
|
||||
|
||||
int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type,
|
||||
ntfschar *name, u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask)
|
||||
{
|
||||
ntfs_attr_search_ctx *ctx;
|
||||
int res;
|
||||
|
||||
res = -1;
|
||||
/* Search for designated attribute */
|
||||
ctx = ntfs_attr_get_search_ctx(ni, NULL);
|
||||
if (ctx) {
|
||||
if (!ntfs_attr_lookup(type, name, name_len,
|
||||
CASE_SENSITIVE, 0, NULL, 0, ctx)) {
|
||||
/* do the requested change (all small endian le16) */
|
||||
ctx->attr->flags = (ctx->attr->flags & ~mask)
|
||||
| (flags & mask);
|
||||
NInoSetDirty(ni);
|
||||
res = 0;
|
||||
}
|
||||
ntfs_attr_put_search_ctx(ctx);
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ntfs_attr_rm - remove attribute from ntfs inode
|
||||
* @na: opened ntfs attribute to delete
|
||||
|
@ -3724,7 +4097,6 @@ static int ntfs_attr_make_non_resident(ntfs_attr *na,
|
|||
* FIXME: For now just clear all of these as we don't support them when
|
||||
* writing.
|
||||
*/
|
||||
NAttrClearCompressed(na);
|
||||
NAttrClearSparse(na);
|
||||
NAttrClearEncrypted(na);
|
||||
|
||||
|
@ -3751,7 +4123,10 @@ static int ntfs_attr_make_non_resident(ntfs_attr *na,
|
|||
goto cluster_free_err_out;
|
||||
}
|
||||
/* Calculate new offsets for the name and the mapping pairs array. */
|
||||
name_ofs = (sizeof(ATTR_REC) - sizeof(a->compressed_size) + 7) & ~7;
|
||||
if (na->ni->flags & FILE_ATTR_COMPRESSED)
|
||||
name_ofs = (sizeof(ATTR_REC) + 7) & ~7;
|
||||
else
|
||||
name_ofs = (sizeof(ATTR_REC) - sizeof(a->compressed_size) + 7) & ~7;
|
||||
mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7;
|
||||
/*
|
||||
* Determine the size of the resident part of the non-resident
|
||||
|
@ -3778,10 +4153,6 @@ static int ntfs_attr_make_non_resident(ntfs_attr *na,
|
|||
a->name_length * sizeof(ntfschar));
|
||||
a->name_offset = cpu_to_le16(name_ofs);
|
||||
|
||||
/* Update the flags to match the in-memory ones. */
|
||||
a->flags &= ~(ATTR_IS_SPARSE | ATTR_IS_ENCRYPTED |
|
||||
ATTR_COMPRESSION_MASK);
|
||||
|
||||
/* Setup the fields specific to non-resident attributes. */
|
||||
a->lowest_vcn = cpu_to_sle64(0);
|
||||
a->highest_vcn = cpu_to_sle64((new_allocated_size - 1) >>
|
||||
|
@ -3789,7 +4160,26 @@ static int ntfs_attr_make_non_resident(ntfs_attr *na,
|
|||
|
||||
a->mapping_pairs_offset = cpu_to_le16(mp_ofs);
|
||||
|
||||
a->compression_unit = 0;
|
||||
/*
|
||||
* Update the flags to match the in-memory ones.
|
||||
* However cannot change the compression state if we had
|
||||
* a fuse_file_info open with a mark for release.
|
||||
* The decisions about compression can only be made when
|
||||
* creating/recreating the stream, not when making non resident.
|
||||
*/
|
||||
a->flags &= ~(ATTR_IS_SPARSE | ATTR_IS_ENCRYPTED);
|
||||
if ((a->flags & ATTR_COMPRESSION_MASK) == ATTR_IS_COMPRESSED) {
|
||||
/* support only ATTR_IS_COMPRESSED compression mode */
|
||||
a->compression_unit = STANDARD_COMPRESSION_UNIT;
|
||||
a->compressed_size = const_cpu_to_le64(0);
|
||||
na->compression_block_size
|
||||
= 1 << (a->compression_unit + vol->cluster_size_bits);
|
||||
na->compression_block_clusters = 1 << STANDARD_COMPRESSION_UNIT;
|
||||
} else {
|
||||
a->compression_unit = 0;
|
||||
a->flags &= ~ATTR_COMPRESSION_MASK;
|
||||
na->data_flags = a->flags;
|
||||
}
|
||||
|
||||
memset(&a->reserved1, 0, sizeof(a->reserved1));
|
||||
|
||||
|
@ -3886,7 +4276,8 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize)
|
|||
/* Update attribute size everywhere. */
|
||||
na->data_size = na->initialized_size = newsize;
|
||||
na->allocated_size = (newsize + 7) & ~7;
|
||||
if (NAttrCompressed(na) || NAttrSparse(na))
|
||||
if ((na->data_flags & ATTR_COMPRESSION_MASK)
|
||||
|| NAttrSparse(na))
|
||||
na->compressed_size = na->allocated_size;
|
||||
if (na->type == AT_DATA && na->name == AT_UNNAMED) {
|
||||
na->ni->data_size = na->data_size;
|
||||
|
@ -4109,8 +4500,8 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx)
|
|||
if (ntfs_attr_can_be_resident(vol, na->type))
|
||||
return -1;
|
||||
|
||||
if (NAttrCompressed(na) || NAttrEncrypted(na)) {
|
||||
ntfs_log_trace("Making compressed or encrypted files resident is not "
|
||||
if (NAttrEncrypted(na)) {
|
||||
ntfs_log_trace("Making encrypted files resident is not "
|
||||
"implemented yet.\n");
|
||||
errno = EOPNOTSUPP;
|
||||
return -1;
|
||||
|
@ -4158,6 +4549,16 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx)
|
|||
a->flags = 0;
|
||||
a->value_length = cpu_to_le32(na->data_size);
|
||||
a->value_offset = cpu_to_le16(val_ofs);
|
||||
/*
|
||||
* If a data stream was wiped out, adjust the compression mode
|
||||
* to current state of compression flag
|
||||
*/
|
||||
if (!na->data_size
|
||||
&& (na->type == AT_DATA)
|
||||
&& (na->ni->flags & FILE_ATTR_COMPRESSED)) {
|
||||
a->flags |= ATTR_IS_COMPRESSED;
|
||||
na->data_flags = a->flags;
|
||||
}
|
||||
/*
|
||||
* File names cannot be non-resident so we would never see this here
|
||||
* but at least it serves as a reminder that there may be attributes
|
||||
|
@ -4211,7 +4612,6 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx)
|
|||
|
||||
/* Update in-memory struct ntfs_attr. */
|
||||
NAttrClearNonResident(na);
|
||||
NAttrClearCompressed(na);
|
||||
NAttrClearSparse(na);
|
||||
NAttrClearEncrypted(na);
|
||||
na->initialized_size = na->data_size;
|
||||
|
@ -4279,8 +4679,8 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m,
|
|||
|
||||
NAttrSetSparse(na);
|
||||
a->flags |= ATTR_IS_SPARSE;
|
||||
a->compression_unit = 4; /* Windows set it so, even if attribute
|
||||
is not actually compressed. */
|
||||
a->compression_unit = STANDARD_COMPRESSION_UNIT; /* Windows
|
||||
set it so, even if attribute is not actually compressed. */
|
||||
|
||||
memmove((u8*)a + le16_to_cpu(a->name_offset) + 8,
|
||||
(u8*)a + le16_to_cpu(a->name_offset),
|
||||
|
@ -4312,7 +4712,7 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m,
|
|||
}
|
||||
|
||||
/* Update compressed size if required. */
|
||||
if (sparse) {
|
||||
if (sparse || (na->data_flags & ATTR_COMPRESSION_MASK)) {
|
||||
s64 new_compr_size;
|
||||
|
||||
new_compr_size = ntfs_rl_get_compressed_size(na->ni->vol, na->rl);
|
||||
|
@ -4588,7 +4988,8 @@ retry:
|
|||
cur_max_mp_size = le32_to_cpu(m->bytes_allocated) -
|
||||
le32_to_cpu(m->bytes_in_use) -
|
||||
(offsetof(ATTR_RECORD, compressed_size) +
|
||||
((NAttrCompressed(na) || NAttrSparse(na)) ?
|
||||
(((na->data_flags & ATTR_COMPRESSION_MASK)
|
||||
|| NAttrSparse(na)) ?
|
||||
sizeof(a->compressed_size) : 0)) -
|
||||
((sizeof(ntfschar) * na->name_len + 7) & ~7);
|
||||
if (mp_size > cur_max_mp_size)
|
||||
|
@ -5047,6 +5448,8 @@ static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize)
|
|||
int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize)
|
||||
{
|
||||
int ret = STATUS_ERROR;
|
||||
s64 fullsize;
|
||||
BOOL compressed;
|
||||
|
||||
if (!na || newsize < 0 ||
|
||||
(na->ni->mft_no == FILE_MFT && na->type == AT_DATA)) {
|
||||
|
@ -5075,17 +5478,41 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize)
|
|||
}
|
||||
/*
|
||||
* TODO: Implement making handling of compressed attributes.
|
||||
* Currently we can only expand the attribute or delete it,
|
||||
* and only for ATTR_IS_COMPRESSED. This is however possible
|
||||
* for resident attributes when there is no open fuse context
|
||||
* (important case : $INDEX_ROOT:$I30)
|
||||
*/
|
||||
if (NAttrCompressed(na)) {
|
||||
compressed = (na->data_flags & ATTR_COMPRESSION_MASK)
|
||||
!= const_cpu_to_le16(0);
|
||||
if (compressed
|
||||
&& NAttrNonResident(na)
|
||||
&& (((na->data_flags & ATTR_COMPRESSION_MASK) != ATTR_IS_COMPRESSED)
|
||||
|| (newsize && (newsize < na->data_size)))) {
|
||||
errno = EOPNOTSUPP;
|
||||
ntfs_log_perror("Failed to truncate compressed attribute");
|
||||
goto out;
|
||||
}
|
||||
if (NAttrNonResident(na)) {
|
||||
if (newsize > na->data_size)
|
||||
ret = ntfs_non_resident_attr_expand(na, newsize);
|
||||
/*
|
||||
* For compressed data, the last block must be fully
|
||||
* allocated, and we do not known the size of compression
|
||||
* block until the attribute has been made non-resident.
|
||||
* Moreover we can only process a single compression
|
||||
* block at a time, so we silently do not allocate more.
|
||||
*
|
||||
* Note : do not request truncate on compressed files
|
||||
* unless being able to face the consequences !
|
||||
*/
|
||||
if (compressed && newsize)
|
||||
fullsize = (na->data_size
|
||||
| (na->compression_block_size - 1)) + 1;
|
||||
else
|
||||
ret = ntfs_non_resident_attr_shrink(na, newsize);
|
||||
fullsize = newsize;
|
||||
if (fullsize > na->data_size)
|
||||
ret = ntfs_non_resident_attr_expand(na, fullsize);
|
||||
else
|
||||
ret = ntfs_non_resident_attr_shrink(na, fullsize);
|
||||
} else
|
||||
ret = ntfs_resident_attr_resize(na, newsize);
|
||||
out:
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* Copyright (c) 2004-2005 Anton Altaparmakov
|
||||
* Copyright (c) 2004-2006 Szabolcs Szakacsits
|
||||
* Copyright (c) 2005 Yura Pakhuchiy
|
||||
* Copyright (c) 2009 Jean-Pierre Andre
|
||||
*
|
||||
* 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
|
||||
|
@ -46,6 +47,7 @@
|
|||
#include "layout.h"
|
||||
#include "runlist.h"
|
||||
#include "compress.h"
|
||||
#include "lcnalloc.h"
|
||||
#include "logging.h"
|
||||
#include "misc.h"
|
||||
|
||||
|
@ -64,6 +66,261 @@ typedef enum {
|
|||
NTFS_SB_IS_COMPRESSED = 0x8000,
|
||||
} ntfs_compression_constants;
|
||||
|
||||
#define THRESHOLD 3 /* minimal match length for compression */
|
||||
#define NIL NTFS_SB_SIZE /* End of tree's node */
|
||||
|
||||
struct COMPRESS_CONTEXT {
|
||||
const unsigned char *inbuf;
|
||||
unsigned int len;
|
||||
unsigned int nbt;
|
||||
int match_position;
|
||||
unsigned int match_length;
|
||||
u16 lson[NTFS_SB_SIZE + 1];
|
||||
u16 rson[NTFS_SB_SIZE + 257];
|
||||
u16 dad[NTFS_SB_SIZE + 1];
|
||||
} ;
|
||||
|
||||
/*
|
||||
* Initialize the match tree
|
||||
*/
|
||||
|
||||
static void ntfs_init_compress_tree(struct COMPRESS_CONTEXT *pctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = NTFS_SB_SIZE + 1; i <= NTFS_SB_SIZE + 256; i++)
|
||||
pctx->rson[i] = NIL; /* root */
|
||||
for (i = 0; i < NTFS_SB_SIZE; i++)
|
||||
pctx->dad[i] = NIL; /* node */
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert a new node into match tree for quickly locating
|
||||
* further similar strings
|
||||
*/
|
||||
|
||||
static void ntfs_new_node (struct COMPRESS_CONTEXT *pctx,
|
||||
unsigned int r)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int pp;
|
||||
BOOL less;
|
||||
BOOL done;
|
||||
const unsigned char *key;
|
||||
int c;
|
||||
unsigned int mxi;
|
||||
unsigned int mxl;
|
||||
|
||||
mxl = (1 << (16 - pctx->nbt)) + 2;
|
||||
less = FALSE;
|
||||
done = FALSE;
|
||||
key = &pctx->inbuf[r];
|
||||
pp = NTFS_SB_SIZE + 1 + key[0];
|
||||
pctx->rson[r] = pctx->lson[r] = NIL;
|
||||
pctx->match_length = 0;
|
||||
do {
|
||||
if (!less) {
|
||||
if (pctx->rson[pp] != NIL)
|
||||
pp = pctx->rson[pp];
|
||||
else {
|
||||
pctx->rson[pp] = r;
|
||||
pctx->dad[r] = pp;
|
||||
done = TRUE;
|
||||
}
|
||||
} else {
|
||||
if (pctx->lson[pp] != NIL)
|
||||
pp = pctx->lson[pp];
|
||||
else {
|
||||
pctx->lson[pp] = r;
|
||||
pctx->dad[r] = pp;
|
||||
done = TRUE;
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
register const unsigned char *p1,*p2;
|
||||
|
||||
i = 1;
|
||||
p1 = key;
|
||||
p2 = &pctx->inbuf[pp];
|
||||
mxi = NTFS_SB_SIZE - r;
|
||||
do {
|
||||
} while ((p1[i] == p2[i]) && (++i < mxi));
|
||||
less = (i < mxi) && (p1[i] < p2[i]);
|
||||
if (i >= THRESHOLD) {
|
||||
if (i > pctx->match_length) {
|
||||
pctx->match_position =
|
||||
r - pp + 2*NTFS_SB_SIZE - 1;
|
||||
if ((pctx->match_length = i) > mxl) {
|
||||
i = pctx->dad[pp];
|
||||
pctx->dad[r] = i;
|
||||
pctx->lson[r] = pctx->lson[pp];
|
||||
pctx->rson[r] = pctx->rson[pp];
|
||||
pctx->dad[pctx->lson[pp]] = r;
|
||||
pctx->dad[pctx->rson[pp]] = r;
|
||||
if (pctx->rson[i] == pp)
|
||||
pctx->rson[i] = r;
|
||||
else
|
||||
pctx->lson[i] = r;
|
||||
pctx->dad[pp] = NIL; /* remove pp */
|
||||
done = TRUE;
|
||||
pctx->match_length = mxl;
|
||||
}
|
||||
} else
|
||||
if ((i == pctx->match_length)
|
||||
&& ((c = (r - pp + 2*NTFS_SB_SIZE - 1))
|
||||
< pctx->match_position))
|
||||
pctx->match_position = c;
|
||||
}
|
||||
}
|
||||
} while (!done);
|
||||
}
|
||||
|
||||
/*
|
||||
* Search for the longest previous string matching the
|
||||
* current one
|
||||
*
|
||||
* Returns the end of the longest current string which matched
|
||||
* or zero if there was a bug
|
||||
*/
|
||||
|
||||
static unsigned int ntfs_nextmatch(struct COMPRESS_CONTEXT *pctx, unsigned int rr, int dd)
|
||||
{
|
||||
unsigned int bestlen = 0;
|
||||
|
||||
do {
|
||||
rr++;
|
||||
if (pctx->match_length > 0)
|
||||
pctx->match_length--;
|
||||
if (!pctx->len) {
|
||||
ntfs_log_error("compress bug : void run\n");
|
||||
goto bug;
|
||||
}
|
||||
if (--pctx->len) {
|
||||
if (rr >= NTFS_SB_SIZE) {
|
||||
ntfs_log_error("compress bug : buffer overflow\n");
|
||||
goto bug;
|
||||
}
|
||||
if (((rr + bestlen) < NTFS_SB_SIZE)) {
|
||||
while ((unsigned int)(1 << pctx->nbt) <= (rr - 1))
|
||||
pctx->nbt++;
|
||||
ntfs_new_node(pctx,rr);
|
||||
if (pctx->match_length > bestlen)
|
||||
bestlen = pctx->match_length;
|
||||
} else
|
||||
if (dd > 0) {
|
||||
rr += dd;
|
||||
if ((int)pctx->match_length > dd)
|
||||
pctx->match_length -= dd;
|
||||
else
|
||||
pctx->match_length = 0;
|
||||
if ((int)pctx->len < dd) {
|
||||
ntfs_log_error("compress bug : run overflows\n");
|
||||
goto bug;
|
||||
}
|
||||
pctx->len -= dd;
|
||||
dd = 0;
|
||||
}
|
||||
}
|
||||
} while (dd-- > 0);
|
||||
return (rr);
|
||||
bug :
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compress an input block
|
||||
*
|
||||
* Returns the size of the compressed block (including header)
|
||||
* or zero if there was an error
|
||||
*/
|
||||
|
||||
static unsigned int ntfs_compress_block(const char *inbuf, unsigned int size, char *outbuf)
|
||||
{
|
||||
struct COMPRESS_CONTEXT *pctx;
|
||||
char *ptag;
|
||||
int dd;
|
||||
unsigned int rr;
|
||||
unsigned int last_match_length;
|
||||
unsigned int q;
|
||||
unsigned int xout;
|
||||
unsigned int ntag;
|
||||
|
||||
pctx = (struct COMPRESS_CONTEXT*)malloc(sizeof(struct COMPRESS_CONTEXT));
|
||||
if (pctx) {
|
||||
pctx->inbuf = (const unsigned char*)inbuf;
|
||||
ntfs_init_compress_tree(pctx);
|
||||
xout = 2;
|
||||
ntag = 0;
|
||||
ptag = &outbuf[xout++];
|
||||
*ptag = 0;
|
||||
rr = 0;
|
||||
pctx->nbt = 4;
|
||||
pctx->len = size;
|
||||
pctx->match_length = 0;
|
||||
ntfs_new_node(pctx,0);
|
||||
do {
|
||||
if (pctx->match_length > pctx->len)
|
||||
pctx->match_length = pctx->len;
|
||||
if (pctx->match_length < THRESHOLD) {
|
||||
pctx->match_length = 1;
|
||||
if (ntag >= 8) {
|
||||
ntag = 0;
|
||||
ptag = &outbuf[xout++];
|
||||
*ptag = 0;
|
||||
}
|
||||
outbuf[xout++] = inbuf[rr];
|
||||
ntag++;
|
||||
} else {
|
||||
while ((unsigned int)(1 << pctx->nbt) <= (rr - 1))
|
||||
pctx->nbt++;
|
||||
q = (pctx->match_position << (16 - pctx->nbt))
|
||||
+ pctx->match_length - THRESHOLD;
|
||||
if (ntag >= 8) {
|
||||
ntag = 0;
|
||||
ptag = &outbuf[xout++];
|
||||
*ptag = 0;
|
||||
}
|
||||
*ptag |= 1 << ntag++;
|
||||
outbuf[xout++] = q & 255;
|
||||
outbuf[xout++] = (q >> 8) & 255;
|
||||
}
|
||||
last_match_length = pctx->match_length;
|
||||
dd = last_match_length;
|
||||
if (dd-- > 0) {
|
||||
rr = ntfs_nextmatch(pctx,rr,dd);
|
||||
if (!rr)
|
||||
goto bug;
|
||||
}
|
||||
/*
|
||||
* stop if input is exhausted or output has exceeded
|
||||
* the maximum size. Two extra bytes have to be
|
||||
* reserved in output buffer, as 3 bytes may be
|
||||
* output in a loop.
|
||||
*/
|
||||
} while ((pctx->len > 0)
|
||||
&& (rr < size) && (xout < (NTFS_SB_SIZE + 2)));
|
||||
/* uncompressed must be full size, so accept if better */
|
||||
if (xout < (NTFS_SB_SIZE + 2)) {
|
||||
outbuf[0] = (xout - 3) & 255;
|
||||
outbuf[1] = 0xb0 + (((xout - 3) >> 8) & 15);
|
||||
} else {
|
||||
memcpy(&outbuf[2],inbuf,size);
|
||||
if (size < NTFS_SB_SIZE)
|
||||
memset(&outbuf[size+2],0,NTFS_SB_SIZE - size);
|
||||
outbuf[0] = 0xff;
|
||||
outbuf[1] = 0x3f;
|
||||
xout = NTFS_SB_SIZE + 2;
|
||||
}
|
||||
free(pctx);
|
||||
} else {
|
||||
xout = 0;
|
||||
errno = ENOMEM;
|
||||
}
|
||||
return (xout); /* 0 for an error, > size if cannot compress */
|
||||
bug :
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_decompress - decompress a compression block into an array of pages
|
||||
* @dest: buffer to which to write the decompressed data
|
||||
|
@ -108,7 +365,7 @@ do_next_sb:
|
|||
* position in the compression block is one byte before its end so the
|
||||
* first two checks do not detect it.
|
||||
*/
|
||||
if (cb == cb_end || !le16_to_cpup((u16*)cb) || dest == dest_end) {
|
||||
if (cb == cb_end || !le16_to_cpup((le16*)cb) || dest == dest_end) {
|
||||
ntfs_log_debug("Completed. Returning success (0).\n");
|
||||
return 0;
|
||||
}
|
||||
|
@ -123,12 +380,12 @@ do_next_sb:
|
|||
goto return_overflow;
|
||||
/* Setup the current sub-block source pointers and validate range. */
|
||||
cb_sb_start = cb;
|
||||
cb_sb_end = cb_sb_start + (le16_to_cpup((u16*)cb) & NTFS_SB_SIZE_MASK)
|
||||
cb_sb_end = cb_sb_start + (le16_to_cpup((le16*)cb) & NTFS_SB_SIZE_MASK)
|
||||
+ 3;
|
||||
if (cb_sb_end > cb_end)
|
||||
goto return_overflow;
|
||||
/* Now, we are ready to process the current sub-block (sb). */
|
||||
if (!(le16_to_cpup((u16*)cb) & NTFS_SB_IS_COMPRESSED)) {
|
||||
if (!(le16_to_cpup((le16*)cb) & NTFS_SB_IS_COMPRESSED)) {
|
||||
ntfs_log_debug("Found uncompressed sub-block.\n");
|
||||
/* This sb is not compressed, just copy it into destination. */
|
||||
/* Advance source position to first data byte. */
|
||||
|
@ -202,7 +459,7 @@ do_next_tag:
|
|||
for (i = dest - dest_sb_start - 1; i >= 0x10; i >>= 1)
|
||||
lg++;
|
||||
/* Get the phrase token into i. */
|
||||
pt = le16_to_cpup((u16*)cb);
|
||||
pt = le16_to_cpup((le16*)cb);
|
||||
/*
|
||||
* Calculate starting position of the byte sequence in
|
||||
* the destination using the fact that p = (pt >> (12 - lg)) + 1
|
||||
|
@ -332,13 +589,19 @@ s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b)
|
|||
u8 *dest, *cb, *cb_pos, *cb_end;
|
||||
u32 cb_size;
|
||||
int err;
|
||||
ATTR_FLAGS data_flags;
|
||||
FILE_ATTR_FLAGS compression;
|
||||
unsigned int nr_cbs, cb_clusters;
|
||||
|
||||
ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, pos 0x%llx, count 0x%llx.\n",
|
||||
(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) {
|
||||
data_flags = na->data_flags;
|
||||
compression = na->ni->flags & FILE_ATTR_COMPRESSED;
|
||||
if (!na || !na->ni || !na->ni->vol || !b
|
||||
|| ((data_flags & ATTR_COMPRESSION_MASK)
|
||||
!= ATTR_IS_COMPRESSED)
|
||||
|| pos < 0 || count < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
@ -379,12 +642,12 @@ s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, void *b)
|
|||
cb_clusters = na->compression_block_clusters;
|
||||
|
||||
/* Need a temporary buffer for each loaded compression block. */
|
||||
cb = ntfs_malloc(cb_size);
|
||||
cb = (u8*)ntfs_malloc(cb_size);
|
||||
if (!cb)
|
||||
return -1;
|
||||
|
||||
/* Need a temporary buffer for each uncompressed block. */
|
||||
dest = ntfs_malloc(cb_size);
|
||||
dest = (u8*)ntfs_malloc(cb_size);
|
||||
if (!dest) {
|
||||
free(cb);
|
||||
return -1;
|
||||
|
@ -450,6 +713,7 @@ do_next_cb:
|
|||
to_read = min(count, cb_size - ofs);
|
||||
ofs += vcn << vol->cluster_size_bits;
|
||||
NAttrClearCompressed(na);
|
||||
na->data_flags &= ~ATTR_COMPRESSION_MASK;
|
||||
tdata_size = na->data_size;
|
||||
tinitialized_size = na->initialized_size;
|
||||
na->data_size = na->initialized_size = na->allocated_size;
|
||||
|
@ -459,7 +723,8 @@ do_next_cb:
|
|||
err = errno;
|
||||
na->data_size = tdata_size;
|
||||
na->initialized_size = tinitialized_size;
|
||||
NAttrSetCompressed(na);
|
||||
na->ni->flags |= compression;
|
||||
na->data_flags = data_flags;
|
||||
free(cb);
|
||||
free(dest);
|
||||
if (total)
|
||||
|
@ -475,7 +740,8 @@ do_next_cb:
|
|||
} while (to_read > 0);
|
||||
na->data_size = tdata_size;
|
||||
na->initialized_size = tinitialized_size;
|
||||
NAttrSetCompressed(na);
|
||||
na->ni->flags |= compression;
|
||||
na->data_flags = data_flags;
|
||||
ofs = 0;
|
||||
} else {
|
||||
s64 tdata_size, tinitialized_size;
|
||||
|
@ -496,6 +762,7 @@ do_next_cb:
|
|||
*/
|
||||
to_read = cb_size;
|
||||
NAttrClearCompressed(na);
|
||||
na->data_flags &= ~ATTR_COMPRESSION_MASK;
|
||||
tdata_size = na->data_size;
|
||||
tinitialized_size = na->initialized_size;
|
||||
na->data_size = na->initialized_size = na->allocated_size;
|
||||
|
@ -507,7 +774,8 @@ do_next_cb:
|
|||
err = errno;
|
||||
na->data_size = tdata_size;
|
||||
na->initialized_size = tinitialized_size;
|
||||
NAttrSetCompressed(na);
|
||||
na->ni->flags |= compression;
|
||||
na->data_flags = data_flags;
|
||||
free(cb);
|
||||
free(dest);
|
||||
if (total)
|
||||
|
@ -520,7 +788,8 @@ do_next_cb:
|
|||
} while (to_read > 0);
|
||||
na->data_size = tdata_size;
|
||||
na->initialized_size = tinitialized_size;
|
||||
NAttrSetCompressed(na);
|
||||
na->ni->flags |= compression;
|
||||
na->data_flags = data_flags;
|
||||
/* Just a precaution. */
|
||||
if (cb_pos + 2 <= cb_end)
|
||||
*(u16*)cb_pos = 0;
|
||||
|
@ -550,3 +819,537 @@ do_next_cb:
|
|||
/* Return number of bytes read. */
|
||||
return total + total2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read data from a set of clusters
|
||||
*
|
||||
* Returns the amount of data read
|
||||
*/
|
||||
|
||||
static int read_clusters(ntfs_volume *vol, const runlist_element *rl,
|
||||
s64 offs, int to_read, char *inbuf)
|
||||
{
|
||||
int count;
|
||||
int got, xgot;
|
||||
s64 xpos;
|
||||
BOOL first;
|
||||
char *xinbuf;
|
||||
const runlist_element *xrl;
|
||||
|
||||
got = 0;
|
||||
xrl = rl;
|
||||
xinbuf = inbuf;
|
||||
first = TRUE;
|
||||
do {
|
||||
count = xrl->length << vol->cluster_size_bits;
|
||||
xpos = xrl->lcn << vol->cluster_size_bits;
|
||||
if (first) {
|
||||
count -= offs;
|
||||
xpos += offs;
|
||||
}
|
||||
if ((to_read - got) < count)
|
||||
count = to_read - got;
|
||||
xgot = ntfs_pread(vol->dev, xpos, count, xinbuf);
|
||||
if (xgot == count) {
|
||||
got += count;
|
||||
xpos += count;
|
||||
xinbuf += count;
|
||||
xrl++;
|
||||
}
|
||||
first = FALSE;
|
||||
} while ((xgot == count) && (got < to_read));
|
||||
return (got);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write data to a set of clusters
|
||||
*
|
||||
* Returns the amount of data written
|
||||
*/
|
||||
|
||||
static int write_clusters(ntfs_volume *vol, const runlist_element *rl,
|
||||
s64 offs, int to_write, const char *outbuf)
|
||||
{
|
||||
int count;
|
||||
int put, xput;
|
||||
s64 xpos;
|
||||
BOOL first;
|
||||
const char *xoutbuf;
|
||||
const runlist_element *xrl;
|
||||
|
||||
put = 0;
|
||||
xrl = rl;
|
||||
xoutbuf = outbuf;
|
||||
first = TRUE;
|
||||
do {
|
||||
count = xrl->length << vol->cluster_size_bits;
|
||||
xpos = xrl->lcn << vol->cluster_size_bits;
|
||||
if (first) {
|
||||
count -= offs;
|
||||
xpos += offs;
|
||||
}
|
||||
if ((to_write - put) < count)
|
||||
count = to_write - put;
|
||||
xput = ntfs_pwrite(vol->dev, xpos, count, xoutbuf);
|
||||
if (xput == count) {
|
||||
put += count;
|
||||
xpos += count;
|
||||
xoutbuf += count;
|
||||
xrl++;
|
||||
}
|
||||
first = FALSE;
|
||||
} while ((xput == count) && (put < to_write));
|
||||
return (put);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compress and write a set of blocks
|
||||
*
|
||||
* returns the size actually written (rounded to a full cluster)
|
||||
* or 0 if could not compress (nothing is written)
|
||||
* or -1 if there were an irrecoverable error (errno set)
|
||||
*/
|
||||
|
||||
static int ntfs_comp_set(ntfs_attr *na, runlist_element *rl,
|
||||
s64 offs, unsigned int insz, const char *inbuf)
|
||||
{
|
||||
ntfs_volume *vol;
|
||||
char *outbuf;
|
||||
unsigned int compsz;
|
||||
int written;
|
||||
int rounded;
|
||||
unsigned int clsz;
|
||||
unsigned int p;
|
||||
unsigned int sz;
|
||||
unsigned int bsz;
|
||||
BOOL fail;
|
||||
|
||||
vol = na->ni->vol;
|
||||
written = 0; /* default return */
|
||||
clsz = 1 << vol->cluster_size_bits;
|
||||
/* may need 2 extra bytes per block and 2 more bytes */
|
||||
outbuf = (char*)ntfs_malloc(na->compression_block_size
|
||||
+ 2*(na->compression_block_size/NTFS_SB_SIZE)
|
||||
+ 2);
|
||||
if (outbuf) {
|
||||
fail = FALSE;
|
||||
compsz = 0;
|
||||
for (p=0; (p<insz) && !fail; p+=NTFS_SB_SIZE) {
|
||||
if ((p + NTFS_SB_SIZE) < insz)
|
||||
bsz = NTFS_SB_SIZE;
|
||||
else
|
||||
bsz = insz - p;
|
||||
sz = ntfs_compress_block(&inbuf[p],bsz,
|
||||
&outbuf[compsz]);
|
||||
/* fail if all the clusters (or more) are needed */
|
||||
if (!sz || ((compsz + sz + clsz + 2)
|
||||
> na->compression_block_size))
|
||||
fail = TRUE;
|
||||
else
|
||||
compsz += sz;
|
||||
}
|
||||
if (!fail) {
|
||||
/* add a couple of null bytes, space has been checked */
|
||||
outbuf[compsz++] = 0;
|
||||
outbuf[compsz++] = 0;
|
||||
/* write a full cluster, to avoid partial reading */
|
||||
rounded = ((compsz - 1) | (clsz - 1)) + 1;
|
||||
written = write_clusters(vol, rl, offs, rounded, outbuf);
|
||||
if (written != rounded) {
|
||||
// previously written text has been spoilt, should return a specific error
|
||||
ntfs_log_error("error writing compressed data\n");
|
||||
errno = EIO;
|
||||
written = -1;
|
||||
}
|
||||
}
|
||||
free(outbuf);
|
||||
}
|
||||
return (written);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free unneeded clusters after compression
|
||||
*
|
||||
* This generally requires an empty slot at the end of runlist,
|
||||
* but we do not want to reallocate the runlist here because
|
||||
* there are many pointers to it.
|
||||
* So the empty slot has to be reserved beforehand
|
||||
*
|
||||
* Returns zero unless some error occurred (described by errno)
|
||||
*/
|
||||
|
||||
static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl,
|
||||
s64 used, s64 reserved)
|
||||
{
|
||||
int freecnt;
|
||||
int usedcnt;
|
||||
int res;
|
||||
s64 freelcn;
|
||||
s64 freevcn;
|
||||
int freelength;
|
||||
ntfs_volume *vol;
|
||||
runlist_element *freerl;
|
||||
|
||||
res = -1; /* default return */
|
||||
vol = na->ni->vol;
|
||||
freecnt = (reserved - used) >> vol->cluster_size_bits;
|
||||
usedcnt = (reserved >> vol->cluster_size_bits) - freecnt;
|
||||
/* skip entries fully used, if any */
|
||||
while (rl->length && (rl->length < usedcnt)) {
|
||||
usedcnt -= rl->length;
|
||||
rl++;
|
||||
}
|
||||
if (rl->length) {
|
||||
/*
|
||||
* Splitting the current allocation block requires
|
||||
* an extra runlist element to create the hole.
|
||||
* The required entry has been prereserved when
|
||||
* mapping the runlist.
|
||||
*/
|
||||
freelcn = rl->lcn + usedcnt;
|
||||
freevcn = rl->vcn + usedcnt;
|
||||
freelength = rl->length - usedcnt;
|
||||
/* new count of allocated clusters */
|
||||
rl->length = usedcnt;
|
||||
freerl = ++rl;
|
||||
if (!((freevcn + freecnt)
|
||||
& (na->compression_block_clusters - 1))) {
|
||||
if (freelength > 0) {
|
||||
/*
|
||||
* move the unused part to the end. Doing so,
|
||||
* the vcn will be out of order. This does
|
||||
* not harm, the vcn are meaningless now, and
|
||||
* only the lcn are meaningful for freeing.
|
||||
*/
|
||||
/* locate current end */
|
||||
while (rl->length)
|
||||
rl++;
|
||||
/* new terminator relocated */
|
||||
rl[1].vcn = rl->vcn;
|
||||
rl[1].lcn = LCN_ENOENT;
|
||||
rl[1].length = 0;
|
||||
/* hole, currently allocated */
|
||||
rl->vcn = freevcn;
|
||||
rl->lcn = freelcn;
|
||||
rl->length = freelength;
|
||||
}
|
||||
/* free the hole */
|
||||
res = ntfs_cluster_free_from_rl(vol,freerl);
|
||||
if (!res) {
|
||||
/* mark hole as free */
|
||||
freerl->lcn = LCN_HOLE;
|
||||
freerl->vcn = freevcn;
|
||||
freerl->length = freecnt;
|
||||
/* and set up the new end */
|
||||
freerl[1].lcn = LCN_ENOENT;
|
||||
freerl[1].vcn = freevcn + freecnt;
|
||||
freerl[1].length = 0;
|
||||
}
|
||||
} else {
|
||||
ntfs_log_error("Bad end of a compression block set\n");
|
||||
errno = EIO;
|
||||
}
|
||||
} else {
|
||||
ntfs_log_error("No cluster to free after compression\n");
|
||||
errno = EIO;
|
||||
}
|
||||
return (res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read existing data, decompress and append buffer
|
||||
* Do nothing if something fails
|
||||
*/
|
||||
|
||||
static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl,
|
||||
s64 offs, int compsz, int pos,
|
||||
char *outbuf, s64 to_write, const void *b)
|
||||
{
|
||||
int fail = 1;
|
||||
char *compbuf;
|
||||
int decompsz;
|
||||
int got;
|
||||
|
||||
compbuf = (char*)ntfs_malloc(compsz);
|
||||
if (compbuf) {
|
||||
/* must align to full block for decompression */
|
||||
decompsz = ((pos - 1) | (NTFS_SB_SIZE - 1)) + 1;
|
||||
got = read_clusters(na->ni->vol, rl, offs, compsz, compbuf);
|
||||
if ((got == compsz)
|
||||
&& !ntfs_decompress((u8*)outbuf,decompsz,
|
||||
(u8*)compbuf,compsz)) {
|
||||
memcpy(&outbuf[pos],b,to_write);
|
||||
fail = 0;
|
||||
}
|
||||
free(compbuf);
|
||||
}
|
||||
return (fail);
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush a full compression block
|
||||
*
|
||||
* returns the size actually written (rounded to a full cluster)
|
||||
* or 0 if could not compress (and written uncompressed)
|
||||
* or -1 if there were an irrecoverable error (errno set)
|
||||
*/
|
||||
|
||||
static int ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs,
|
||||
const char *outbuf, int count, BOOL compress)
|
||||
{
|
||||
int rounded;
|
||||
int written;
|
||||
int clsz;
|
||||
|
||||
if (compress) {
|
||||
written = ntfs_comp_set(na, rl, offs, count, outbuf);
|
||||
if (!written)
|
||||
compress = FALSE;
|
||||
if ((written > 0)
|
||||
&& ntfs_compress_free(na,rl,written,
|
||||
na->compression_block_size))
|
||||
written = -1;
|
||||
}
|
||||
if (!compress) {
|
||||
clsz = 1 << na->ni->vol->cluster_size_bits;
|
||||
rounded = ((count - 1) | (clsz - 1)) + 1;
|
||||
written = write_clusters(na->ni->vol, rl,
|
||||
offs, rounded, outbuf);
|
||||
if (written != rounded)
|
||||
written = -1;
|
||||
}
|
||||
return (written);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write some data to be compressed.
|
||||
* Compression only occurs when a few clusters (usually 16) are
|
||||
* full. When this occurs an extra runlist slot may be needed, so
|
||||
* it has to be reserved beforehand.
|
||||
*
|
||||
* Returns the size of uncompressed data written,
|
||||
* or zero if an error occurred.
|
||||
* When the returned size is less than requested, new clusters have
|
||||
* to be allocated before the function is called again.
|
||||
*/
|
||||
|
||||
s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos,
|
||||
s64 offs, s64 to_write, s64 rounded,
|
||||
const void *b, int compressed_part)
|
||||
{
|
||||
ntfs_volume *vol;
|
||||
runlist_element *brl; /* entry containing the beginning of block */
|
||||
int compression_length;
|
||||
s64 written;
|
||||
s64 to_read;
|
||||
s64 roffs;
|
||||
s64 got;
|
||||
s64 start_vcn;
|
||||
s64 nextblock;
|
||||
int compsz;
|
||||
char *inbuf;
|
||||
char *outbuf;
|
||||
BOOL fail;
|
||||
BOOL done;
|
||||
BOOL compress;
|
||||
|
||||
written = 0; /* default return */
|
||||
vol = na->ni->vol;
|
||||
compression_length = na->compression_block_clusters;
|
||||
compress = FALSE;
|
||||
done = FALSE;
|
||||
/*
|
||||
* Cannot accept writing beyond the current compression set
|
||||
* because when compression occurs, clusters are freed
|
||||
* and have to be reallocated.
|
||||
* (cannot happen with standard fuse 4K buffers)
|
||||
* Caller has to avoid this situation, or face consequences.
|
||||
*/
|
||||
nextblock = ((offs + (wrl->vcn << vol->cluster_size_bits))
|
||||
| (na->compression_block_size - 1)) + 1;
|
||||
if ((offs + to_write + (wrl->vcn << vol->cluster_size_bits))
|
||||
>= nextblock) {
|
||||
/* it is time to compress */
|
||||
compress = TRUE;
|
||||
/* only process what we can */
|
||||
to_write = rounded = nextblock
|
||||
- (offs + (wrl->vcn << vol->cluster_size_bits));
|
||||
}
|
||||
start_vcn = 0;
|
||||
fail = FALSE;
|
||||
brl = wrl;
|
||||
roffs = 0;
|
||||
/*
|
||||
* If we are about to compress or we need to decompress
|
||||
* existing data, we have to process a full set of blocks.
|
||||
* So relocate the parameters to the beginning of allocation
|
||||
* containing the first byte of the set of blocks.
|
||||
*/
|
||||
if (compress || compressed_part) {
|
||||
/* find the beginning of block */
|
||||
start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits))
|
||||
& -compression_length;
|
||||
while (brl->vcn && (brl->vcn > start_vcn)) {
|
||||
/* jumping back a hole means big trouble */
|
||||
if (brl->lcn == (LCN)LCN_HOLE) {
|
||||
ntfs_log_error("jump back over a hole when appending\n");
|
||||
fail = TRUE;
|
||||
errno = EIO;
|
||||
}
|
||||
brl--;
|
||||
offs += brl->length << vol->cluster_size_bits;
|
||||
}
|
||||
roffs = (start_vcn - brl->vcn) << vol->cluster_size_bits;
|
||||
}
|
||||
if (compressed_part && !fail) {
|
||||
/*
|
||||
* The set of compression blocks contains compressed data
|
||||
* (we are reopening an existing file to append to it)
|
||||
* Decompress the data and append
|
||||
*/
|
||||
compsz = compressed_part << vol->cluster_size_bits;
|
||||
// improve the needed size
|
||||
outbuf = (char*)ntfs_malloc(na->compression_block_size);
|
||||
if (outbuf) {
|
||||
to_read = offs - roffs;
|
||||
if (!ntfs_read_append(na, brl, roffs, compsz,
|
||||
to_read, outbuf, to_write, b)) {
|
||||
written = ntfs_flush(na, brl, roffs,
|
||||
outbuf, to_read + to_write, compress);
|
||||
if (written >= 0) {
|
||||
written = to_write;
|
||||
done = TRUE;
|
||||
}
|
||||
}
|
||||
free(outbuf);
|
||||
}
|
||||
} else {
|
||||
if (compress && !fail) {
|
||||
/*
|
||||
* we are filling up a block, read the full set of blocks
|
||||
* and compress it
|
||||
*/
|
||||
inbuf = (char*)ntfs_malloc(na->compression_block_size);
|
||||
if (inbuf) {
|
||||
to_read = offs - roffs;
|
||||
if (to_read)
|
||||
got = read_clusters(vol, brl, roffs,
|
||||
to_read, inbuf);
|
||||
else
|
||||
got = 0;
|
||||
if (got == to_read) {
|
||||
memcpy(&inbuf[to_read],b,to_write);
|
||||
written = ntfs_comp_set(na, brl, roffs,
|
||||
to_read + to_write, inbuf);
|
||||
/*
|
||||
* if compression was not successful,
|
||||
* only write the part which was requested
|
||||
*/
|
||||
if ((written > 0)
|
||||
/* free the unused clusters */
|
||||
&& !ntfs_compress_free(na,brl,
|
||||
written + roffs,
|
||||
na->compression_block_size
|
||||
+ roffs)) {
|
||||
done = TRUE;
|
||||
written = to_write;
|
||||
}
|
||||
}
|
||||
free(inbuf);
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
/*
|
||||
* if the compression block is not full, or
|
||||
* if compression failed for whatever reason,
|
||||
* write uncompressed
|
||||
*/
|
||||
/* check we are not overflowing current allocation */
|
||||
if ((wpos + rounded)
|
||||
> ((wrl->lcn + wrl->length)
|
||||
<< vol->cluster_size_bits)) {
|
||||
ntfs_log_error("writing on unallocated clusters\n");
|
||||
errno = EIO;
|
||||
} else {
|
||||
written = ntfs_pwrite(vol->dev, wpos,
|
||||
rounded, b);
|
||||
if (written == rounded)
|
||||
written = to_write;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (written);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close a file written compressed.
|
||||
* This compresses the last partial compression block of the file.
|
||||
* An empty runlist slot has to be reserved beforehand.
|
||||
*
|
||||
* Returns zero if closing is successful.
|
||||
*/
|
||||
|
||||
int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs)
|
||||
{
|
||||
ntfs_volume *vol;
|
||||
runlist_element *brl; /* entry containing the beginning of block */
|
||||
int compression_length;
|
||||
s64 written;
|
||||
s64 to_read;
|
||||
s64 roffs;
|
||||
s64 got;
|
||||
s64 start_vcn;
|
||||
char *inbuf;
|
||||
BOOL fail;
|
||||
BOOL done;
|
||||
|
||||
vol = na->ni->vol;
|
||||
compression_length = na->compression_block_clusters;
|
||||
done = FALSE;
|
||||
/*
|
||||
* There generally is an uncompressed block at end of file,
|
||||
* read the full block and compress it
|
||||
*/
|
||||
inbuf = (char*)ntfs_malloc(na->compression_block_size);
|
||||
if (inbuf) {
|
||||
start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits))
|
||||
& -compression_length;
|
||||
to_read = offs + ((wrl->vcn - start_vcn) << vol->cluster_size_bits);
|
||||
brl = wrl;
|
||||
fail = FALSE;
|
||||
while (brl->vcn && (brl->vcn > start_vcn)) {
|
||||
if (brl->lcn == (LCN)LCN_HOLE) {
|
||||
ntfs_log_error("jump back over a hole when closing\n");
|
||||
fail = TRUE;
|
||||
errno = EIO;
|
||||
}
|
||||
brl--;
|
||||
}
|
||||
if (!fail) {
|
||||
/* roffs can be an offset from another uncomp block */
|
||||
roffs = (start_vcn - brl->vcn) << vol->cluster_size_bits;
|
||||
if (to_read) {
|
||||
got = read_clusters(vol, brl, roffs, to_read,
|
||||
inbuf);
|
||||
if (got == to_read) {
|
||||
written = ntfs_comp_set(na, brl, roffs,
|
||||
to_read, inbuf);
|
||||
if ((written > 0)
|
||||
/* free the unused clusters */
|
||||
&& !ntfs_compress_free(na,brl,
|
||||
written + roffs,
|
||||
na->compression_block_size + roffs)) {
|
||||
done = TRUE;
|
||||
} else
|
||||
/* if compression failed, leave uncompressed */
|
||||
if (!written)
|
||||
done = TRUE;
|
||||
}
|
||||
} else
|
||||
done = TRUE;
|
||||
free(inbuf);
|
||||
}
|
||||
}
|
||||
return (!done);
|
||||
}
|
||||
|
|
|
@ -1218,6 +1218,9 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid,
|
|||
si->file_attributes = FILE_ATTR_SYSTEM;
|
||||
ni->flags = FILE_ATTR_SYSTEM;
|
||||
}
|
||||
if ((dir_ni->flags & FILE_ATTR_COMPRESSED)
|
||||
&& (S_ISREG(type) || S_ISDIR(type)))
|
||||
ni->flags |= FILE_ATTR_COMPRESSED;
|
||||
/* Add STANDARD_INFORMATION to inode. */
|
||||
if (ntfs_attr_add(ni, AT_STANDARD_INFORMATION, AT_UNNAMED, 0,
|
||||
(u8*)si, si_len)) {
|
||||
|
|
|
@ -111,6 +111,27 @@ static runlist_element *ntfs_rl_realloc(runlist_element *rl, int old_size,
|
|||
return realloc(rl, new_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extend a runlist by some entry count
|
||||
* The runlist may have to be reallocated
|
||||
*
|
||||
* Returns the reallocated runlist
|
||||
* or NULL if reallocation was not possible (with errno set)
|
||||
*/
|
||||
|
||||
runlist_element *ntfs_rl_extend(runlist_element *rl, int more_entries)
|
||||
{
|
||||
int last;
|
||||
|
||||
last = 0;
|
||||
while (rl[last].length)
|
||||
last++;
|
||||
rl = ntfs_rl_realloc(rl,last+1,last+more_entries+1);
|
||||
if (!rl)
|
||||
errno = ENOMEM;
|
||||
return (rl);
|
||||
}
|
||||
|
||||
/**
|
||||
* ntfs_rl_are_mergeable - test if two runlists can be joined together
|
||||
* @dst: original runlist
|
||||
|
|
|
@ -3877,6 +3877,7 @@ int ntfs_set_ntfs_attrib(const char *path __attribute__((unused)),
|
|||
{
|
||||
u32 attrib;
|
||||
le32 settable;
|
||||
ATTR_FLAGS dirflags;
|
||||
int res;
|
||||
|
||||
res = -1;
|
||||
|
@ -3885,12 +3886,31 @@ int ntfs_set_ntfs_attrib(const char *path __attribute__((unused)),
|
|||
/* copy to avoid alignment problems */
|
||||
memcpy(&attrib,value,sizeof(FILE_ATTR_FLAGS));
|
||||
settable = FILE_ATTR_SETTABLE;
|
||||
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
|
||||
settable |= FILE_ATTR_COMPRESSED;
|
||||
ni->flags = (ni->flags & ~settable)
|
||||
| (cpu_to_le32(attrib) & settable);
|
||||
NInoSetDirty(ni);
|
||||
res = 0;
|
||||
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
|
||||
/*
|
||||
* Accept changing compression for a directory
|
||||
* and set index root accordingly
|
||||
*/
|
||||
settable |= FILE_ATTR_COMPRESSED;
|
||||
if ((ni->flags ^ cpu_to_le32(attrib))
|
||||
& FILE_ATTR_COMPRESSED) {
|
||||
if (ni->flags & FILE_ATTR_COMPRESSED)
|
||||
dirflags = const_cpu_to_le16(0);
|
||||
else
|
||||
dirflags = ATTR_IS_COMPRESSED;
|
||||
res = ntfs_attr_set_flags(ni,
|
||||
AT_INDEX_ROOT,
|
||||
NTFS_INDEX_I30, 4,
|
||||
dirflags,
|
||||
ATTR_COMPRESSION_MASK);
|
||||
}
|
||||
}
|
||||
if (!res) {
|
||||
ni->flags = (ni->flags & ~settable)
|
||||
| (cpu_to_le32(attrib) & settable);
|
||||
NInoSetDirty(ni);
|
||||
}
|
||||
} else
|
||||
errno = EEXIST;
|
||||
} else
|
||||
|
@ -4461,6 +4481,7 @@ BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi,
|
|||
{
|
||||
ntfs_inode *ni;
|
||||
le32 settable;
|
||||
ATTR_FLAGS dirflags;
|
||||
int res;
|
||||
|
||||
res = 0; /* default return */
|
||||
|
@ -4468,11 +4489,30 @@ BOOL ntfs_set_file_attributes(struct SECURITY_API *scapi,
|
|||
ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path);
|
||||
if (ni) {
|
||||
settable = FILE_ATTR_SETTABLE;
|
||||
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)
|
||||
if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) {
|
||||
/*
|
||||
* Accept changing compression for a directory
|
||||
* and set index root accordingly
|
||||
*/
|
||||
settable |= FILE_ATTR_COMPRESSED;
|
||||
ni->flags = (ni->flags & ~settable)
|
||||
| (cpu_to_le32(attrib) & settable);
|
||||
NInoSetDirty(ni);
|
||||
if ((ni->flags ^ cpu_to_le32(attrib))
|
||||
& FILE_ATTR_COMPRESSED) {
|
||||
if (ni->flags & FILE_ATTR_COMPRESSED)
|
||||
dirflags = const_cpu_to_le16(0);
|
||||
else
|
||||
dirflags = ATTR_IS_COMPRESSED;
|
||||
res = ntfs_attr_set_flags(ni,
|
||||
AT_INDEX_ROOT,
|
||||
NTFS_INDEX_I30, 4,
|
||||
dirflags,
|
||||
ATTR_COMPRESSION_MASK);
|
||||
}
|
||||
}
|
||||
if (!res) {
|
||||
ni->flags = (ni->flags & ~settable)
|
||||
| (cpu_to_le32(attrib) & settable);
|
||||
NInoSetDirty(ni);
|
||||
}
|
||||
if (!ntfs_inode_close(ni))
|
||||
res = -1;
|
||||
} else
|
||||
|
|
114
src/ntfs-3g.c
114
src/ntfs-3g.c
|
@ -880,6 +880,11 @@ static int ntfs_fuse_open(const char *org_path,
|
|||
if (NAttrEncrypted(na))
|
||||
res = -EACCES;
|
||||
#endif
|
||||
/* mark a future need to compress the last chunk */
|
||||
if ((res >= 0)
|
||||
&& (na->data_flags & ATTR_COMPRESSION_MASK)
|
||||
&& (fi->flags & (O_WRONLY | O_RDWR)))
|
||||
fi->fh |= 1;
|
||||
ntfs_attr_close(na);
|
||||
} else
|
||||
res = -errno;
|
||||
|
@ -978,10 +983,6 @@ static int ntfs_fuse_write(const char *org_path, const char *buf, size_t size,
|
|||
}
|
||||
while (size) {
|
||||
s64 ret = ntfs_attr_pwrite(na, offset, size, buf);
|
||||
if (0 <= ret && ret < (s64)size)
|
||||
ntfs_log_perror("ntfs_attr_pwrite partial write to '%s'"
|
||||
" (%lld: %lld <> %lld)", path, (long long)offset,
|
||||
(long long)size, (long long)ret);
|
||||
if (ret <= 0) {
|
||||
res = -errno;
|
||||
goto exit;
|
||||
|
@ -1005,6 +1006,48 @@ out:
|
|||
return res;
|
||||
}
|
||||
|
||||
static int ntfs_fuse_release(const char *org_path,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
ntfs_inode *ni = NULL;
|
||||
ntfs_attr *na = NULL;
|
||||
char *path = NULL;
|
||||
ntfschar *stream_name;
|
||||
int stream_name_len, res;
|
||||
|
||||
/* Only for marked descriptors there is something to do */
|
||||
if (!(fi->fh & 1)) {
|
||||
res = 0;
|
||||
goto out;
|
||||
}
|
||||
stream_name_len = ntfs_fuse_parse_path(org_path, &path, &stream_name);
|
||||
if (stream_name_len < 0) {
|
||||
res = stream_name_len;
|
||||
goto out;
|
||||
}
|
||||
ni = ntfs_pathname_to_inode(ctx->vol, NULL, path);
|
||||
if (!ni) {
|
||||
res = -errno;
|
||||
goto exit;
|
||||
}
|
||||
na = ntfs_attr_open(ni, AT_DATA, stream_name, stream_name_len);
|
||||
if (!na) {
|
||||
res = -errno;
|
||||
goto exit;
|
||||
}
|
||||
res = ntfs_attr_pclose(na);
|
||||
exit:
|
||||
if (na)
|
||||
ntfs_attr_close(na);
|
||||
if (ntfs_inode_close(ni))
|
||||
set_fuse_error(&res);
|
||||
free(path);
|
||||
if (stream_name_len)
|
||||
free(stream_name);
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Common part for truncate() and ftruncate()
|
||||
*/
|
||||
|
@ -1049,7 +1092,11 @@ static int ntfs_fuse_trunc(const char *org_path, off_t size,
|
|||
goto exit;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* for compressed files, only deleting contents is implemented */
|
||||
if (NAttrCompressed(na) && size) {
|
||||
errno = EOPNOTSUPP;
|
||||
goto exit;
|
||||
}
|
||||
if (ntfs_attr_truncate(na, size))
|
||||
goto exit;
|
||||
|
||||
|
@ -1211,7 +1258,7 @@ static int ntfs_fuse_access(const char *path, int type)
|
|||
#endif
|
||||
|
||||
static int ntfs_fuse_create(const char *org_path, mode_t typemode, dev_t dev,
|
||||
const char *target)
|
||||
const char *target, struct fuse_file_info *fi)
|
||||
{
|
||||
char *name;
|
||||
ntfschar *uname = NULL, *utarget = NULL;
|
||||
|
@ -1325,6 +1372,10 @@ static int ntfs_fuse_create(const char *org_path, mode_t typemode, dev_t dev,
|
|||
security.uid, security.gid, perm) < 0)
|
||||
set_fuse_error(&res);
|
||||
#endif
|
||||
}
|
||||
/* mark a need to compress the end of file */
|
||||
if (fi && (ni->flags & FILE_ATTR_COMPRESSED)) {
|
||||
fi->fh |= 1;
|
||||
}
|
||||
NInoSetDirty(ni);
|
||||
if (ntfs_inode_close(ni))
|
||||
|
@ -1349,7 +1400,8 @@ exit:
|
|||
}
|
||||
|
||||
static int ntfs_fuse_create_stream(const char *path,
|
||||
ntfschar *stream_name, const int stream_name_len)
|
||||
ntfschar *stream_name, const int stream_name_len,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
ntfs_inode *ni;
|
||||
int res = 0;
|
||||
|
@ -1361,11 +1413,13 @@ static int ntfs_fuse_create_stream(const char *path,
|
|||
/*
|
||||
* If such file does not exist, create it and try once
|
||||
* again to add stream to it.
|
||||
* Note : no fuse_file_info for creation of main file
|
||||
*/
|
||||
res = ntfs_fuse_create(path, S_IFREG, 0, NULL);
|
||||
res = ntfs_fuse_create(path, S_IFREG, 0, NULL,
|
||||
(struct fuse_file_info*)NULL);
|
||||
if (!res)
|
||||
return ntfs_fuse_create_stream(path,
|
||||
stream_name, stream_name_len);
|
||||
stream_name, stream_name_len,fi);
|
||||
else
|
||||
res = -errno;
|
||||
}
|
||||
|
@ -1373,12 +1427,16 @@ static int ntfs_fuse_create_stream(const char *path,
|
|||
}
|
||||
if (ntfs_attr_add(ni, AT_DATA, stream_name, stream_name_len, NULL, 0))
|
||||
res = -errno;
|
||||
if ((res >= 0) && (ni->flags & FILE_ATTR_COMPRESSED)
|
||||
&& (fi->flags & (O_WRONLY | O_RDWR)))
|
||||
fi->fh |= 1;
|
||||
if (ntfs_inode_close(ni))
|
||||
set_fuse_error(&res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int ntfs_fuse_mknod_common(const char *org_path, mode_t mode, dev_t dev)
|
||||
static int ntfs_fuse_mknod_common(const char *org_path, mode_t mode, dev_t dev,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
char *path = NULL;
|
||||
ntfschar *stream_name;
|
||||
|
@ -1393,10 +1451,11 @@ static int ntfs_fuse_mknod_common(const char *org_path, mode_t mode, dev_t dev)
|
|||
goto exit;
|
||||
}
|
||||
if (!stream_name_len)
|
||||
res = ntfs_fuse_create(path, mode & (S_IFMT | 07777), dev, NULL);
|
||||
res = ntfs_fuse_create(path, mode & (S_IFMT | 07777), dev,
|
||||
NULL,fi);
|
||||
else
|
||||
res = ntfs_fuse_create_stream(path, stream_name,
|
||||
stream_name_len);
|
||||
stream_name_len,fi);
|
||||
exit:
|
||||
free(path);
|
||||
if (stream_name_len)
|
||||
|
@ -1406,20 +1465,22 @@ exit:
|
|||
|
||||
static int ntfs_fuse_mknod(const char *path, mode_t mode, dev_t dev)
|
||||
{
|
||||
return ntfs_fuse_mknod_common(path, mode, dev);
|
||||
return ntfs_fuse_mknod_common(path, mode, dev,
|
||||
(struct fuse_file_info*)NULL);
|
||||
}
|
||||
|
||||
static int ntfs_fuse_create_file(const char *path, mode_t mode,
|
||||
struct fuse_file_info *fi __attribute__((unused)))
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
return ntfs_fuse_mknod_common(path, mode, 0);
|
||||
return ntfs_fuse_mknod_common(path, mode, 0, fi);
|
||||
}
|
||||
|
||||
static int ntfs_fuse_symlink(const char *to, const char *from)
|
||||
{
|
||||
if (ntfs_fuse_is_named_data_stream(from))
|
||||
return -EINVAL; /* n/a for named data streams. */
|
||||
return ntfs_fuse_create(from, S_IFLNK, 0, to);
|
||||
return ntfs_fuse_create(from, S_IFLNK, 0, to,
|
||||
(struct fuse_file_info*)NULL);
|
||||
}
|
||||
|
||||
static int ntfs_fuse_link(const char *old_path, const char *new_path)
|
||||
|
@ -1754,7 +1815,8 @@ static int ntfs_fuse_mkdir(const char *path,
|
|||
{
|
||||
if (ntfs_fuse_is_named_data_stream(path))
|
||||
return -EINVAL; /* n/a for named data streams. */
|
||||
return ntfs_fuse_create(path, S_IFDIR | (mode & 07777), 0, NULL);
|
||||
return ntfs_fuse_create(path, S_IFDIR | (mode & 07777), 0, NULL,
|
||||
(struct fuse_file_info*)NULL);
|
||||
}
|
||||
|
||||
static int ntfs_fuse_rmdir(const char *path)
|
||||
|
@ -2357,6 +2419,7 @@ static int ntfs_fuse_setxattr(const char *path, const char *name,
|
|||
ntfs_attr *na = NULL;
|
||||
ntfschar *lename = NULL;
|
||||
int res, lename_len;
|
||||
size_t part, total;
|
||||
int attr;
|
||||
int namespace;
|
||||
struct SECURITY_CONTEXT security;
|
||||
|
@ -2522,15 +2585,25 @@ static int ntfs_fuse_setxattr(const char *path, const char *name,
|
|||
goto exit;
|
||||
}
|
||||
} else {
|
||||
if (ntfs_attr_truncate(na, (s64)size)) {
|
||||
/* currently compressed streams can only be wiped out */
|
||||
if (ntfs_attr_truncate(na, (s64)0 /* size */)) {
|
||||
res = -errno;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
res = ntfs_attr_pwrite(na, 0, size, value);
|
||||
if (res != (s64) size)
|
||||
total = 0;
|
||||
if (size) {
|
||||
do {
|
||||
part = ntfs_attr_pwrite(na, total, size - total,
|
||||
&value[total]);
|
||||
if (part > 0)
|
||||
total += part;
|
||||
} while ((part > 0) && (total < size));
|
||||
if (total != size)
|
||||
res = -errno;
|
||||
else
|
||||
res = ntfs_attr_pclose(na);
|
||||
} else
|
||||
res = 0;
|
||||
exit:
|
||||
if (na)
|
||||
|
@ -2754,6 +2827,7 @@ static struct fuse_operations ntfs_3g_ops = {
|
|||
.readlink = ntfs_fuse_readlink,
|
||||
.readdir = ntfs_fuse_readdir,
|
||||
.open = ntfs_fuse_open,
|
||||
.release = ntfs_fuse_release,
|
||||
.read = ntfs_fuse_read,
|
||||
.write = ntfs_fuse_write,
|
||||
.truncate = ntfs_fuse_truncate,
|
||||
|
|
Loading…
Reference in New Issue